diff --git a/.eslintrc.js b/.eslintrc.js index 82dfe9e9bd..6183fa8aec 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -72,6 +72,6 @@ module.exports = { "spaced-comment": ["error", "always", { "line": { "markers": ["/"] } }], - "space-before-function-paren": ["error", {"anonymous": "always", "named": "never"}] + "space-before-function-paren": ["error", {"anonymous": "ignore", "named": "never"}] } }; diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d42be3d95..a167207683 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,9 @@ else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter") if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion") + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "5.1") # gcc 5.1 and on have suggest-override + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override") + endif () endif () endif(WIN32) diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 63d4cfa4d6..3f4e36374c 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -52,10 +52,10 @@ public: float getLastReceivedAudioLoudness() const { return _lastReceivedAudioLoudness; } QUuid getSessionUUID() const; - virtual void aboutToFinish(); + virtual void aboutToFinish() override; public slots: - void run(); + void run() override; void playAvatarSound(SharedSoundPointer avatarSound) { setAvatarSound(avatarSound); } private slots: diff --git a/assignment-client/src/AssignmentAction.h b/assignment-client/src/AssignmentAction.h index 6b55b280bb..98504b3545 100644 --- a/assignment-client/src/AssignmentAction.h +++ b/assignment-client/src/AssignmentAction.h @@ -24,27 +24,27 @@ public: AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity); virtual ~AssignmentAction(); - virtual void removeFromSimulation(EntitySimulationPointer simulation) const; - virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; } - virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; } - virtual bool updateArguments(QVariantMap arguments); - virtual QVariantMap getArguments(); + virtual void removeFromSimulation(EntitySimulationPointer simulation) const override; + virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; } + virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; } + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; - virtual QByteArray serialize() const; - virtual void deserialize(QByteArray serializedArguments); + virtual QByteArray serialize() const override; + virtual void deserialize(QByteArray serializedArguments) override; private: QByteArray _data; protected: - virtual glm::vec3 getPosition(); - virtual void setPosition(glm::vec3 position); - virtual glm::quat getRotation(); - virtual void setRotation(glm::quat rotation); - virtual glm::vec3 getLinearVelocity(); - virtual void setLinearVelocity(glm::vec3 linearVelocity); - virtual glm::vec3 getAngularVelocity(); - virtual void setAngularVelocity(glm::vec3 angularVelocity); + virtual glm::vec3 getPosition() override; + virtual void setPosition(glm::vec3 position) override; + virtual glm::quat getRotation() override; + virtual void setRotation(glm::quat rotation) override; + virtual glm::vec3 getLinearVelocity() override; + virtual void setLinearVelocity(glm::vec3 linearVelocity) override; + virtual glm::vec3 getAngularVelocity() override; + virtual void setAngularVelocity(glm::vec3 angularVelocity) override; bool _active; EntityItemWeakPointer _ownerEntity; diff --git a/assignment-client/src/AssignmentActionFactory.h b/assignment-client/src/AssignmentActionFactory.h index e2d58f3e6a..87970c9431 100644 --- a/assignment-client/src/AssignmentActionFactory.h +++ b/assignment-client/src/AssignmentActionFactory.h @@ -22,8 +22,8 @@ public: virtual EntityActionPointer factory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity, - QVariantMap arguments); - virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data); + QVariantMap arguments) override; + virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override; }; #endif // hifi_AssignmentActionFactory_h diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 07ff0a92b3..132fb51433 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -26,7 +26,7 @@ public: AssetServer(ReceivedMessage& message); public slots: - void run(); + void run() override; private slots: void completeSetup(); @@ -35,9 +35,9 @@ private slots: void handleAssetGet(QSharedPointer packet, SharedNodePointer senderNode); void handleAssetUpload(QSharedPointer packetList, SharedNodePointer senderNode); void handleAssetMappingOperation(QSharedPointer message, SharedNodePointer senderNode); - - void sendStatsPacket(); - + + void sendStatsPacket() override; + private: using Mappings = QVariantHash; diff --git a/assignment-client/src/assets/SendAssetTask.h b/assignment-client/src/assets/SendAssetTask.h index 9fef8662d4..38da5e728b 100644 --- a/assignment-client/src/assets/SendAssetTask.h +++ b/assignment-client/src/assets/SendAssetTask.h @@ -27,7 +27,7 @@ class SendAssetTask : public QRunnable { public: SendAssetTask(QSharedPointer message, const SharedNodePointer& sendToNode, const QDir& resourcesDir); - void run(); + void run() override; private: QSharedPointer _message; diff --git a/assignment-client/src/assets/UploadAssetTask.h b/assignment-client/src/assets/UploadAssetTask.h index aa7beccd5c..700eecbf9a 100644 --- a/assignment-client/src/assets/UploadAssetTask.h +++ b/assignment-client/src/assets/UploadAssetTask.h @@ -27,9 +27,9 @@ class Node; class UploadAssetTask : public QRunnable { public: UploadAssetTask(QSharedPointer message, QSharedPointer senderNode, const QDir& resourcesDir); - - void run(); - + + void run() override; + private: QSharedPointer _receivedMessage; QSharedPointer _senderNode; diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 9b26989847..7e3ef8a69b 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -35,9 +35,9 @@ public: public slots: /// threaded run of assignment - void run(); + void run() override; - void sendStatsPacket(); + void sendStatsPacket() override; static const InboundAudioStream::Settings& getStreamSettings() { return _streamSettings; } diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 106841ee03..2f8ff4d049 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -49,17 +49,17 @@ public: // removes an AudioHRTF object for a given stream void removeHRTFForStream(const QUuid& nodeID, const QUuid& streamID = QUuid()); - - int parseData(ReceivedMessage& message); + + int parseData(ReceivedMessage& message) override; void checkBuffersBeforeFrameSend(); void removeDeadInjectedStreams(); QJsonObject getAudioStreamStats(); - + void sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode); - + void incrementOutgoingMixedAudioSequenceNumber() { _outgoingMixedAudioSequenceNumber++; } quint16 getOutgoingSequenceNumber() const { return _outgoingMixedAudioSequenceNumber; } diff --git a/assignment-client/src/audio/AvatarAudioStream.h b/assignment-client/src/audio/AvatarAudioStream.h index cc2ff1aca7..d2e1137ae6 100644 --- a/assignment-client/src/audio/AvatarAudioStream.h +++ b/assignment-client/src/audio/AvatarAudioStream.h @@ -25,7 +25,7 @@ private: AvatarAudioStream(const AvatarAudioStream&); AvatarAudioStream& operator= (const AvatarAudioStream&); - int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples); + int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) override; }; #endif // hifi_AvatarAudioStream_h diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 00cf457d40..9286cd4691 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -27,11 +27,11 @@ public: ~AvatarMixer(); public slots: /// runs the avatar mixer - void run(); + void run() override; void nodeKilled(SharedNodePointer killedNode); - - void sendStatsPacket(); + + void sendStatsPacket() override; private slots: void handleAvatarDataPacket(QSharedPointer message, SharedNodePointer senderNode); @@ -45,14 +45,14 @@ private slots: private: void broadcastAvatarData(); void parseDomainServerSettings(const QJsonObject& domainSettings); - + QThread _broadcastThread; - + p_high_resolution_clock::time_point _lastFrameTimestamp; - + float _trailingSleepRatio { 1.0f }; float _performanceThrottlingRatio { 0.0f }; - + int _sumListeners { 0 }; int _numStatFrames { 0 }; int _sumIdentityPackets { 0 }; diff --git a/assignment-client/src/entities/AssignmentParentFinder.h b/assignment-client/src/entities/AssignmentParentFinder.h index 0c16143143..0ac915817c 100644 --- a/assignment-client/src/entities/AssignmentParentFinder.h +++ b/assignment-client/src/entities/AssignmentParentFinder.h @@ -25,7 +25,8 @@ class AssignmentParentFinder : public SpatialParentFinder { public: AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { } virtual ~AssignmentParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, + SpatialParentTree* entityTree = nullptr) const override; protected: EntityTreePointer _tree; diff --git a/assignment-client/src/entities/EntityNodeData.h b/assignment-client/src/entities/EntityNodeData.h index 0ca0834fef..e1f8a91030 100644 --- a/assignment-client/src/entities/EntityNodeData.h +++ b/assignment-client/src/entities/EntityNodeData.h @@ -18,7 +18,7 @@ class EntityNodeData : public OctreeQueryNode { public: - virtual PacketType getMyPacketType() const { return PacketType::EntityData; } + virtual PacketType getMyPacketType() const override { return PacketType::EntityData; } quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; } void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; } diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 76ae2f7195..800d42199b 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -24,9 +24,9 @@ public: MessagesMixer(ReceivedMessage& message); public slots: - void run(); + void run() override; void nodeKilled(SharedNodePointer killedNode); - void sendStatsPacket(); + void sendStatsPacket() override; private slots: void handleMessages(QSharedPointer message, SharedNodePointer senderNode); diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.h b/assignment-client/src/octree/OctreeInboundPacketProcessor.h index 8d0245c2b1..4611fcada0 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.h +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.h @@ -74,15 +74,15 @@ public: NodeToSenderStatsMap getSingleSenderStats() { QReadLocker locker(&_senderStatsLock); return _singleSenderStats; } - virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); } + virtual void terminating() override { _shuttingDown = true; ReceivedPacketProcessor::terminating(); } protected: - virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode); + virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode) override; - virtual unsigned long getMaxWait() const; - virtual void preProcess(); - virtual void midProcess(); + virtual unsigned long getMaxWait() const override; + virtual void preProcess() override; + virtual void midProcess() override; private: int sendNackPackets(); diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 166171c7f9..7efe5b3a86 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -47,7 +47,7 @@ public: protected: /// Implements generic processing behavior for this thread. - virtual bool process(); + virtual bool process() override; private: int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent, bool dontSuppressDuplicate = false); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index d763d1abe7..48ffc2fdbc 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1063,6 +1063,12 @@ void OctreeServer::readConfiguration() { _wantBackup = !noBackup; qDebug() << "wantBackup=" << _wantBackup; + if (!readOptionString("backupDirectoryPath", settingsSectionObject, _backupDirectoryPath)) { + _backupDirectoryPath = ""; + } + + qDebug() << "backupDirectoryPath=" << _backupDirectoryPath; + readOptionBool(QString("persistFileDownload"), settingsSectionObject, _persistFileDownload); qDebug() << "persistFileDownload=" << _persistFileDownload; @@ -1160,25 +1166,25 @@ void OctreeServer::domainSettingsRequestComplete() { // If persist filename does not exist, let's see if there is one beside the application binary // If there is, let's copy it over to our target persist directory QDir persistPath { _persistFilePath }; - QString absoluteFilePath = persistPath.absolutePath(); + QString persistAbsoluteFilePath = persistPath.absolutePath(); if (persistPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - absoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); + persistAbsoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; // force the persist file to end with .json.gz - if (!absoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) { - absoluteFilePath += ENTITY_PERSIST_EXTENSION; + if (!persistAbsoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) { + persistAbsoluteFilePath += ENTITY_PERSIST_EXTENSION; } else { // make sure the casing of .json.gz is correct - absoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive); + persistAbsoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive); } - if (!QFile::exists(absoluteFilePath)) { + if (!QFile::exists(persistAbsoluteFilePath)) { qDebug() << "Persist file does not exist, checking for existence of persist file next to application"; static const QString OLD_DEFAULT_PERSIST_FILENAME = "resources/models.json.gz"; @@ -1204,7 +1210,7 @@ void OctreeServer::domainSettingsRequestComplete() { pathToCopyFrom = oldDefaultPersistPath; } - QDir persistFileDirectory { QDir::cleanPath(absoluteFilePath + "/..") }; + QDir persistFileDirectory { QDir::cleanPath(persistAbsoluteFilePath + "/..") }; if (!persistFileDirectory.exists()) { qDebug() << "Creating data directory " << persistFileDirectory.absolutePath(); @@ -1212,16 +1218,46 @@ void OctreeServer::domainSettingsRequestComplete() { } if (shouldCopy) { - qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << absoluteFilePath; + qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistAbsoluteFilePath; - QFile::copy(pathToCopyFrom, absoluteFilePath); + QFile::copy(pathToCopyFrom, persistAbsoluteFilePath); } else { qDebug() << "No existing persist file found"; } } + + auto persistFileDirectory = QFileInfo(persistAbsoluteFilePath).absolutePath(); + if (_backupDirectoryPath.isEmpty()) { + // Use the persist file's directory to store backups + _backupDirectoryPath = persistFileDirectory; + } else { + // The backup directory has been set. + // If relative, make it relative to the entities directory in the application data directory + // If absolute, no resolution is necessary + QDir backupDirectory { _backupDirectoryPath }; + QString absoluteBackupDirectory; + if (backupDirectory.isRelative()) { + absoluteBackupDirectory = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); + absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath(); + } else { + absoluteBackupDirectory = backupDirectory.absolutePath(); + } + backupDirectory = QDir(absoluteBackupDirectory); + if (!backupDirectory.exists()) { + if (backupDirectory.mkpath(".")) { + qDebug() << "Created backup directory"; + } else { + qDebug() << "ERROR creating backup directory, using persist file directory"; + _backupDirectoryPath = persistFileDirectory; + } + } else { + _backupDirectoryPath = absoluteBackupDirectory; + } + } + qDebug() << "Backups will be stored in: " << _backupDirectoryPath; // now set up PersistThread - _persistThread = new OctreePersistThread(_tree, absoluteFilePath, _persistInterval, + _persistThread = new OctreePersistThread(_tree, persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval, _wantBackup, _settings, _debugTimestampNow, _persistAsFileType); _persistThread->initialize(true); } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index d153f31154..2bcf36628d 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -120,16 +120,16 @@ public: static int howManyThreadsDidHandlePacketSend(quint64 since = 0); static int howManyThreadsDidCallWriteDatagram(quint64 since = 0); - bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) override; - virtual void aboutToFinish(); + virtual void aboutToFinish() override; public slots: /// runs the octree server assignment - void run(); + void run() override; virtual void nodeAdded(SharedNodePointer node); virtual void nodeKilled(SharedNodePointer node); - void sendStatsPacket(); + void sendStatsPacket() override; private slots: void domainSettingsRequestComplete(); @@ -172,6 +172,7 @@ protected: QString _persistFilePath; QString _persistAsFileType; + QString _backupDirectoryPath; int _packetsPerClientPerInterval; int _packetsTotalPerInterval; OctreePointer _tree; // this IS a reaveraging tree diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt index 930a339d12..1cd4c071f1 100644 --- a/cmake/externals/openvr/CMakeLists.txt +++ b/cmake/externals/openvr/CMakeLists.txt @@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://github.com/ValveSoftware/openvr/archive/v0.9.19.zip - URL_MD5 843f9dde488584d8af1f3ecf2252b4e0 + URL https://github.com/ValveSoftware/openvr/archive/v1.0.2.zip + URL_MD5 0d1cf5f579cf092e33f34759967b7046 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index dfa59943d6..e586304503 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -23,13 +23,13 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) #Extract the unique include shader paths set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}) - #message(Hifi for includes ${INCLUDES}) - foreach(EXTRA_SHADER_INCLUDE ${INCLUDES}) + #message(${TARGET_NAME} Hifi for includes ${INCLUDES}) + foreach(EXTRA_SHADER_INCLUDE ${INCLUDES}) list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE}) endforeach() list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS) - #message(ready for includes ${SHADER_INCLUDES_PATHS}) + #message(ready for includes ${SHADER_INCLUDES_PATHS}) # make the scribe include arguments set(SCRIBE_INCLUDES) @@ -77,6 +77,7 @@ endfunction() macro(AUTOSCRIBE_SHADER_LIB) + set(HIFI_LIBRARIES_SHADER_INCLUDE_FILES "") file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}") foreach(HIFI_LIBRARY ${ARGN}) #if (NOT TARGET ${HIFI_LIBRARY}) @@ -86,7 +87,7 @@ macro(AUTOSCRIBE_SHADER_LIB) #file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src/*.slh) list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src) endforeach() - #message(${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}) + #message("${TARGET_NAME} ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}") file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh) file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf src/*.slg) @@ -95,13 +96,14 @@ macro(AUTOSCRIBE_SHADER_LIB) set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}") file(MAKE_DIRECTORY ${SHADERS_DIR}) - #message(${SHADER_INCLUDE_FILES}) + #message("${TARGET_NAME} ${SHADER_INCLUDE_FILES}") + set(AUTOSCRIBE_SHADER_SRC "") foreach(SHADER_FILE ${SHADER_SOURCE_FILES}) AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES}) file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE) list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE}) endforeach() - #message(${AUTOSCRIBE_SHADER_SRC}) + #message(${TARGET_NAME} ${AUTOSCRIBE_SHADER_SRC}) if (WIN32) source_group("Shaders" FILES ${SHADER_INCLUDE_FILES}) @@ -116,4 +118,4 @@ macro(AUTOSCRIBE_SHADER_LIB) # Link library shaders, if they exist include_directories("${SHADERS_DIR}") -endmacro() +endmacro() \ No newline at end of file diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index 26c769c6e6..a10c7c11e6 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -54,8 +54,9 @@ macro(SETUP_HIFI_LIBRARY) target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) endforeach() - # Don't make scribed shaders cumulative + # Don't make scribed shaders or QT resource files cumulative set(AUTOSCRIBE_SHADER_LIB_SRC "") + set(QT_RESOURCES_FILE "") target_glm() diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index c888fa301b..c9d7ea77d5 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1108,6 +1108,14 @@ "default": "models.json.gz", "advanced": true }, + { + "name": "backupDirectoryPath", + "label": "Entities Backup Directory Path", + "help": "The path to the directory to store backups in.
If this path is relative it will be relative to the application data directory.", + "placeholder": "", + "default": "", + "advanced": true + }, { "name": "persistInterval", "label": "Save Check Interval", diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index c827e79223..23a53c3eb0 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -509,9 +509,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, } } else { if (!senderSockAddr.isNull()) { - qDebug() << "Insufficient data to decrypt username signature - denying connection."; - sendConnectionDeniedPacket("Insufficient data", senderSockAddr, - DomainHandler::ConnectionRefusedReason::LoginError); + qDebug() << "Insufficient data to decrypt username signature - delaying connection."; } } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 6c4b12d4c0..cbf533bf64 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -118,8 +118,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : setupNodeListAndAssignments(); - if (_type == MetaverseDomain) { - // if we have a metaverse domain, we'll need an access token to heartbeat handle auto-networking + if (_type != NonMetaverse) { + // if we have a metaverse domain, we'll use an access token for API calls resetAccountManagerAccessToken(); } @@ -469,8 +469,8 @@ bool DomainServer::resetAccountManagerAccessToken() { if (accessTokenVariant && accessTokenVariant->canConvert(QMetaType::QString)) { accessToken = accessTokenVariant->toString(); } else { - qDebug() << "A domain-server feature that requires authentication is enabled but no access token is present."; - qDebug() << "Set an access token via the web interface, in your user or master config" + qWarning() << "No access token is present. Some operations that use the metaverse API will fail."; + qDebug() << "Set an access token via the web interface, in your user config" << "at keypath metaverse.access_token or in your ENV at key DOMAIN_SERVER_ACCESS_TOKEN"; // clear any existing access token from AccountManager @@ -480,7 +480,7 @@ bool DomainServer::resetAccountManagerAccessToken() { } } else { qDebug() << "Using access token from DOMAIN_SERVER_ACCESS_TOKEN in env. This overrides any access token present" - << " in the user or master config."; + << " in the user config."; } // give this access token to the AccountManager @@ -1233,7 +1233,10 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() { callbackParameters.errorCallbackReceiver = this; callbackParameters.errorCallbackMethod = "handleFailedICEServerAddressUpdate"; - qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" << _iceServerSocket.getAddress().toString(); + static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex + ("Updating ice-server address in High Fidelity Metaverse API to [^ \n]+"); + qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" + << _iceServerSocket.getAddress().toString(); static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address"; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 06b3ed2c0a..e30f4515cc 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -50,8 +50,8 @@ public: static int const EXIT_CODE_REBOOT; - bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); - bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; + bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false) override; public slots: /// Called by NodeList to inform us a node has been added diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 47187fac5c..c7944bbcad 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -532,9 +532,12 @@ void DomainServerSettingsManager::unpackPermissions() { // we don't have permissions for one of the standard groups, so we'll add them now NodePermissionsPointer perms { new NodePermissions(standardKey) }; - // the localhost user is granted all permissions by default if (standardKey == NodePermissions::standardNameLocalhost) { + // the localhost user is granted all permissions by default perms->setAll(true); + } else { + // anonymous, logged in, and friend users get connect permissions by default + perms->set(NodePermissions::Permission::canConnectToDomain); } // add the permissions to the standard map diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 0037e3741d..8baf56684a 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -63,6 +63,19 @@ ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] ] }, + "when": "Application.CameraFirstPerson", + "to": "Actions.Yaw" + }, + { "from": { "makeAxis" : [ + ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], + ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] + ] + }, + "when": "Application.CameraThirdPerson", + "to": "Actions.Yaw" + }, + { "from": { "makeAxis" : [ ["Keyboard.A"], ["Keyboard.D"] ] }, + "when": "Application.CameraFSM", "to": "Actions.Yaw" }, @@ -81,9 +94,10 @@ { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, { "from": "Keyboard.Up", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, - - { "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" }, - { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.Down", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, diff --git a/interface/resources/controllers/oculus_touch.json b/interface/resources/controllers/oculus_touch.json index 82f52e50db..c50f7681d9 100644 --- a/interface/resources/controllers/oculus_touch.json +++ b/interface/resources/controllers/oculus_touch.json @@ -1,8 +1,13 @@ { "name": "Oculus Touch to Standard", "channels": [ - { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb" }, - { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb" }, + { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb", "peek": true }, + { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb", "peek": true }, + + { "from": "OculusTouch.A", "to": "Standard.A" }, + { "from": "OculusTouch.B", "to": "Standard.B" }, + { "from": "OculusTouch.X", "to": "Standard.X" }, + { "from": "OculusTouch.Y", "to": "Standard.Y" }, { "from": "OculusTouch.LY", "to": "Standard.LY", "filters": [ @@ -17,7 +22,11 @@ }, { "from": "OculusTouch.LT", "to": "Standard.LT" }, { "from": "OculusTouch.LS", "to": "Standard.LS" }, - { "from": "OculusTouch.LeftGrip", "to": "Standard.LeftGrip" }, + { "from": "OculusTouch.LeftGrip", "to": "Standard.LTClick", + "peek": true, + "filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ] + }, + { "from": "OculusTouch.LeftGrip", "to": "Standard.LT" }, { "from": "OculusTouch.LeftHand", "to": "Standard.LeftHand" }, { "from": "OculusTouch.RY", "to": "Standard.RY", @@ -33,7 +42,11 @@ }, { "from": "OculusTouch.RT", "to": "Standard.RT" }, { "from": "OculusTouch.RS", "to": "Standard.RS" }, - { "from": "OculusTouch.RightGrip", "to": "Standard.RightGrip" }, + { "from": "OculusTouch.RightGrip", "to": "Standard.LTClick", + "peek": true, + "filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ] + }, + { "from": "OculusTouch.RightGrip", "to": "Standard.RT" }, { "from": "OculusTouch.RightHand", "to": "Standard.RightHand" }, { "from": "OculusTouch.LeftApplicationMenu", "to": "Standard.Back" }, @@ -54,4 +67,3 @@ ] } - diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 5c0ea09939..c9e91c8666 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -11,7 +11,7 @@ [ { "type": "deadZone", "min": 0.15 }, "constrainToInteger", - { "type": "pulse", "interval": 0.5 }, + { "type": "pulse", "interval": 0.25 }, { "type": "scale", "scale": 22.5 } ] }, @@ -32,9 +32,6 @@ { "from": "Standard.Back", "to": "Actions.CycleCamera" }, { "from": "Standard.Start", "to": "Actions.ContextMenu" }, - { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, - { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" }, - { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, { "from": "Standard.RT", "to": "Actions.RightHandClick" }, diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 79114b8141..dce3e9660c 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -4,8 +4,8 @@ { "from": "Vive.LY", "when": "Vive.LSY", "filters": ["invert"], "to": "Standard.LY" }, { "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" }, { - "from": "Vive.LT", "to": "Standard.LT", - "filters": [ + "from": "Vive.LT", "to": "Standard.LT", + "filters": [ { "type": "deadZone", "min": 0.05 } ] }, @@ -18,8 +18,8 @@ { "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" }, { "from": "Vive.RX", "when": "Vive.RSX", "to": "Standard.RX" }, { - "from": "Vive.RT", "to": "Standard.RT", - "filters": [ + "from": "Vive.RT", "to": "Standard.RT", + "filters": [ { "type": "deadZone", "min": 0.05 } ] }, @@ -37,4 +37,4 @@ { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, { "from": "Vive.RightHand", "to": "Standard.RightHand" } ] -} +} \ No newline at end of file diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json index fdac70ff33..1234372a7e 100644 --- a/interface/resources/controllers/xbox.json +++ b/interface/resources/controllers/xbox.json @@ -15,12 +15,14 @@ { "from": "GamePad.Back", "to": "Standard.Back" }, { "from": "GamePad.Start", "to": "Standard.Start" }, - + + { "from": [ "GamePad.DU", "GamePad.DL", "GamePad.DR", "GamePad.DD" ], "to": "Standard.LeftPrimaryThumb", "peek": true }, { "from": "GamePad.DU", "to": "Standard.DU" }, { "from": "GamePad.DD", "to": "Standard.DD" }, { "from": "GamePad.DL", "to": "Standard.DL" }, { "from": "GamePad.DR", "to": "Standard.DR" }, + { "from": [ "GamePad.A", "GamePad.B", "GamePad.X", "GamePad.Y" ], "to": "Standard.RightPrimaryThumb", "peek": true }, { "from": "GamePad.A", "to": "Standard.A" }, { "from": "GamePad.B", "to": "Standard.B" }, { "from": "GamePad.X", "to": "Standard.X" }, diff --git a/interface/resources/images/Default-Sky-9-ambient.jpg b/interface/resources/images/Default-Sky-9-ambient.jpg new file mode 100644 index 0000000000..8fb383c5e8 Binary files /dev/null and b/interface/resources/images/Default-Sky-9-ambient.jpg differ diff --git a/interface/resources/images/Default-Sky-9-cubemap.jpg b/interface/resources/images/Default-Sky-9-cubemap.jpg new file mode 100644 index 0000000000..697fd9aeea Binary files /dev/null and b/interface/resources/images/Default-Sky-9-cubemap.jpg differ diff --git a/interface/resources/images/preview.png b/interface/resources/images/preview.png index faebbfce8f..85a62f98bd 100644 Binary files a/interface/resources/images/preview.png and b/interface/resources/images/preview.png differ diff --git a/interface/resources/images/steam-sign-in.png b/interface/resources/images/steam-sign-in.png new file mode 100644 index 0000000000..148e6ab280 Binary files /dev/null and b/interface/resources/images/steam-sign-in.png differ diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 1ad2d1a1e4..050bc8e99e 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -521,14 +521,15 @@ ScrollingWindow { anchors.fill: parent acceptedButtons: Qt.RightButton onClicked: { - var index = treeView.indexAt(mouse.x, mouse.y); - - treeView.selection.setCurrentIndex(index, 0x0002); - - contextMenu.currentIndex = index; - contextMenu.popup(); + if (!HMD.active) { // Popup only displays properly on desktop + var index = treeView.indexAt(mouse.x, mouse.y); + treeView.selection.setCurrentIndex(index, 0x0002); + contextMenu.currentIndex = index; + contextMenu.popup(); + } } } + } HifiControls.ContentSection { id: uploadSection diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index f75e83e36e..1f84024e15 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -10,296 +10,70 @@ import Hifi 1.0 import QtQuick 2.4 -import "controls" -import "styles" + +import "controls-uit" +import "styles-uit" import "windows" -ScrollingWindow { +import "LoginDialog" + +ModalWindow { id: root HifiConstants { id: hifi } objectName: "LoginDialog" - height: loginDialog.implicitHeight - width: loginDialog.implicitWidth - // FIXME make movable - anchors.centerIn: parent - destroyOnHidden: false - hideBackground: true - shown: false + implicitWidth: 520 + implicitHeight: 320 + destroyOnCloseButton: true + destroyOnHidden: true + visible: true + + property string iconText: "" + property int iconSize: 50 + + property string title: "" + property int titleWidth: 0 LoginDialog { id: loginDialog - implicitWidth: backgroundRectangle.width - implicitHeight: backgroundRectangle.height - readonly property int inputWidth: 500 - readonly property int inputHeight: 60 - readonly property int borderWidth: 30 - readonly property int closeMargin: 16 - readonly property real tan30: 0.577 // tan(30°) - readonly property int inputSpacing: 16 - Rectangle { - id: backgroundRectangle - width: loginDialog.inputWidth + loginDialog.borderWidth * 2 - height: loginDialog.inputHeight * 6 + loginDialog.closeMargin * 2 - radius: loginDialog.closeMargin * 2 - color: "#2c86b1" - opacity: 0.85 - } - - Column { - id: mainContent - width: loginDialog.inputWidth - spacing: loginDialog.inputSpacing - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: parent.verticalCenter - } - - Item { - // Offset content down a little - width: loginDialog.inputWidth - height: loginDialog.closeMargin - } - - Rectangle { - width: loginDialog.inputWidth - height: loginDialog.inputHeight - radius: height / 2 - color: "#ebebeb" - - Image { - source: "../images/login-username.svg" - width: loginDialog.inputHeight * 0.65 - height: width - sourceSize: Qt.size(width, height); - anchors { - verticalCenter: parent.verticalCenter - left: parent.left - leftMargin: loginDialog.inputHeight / 4 - } - } - - TextInput { - id: username - anchors { - fill: parent - leftMargin: loginDialog.inputHeight - rightMargin: loginDialog.inputHeight / 2 - } - - helperText: "username or email" - color: hifi.colors.text - - KeyNavigation.tab: password - KeyNavigation.backtab: password - } - } - - Rectangle { - width: loginDialog.inputWidth - height: loginDialog.inputHeight - radius: height / 2 - color: "#ebebeb" - - Image { - source: "../images/login-password.svg" - width: loginDialog.inputHeight * 0.65 - height: width - sourceSize: Qt.size(width, height); - anchors { - verticalCenter: parent.verticalCenter - left: parent.left - leftMargin: loginDialog.inputHeight / 4 - } - } - - TextInput { - id: password - anchors { - fill: parent - leftMargin: loginDialog.inputHeight - rightMargin: loginDialog.inputHeight / 2 - } - - helperText: "password" - echoMode: TextInput.Password - color: hifi.colors.text - - KeyNavigation.tab: username - KeyNavigation.backtab: username - onFocusChanged: { - if (password.focus) { - password.selectAll() - } - } - } - } - - Item { - width: loginDialog.inputWidth - height: loginDialog.inputHeight / 2 - - Text { - id: messageText - - visible: loginDialog.statusText != "" && loginDialog.statusText != "Logging in..." - - width: loginDialog.inputWidth - height: loginDialog.inputHeight / 2 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - text: loginDialog.statusText - color: "white" - } - - Row { - id: messageSpinner - - visible: loginDialog.statusText == "Logging in..." - onVisibleChanged: visible ? messageSpinnerAnimation.restart() : messageSpinnerAnimation.stop() - - spacing: 24 - anchors { - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter - } - - Rectangle { - id: spinner1 - width: 10 - height: 10 - color: "#ebebeb" - opacity: 0.05 - } - - Rectangle { - id: spinner2 - width: 10 - height: 10 - color: "#ebebeb" - opacity: 0.05 - } - - Rectangle { - id: spinner3 - width: 10 - height: 10 - color: "#ebebeb" - opacity: 0.05 - } - - SequentialAnimation { - id: messageSpinnerAnimation - running: messageSpinner.visible - loops: Animation.Infinite - NumberAnimation { target: spinner1; property: "opacity"; to: 1.0; duration: 1000 } - NumberAnimation { target: spinner2; property: "opacity"; to: 1.0; duration: 1000 } - NumberAnimation { target: spinner3; property: "opacity"; to: 1.0; duration: 1000 } - NumberAnimation { target: spinner1; property: "opacity"; to: 0.05; duration: 0 } - NumberAnimation { target: spinner2; property: "opacity"; to: 0.05; duration: 0 } - NumberAnimation { target: spinner3; property: "opacity"; to: 0.05; duration: 0 } - } - } - } - - Rectangle { - width: loginDialog.inputWidth - height: loginDialog.inputHeight - radius: height / 2 - color: "#353535" - - TextInput { - anchors.fill: parent - text: "Login" - color: "white" - horizontalAlignment: Text.AlignHCenter - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - loginDialog.login(username.text, password.text) - } - } - } - - Item { - anchors { left: parent.left; right: parent.right; } - height: loginDialog.inputHeight - - Image { - id: hifiIcon - source: "../images/hifi-logo-blackish.svg" - width: loginDialog.inputHeight - height: width - sourceSize: Qt.size(width, height); - anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter } - } - - Text { - anchors { verticalCenter: parent.verticalCenter; right: hifiIcon.left; margins: loginDialog.inputSpacing } - text: "Password?" - scale: 0.8 - font.underline: true - color: "#e0e0e0" - MouseArea { - anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 } - cursorShape: Qt.PointingHandCursor - onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new") - } - } - - Text { - anchors { verticalCenter: parent.verticalCenter; left: hifiIcon.right; margins: loginDialog.inputSpacing } - text: "Register" - scale: 0.8 - font.underline: true - color: "#e0e0e0" - - MouseArea { - anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 } - cursorShape: Qt.PointingHandCursor - onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/signup") - } - } - - } - } - } - - onShownChanged: { - if (!shown) { - username.text = "" - password.text = "" - loginDialog.statusText = "" - } else { - username.forceActiveFocus() + Loader { + id: bodyLoader + anchors.fill: parent + source: loginDialog.isSteamRunning() ? "LoginDialog/SignInBody.qml" : "LoginDialog/LinkAccountBody.qml" } } Keys.onPressed: { - switch (event.key) { + if (!visible) { + return + } + + if (event.modifiers === Qt.ControlModifier) + switch (event.key) { + case Qt.Key_A: + event.accepted = true + detailedText.selectAll() + break + case Qt.Key_C: + event.accepted = true + detailedText.copy() + break + case Qt.Key_Period: + if (Qt.platform.os === "osx") { + event.accepted = true + content.reject() + } + break + } else switch (event.key) { case Qt.Key_Escape: case Qt.Key_Back: - root.shown = false; - event.accepted = true; - break; + event.accepted = true + destroy() + break case Qt.Key_Enter: case Qt.Key_Return: - if (username.activeFocus) { - event.accepted = true - password.forceActiveFocus() - } else if (password.activeFocus) { - event.accepted = true - if (username.text == "") { - username.forceActiveFocus() - } else { - loginDialog.login(username.text, password.text) - } - } + event.accepted = true break } } diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml new file mode 100644 index 0000000000..fc7eac3d00 --- /dev/null +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -0,0 +1,125 @@ +// +// CompleteProfileBody.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles + +import "../controls-uit" +import "../styles-uit" + +Item { + id: completeProfileBody + clip: true + width: pane.width + height: pane.height + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, additionalTextContainer.contentWidth) + var targetHeight = 4 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + Row { + id: buttons + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: 3 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + width: 200 + + text: qsTr("Create your profile") + color: hifi.buttons.blue + + onClicked: loginDialog.createAccountFromStream() + } + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Cancel") + + + onClicked: root.destroy() + } + } + + ShortcutText { + id: additionalTextContainer + anchors { + top: buttons.bottom + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: "Already have a High Fidelity profile? Link to an existing profile here." + + wrapMode: Text.WordWrap + lineHeight: 2 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + + onLinkActivated: { + bodyLoader.source = "LinkAccountBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } + + Component.onCompleted: { + root.title = qsTr("Complete Your Profile") + root.iconText = "<" + d.resize(); + } + + Connections { + target: loginDialog + onHandleCreateCompleted: { + console.log("Create Succeeded") + + loginDialog.loginThroughSteam() + } + onHandleCreateFailed: { + console.log("Create Failed: " + error) + + bodyLoader.source = "UsernameCollisionBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLoginCompleted: { + console.log("Login Succeeded") + + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLoginFailed: { + console.log("Login Failed") + } + } +} diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml new file mode 100644 index 0000000000..137556964f --- /dev/null +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -0,0 +1,215 @@ +// +// LinkAccountBody.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles + +import "../controls-uit" +import "../styles-uit" + +Item { + id: linkAccountBody + clip: true + width: root.pane.width + height: root.pane.height + + function login() { + mainTextContainer.visible = false + loginDialog.login(usernameField.text, passwordField.text) + } + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, form.contentWidth) + var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height + + 4 * hifi.dimensions.contentSpacing.y + form.height + + 4 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + ShortcutText { + id: mainTextContainer + anchors { + top: parent.top + left: parent.left + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + visible: false + + text: qsTr("Username or password incorrect.") + wrapMode: Text.WordWrap + color: hifi.colors.redAccent + lineHeight: 1 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + Column { + id: form + anchors { + top: mainTextContainer.bottom + left: parent.left + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + spacing: 2 * hifi.dimensions.contentSpacing.y + + Row { + spacing: hifi.dimensions.contentSpacing.x + + TextField { + id: usernameField + anchors { + verticalCenter: parent.verticalCenter + } + width: 350 + + label: "User Name or Email" + } + + ShortcutText { + anchors { + verticalCenter: parent.verticalCenter + } + + text: "Forgot Username?" + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + onLinkActivated: loginDialog.openUrl(link) + } + } + Row { + spacing: hifi.dimensions.contentSpacing.x + + TextField { + id: passwordField + anchors { + verticalCenter: parent.verticalCenter + } + width: 350 + + label: "Password" + echoMode: TextInput.Password + } + + ShortcutText { + anchors { + verticalCenter: parent.verticalCenter + } + + text: "Forgot Password?" + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + onLinkActivated: loginDialog.openUrl(link) + } + } + + } + + Row { + id: buttons + anchors { + top: form.bottom + right: parent.right + margins: 0 + topMargin: 3 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + id: linkAccountButton + anchors.verticalCenter: parent.verticalCenter + width: 200 + + text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login") + color: hifi.buttons.blue + + onClicked: linkAccountBody.login() + } + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Cancel") + + onClicked: root.destroy() + } + } + + Component.onCompleted: { + root.title = qsTr("Sign Into High Fidelity") + root.iconText = "<" + d.resize(); + + usernameField.forceActiveFocus() + } + + Connections { + target: loginDialog + onHandleLoginCompleted: { + console.log("Login Succeeded, linking steam account") + + if (loginDialog.isSteamRunning()) { + loginDialog.linkSteam() + } else { + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } + onHandleLoginFailed: { + console.log("Login Failed") + mainTextContainer.visible = true + } + onHandleLinkCompleted: { + console.log("Link Succeeded") + + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLinkFailed: { + console.log("Link Failed") + + } + } + + Keys.onPressed: { + if (!visible) { + return + } + + switch (event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + linkAccountBody.login() + break + } + } +} diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml new file mode 100644 index 0000000000..7bbba683c3 --- /dev/null +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -0,0 +1,128 @@ +// +// SignInBody.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles + +import "../controls-uit" +import "../styles-uit" + +Item { + id: signInBody + clip: true + width: pane.width + height: pane.height + + property bool required: false + + function login() { + console.log("Trying to log in") + loginDialog.loginThroughSteam() + } + + function cancel() { + root.destroy() + } + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth) + var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + InfoItem { + id: mainTextContainer + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: required ? qsTr("This domain's owner requires that you sign in:") + : qsTr("Sign in to access your user account:") + wrapMode: Text.WordWrap + color: hifi.colors.baseGrayHighlight + lineHeight: 2 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + Row { + id: buttons + anchors { + top: mainTextContainer.bottom + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + + width: undefined // invalidate so that the image's size sets the width + height: undefined // invalidate so that the image's size sets the height + focus: true + + style: OriginalStyles.ButtonStyle { + background: Image { + id: buttonImage + source: "../../images/steam-sign-in.png" + } + } + onClicked: signInBody.login() + } + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Cancel"); + + onClicked: signInBody.cancel() + } + } + + Component.onCompleted: { + root.title = required ? qsTr("Sign In Required") + : qsTr("Sign In") + root.iconText = "" + d.resize(); + } + + Connections { + target: loginDialog + onHandleLoginCompleted: { + console.log("Login Succeeded") + + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLoginFailed: { + console.log("Login Failed") + + bodyLoader.source = "CompleteProfileBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } +} diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml new file mode 100644 index 0000000000..b949c660d6 --- /dev/null +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -0,0 +1,173 @@ +// +// UsernameCollisionBody.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles + +import "../controls-uit" +import "../styles-uit" + +Item { + id: usernameCollisionBody + clip: true + width: root.pane.width + height: root.pane.height + + function create() { + mainTextContainer.visible = false + loginDialog.createAccountFromStream(textField.text) + } + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, Math.max(mainTextContainer.contentWidth, + termsContainer.contentWidth)) + var targetHeight = mainTextContainer.height + + 2 * hifi.dimensions.contentSpacing.y + textField.height + + 5 * hifi.dimensions.contentSpacing.y + termsContainer.height + + 1 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + ShortcutText { + id: mainTextContainer + anchors { + top: parent.top + left: parent.left + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: qsTr("Your Steam username is not available.") + wrapMode: Text.WordWrap + color: hifi.colors.redAccent + lineHeight: 1 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + + TextField { + id: textField + anchors { + top: mainTextContainer.bottom + left: parent.left + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + width: 250 + + placeholderText: "Choose your own" + } + + InfoItem { + id: termsContainer + anchors { + top: textField.bottom + left: parent.left + margins: 0 + topMargin: 3 * hifi.dimensions.contentSpacing.y + } + + text: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service") + wrapMode: Text.WordWrap + color: hifi.colors.baseGrayHighlight + lineHeight: 1 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + + onLinkActivated: loginDialog.openUrl(link) + } + + Row { + id: buttons + anchors { + top: termsContainer.bottom + right: parent.right + margins: 0 + topMargin: 1 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + width: 200 + + text: qsTr("Create your profile") + color: hifi.buttons.blue + + onClicked: usernameCollisionBody.create() + } + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Cancel") + + onClicked: root.destroy() + } + } + + Component.onCompleted: { + root.title = qsTr("Complete Your Profile") + root.iconText = "<" + d.resize(); + } + Connections { + target: loginDialog + onHandleCreateCompleted: { + console.log("Create Succeeded") + + loginDialog.loginThroughSteam() + } + onHandleCreateFailed: { + console.log("Create Failed: " + error) + + mainTextContainer.visible = true + mainTextContainer.text = "\"" + textField.text + qsTr("\" is invalid or already taken.") + } + onHandleLoginCompleted: { + console.log("Login Succeeded") + + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLoginFailed: { + console.log("Login Failed") + } + } + + Keys.onPressed: { + if (!visible) { + return + } + + switch (event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + usernameCollisionBody.create() + break + } + } +} diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml new file mode 100644 index 0000000000..5fed9addf8 --- /dev/null +++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml @@ -0,0 +1,90 @@ +// +// WelcomeBody.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import Hifi 1.0 +import QtQuick 2.4 + +import "../controls-uit" +import "../styles-uit" + +Item { + id: welcomeBody + clip: true + width: pane.width + height: pane.height + + property bool welcomeBack: false + + function setTitle() { + root.title = (welcomeBack ? qsTr("Welcome back ") : qsTr("Welcome ")) + Account.username + qsTr("!") + root.iconText = "" + d.resize(); + } + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth) + var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + InfoItem { + id: mainTextContainer + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: qsTr("You are now signed into High Fidelity") + wrapMode: Text.WordWrap + color: hifi.colors.baseGrayHighlight + lineHeight: 2 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + Row { + id: buttons + anchors { + top: mainTextContainer.bottom + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Close"); + + onClicked: root.destroy() + } + } + + Component.onCompleted: welcomeBody.setTitle() + + Connections { + target: Account + onUsernameChanged: welcomeBody.setTitle() + } +} diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index fe88899658..180e5e1bcc 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -99,6 +99,12 @@ Item { font.pixelSize: root.fontSize text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Asset Mbps In/Out: " + root.assetMbpsIn.toFixed(2) + "/" + root.assetMbpsOut.toFixed(2) + } } } diff --git a/interface/resources/qml/controls-uit/HorizontalRule.qml b/interface/resources/qml/controls-uit/HorizontalRule.qml new file mode 100644 index 0000000000..425500f1d4 --- /dev/null +++ b/interface/resources/qml/controls-uit/HorizontalRule.qml @@ -0,0 +1,20 @@ +// +// HorizontalRule.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: hifi.colors.lightGray +} diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml new file mode 100644 index 0000000000..545154ab44 --- /dev/null +++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml @@ -0,0 +1,21 @@ +// +// HorizontalSpacer.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 + +import "../styles-uit" + +Item { + id: root + property alias size: root.width + + width: hifi.dimensions.controlInterlineHeight + height: 1 // Must be non-zero +} diff --git a/interface/resources/qml/controls-uit/VerticalSpacer.qml b/interface/resources/qml/controls-uit/VerticalSpacer.qml index 6fc49605c0..2df65f1002 100644 --- a/interface/resources/qml/controls-uit/VerticalSpacer.qml +++ b/interface/resources/qml/controls-uit/VerticalSpacer.qml @@ -13,6 +13,9 @@ import QtQuick 2.5 import "../styles-uit" Item { + id: root + property alias size: root.height + width: 1 // Must be non-zero height: hifi.dimensions.controlInterlineHeight } diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index d390ea08bf..40c5a01e15 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -21,8 +21,6 @@ import "messageDialog" ModalWindow { id: root HifiConstants { id: hifi } - implicitWidth: 640 - implicitHeight: 320 destroyOnCloseButton: true destroyOnHidden: true visible: true @@ -70,7 +68,7 @@ ModalWindow { QtObject { id: d readonly property int minWidth: 480 - readonly property int maxWdith: 1280 + readonly property int maxWidth: 1280 readonly property int minHeight: 120 readonly property int maxHeight: 720 @@ -80,7 +78,7 @@ ModalWindow { + (informativeTextContainer.text != "" ? informativeTextContainer.contentHeight + 3 * hifi.dimensions.contentSpacing.y : 0) + buttons.height + (content.state === "expanded" ? details.implicitHeight + hifi.dimensions.contentSpacing.y : 0) - root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth) + root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWidth) ? d.maxWidth : targetWidth) root.height = (targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight) } } diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml index 04e3934535..6d371741ea 100644 --- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml +++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml @@ -24,6 +24,7 @@ Item { Rectangle { color: hifi.colors.baseGray; anchors.fill: parent; radius: 4 } Component.onCompleted: { + jointChooser.model = MyAvatar.jointNames; completed = true; } @@ -82,7 +83,6 @@ Item { HifiControls.ComboBox { id: jointChooser; anchors { bottom: parent.bottom; left: parent.left; right: parent.right } - model: MyAvatar.jointNames colorScheme: hifi.colorSchemes.dark currentIndex: attachment ? model.indexOf(attachment.jointName) : -1 onCurrentIndexChanged: { diff --git a/interface/resources/qml/styles-uit/ButtonLabel.qml b/interface/resources/qml/styles-uit/ButtonLabel.qml new file mode 100644 index 0000000000..aade5fb439 --- /dev/null +++ b/interface/resources/qml/styles-uit/ButtonLabel.qml @@ -0,0 +1,18 @@ +// +// ButtonLabel.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayBold { + font.pixelSize: hifi.fontSizes.buttonLabel +} diff --git a/interface/resources/qml/styles-uit/IconButton.qml b/interface/resources/qml/styles-uit/IconButton.qml new file mode 100644 index 0000000000..84c1ef14c1 --- /dev/null +++ b/interface/resources/qml/styles-uit/IconButton.qml @@ -0,0 +1,20 @@ +// +// IconButton.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.iconButton + font.capitalization: Font.AllUppercase + font.letterSpacing: 1.5 +} diff --git a/interface/resources/qml/styles-uit/InfoItem.qml b/interface/resources/qml/styles-uit/InfoItem.qml new file mode 100644 index 0000000000..83781a4ef5 --- /dev/null +++ b/interface/resources/qml/styles-uit/InfoItem.qml @@ -0,0 +1,19 @@ +// +// InfoItem.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewaySemiBold { + lineHeight: 2 + font.pixelSize: hifi.fontSizes.menuItem +} diff --git a/interface/resources/qml/styles-uit/InputLabel.qml b/interface/resources/qml/styles-uit/InputLabel.qml new file mode 100644 index 0000000000..59657a554d --- /dev/null +++ b/interface/resources/qml/styles-uit/InputLabel.qml @@ -0,0 +1,18 @@ +// +// InputLabel.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewaySemiBold { + font.pixelSize: hifi.fontSizes.inputLabel +} diff --git a/interface/resources/qml/styles-uit/ListItem.qml b/interface/resources/qml/styles-uit/ListItem.qml new file mode 100644 index 0000000000..f707686edc --- /dev/null +++ b/interface/resources/qml/styles-uit/ListItem.qml @@ -0,0 +1,18 @@ +// +// ListItem.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.listItem +} diff --git a/interface/resources/qml/styles-uit/Logs.qml b/interface/resources/qml/styles-uit/Logs.qml new file mode 100644 index 0000000000..577fe2f8d8 --- /dev/null +++ b/interface/resources/qml/styles-uit/Logs.qml @@ -0,0 +1,18 @@ +// +// Logs.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +AnonymousProRegular { + font.pixelSize: hifi.fontSizes.logs +} diff --git a/interface/resources/qml/styles-uit/OverlayTitle.qml b/interface/resources/qml/styles-uit/OverlayTitle.qml new file mode 100644 index 0000000000..e23b9eca14 --- /dev/null +++ b/interface/resources/qml/styles-uit/OverlayTitle.qml @@ -0,0 +1,18 @@ +// +// OverlayTitle.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.overlayTitle +} diff --git a/interface/resources/qml/styles-uit/SectionName.qml b/interface/resources/qml/styles-uit/SectionName.qml new file mode 100644 index 0000000000..5438fec7bc --- /dev/null +++ b/interface/resources/qml/styles-uit/SectionName.qml @@ -0,0 +1,19 @@ +// +// SectionName.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.sectionName + font.capitalization: Font.AllUppercase +} diff --git a/interface/resources/qml/styles-uit/ShortcutText.qml b/interface/resources/qml/styles-uit/ShortcutText.qml new file mode 100644 index 0000000000..a3ab351870 --- /dev/null +++ b/interface/resources/qml/styles-uit/ShortcutText.qml @@ -0,0 +1,18 @@ +// +// ShortcutText.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayLight { + font.pixelSize: hifi.fontSizes.shortcutText +} diff --git a/interface/resources/qml/styles-uit/TabName.qml b/interface/resources/qml/styles-uit/TabName.qml new file mode 100644 index 0000000000..eb4e790e7e --- /dev/null +++ b/interface/resources/qml/styles-uit/TabName.qml @@ -0,0 +1,19 @@ +// +// TabName.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.tabName + font.capitalization: Font.AllUppercase +} diff --git a/interface/resources/qml/styles-uit/TextFieldInput.qml b/interface/resources/qml/styles-uit/TextFieldInput.qml new file mode 100644 index 0000000000..010b4d03ad --- /dev/null +++ b/interface/resources/qml/styles-uit/TextFieldInput.qml @@ -0,0 +1,18 @@ +// +// TextFieldInput.qml +// +// Created by Clement on 7/18/16 +// 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +FiraSansSemiBold { + font.pixelSize: hifi.fontSizes.textFieldInput +} diff --git a/interface/resources/shaders/hmd_hand_lasers.frag b/interface/resources/shaders/hmd_hand_lasers.frag deleted file mode 100644 index 6fffb1c521..0000000000 --- a/interface/resources/shaders/hmd_hand_lasers.frag +++ /dev/null @@ -1,35 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/07/11 -// Copyright 2013-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 -// - -#version 410 core - -uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0); - -layout(location = 0) in vec3 inLineDistance; - -out vec4 FragColor; - -void main() { - vec2 d = inLineDistance.xy; - d.y = abs(d.y); - d.x = abs(d.x); - if (d.x > 1.0) { - d.x = (d.x - 1.0) / 0.02; - } else { - d.x = 0.0; - } - float alpha = 1.0 - length(d); - if (alpha <= 0.0) { - discard; - } - alpha = pow(alpha, 10.0); - if (alpha < 0.05) { - discard; - } - FragColor = vec4(color.rgb, alpha); -} diff --git a/interface/resources/shaders/hmd_hand_lasers.geom b/interface/resources/shaders/hmd_hand_lasers.geom deleted file mode 100644 index 16b5dafadd..0000000000 --- a/interface/resources/shaders/hmd_hand_lasers.geom +++ /dev/null @@ -1,70 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/07/11 -// Copyright 2013-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 -// -#version 410 core -#extension GL_EXT_geometry_shader4 : enable - -layout(location = 0) out vec3 outLineDistance; - -layout(lines) in; -layout(triangle_strip, max_vertices = 24) out; - -vec3[2] getOrthogonals(in vec3 n, float scale) { - float yDot = abs(dot(n, vec3(0, 1, 0))); - - vec3 result[2]; - if (yDot < 0.9) { - result[0] = normalize(cross(n, vec3(0, 1, 0))); - } else { - result[0] = normalize(cross(n, vec3(1, 0, 0))); - } - // The cross of result[0] and n is orthogonal to both, which are orthogonal to each other - result[1] = cross(result[0], n); - result[0] *= scale; - result[1] *= scale; - return result; -} - - -vec2 orthogonal(vec2 v) { - vec2 result = v.yx; - result.y *= -1.0; - return result; -} - -void main() { - vec2 endpoints[2]; - for (int i = 0; i < 2; ++i) { - endpoints[i] = gl_PositionIn[i].xy / gl_PositionIn[i].w; - } - vec2 lineNormal = normalize(endpoints[1] - endpoints[0]); - vec2 lineOrthogonal = orthogonal(lineNormal); - lineNormal *= 0.02; - lineOrthogonal *= 0.02; - - gl_Position = gl_PositionIn[0]; - gl_Position.xy -= lineOrthogonal; - outLineDistance = vec3(-1.02, -1, gl_Position.z); - EmitVertex(); - - gl_Position = gl_PositionIn[0]; - gl_Position.xy += lineOrthogonal; - outLineDistance = vec3(-1.02, 1, gl_Position.z); - EmitVertex(); - - gl_Position = gl_PositionIn[1]; - gl_Position.xy -= lineOrthogonal; - outLineDistance = vec3(1.02, -1, gl_Position.z); - EmitVertex(); - - gl_Position = gl_PositionIn[1]; - gl_Position.xy += lineOrthogonal; - outLineDistance = vec3(1.02, 1, gl_Position.z); - EmitVertex(); - - EndPrimitive(); -} diff --git a/interface/resources/shaders/hmd_hand_lasers.vert b/interface/resources/shaders/hmd_hand_lasers.vert deleted file mode 100644 index db5c7c1ecd..0000000000 --- a/interface/resources/shaders/hmd_hand_lasers.vert +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/07/11 -// Copyright 2013-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 -// -#version 410 core -uniform mat4 mvp = mat4(1); - -in vec3 Position; - -void main() { - gl_Position = mvp * vec4(Position, 1); -} diff --git a/interface/resources/shaders/hmd_ui_glow.frag b/interface/resources/shaders/hmd_ui_glow.frag index 733f32d718..9270842092 100644 --- a/interface/resources/shaders/hmd_ui_glow.frag +++ b/interface/resources/shaders/hmd_ui_glow.frag @@ -6,18 +6,27 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#version 410 core - uniform sampler2D sampler; -uniform float alpha = 1.0; -uniform vec4 glowPoints = vec4(-1); -uniform vec4 glowColors[2]; -uniform vec2 resolution = vec2(3960.0, 1188.0); -uniform float radius = 0.005; + +struct OverlayData { + mat4 mvp; + vec4 glowPoints; + vec4 glowColors[2]; + vec4 resolutionRadiusAlpha; +}; + +layout(std140) uniform overlayBuffer { + OverlayData overlay; +}; + +vec2 resolution = overlay.resolutionRadiusAlpha.xy; +float radius = overlay.resolutionRadiusAlpha.z; +float alpha = overlay.resolutionRadiusAlpha.w; +vec4 glowPoints = overlay.glowPoints; +vec4 glowColors[2] = overlay.glowColors; in vec3 vPosition; in vec2 vTexCoord; -in vec4 vGlowPoints; out vec4 FragColor; @@ -31,9 +40,10 @@ float easeInOutCubic(float f) { } void main() { + FragColor = texture(sampler, vTexCoord); + vec2 aspect = resolution; aspect /= resolution.x; - FragColor = texture(sampler, vTexCoord); float glowIntensity = 0.0; float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect); diff --git a/interface/resources/shaders/hmd_ui_glow.vert b/interface/resources/shaders/hmd_ui_glow.vert index 5defec085f..54eb062590 100644 --- a/interface/resources/shaders/hmd_ui_glow.vert +++ b/interface/resources/shaders/hmd_ui_glow.vert @@ -6,12 +6,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#version 410 core +struct OverlayData { + mat4 mvp; + vec4 glowPoints; + vec4 glowColors[2]; + vec4 resolutionRadiusAlpha; +}; -uniform mat4 mvp = mat4(1); +layout(std140) uniform overlayBuffer { + OverlayData overlay; +}; -in vec3 Position; -in vec2 TexCoord; +mat4 mvp = overlay.mvp; + +layout(location = 0) in vec3 Position; +layout(location = 3) in vec2 TexCoord; out vec3 vPosition; out vec2 vTexCoord; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d50a1c9fe..6b4eb8d140 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -86,15 +86,16 @@ #include #include #include +#include #include #include #include #include #include -#include +#include #include #include -#include +#include #include #include #include @@ -139,7 +140,6 @@ #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif -#include "Stars.h" #include "ui/AddressBarDialog.h" #include "ui/AvatarInputs.h" #include "ui/DialogsManager.h" @@ -257,7 +257,10 @@ public: void run() override { while (!_quit) { QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); - + // Don't do heartbeat detection under nsight + if (nsightActive()) { + continue; + } uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us uint64_t now = usecTimestampNow(); auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0; @@ -305,8 +308,6 @@ public: // Don't actually crash in debug builds, in case this apparent deadlock is simply from // the developer actively debugging code #ifdef NDEBUG - - deadlockDetectionCrash(); #endif } @@ -391,6 +392,11 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt } static const QString STATE_IN_HMD = "InHMD"; +static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM"; +static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson"; +static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson"; +static const QString STATE_CAMERA_ENTITY = "CameraEntity"; +static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent"; static const QString STATE_SNAP_TURN = "SnapTurn"; static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; @@ -470,7 +476,9 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } }); + controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, + STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, + STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -739,7 +747,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); - ResourceCache::setRequestLimit(MAX_CONCURRENT_RESOURCE_DOWNLOADS); + const char** constArgv = const_cast(argv); + QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads"); + bool success; + int concurrentDownloads = concurrentDownloadsStr.toInt(&success); + if (!success) { + concurrentDownloads = MAX_CONCURRENT_RESOURCE_DOWNLOADS; + } + ResourceCache::setRequestLimit(concurrentDownloads); _glWidget = new GLCanvas(); getApplicationCompositor().setRenderingWidget(_glWidget); @@ -765,16 +780,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _glWidget->makeCurrent(); _glWidget->initializeGL(); - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->create(_glWidget->context()->contextHandle()); - _chromiumShareContext->makeCurrent(); - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); - - _offscreenContext = new OffscreenGLCanvas(); - _offscreenContext->create(_glWidget->context()->contextHandle()); - _offscreenContext->makeCurrent(); initializeGL(); - _offscreenContext->makeCurrent(); // Make sure we don't time out during slow operations at startup updateHeartbeat(); @@ -811,6 +817,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } UserActivityLogger::getInstance().logAction("launch", properties); + _connectionMonitor.init(); // Tell our entity edit sender about our known jurisdictions _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions); @@ -831,7 +838,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : QSharedPointer bandwidthRecorder = DependencyManager::get(); connect(nodeList.data(), &LimitedNodeList::dataSent, bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData); - connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived, + connect(nodeList.data(), &LimitedNodeList::dataReceived, bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); // FIXME -- I'm a little concerned about this. @@ -954,6 +961,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _applicationStateDevice->setInputVariant(STATE_IN_HMD, []() -> float { return qApp->isHMDMode() ? 1 : 0; }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_FULL_SCREEN_MIRROR, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_ENTITY, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_ENTITY ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0; + }); _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float { return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0; }); @@ -1116,10 +1138,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : static int SEND_STATS_INTERVAL_MS = 10000; static int NEARBY_AVATAR_RADIUS_METERS = 10; + static glm::vec3 lastAvatarPosition = getMyAvatar()->getPosition(); + static glm::mat4 lastHMDHeadPose = getHMDSensorPose(); + static controller::Pose lastLeftHandPose = getMyAvatar()->getLeftHandPose(); + static controller::Pose lastRightHandPose = getMyAvatar()->getRightHandPose(); + // Periodically send fps as a user activity event QTimer* sendStatsTimer = new QTimer(this); sendStatsTimer->setInterval(SEND_STATS_INTERVAL_MS); connect(sendStatsTimer, &QTimer::timeout, this, [this]() { + QJsonObject properties = {}; MemoryInfo memInfo; if (getMemoryInfo(memInfo)) { @@ -1161,6 +1189,31 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false; + glm::vec3 avatarPosition = getMyAvatar()->getPosition(); + properties["avatar_has_moved"] = lastAvatarPosition != avatarPosition; + lastAvatarPosition = avatarPosition; + + auto entityScriptingInterface = DependencyManager::get(); + auto entityActivityTracking = entityScriptingInterface->getActivityTracking(); + entityScriptingInterface->resetActivityTracking(); + properties["added_entity_cnt"] = entityActivityTracking.addedEntityCount; + properties["deleted_entity_cnt"] = entityActivityTracking.deletedEntityCount; + properties["edited_entity_cnt"] = entityActivityTracking.editedEntityCount; + + auto hmdHeadPose = getHMDSensorPose(); + properties["hmd_head_pose_changed"] = isHMDMode() && (hmdHeadPose != lastHMDHeadPose); + lastHMDHeadPose = hmdHeadPose; + + auto leftHandPose = getMyAvatar()->getLeftHandPose(); + auto rightHandPose = getMyAvatar()->getRightHandPose(); + // controller::Pose considers two poses to be different if either are invalid. In our case, we actually + // want to consider the pose to be unchanged if it was invalid and still is invalid, so we check that first. + properties["hand_pose_changed"] = + ((leftHandPose.valid || lastLeftHandPose.valid) && (leftHandPose != lastLeftHandPose)) + || ((rightHandPose.valid || lastRightHandPose.valid) && (rightHandPose != lastRightHandPose)); + lastLeftHandPose = leftHandPose; + lastRightHandPose = rightHandPose; + UserActivityLogger::getInstance().logAction("stats", properties); }); sendStatsTimer->start(); @@ -1204,6 +1257,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); + auto textureCache = DependencyManager::get(); + + QString skyboxUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.jpg" }; + QString skyboxAmbientUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-ambient.jpg" }; + + _defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", false } }); + _defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", true } }); + + _defaultSkybox->setCubemap(_defaultSkyboxTexture); + _defaultSkybox->setColor({ 1.0, 1.0, 1.0 }); + + EntityItem::setEntitiesShouldFadeFunction([this]() { + SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer); + return entityServerNode && !isPhysicsEnabled(); + }); + // After all of the constructor is completed, then set firstRun to false. Setting::Handle firstRun{ Settings::firstRun, true }; firstRun.set(false); @@ -1457,11 +1526,18 @@ void Application::initializeGL() { _isGLInitialized = true; } + _glWidget->makeCurrent(); + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->create(_glWidget->context()->contextHandle()); + _chromiumShareContext->makeCurrent(); + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + + _glWidget->makeCurrent(); gpu::Context::init(); _gpuContext = std::make_shared(); // The gpu context can make child contexts for transfers, so // we need to restore primary rendering context - _offscreenContext->makeCurrent(); + _glWidget->makeCurrent(); initDisplay(); qCDebug(interfaceapp, "Initialized Display."); @@ -1480,7 +1556,8 @@ void Application::initializeGL() { // Needs to happen AFTER the render engine initialization to access its configuration initializeUi(); qCDebug(interfaceapp, "Initialized Offscreen UI."); - _offscreenContext->makeCurrent(); + _glWidget->makeCurrent(); + // call Menu getInstance static method to set up the menu // Needs to happen AFTER the QML UI initialization @@ -1496,8 +1573,13 @@ void Application::initializeGL() { _idleLoopStdev.reset(); + _offscreenContext = new OffscreenGLCanvas(); + _offscreenContext->create(_glWidget->context()->contextHandle()); + _offscreenContext->makeCurrent(); + // update before the first render update(0); + } FrameTimingsScriptingInterface _frameTimingsScriptingInterface; @@ -1514,7 +1596,7 @@ void Application::initializeUi() { auto offscreenUi = DependencyManager::get(); - offscreenUi->create(_offscreenContext->getContext()); + offscreenUi->create(_glWidget->context()->contextHandle()); auto rootContext = offscreenUi->getRootContext(); @@ -1591,6 +1673,8 @@ void Application::initializeUi() { rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface()); rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); + + rootContext->setContextProperty("Steam", new SteamScriptingInterface(engine)); _glWidget->installEventFilter(offscreenUi.data()); @@ -1683,17 +1767,7 @@ void Application::paintGL() { PerformanceWarning warn(showWarnings, "Application::paintGL()"); resizeGL(); - // Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened - { - PerformanceTimer perfTimer("syncCache"); - renderArgs._context->syncCache(); - } - - auto framebufferCache = DependencyManager::get(); - // Final framebuffer that will be handled to the display-plugin - auto finalFramebuffer = framebufferCache->getFramebuffer(); - - _gpuContext->beginFrame(finalFramebuffer, getHMDSensorPose()); + _gpuContext->beginFrame(getHMDSensorPose()); // Reset the gpu::Context Stages // Back to the default framebuffer; gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) { @@ -1823,7 +1897,10 @@ void Application::paintGL() { getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform()); // Primary rendering pass + auto framebufferCache = DependencyManager::get(); const QSize size = framebufferCache->getFrameBufferSize(); + // Final framebuffer that will be handled to the display-plugin + auto finalFramebuffer = framebufferCache->getFramebuffer(); { PROFILE_RANGE(__FUNCTION__ "/mainRender"); @@ -1848,7 +1925,6 @@ void Application::paintGL() { auto baseProjection = renderArgs.getViewFrustum().getProjection(); auto hmdInterface = DependencyManager::get(); float IPDScale = hmdInterface->getIPDScale(); - mat4 headPose = displayPlugin->getHeadPose(); // FIXME we probably don't need to set the projection matrix every frame, // only when the display plugin changes (or in non-HMD modes when the user @@ -1864,13 +1940,6 @@ void Application::paintGL() { // Apply IPD scaling mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale); eyeOffsets[eye] = eyeOffsetTransform; - - // Tell the plugin what pose we're using to render. In this case we're just using the - // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it - // for rotational timewarp. If we move to support positonal timewarp, we need to - // ensure this contains the full pose composed with the eye offsets. - displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform)); - eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection); }); renderArgs._context->setStereoProjections(eyeProjections); @@ -1878,36 +1947,26 @@ void Application::paintGL() { } renderArgs._blitFramebuffer = finalFramebuffer; displaySide(&renderArgs, _myCamera); - - renderArgs._blitFramebuffer.reset(); - renderArgs._context->enableStereo(false); } - _gpuContext->endFrame(); - - gpu::TexturePointer overlayTexture = _applicationOverlay.acquireOverlay(); - if (overlayTexture) { - displayPlugin->submitOverlayTexture(overlayTexture); - } - - // deliver final composited scene to the display plugin + auto frame = _gpuContext->endFrame(); + frame->frameIndex = _frameCount; + frame->framebuffer = finalFramebuffer; + frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){ + DependencyManager::get()->releaseFramebuffer(framebuffer); + }; + frame->overlay = _applicationOverlay.getOverlayTexture(); + // deliver final scene rendering commands to the display plugin { PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); PerformanceTimer perfTimer("pluginOutput"); - - auto finalTexture = finalFramebuffer->getRenderBuffer(0); - Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture)); - _lockedFramebufferMap[finalTexture] = finalFramebuffer; - - Q_ASSERT(isCurrentContext(_offscreenContext->getContext())); - { - PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene"); - PerformanceTimer perfTimer("pluginSubmitScene"); - displayPlugin->submitSceneTexture(_frameCount, finalTexture); - } - Q_ASSERT(isCurrentContext(_offscreenContext->getContext())); + displayPlugin->submitFrame(frame); } + // Reset the framebuffer and stereo state + renderArgs._blitFramebuffer.reset(); + renderArgs._context->enableStereo(false); + { Stats::getInstance()->setRenderDetails(renderArgs._details); } @@ -2272,7 +2331,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } case Qt::Key_Asterisk: - Menu::getInstance()->triggerOption(MenuOption::Stars); + Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox); break; case Qt::Key_S: @@ -2903,6 +2962,8 @@ void Application::idle(float nsecsElapsed) { PROFILE_RANGE(__FUNCTION__); + SteamClient::runCallbacks(); + float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND; // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. @@ -3211,6 +3272,14 @@ void Application::init() { addressLookupString = arguments().value(urlIndex + 1); } + // when +connect_lobby in command line, join steam lobby + const QString STEAM_LOBBY_COMMAND_LINE_KEY = "+connect_lobby"; + int lobbyIndex = arguments().indexOf(STEAM_LOBBY_COMMAND_LINE_KEY); + if (lobbyIndex != -1) { + QString lobbyId = arguments().value(lobbyIndex + 1); + SteamClient::joinLobby(lobbyId); + } + Setting::Handle firstRun { Settings::firstRun, true }; if (addressLookupString.isEmpty() && firstRun.get()) { qDebug() << "First run and no URL passed... attempting to go to Home or Entry..."; @@ -3240,6 +3309,18 @@ void Application::init() { getEntities()->setViewFrustum(_viewFrustum); } + getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) { + auto dims = item.getDimensions(); + auto maxSize = glm::max(dims.x, dims.y, dims.z); + + if (maxSize <= 0.0f) { + return 0.0f; + } + + auto distance = glm::distance(getMyAvatar()->getPosition(), item.getPosition()); + return atan2(maxSize, distance); + }); + ObjectMotionState::setShapeManager(&_shapeManager); _physicsEngine->init(); @@ -4185,8 +4266,6 @@ public: typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - Stars _stars; - static render::ItemID _item; // unique WorldBoxRenderData }; @@ -4210,29 +4289,44 @@ namespace render { auto backgroundMode = skyStage->getBackgroundMode(); switch (backgroundMode) { + case model::SunSkyStage::SKY_DEFAULT: { + static const glm::vec3 DEFAULT_SKYBOX_COLOR{ 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f }; + static const float DEFAULT_SKYBOX_INTENSITY{ 0.2f }; + static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY{ 2.0f }; + static const glm::vec3 DEFAULT_SKYBOX_DIRECTION{ 0.0f, 0.0f, -1.0f }; + + auto scene = DependencyManager::get()->getStage(); + auto sceneKeyLight = scene->getKeyLight(); + scene->setSunModelEnable(false); + sceneKeyLight->setColor(DEFAULT_SKYBOX_COLOR); + sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY); + sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY); + sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION); + // fall through: render a skybox, if available + } case model::SunSkyStage::SKY_BOX: { auto skybox = skyStage->getSkybox(); - if (skybox) { + if (!skybox->empty()) { PerformanceTimer perfTimer("skybox"); skybox->render(batch, args->getViewFrustum()); break; } + // fall through: render defaults, if available } - - // Fall through: if no skybox is available, render the SKY_DOME - case model::SunSkyStage::SKY_DOME: { - if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { - PerformanceTimer perfTimer("stars"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::payloadRender() ... My god, it's full of stars..."); - // should be the first rendering pass - w/o depth buffer / lighting - - static const float alpha = 1.0f; - background->_stars.render(args, alpha); + case model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE: { + if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) { + auto scene = DependencyManager::get()->getStage(); + auto sceneKeyLight = scene->getKeyLight(); + auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture(); + // do not set the ambient sphere - it peaks too high, and causes flashing when turning + sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); } + // fall through: render defaults, if available } - break; - + case model::SunSkyStage::SKY_DEFAULT_TEXTURE: + if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) { + qApp->getDefaultSkybox()->render(batch, args->getViewFrustum()); + } case model::SunSkyStage::NO_BACKGROUND: default: // this line intentionally left blank @@ -4423,7 +4517,6 @@ void Application::updateWindowTitle() const { #endif _window->setWindowTitle(title); } - void Application::clearDomainOctreeDetails() { // if we're about to quit, we really don't need to do any of these things... @@ -4449,7 +4542,8 @@ void Application::clearDomainOctreeDetails() { getEntities()->clear(); auto skyStage = DependencyManager::get()->getSkyStage(); - skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME); + + skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT); _recentlyClearedDomain = true; } @@ -4776,6 +4870,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Users", DependencyManager::get().data()); + + scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine)); } bool Application::canAcceptURL(const QString& urlString) const { @@ -4814,7 +4910,7 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) { } if (defaultUpload) { - toggleAssetServerWidget(urlString); + showAssetServerWidget(urlString); } return defaultUpload; } @@ -5002,7 +5098,7 @@ void Application::toggleRunningScriptsWidget() const { //} } -void Application::toggleAssetServerWidget(QString filePath) { +void Application::showAssetServerWidget(QString filePath) { if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { return; } @@ -5053,8 +5149,10 @@ void Application::setPreviousScriptLocation(const QString& location) { } void Application::loadScriptURLDialog() const { - auto newScript = OffscreenUi::getText(nullptr, "Open and Run Script", "Script URL"); - if (!newScript.isEmpty()) { + auto newScript = OffscreenUi::getText(OffscreenUi::ICON_NONE, "Open and Run Script", "Script URL"); + if (QUrl(newScript).scheme() == "atp") { + OffscreenUi::warning("Error Loading Script", "Cannot load client script over ATP"); + } else if (!newScript.isEmpty()) { DependencyManager::get()->loadScript(newScript); } } @@ -5327,6 +5425,7 @@ void Application::updateDisplayMode() { DisplayPluginList advanced; DisplayPluginList developer; foreach(auto displayPlugin, displayPlugins) { + displayPlugin->setContext(_gpuContext); auto grouping = displayPlugin->getGrouping(); switch (grouping) { case Plugin::ADVANCED: @@ -5396,9 +5495,6 @@ void Application::updateDisplayMode() { _displayPlugin->deactivate(); } - // FIXME probably excessive and useless context switching - _offscreenContext->makeCurrent(); - bool active = newDisplayPlugin->activate(); if (!active) { @@ -5543,20 +5639,6 @@ bool Application::makeRenderingContextCurrent() { return _offscreenContext->makeCurrent(); } -void Application::releaseSceneTexture(const gpu::TexturePointer& texture) { - Q_ASSERT(QThread::currentThread() == thread()); - auto& framebufferMap = _lockedFramebufferMap; - Q_ASSERT(framebufferMap.contains(texture)); - auto framebufferPointer = framebufferMap[texture]; - framebufferMap.remove(texture); - auto framebufferCache = DependencyManager::get(); - framebufferCache->releaseFramebuffer(framebufferPointer); -} - -void Application::releaseOverlayTexture(const gpu::TexturePointer& texture) { - _applicationOverlay.releaseOverlay(texture); -} - bool Application::isForeground() const { return _isForeground && !_window->isMinimized(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 0af65f665f..890dd51b15 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -47,6 +47,7 @@ #include "avatar/MyAvatar.h" #include "Bookmarks.h" #include "Camera.h" +#include "ConnectionMonitor.h" #include "FileLogger.h" #include "gpu/Context.h" #include "Menu.h" @@ -64,6 +65,9 @@ #include "ui/overlays/Overlays.h" #include "UndoStackScriptingInterface.h" +#include +#include + class OffscreenGLCanvas; class GLCanvas; class FaceTracker; @@ -108,8 +112,6 @@ public: virtual MainWindow* getPrimaryWindow() override; virtual QOpenGLContext* getPrimaryContext() override; virtual bool makeRenderingContextCurrent() override; - virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override; - virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override; virtual bool isForeground() const override; virtual DisplayPluginPointer getActiveDisplayPlugin() const override; @@ -248,6 +250,10 @@ public: float getAvatarSimrate() const { return _avatarSimCounter.rate(); } float getAverageSimsPerSecond() const { return _simCounter.rate(); } + model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; } + gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; } + gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; } + signals: void svoImportRequested(const QString& url); @@ -269,7 +275,7 @@ public slots: Q_INVOKABLE void loadScriptURLDialog() const; void toggleLogDialog(); void toggleRunningScriptsWidget() const; - void toggleAssetServerWidget(QString filePath = ""); + Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); void handleLocalServerConnection() const; void readArgumentsFromLocalSocket() const; @@ -426,7 +432,6 @@ private: InputPluginList _activeInputPlugins; bool _activatingDisplayPlugin { false }; - QMap _lockedFramebufferMap; QUndoStack _undoStack; UndoStackScriptingInterface _undoStackScriptingInterface; @@ -562,6 +567,13 @@ private: bool _recentlyClearedDomain { false }; QString _returnFromFullScreenMirrorTo; + + ConnectionMonitor _connectionMonitor; + + model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ; + gpu::TexturePointer _defaultSkyboxTexture; + gpu::TexturePointer _defaultSkyboxAmbientTexture; }; + #endif // hifi_Application_h diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp new file mode 100644 index 0000000000..1956ace67b --- /dev/null +++ b/interface/src/ConnectionMonitor.cpp @@ -0,0 +1,52 @@ +// +// ConnectionMonitor.cpp +// interface/src +// +// Created by Ryan Huffman on 8/4/15. +// 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 "ConnectionMonitor.h" + +#include "ui/DialogsManager.h" + +#include +#include +#include +#include + +static const int DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 5000; + +void ConnectionMonitor::init() { + // Connect to domain disconnected message + auto nodeList = DependencyManager::get(); + const DomainHandler& domainHandler = nodeList->getDomainHandler(); + connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &ConnectionMonitor::disconnectedFromDomain); + connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::connectedToDomain); + + // Connect to AddressManager::hostChanged + auto addressManager = DependencyManager::get(); + connect(addressManager.data(), &AddressManager::hostChanged, this, &ConnectionMonitor::hostChanged); + + _timer.setSingleShot(true); + _timer.setInterval(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS); + _timer.start(); + + auto dialogsManager = DependencyManager::get(); + connect(&_timer, &QTimer::timeout, dialogsManager.data(), &DialogsManager::showAddressBar); +} + +void ConnectionMonitor::disconnectedFromDomain() { + _timer.start(); +} + +void ConnectionMonitor::connectedToDomain(const QString& name) { + _timer.stop(); +} + +void ConnectionMonitor::hostChanged(const QString& name) { + _timer.start(); +} diff --git a/interface/src/ConnectionMonitor.h b/interface/src/ConnectionMonitor.h new file mode 100644 index 0000000000..bba420715e --- /dev/null +++ b/interface/src/ConnectionMonitor.h @@ -0,0 +1,34 @@ +// +// ConnectionMonitor.h +// interface/src +// +// Created by Ryan Huffman on 8/4/15. +// 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 +// + +#ifndef hifi_ConnectionMonitor_h +#define hifi_ConnectionMonitor_h + +#include +#include + +class QString; + +class ConnectionMonitor : public QObject { + Q_OBJECT +public: + void init(); + +private slots: + void disconnectedFromDomain(); + void connectedToDomain(const QString& name); + void hostChanged(const QString& name); + +private: + QTimer _timer; +}; + +#endif // hifi_ConnectionMonitor_h \ No newline at end of file diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp index 4051bd8a1e..dd80dadca7 100644 --- a/interface/src/DiscoverabilityManager.cpp +++ b/interface/src/DiscoverabilityManager.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -36,11 +37,11 @@ const QString SESSION_ID_KEY = "session_id"; void DiscoverabilityManager::updateLocation() { auto accountManager = DependencyManager::get(); - - if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) { - auto addressManager = DependencyManager::get(); - DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); + auto addressManager = DependencyManager::get(); + auto& domainHandler = DependencyManager::get()->getDomainHandler(); + + if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) { // construct a QJsonObject given the user's current address information QJsonObject rootObject; @@ -48,8 +49,6 @@ void DiscoverabilityManager::updateLocation() { QString pathString = addressManager->currentPath(); - const QString LOCATION_KEY_IN_ROOT = "location"; - const QString PATH_KEY_IN_LOCATION = "path"; locationObject.insert(PATH_KEY_IN_LOCATION, pathString); @@ -90,6 +89,7 @@ void DiscoverabilityManager::updateLocation() { // we have a changed location, send it now _lastLocationObject = locationObject; + const QString LOCATION_KEY_IN_ROOT = "location"; rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject); apiPath = API_USER_LOCATION_PATH; @@ -109,6 +109,9 @@ void DiscoverabilityManager::updateLocation() { accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation, callbackParameters); } + + // Update Steam + SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentFacingAddress()); } void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) { diff --git a/interface/src/FileLogger.h b/interface/src/FileLogger.h index 28ac6fba40..950590e789 100644 --- a/interface/src/FileLogger.h +++ b/interface/src/FileLogger.h @@ -48,7 +48,7 @@ signals: protected: void rollFileIfNecessary(QFile& file, bool notifyListenersIfRolled = true); - virtual bool processQueueItems(const Queue& messages); + virtual bool processQueueItems(const Queue& messages) override; private: const FileLogger& _logger; diff --git a/interface/src/InterfaceActionFactory.h b/interface/src/InterfaceActionFactory.h index 2031f5c57a..3e8a17d871 100644 --- a/interface/src/InterfaceActionFactory.h +++ b/interface/src/InterfaceActionFactory.h @@ -21,9 +21,9 @@ public: virtual EntityActionPointer factory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity, - QVariantMap arguments); + QVariantMap arguments) override; virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, - QByteArray data); + QByteArray data) override; }; #endif // hifi_InterfaceActionFactory_h diff --git a/interface/src/InterfaceParentFinder.h b/interface/src/InterfaceParentFinder.h index a2e9fb50e4..19f7e30dca 100644 --- a/interface/src/InterfaceParentFinder.h +++ b/interface/src/InterfaceParentFinder.h @@ -21,7 +21,8 @@ class InterfaceParentFinder : public SpatialParentFinder { public: InterfaceParentFinder() { } virtual ~InterfaceParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, + SpatialParentTree* entityTree = nullptr) const override; }; #endif // hifi_InterfaceParentFinder_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6308ac6c73..50dc748461 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -134,7 +134,7 @@ Menu::Menu() { // Edit > My Asset Server auto assetServerAction = addActionToQMenuAndActionHash(editMenu, MenuOption::AssetServer, Qt::CTRL | Qt::SHIFT | Qt::Key_A, - qApp, SLOT(toggleAssetServerWidget())); + qApp, SLOT(showAssetServerWidget())); auto nodeList = DependencyManager::get(); QObject::connect(nodeList.data(), &NodeList::canWriteAssetsChanged, assetServerAction, &QAction::setEnabled); assetServerAction->setEnabled(nodeList->getThisNodeCanWriteAssets()); @@ -337,7 +337,7 @@ Menu::Menu() { // Developer > Render >>> MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DefaultSkybox, 0, true); // Developer > Render > Throttle FPS If Not Focus addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 503cbf51fa..d47b6842a5 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -80,6 +80,7 @@ namespace MenuOption { const QString CrashNewFaultThreaded = "New Fault (threaded)"; const QString DeadlockInterface = "Deadlock Interface"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; + const QString DefaultSkybox = "Default Skybox"; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger"; const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; @@ -175,7 +176,6 @@ namespace MenuOption { const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; const QString SimulateEyeTracking = "Simulate"; const QString SMIEyeTracking = "SMI Eye Tracking"; - const QString Stars = "Stars"; const QString Stats = "Stats"; const QString StopAllScripts = "Stop All Scripts"; const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; diff --git a/interface/src/ModelSelector.h b/interface/src/ModelSelector.h index 0ac3df5963..ee9e75c17a 100644 --- a/interface/src/ModelSelector.h +++ b/interface/src/ModelSelector.h @@ -24,23 +24,23 @@ class QPushButton; class ModelSelector : public QDialog { Q_OBJECT - + public: ModelSelector(); - + QFileInfo getFileInfo() const; FSTReader::ModelType getModelType() const; - + public slots: - virtual void accept(); - + virtual void accept() override; + private slots: void browse(); - + private: QFileInfo _modelFile; QPushButton* _browseButton; QComboBox* _modelType; }; -#endif // hifi_ModelSelector_h \ No newline at end of file +#endif // hifi_ModelSelector_h diff --git a/interface/src/ScriptHighlighting.h b/interface/src/ScriptHighlighting.h index 2eb40796e3..8952b1bcbd 100644 --- a/interface/src/ScriptHighlighting.h +++ b/interface/src/ScriptHighlighting.h @@ -26,7 +26,7 @@ public: }; protected: - void highlightBlock(const QString& text); + void highlightBlock(const QString& text) override; void highlightKeywords(const QString& text); void formatComments(const QString& text); void formatQuotedText(const QString& text); diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp deleted file mode 100644 index 9510710eb3..0000000000 --- a/interface/src/Stars.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// -// Stars.cpp -// interface/src -// -// Created by Tobias Schwinger on 3/22/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 "Stars.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -//static const float TILT = 0.23f; -static const float TILT = 0.0f; -static const unsigned int STARFIELD_NUM_STARS = 50000; -static const unsigned int STARFIELD_SEED = 1; -static const float STAR_COLORIZATION = 0.1f; - -static const float TAU = 6.28318530717958f; -//static const float HALF_TAU = TAU / 2.0f; -//static const float QUARTER_TAU = TAU / 4.0f; -//static const float MILKY_WAY_WIDTH = TAU / 30.0f; // width in radians of one half of the Milky Way -//static const float MILKY_WAY_INCLINATION = 0.0f; // angle of Milky Way from horizontal in degrees -//static const float MILKY_WAY_RATIO = 0.4f; -static const char* UNIFORM_TIME_NAME = "iGlobalTime"; - -// Produce a random float value between 0 and 1 -static float frand() { - return (float)rand() / (float)RAND_MAX; -} - -// http://mathworld.wolfram.com/SpherePointPicking.html -static vec2 randPolar() { - vec2 result(frand(), frand()); - result.x *= TAU; - result.y = powf(result.y, 2.0) / 2.0f; - if (frand() > 0.5f) { - result.y = 0.5f - result.y; - } else { - result.y += 0.5f; - } - result.y = acos((2.0f * result.y) - 1.0f); - return result; -} - - -static vec3 fromPolar(const vec2& polar) { - float sinTheta = sin(polar.x); - float cosTheta = cos(polar.x); - float sinPhi = sin(polar.y); - float cosPhi = cos(polar.y); - return vec3( - cosTheta * sinPhi, - cosPhi, - sinTheta * sinPhi); -} - - -// computeStarColor -// - Generate a star color. -// -// colorization can be a value between 0 and 1 specifying how colorful the resulting star color is. -// -// 0 = completely black & white -// 1 = very colorful -unsigned computeStarColor(float colorization) { - unsigned char red, green, blue; - if (randFloat() < 0.3f) { - // A few stars are colorful - red = 2 + (rand() % 254); - green = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization)); - blue = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization)); - } else { - // Most stars are dimmer and white - red = green = blue = 2 + (rand() % 128); - } - return red | (green << 8) | (blue << 16); -} - -struct StarVertex { - vec4 position; - vec4 colorAndSize; -}; - -static const int STARS_VERTICES_SLOT{ 0 }; -static const int STARS_COLOR_SLOT{ 1 }; - -gpu::PipelinePointer Stars::_gridPipeline{}; -gpu::PipelinePointer Stars::_starsPipeline{}; -int32_t Stars::_timeSlot{ -1 }; - -void Stars::init() { - if (!_gridPipeline) { - auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)); - auto ps = gpu::Shader::createPixel(std::string(starsGrid_frag)); - auto program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::makeProgram((*program)); - _timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME); - if (_timeSlot == gpu::Shader::INVALID_LOCATION) { - _timeSlot = program->getUniforms().findLocation(UNIFORM_TIME_NAME); - } - auto state = gpu::StatePointer(new gpu::State()); - // enable decal blend - state->setDepthTest(gpu::State::DepthTest(false)); - state->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)); - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - _gridPipeline = gpu::Pipeline::create(program, state); - } - - if (!_starsPipeline) { - auto vs = gpu::Shader::createVertex(std::string(stars_vert)); - auto ps = gpu::Shader::createPixel(std::string(stars_frag)); - auto program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::makeProgram((*program)); - auto state = gpu::StatePointer(new gpu::State()); - // enable decal blend - state->setDepthTest(gpu::State::DepthTest(false)); - state->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)); - state->setAntialiasedLineEnable(true); // line smoothing also smooth points - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - _starsPipeline = gpu::Pipeline::create(program, state); - } - - unsigned limit = STARFIELD_NUM_STARS; - std::vector points; - points.resize(limit); - - { // generate stars - QElapsedTimer startTime; - startTime.start(); - - vertexBuffer.reset(new gpu::Buffer); - - srand(STARFIELD_SEED); - for (size_t star = 0; star < limit; ++star) { - points[star].position = vec4(fromPolar(randPolar()), 1); - float size = frand() * 2.5f + 0.5f; - if (frand() < STAR_COLORIZATION) { - vec3 color(frand() / 2.0f + 0.5f, frand() / 2.0f + 0.5f, frand() / 2.0f + 0.5f); - points[star].colorAndSize = vec4(color, size); - } else { - vec3 color(frand() / 2.0f + 0.5f); - points[star].colorAndSize = vec4(color, size); - } - } - - double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms - qDebug() << "Total time to generate stars: " << timeDiff << " msec"; - } - - gpu::Element positionElement, colorElement; - const size_t VERTEX_STRIDE = sizeof(StarVertex); - - vertexBuffer->append(VERTEX_STRIDE * limit, (const gpu::Byte*)&points[0]); - streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone - streamFormat->setAttribute(gpu::Stream::POSITION, STARS_VERTICES_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW), 0); - streamFormat->setAttribute(gpu::Stream::COLOR, STARS_COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA)); - positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element; - colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element; - - size_t offset = offsetof(StarVertex, position); - positionView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement); - - offset = offsetof(StarVertex, colorAndSize); - colorView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, colorElement); -} - -// FIXME star colors -void Stars::render(RenderArgs* renderArgs, float alpha) { - std::call_once(once, [&]{ init(); }); - - - auto modelCache = DependencyManager::get(); - auto textureCache = DependencyManager::get(); - auto geometryCache = DependencyManager::get(); - - - gpu::Batch& batch = *renderArgs->_batch; - batch.setViewTransform(Transform()); - batch.setProjectionTransform(renderArgs->getViewFrustum().getProjection()); - batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->getViewFrustum().getOrientation()) * - quat(vec3(TILT, 0, 0)))); - batch.setResourceTexture(0, textureCache->getWhiteTexture()); - - // Render the world lines - batch.setPipeline(_gridPipeline); - static auto start = usecTimestampNow(); - float msecs = (float)(usecTimestampNow() - start) / (float)USECS_PER_MSEC; - float secs = msecs / (float)MSECS_PER_SECOND; - batch._glUniform1f(_timeSlot, secs); - geometryCache->renderCube(batch); - - // Render the stars - batch.setPipeline(_starsPipeline); - batch.setInputFormat(streamFormat); - batch.setInputBuffer(STARS_VERTICES_SLOT, positionView); - batch.setInputBuffer(STARS_COLOR_SLOT, colorView); - batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS); -} diff --git a/interface/src/Stars.h b/interface/src/Stars.h deleted file mode 100644 index f07caff770..0000000000 --- a/interface/src/Stars.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// Stars.h -// interface/src -// -// Created by Tobias Schwinger on 3/22/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 -// - -#ifndef hifi_Stars_h -#define hifi_Stars_h - -#include - -class RenderArgs; - -// Starfield rendering component. -class Stars { -public: - Stars() = default; - ~Stars() = default; - - Stars(Stars const&) = delete; - Stars& operator=(Stars const&) = delete; - - // Renders the starfield from a local viewer's perspective. - // The parameters specifiy the field of view. - void render(RenderArgs* args, float alpha); - -private: - // Pipelines - static gpu::PipelinePointer _gridPipeline; - static gpu::PipelinePointer _starsPipeline; - static int32_t _timeSlot; - - // Buffers - gpu::BufferPointer vertexBuffer; - gpu::Stream::FormatPointer streamFormat; - gpu::BufferView positionView; - gpu::BufferView colorView; - std::once_flag once; - - void init(); -}; - - -#endif // hifi_Stars_h diff --git a/interface/src/audio/AudioScope.cpp b/interface/src/audio/AudioScope.cpp index d92c5a2fda..1946d216ff 100644 --- a/interface/src/audio/AudioScope.cpp +++ b/interface/src/audio/AudioScope.cpp @@ -142,7 +142,7 @@ void AudioScope::render(RenderArgs* renderArgs, int width, int height) { mat4 legacyProjection = glm::ortho(0, width, height, 0, -1000, 1000); batch.setProjectionTransform(legacyProjection); batch.setModelTransform(Transform()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); geometryCache->renderQuad(batch, x, y, w, h, backgroundColor, _audioScopeBackground); renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b1b30a1acd..d3a38271a0 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -780,7 +780,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const { PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect"); - DependencyManager::get()->bindSimpleProgram(batch, false, true, true, true); + DependencyManager::get()->bindSimpleProgram(batch, false, false, true, true, true); DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height, bevelDistance, backgroundColor); } diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 33ea180d33..29cd06865c 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -82,12 +82,12 @@ public: void setDeltaRoll(float roll) { _deltaRoll = roll; } float getDeltaRoll() const { return _deltaRoll; } - virtual void setFinalYaw(float finalYaw); - virtual void setFinalPitch(float finalPitch); - virtual void setFinalRoll(float finalRoll); - virtual float getFinalPitch() const; - virtual float getFinalYaw() const; - virtual float getFinalRoll() const; + virtual void setFinalYaw(float finalYaw) override; + virtual void setFinalPitch(float finalPitch) override; + virtual void setFinalRoll(float finalRoll) override; + virtual float getFinalPitch() const override; + virtual float getFinalYaw() const override; + virtual float getFinalRoll() const override; void relax(float deltaTime); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index de5455fc14..782ecbcc64 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -122,11 +122,13 @@ MyAvatar::MyAvatar(RigPointer rig) : _driveKeys[i] = 0.0f; } + + // Necessary to select the correct slot + using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool); + // connect to AddressManager signal for location jumps - connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, - [=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){ - goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation); - }); + connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, + this, static_cast(&MyAvatar::goToLocation)); _characterController.setEnabled(true); @@ -514,13 +516,23 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const { return _sensorToWorldMatrixCache.get(); } +// As far as I know no HMD system supports a play area of a kilometer in radius. +static const float MAX_HMD_ORIGIN_DISTANCE = 1000.0f; // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { // update the sensorMatrices based on the new hmd pose _hmdSensorMatrix = hmdSensorMatrix; - _hmdSensorPosition = extractTranslation(hmdSensorMatrix); + auto newHmdSensorPosition = extractTranslation(hmdSensorMatrix); + + if (newHmdSensorPosition != _hmdSensorPosition && + glm::length(newHmdSensorPosition) > MAX_HMD_ORIGIN_DISTANCE) { + qWarning() << "Invalid HMD sensor position " << newHmdSensorPosition; + // Ignore unreasonable HMD sensor data + return; + } + _hmdSensorPosition = newHmdSensorPosition; _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); _hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation); } @@ -1859,7 +1871,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition, glm::quat quatOrientation = cancelOutRollAndPitch(newOrientation); if (shouldFaceLocation) { - quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + quatOrientation = newOrientation * glm::angleAxis(PI, Vectors::UP); // move the user a couple units away const float DISTANCE_TO_USER = 2.0f; diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index 9673f541d2..931ab099e9 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -27,26 +27,26 @@ class DdeFaceTracker : public FaceTracker, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - -public: - virtual void init(); - virtual void reset(); - virtual void update(float deltaTime); - virtual bool isActive() const; - virtual bool isTracking() const; - +public: + virtual void init() override; + virtual void reset() override; + virtual void update(float deltaTime) override; + + virtual bool isActive() const override; + virtual bool isTracking() const override; + float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); } float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); } float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); } float getRightEyeOpen() const { return getBlendshapeCoefficient(_rightEyeOpenIndex); } - + float getBrowDownLeft() const { return getBlendshapeCoefficient(_browDownLeftIndex); } float getBrowDownRight() const { return getBlendshapeCoefficient(_browDownRightIndex); } float getBrowUpCenter() const { return getBlendshapeCoefficient(_browUpCenterIndex); } float getBrowUpLeft() const { return getBlendshapeCoefficient(_browUpLeftIndex); } float getBrowUpRight() const { return getBlendshapeCoefficient(_browUpRightIndex); } - + float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); } float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); } float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); } @@ -55,7 +55,7 @@ public: void setEyeClosingThreshold(float eyeClosingThreshold); public slots: - void setEnabled(bool enabled); + void setEnabled(bool enabled) override; void calibrate(); private slots: @@ -77,18 +77,18 @@ private: QHostAddress _host; quint16 _serverPort; quint16 _controlPort; - + float getBlendshapeCoefficient(int index) const; void decodePacket(const QByteArray& buffer); - + // sockets QUdpSocket _udpSocket; quint64 _lastReceiveTimestamp; - + bool _reset; glm::vec3 _referenceTranslation; glm::quat _referenceRotation; - + int _leftBlinkIndex; int _rightBlinkIndex; int _leftEyeDownIndex; @@ -103,10 +103,10 @@ private: int _browUpCenterIndex; int _browUpLeftIndex; int _browUpRightIndex; - + int _mouthSmileLeftIndex; int _mouthSmileRightIndex; - + int _jawOpenIndex; QVector _coefficients; diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index 4cb7557410..2c5889857c 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -34,12 +34,12 @@ class Faceshift : public FaceTracker, public Dependency { public: #ifdef HAVE_FACESHIFT // If we don't have faceshift, use the base class' methods - virtual void init(); - virtual void update(float deltaTime); - virtual void reset(); + virtual void init() override; + virtual void update(float deltaTime) override; + virtual void reset() override; - virtual bool isActive() const; - virtual bool isTracking() const; + virtual bool isActive() const override; + virtual bool isTracking() const override; #endif bool isConnectedOrConnecting() const; @@ -49,7 +49,7 @@ public: // these pitch/yaw angles are in degrees float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; } float getEyeGazeLeftYaw() const { return _eyeGazeLeftYaw; } - + float getEyeGazeRightPitch() const { return _eyeGazeRightPitch; } float getEyeGazeRightYaw() const { return _eyeGazeRightYaw; } @@ -67,10 +67,10 @@ public: float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); } float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); } float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); } - + QString getHostname() { return _hostname.get(); } void setHostname(const QString& hostname); - + void updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, @@ -79,76 +79,76 @@ public: float mouth3, float mouth4, QVector& coefficients) const; - + signals: void connectionStateChanged(); public slots: - void setEnabled(bool enabled); - + void setEnabled(bool enabled) override; + private slots: void connectSocket(); void noteConnected(); void noteError(QAbstractSocket::SocketError error); void readPendingDatagrams(); - void readFromSocket(); + void readFromSocket(); void noteDisconnected(); private: Faceshift(); virtual ~Faceshift() {} - + void send(const std::string& message); void receive(const QByteArray& buffer); - + QTcpSocket _tcpSocket; QUdpSocket _udpSocket; #ifdef HAVE_FACESHIFT fs::fsBinaryStream _stream; #endif - + bool _tcpEnabled = true; int _tcpRetryCount = 0; bool _tracking = false; quint64 _lastReceiveTimestamp = 0; quint64 _lastMessageReceived = 0; float _averageFrameTime = STARTING_FACESHIFT_FRAME_TIME; - + glm::vec3 _headAngularVelocity = glm::vec3(0.0f); glm::vec3 _headLinearVelocity = glm::vec3(0.0f); glm::vec3 _lastHeadTranslation = glm::vec3(0.0f); glm::vec3 _filteredHeadTranslation = glm::vec3(0.0f); - + // degrees float _eyeGazeLeftPitch = 0.0f; float _eyeGazeLeftYaw = 0.0f; float _eyeGazeRightPitch = 0.0f; float _eyeGazeRightYaw = 0.0f; - + // degrees float _longTermAverageEyePitch = 0.0f; float _longTermAverageEyeYaw = 0.0f; bool _longTermAverageInitialized = false; - + Setting::Handle _hostname; - + // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes int _leftBlinkIndex = 0; int _rightBlinkIndex = 1; int _leftEyeOpenIndex = 8; int _rightEyeOpenIndex = 9; - + // Brows int _browDownLeftIndex = 14; int _browDownRightIndex = 15; int _browUpCenterIndex = 16; int _browUpLeftIndex = 17; int _browUpRightIndex = 18; - + int _mouthSmileLeftIndex = 28; int _mouthSmileRightIndex = 29; - + int _jawOpenIndex = 21; }; diff --git a/interface/src/devices/Leapmotion.h b/interface/src/devices/Leapmotion.h index 266b9beb87..d7981a65e8 100644 --- a/interface/src/devices/Leapmotion.h +++ b/interface/src/devices/Leapmotion.h @@ -33,7 +33,7 @@ public: bool isActive() const { return _active; } - virtual void update(); + virtual void update() override; protected: Leapmotion(); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 8fc0384aee..527b7f2331 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -8,6 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include #include @@ -20,12 +22,13 @@ #include #include +#include + #include "AddressManager.h" #include "Application.h" #include "InterfaceLogging.h" #include "UserActivityLogger.h" #include "MainWindow.h" -#include #ifdef HAS_BUGSPLAT #include @@ -137,6 +140,8 @@ int main(int argc, const char* argv[]) { // or in the main window ctor, before GL startup. Application::initPlugins(arguments); + SteamClient::init(); + int exitCode; { QSettings::setDefaultFormat(QSettings::IniFormat); @@ -202,6 +207,8 @@ int main(int argc, const char* argv[]) { Application::shutdownPlugins(); + SteamClient::shutdown(); + qCDebug(interfaceapp, "Normal exit."); #if !defined(DEBUG) && !defined(Q_OS_LINUX) // HACK: exit immediately (don't handle shutdown callbacks) for Release build diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp index 1328197195..4090c99ac8 100644 --- a/interface/src/scripting/AccountScriptingInterface.cpp +++ b/interface/src/scripting/AccountScriptingInterface.cpp @@ -15,6 +15,9 @@ AccountScriptingInterface* AccountScriptingInterface::getInstance() { static AccountScriptingInterface sharedInstance; + auto accountManager = DependencyManager::get(); + QObject::connect(accountManager.data(), &AccountManager::profileChanged, + &sharedInstance, &AccountScriptingInterface::usernameChanged); return &sharedInstance; } diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h index 888149b836..49648781ce 100644 --- a/interface/src/scripting/AccountScriptingInterface.h +++ b/interface/src/scripting/AccountScriptingInterface.h @@ -17,6 +17,11 @@ class AccountScriptingInterface : public QObject { Q_OBJECT + Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) + +signals: + void usernameChanged(); + public slots: static AccountScriptingInterface* getInstance(); QString getUsername(); diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index fc8d125839..50539e7a05 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -33,16 +33,16 @@ class InputController : public controller::InputController { public: InputController(int deviceTrackerId, int subTrackerId, QObject* parent = NULL); - virtual void update(); - virtual Key getKey() const; + virtual void update() override; + virtual Key getKey() const override; public slots: - virtual bool isActive() const { return _isActive; } - virtual glm::vec3 getAbsTranslation() const { return _eventCache.absTranslation; } - virtual glm::quat getAbsRotation() const { return _eventCache.absRotation; } - virtual glm::vec3 getLocTranslation() const { return _eventCache.locTranslation; } - virtual glm::quat getLocRotation() const { return _eventCache.locRotation; } + virtual bool isActive() const override { return _isActive; } + virtual glm::vec3 getAbsTranslation() const override { return _eventCache.absTranslation; } + virtual glm::quat getAbsRotation() const override { return _eventCache.absRotation; } + virtual glm::vec3 getLocTranslation() const override { return _eventCache.locTranslation; } + virtual glm::quat getLocRotation() const override { return _eventCache.locRotation; } private: diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h index 8859eb5b37..d7a610dd39 100644 --- a/interface/src/scripting/WebWindowClass.h +++ b/interface/src/scripting/WebWindowClass.h @@ -66,7 +66,7 @@ signals: void closed(); protected: - virtual bool eventFilter(QObject* sender, QEvent* event); + virtual bool eventFilter(QObject* sender, QEvent* event) override; private slots: void hasClosed(); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index b165cda135..9d7eee0f8c 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -179,6 +179,10 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString& return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); } +void WindowScriptingInterface::showAssetServer(const QString& upload) { + QMetaObject::invokeMethod(qApp, "showAssetServerWidget", Qt::QueuedConnection, Q_ARG(QString, upload)); +} + int WindowScriptingInterface::getInnerWidth() { return qApp->getWindow()->geometry().width(); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 9d73111333..9f1d2bddf5 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -53,6 +53,7 @@ public slots: CustomPromptResult customPrompt(const QVariant& config); QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + void showAssetServer(const QString& upload = ""); void copyToClipboard(const QString& text); signals: diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 6d5df31766..197fb5b58d 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -28,6 +28,8 @@ #include "Util.h" #include "ui/Stats.h" #include "ui/AvatarInputs.h" +#include "OffscreenUi.h" +#include const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f }; static const float ORTHO_NEAR_CLIP = -1000.0f; @@ -65,7 +67,9 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // Execute the batch into our framebuffer doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { + PROFILE_RANGE_BATCH(batch, "ApplicationOverlayRender"); renderArgs->_batch = &batch; + batch.enableStereo(false); int width = _overlayFramebuffer->getWidth(); int height = _overlayFramebuffer->getHeight(); @@ -99,7 +103,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { geometryCache->useSimpleDrawPipeline(batch); batch.setProjectionTransform(mat4()); batch.setModelTransform(Transform()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _uiTexture); geometryCache->renderUnitQuad(batch, glm::vec4(1)); @@ -119,7 +123,7 @@ void ApplicationOverlay::renderAudioScope(RenderArgs* renderArgs) { mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); batch.setProjectionTransform(legacyProjection); batch.setModelTransform(Transform()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); // Render the audio scope DependencyManager::get()->render(renderArgs, width, height); @@ -138,7 +142,7 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); batch.setProjectionTransform(legacyProjection); batch.setModelTransform(Transform()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); // Render all of the Script based "HUD" aka 2D overlays. // note: we call them HUD, as opposed to 2D, only because there are some cases of 3D HUD overlays, like the @@ -164,7 +168,7 @@ void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); batch.setProjectionTransform(legacyProjection); batch.setModelTransform(Transform()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); float screenRatio = ((float)qApp->getDevicePixelRatio()); float renderRatio = ((float)qApp->getRenderResolutionScale()); @@ -177,13 +181,11 @@ void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { glm::vec2 texCoordMinCorner(0.0f, 0.0f); glm::vec2 texCoordMaxCorner(viewport.width() * renderRatio / float(selfieTexture->getWidth()), viewport.height() * renderRatio / float(selfieTexture->getHeight())); - - geometryCache->useSimpleDrawPipeline(batch, true); batch.setResourceTexture(0, selfieTexture); - geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); + float alpha = DependencyManager::get()->getDesktop()->property("unpinnedAlpha").toFloat(); + geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); batch.setResourceTexture(0, renderArgs->_whiteTexture); - geometryCache->useSimpleDrawPipeline(batch, false); } } @@ -228,7 +230,7 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr geometryCache->useSimpleDrawPipeline(batch); batch.setProjectionTransform(mat4()); batch.setModelTransform(Transform()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture()); // FIXME: THe line width of CONNECTION_STATUS_BORDER_LINE_WIDTH is not supported anymore, we ll need a workaround @@ -246,10 +248,6 @@ static const auto COLOR_FORMAT = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA) static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); -std::mutex _textureGuard; -using Lock = std::unique_lock; -std::queue _availableTextures; - void ApplicationOverlay::buildFramebufferObject() { PROFILE_RANGE(__FUNCTION__); @@ -265,22 +263,6 @@ void ApplicationOverlay::buildFramebufferObject() { _overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT); } - if (!_overlayFramebuffer->getRenderBuffer(0)) { - gpu::TexturePointer newColorAttachment; - { - Lock lock(_textureGuard); - if (!_availableTextures.empty()) { - newColorAttachment = _availableTextures.front(); - _availableTextures.pop(); - } - } - if (newColorAttachment) { - newColorAttachment->resize2D(width, height, newColorAttachment->getNumSamples()); - _overlayFramebuffer->setRenderBuffer(0, newColorAttachment); - } - } - - // If the overlay framebuffer still has no color attachment, no textures were available for rendering, so build a new one if (!_overlayFramebuffer->getRenderBuffer(0)) { const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP); auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER)); @@ -288,20 +270,9 @@ void ApplicationOverlay::buildFramebufferObject() { } } -gpu::TexturePointer ApplicationOverlay::acquireOverlay() { +gpu::TexturePointer ApplicationOverlay::getOverlayTexture() { if (!_overlayFramebuffer) { return gpu::TexturePointer(); } - auto result = _overlayFramebuffer->getRenderBuffer(0); - _overlayFramebuffer->setRenderBuffer(0, gpu::TexturePointer()); - return result; -} - -void ApplicationOverlay::releaseOverlay(gpu::TexturePointer texture) { - if (texture) { - Lock lock(_textureGuard); - _availableTextures.push(texture); - } else { - qWarning() << "Attempted to release null texture"; - } -} + return _overlayFramebuffer->getRenderBuffer(0); +} \ No newline at end of file diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index b77fcc6f89..b7a0529f92 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -26,8 +26,7 @@ public: void renderOverlay(RenderArgs* renderArgs); - gpu::TexturePointer acquireOverlay(); - void releaseOverlay(gpu::TexturePointer pointer); + gpu::TexturePointer getOverlayTexture(); private: void renderStatsAndLogs(RenderArgs* renderArgs); diff --git a/interface/src/ui/AudioStatsDialog.h b/interface/src/ui/AudioStatsDialog.h index 7f14cd6018..3abab258c4 100644 --- a/interface/src/ui/AudioStatsDialog.h +++ b/interface/src/ui/AudioStatsDialog.h @@ -36,13 +36,13 @@ public: AudioStatsDisplay(QFormLayout* form, QString text, unsigned colorRGBA); void updatedDisplay(QString str); void paint(); - + private: QString _strBuf; QLabel* _label; QString _text; unsigned _colorRGBA; - + }; //dialog @@ -51,9 +51,9 @@ class AudioStatsDialog : public QDialog { public: AudioStatsDialog(QWidget* parent); ~AudioStatsDialog(); - - void paintEvent(QPaintEvent*); - + + void paintEvent(QPaintEvent*) override; + private: // audio stats methods for rendering QVector _audioMixerStats; @@ -61,48 +61,47 @@ private: QVector _upstreamMixerStats; QVector _downstreamStats; QVector _upstreamInjectedStats; - + int _audioMixerID; int _upstreamClientID; int _upstreamMixerID; int _downstreamID; int _upstreamInjectedID; - + QVector> _audioDisplayChannels; - + int addChannel(QFormLayout* form, QVector& stats, const unsigned color); void updateStats(QVector& stats, const int channelID); void renderStats(); void clearAllChannels(); void renderAudioStreamStats(const AudioStreamStats* streamStats, QVector* audioStreamstats, bool isDownstreamStats); - - + + const AudioIOStats* _stats; QFormLayout* _form; - + bool _isEnabled; bool _shouldShowInjectedStreams; - - + + signals: - - + + void closed(); - + public slots: - - - void reject(); + + + void reject() override; void updateTimerTimeout(); - + protected: - + // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent*); - + void closeEvent(QCloseEvent*) override; + private: QTimer* averageUpdateTimer = new QTimer(this); - }; diff --git a/interface/src/ui/BandwidthDialog.h b/interface/src/ui/BandwidthDialog.h index 1fc8627191..a53cc21030 100644 --- a/interface/src/ui/BandwidthDialog.h +++ b/interface/src/ui/BandwidthDialog.h @@ -57,7 +57,7 @@ public: BandwidthDialog(QWidget* parent); ~BandwidthDialog(); - void paintEvent(QPaintEvent*); + void paintEvent(QPaintEvent*) override; private: BandwidthChannelDisplay* _audioChannelDisplay; @@ -77,14 +77,14 @@ signals: public slots: - void reject(); + void reject() override; void updateTimerTimeout(); protected: // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent*); + void closeEvent(QCloseEvent*) override; private: QTimer* averageUpdateTimer = new QTimer(this); diff --git a/interface/src/ui/CachesSizeDialog.h b/interface/src/ui/CachesSizeDialog.h index fa01ce4534..025d0f2bac 100644 --- a/interface/src/ui/CachesSizeDialog.h +++ b/interface/src/ui/CachesSizeDialog.h @@ -21,19 +21,19 @@ class CachesSizeDialog : public QDialog { public: // Sets up the UI CachesSizeDialog(QWidget* parent); - + signals: void closed(); - + public slots: - void reject(); + void reject() override; void confirmClicked(bool checked); void resetClicked(bool checked); - + protected: // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent* event); - + void closeEvent(QCloseEvent* event) override; + private: QDoubleSpinBox* _animations = nullptr; QDoubleSpinBox* _geometries = nullptr; @@ -42,4 +42,4 @@ private: QDoubleSpinBox* _textures = nullptr; }; -#endif // hifi_CachesSizeDialog_h \ No newline at end of file +#endif // hifi_CachesSizeDialog_h diff --git a/interface/src/ui/DataWebPage.h b/interface/src/ui/DataWebPage.h index c1c343a216..f9aa5be8a8 100644 --- a/interface/src/ui/DataWebPage.h +++ b/interface/src/ui/DataWebPage.h @@ -18,9 +18,9 @@ class DataWebPage : public QWebPage { public: DataWebPage(QObject* parent = 0); protected: - void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID); - bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type); - virtual QString userAgentForUrl(const QUrl& url) const; + void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) override; + bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) override; + virtual QString userAgentForUrl(const QUrl& url) const override; }; #endif // hifi_DataWebPage_h diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 2f826146ae..dc06c50626 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -50,6 +50,10 @@ void DialogsManager::toggleAddressBar() { emit addressBarToggled(); } +void DialogsManager::showAddressBar() { + AddressBarDialog::show(); +} + void DialogsManager::toggleDiskCacheEditor() { maybeCreateDialog(_diskCacheEditor); _diskCacheEditor->toggle(); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index c48c6df0e6..5b4995029f 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -44,6 +44,7 @@ public: public slots: void toggleAddressBar(); + void showAddressBar(); void toggleDiskCacheEditor(); void toggleLoginDialog(); void showLoginDialog(); diff --git a/interface/src/ui/HMDToolsDialog.h b/interface/src/ui/HMDToolsDialog.h index 7d30fc17f6..16ea090d95 100644 --- a/interface/src/ui/HMDToolsDialog.h +++ b/interface/src/ui/HMDToolsDialog.h @@ -29,18 +29,18 @@ public: QScreen* getLastApplicationScreen() const { return _previousScreen; } bool hasHMDScreen() const { return _hmdScreenNumber >= -1; } void watchWindow(QWindow* window); - + signals: void closed(); public slots: - void reject(); + void reject() override; void screenCountChanged(int newCount); - + protected: - virtual void closeEvent(QCloseEvent*); // Emits a 'closed' signal when this dialog is closed. - virtual void showEvent(QShowEvent* event); - virtual void hideEvent(QHideEvent* event); + virtual void closeEvent(QCloseEvent*) override; // Emits a 'closed' signal when this dialog is closed. + virtual void showEvent(QShowEvent* event) override; + virtual void hideEvent(QHideEvent* event) override; private: void centerCursorOnWidget(QWidget* widget); @@ -59,7 +59,7 @@ private: QScreen* _previousDialogScreen{ nullptr }; QString _hmdPluginName; QString _defaultPluginName; - + QHash _windowWatchers; friend class HMDWindowWatcher; }; @@ -75,7 +75,7 @@ public: public slots: void windowScreenChanged(QScreen* screen); void windowGeometryChanged(int arg); - + private: QWindow* _window; HMDToolsDialog* _hmdTools; diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index cb58beab35..47878fea99 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -40,9 +40,9 @@ public slots: protected: void setAndSelectCommand(const QString& command); - virtual bool eventFilter(QObject* sender, QEvent* event); - virtual void mouseReleaseEvent(QMouseEvent* event); - virtual void showEvent(QShowEvent* event); + virtual bool eventFilter(QObject* sender, QEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void showEvent(QShowEvent* event) override; protected slots: void scrollToBottom(); diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index e5a2dae836..b2390a1cd7 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -24,12 +24,12 @@ class LodToolsDialog : public QDialog { public: // Sets up the UI LodToolsDialog(QWidget* parent); - + signals: void closed(); public slots: - void reject(); + void reject() override; void sizeScaleValueChanged(int value); void resetClicked(bool checked); void reloadSliders(); @@ -38,7 +38,7 @@ public slots: protected: // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent* event); + void closeEvent(QCloseEvent* event) override; private: QSlider* _lodSize; diff --git a/interface/src/ui/LogDialog.h b/interface/src/ui/LogDialog.h index 999a8394cc..c38cf84f00 100644 --- a/interface/src/ui/LogDialog.h +++ b/interface/src/ui/LogDialog.h @@ -30,7 +30,7 @@ public: QString keyword; protected: - void highlightBlock(const QString &text); + void highlightBlock(const QString &text) override; private: QTextCharFormat keywordFormat; @@ -54,8 +54,8 @@ private slots: void handleSearchTextChanged(const QString); protected: - void resizeEvent(QResizeEvent*); - void showEvent(QShowEvent*); + void resizeEvent(QResizeEvent*) override; + void showEvent(QShowEvent*) override; private: QPushButton* _searchButton; diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 80d52b7a07..fef0a12813 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -12,8 +12,11 @@ #include "LoginDialog.h" #include +#include +#include #include +#include #include "AccountManager.h" #include "DependencyManager.h" @@ -21,9 +24,7 @@ HIFI_QML_DEF(LoginDialog) -LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), - _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString()) -{ +LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) { auto accountManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::loginComplete, this, &LoginDialog::handleLoginCompleted); @@ -53,36 +54,102 @@ void LoginDialog::toggleAction() { } } -void LoginDialog::handleLoginCompleted(const QUrl&) { - hide(); +bool LoginDialog::isSteamRunning() const { + return SteamClient::isRunning(); } -void LoginDialog::handleLoginFailed() { - setStatusText("Invalid username or password"); -} - -void LoginDialog::setStatusText(const QString& statusText) { - if (statusText != _statusText) { - _statusText = statusText; - emit statusTextChanged(); - } -} - -QString LoginDialog::statusText() const { - return _statusText; -} - -QString LoginDialog::rootUrl() const { - return _rootUrl; -} - -void LoginDialog::login(const QString& username, const QString& password) { +void LoginDialog::login(const QString& username, const QString& password) const { qDebug() << "Attempting to login " << username; - setStatusText("Logging in..."); DependencyManager::get()->requestAccessToken(username, password); } -void LoginDialog::openUrl(const QString& url) { - qDebug() << url; - QDesktopServices::openUrl(url); +void LoginDialog::loginThroughSteam() { + qDebug() << "Attempting to login through Steam"; + SteamClient::requestTicket([this](Ticket ticket) { + if (ticket.isNull()) { + emit handleLoginFailed(); + return; + } + + DependencyManager::get()->requestAccessTokenWithSteam(ticket); + }); } + +void LoginDialog::linkSteam() { + qDebug() << "Attempting to link Steam account"; + SteamClient::requestTicket([this](Ticket ticket) { + if (ticket.isNull()) { + emit handleLoginFailed(); + return; + } + + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "linkCompleted"; + callbackParams.errorCallbackReceiver = this; + callbackParams.errorCallbackMethod = "linkFailed"; + + const QString LINK_STEAM_PATH = "api/v1/user/steam/link"; + + QJsonObject payload; + payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket))); + + auto accountManager = DependencyManager::get(); + accountManager->sendRequest(LINK_STEAM_PATH, AccountManagerAuth::Required, + QNetworkAccessManager::PostOperation, callbackParams, + QJsonDocument(payload).toJson()); + }); +} + +void LoginDialog::createAccountFromStream(QString username) { + qDebug() << "Attempting to create account from Steam info"; + SteamClient::requestTicket([this, username](Ticket ticket) { + if (ticket.isNull()) { + emit handleLoginFailed(); + return; + } + + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "createCompleted"; + callbackParams.errorCallbackReceiver = this; + callbackParams.errorCallbackMethod = "createFailed"; + + const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/steam/create"; + + QJsonObject payload; + payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket))); + if (!username.isEmpty()) { + payload.insert("username", QJsonValue::fromVariant(QVariant(username))); + } + + auto accountManager = DependencyManager::get(); + accountManager->sendRequest(CREATE_ACCOUNT_FROM_STEAM_PATH, AccountManagerAuth::None, + QNetworkAccessManager::PostOperation, callbackParams, + QJsonDocument(payload).toJson()); + }); + +} + +void LoginDialog::openUrl(const QString& url) const { + auto offscreenUi = DependencyManager::get(); + auto browser = offscreenUi->load("Browser.qml"); + browser->setProperty("url", url); +} + +void LoginDialog::linkCompleted(QNetworkReply& reply) { + emit handleLinkCompleted(); +} + +void LoginDialog::linkFailed(QNetworkReply& reply) { + emit handleLinkFailed(reply.errorString()); +} + +void LoginDialog::createCompleted(QNetworkReply& reply) { + emit handleCreateCompleted(); +} + +void LoginDialog::createFailed(QNetworkReply& reply) { + emit handleCreateFailed(reply.errorString()); +} + diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index 25ecf45898..8b6dc40302 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -16,35 +16,44 @@ #include +class QNetworkReply; + class LoginDialog : public OffscreenQmlDialog { Q_OBJECT HIFI_QML_DECL - Q_PROPERTY(QString statusText READ statusText WRITE setStatusText NOTIFY statusTextChanged) - Q_PROPERTY(QString rootUrl READ rootUrl) - public: static void toggleAction(); LoginDialog(QQuickItem* parent = nullptr); - void setStatusText(const QString& statusText); - QString statusText() const; - - QString rootUrl() const; - signals: - void statusTextChanged(); - -protected: - void handleLoginCompleted(const QUrl& authURL); + void handleLoginCompleted(); void handleLoginFailed(); - Q_INVOKABLE void login(const QString& username, const QString& password); - Q_INVOKABLE void openUrl(const QString& url); -private: - QString _statusText; - const QString _rootUrl; + void handleLinkCompleted(); + void handleLinkFailed(QString error); + + void handleCreateCompleted(); + void handleCreateFailed(QString error); + +public slots: + void linkCompleted(QNetworkReply& reply); + void linkFailed(QNetworkReply& reply); + + void createCompleted(QNetworkReply& reply); + void createFailed(QNetworkReply& reply); + +protected slots: + Q_INVOKABLE bool isSteamRunning() const; + + Q_INVOKABLE void login(const QString& username, const QString& password) const; + Q_INVOKABLE void loginThroughSteam(); + Q_INVOKABLE void linkSteam(); + Q_INVOKABLE void createAccountFromStream(QString username = QString()); + + Q_INVOKABLE void openUrl(const QString& url) const; + }; #endif // hifi_LoginDialog_h diff --git a/interface/src/ui/OctreeStatsDialog.h b/interface/src/ui/OctreeStatsDialog.h index 97f87849ec..67f5c01f65 100644 --- a/interface/src/ui/OctreeStatsDialog.h +++ b/interface/src/ui/OctreeStatsDialog.h @@ -33,15 +33,15 @@ signals: void closed(); public slots: - void reject(); + void reject() override; void moreless(const QString& link); protected: // State <- data model held by BandwidthMeter - void paintEvent(QPaintEvent*); + void paintEvent(QPaintEvent*) override; // Emits a 'closed' signal when this dialog is closed. - void closeEvent(QCloseEvent*); + void closeEvent(QCloseEvent*) override; int AddStatItem(const char* caption, unsigned colorRGBA = DEFAULT_COLOR); void RemoveStatItem(int item); diff --git a/interface/src/ui/ScriptEditBox.h b/interface/src/ui/ScriptEditBox.h index 41d881b904..0b037db16a 100644 --- a/interface/src/ui/ScriptEditBox.h +++ b/interface/src/ui/ScriptEditBox.h @@ -24,7 +24,7 @@ public: int lineNumberAreaWidth(); protected: - void resizeEvent(QResizeEvent* event); + void resizeEvent(QResizeEvent* event) override; private slots: void updateLineNumberAreaWidth(int blockCount); diff --git a/interface/src/ui/ScriptEditorWindow.h b/interface/src/ui/ScriptEditorWindow.h index 185a0f40bd..af9863d136 100644 --- a/interface/src/ui/ScriptEditorWindow.h +++ b/interface/src/ui/ScriptEditorWindow.h @@ -35,8 +35,8 @@ signals: void windowActivated(); protected: - void closeEvent(QCloseEvent* event); - virtual bool event(QEvent* event); + void closeEvent(QCloseEvent* event) override; + virtual bool event(QEvent* event) override; private: Ui::ScriptEditorWindow* _ScriptEditorWindowUI; diff --git a/interface/src/ui/ScriptLineNumberArea.h b/interface/src/ui/ScriptLineNumberArea.h index 00bd078170..77de8244ce 100644 --- a/interface/src/ui/ScriptLineNumberArea.h +++ b/interface/src/ui/ScriptLineNumberArea.h @@ -20,10 +20,10 @@ class ScriptLineNumberArea : public QWidget { public: ScriptLineNumberArea(ScriptEditBox* scriptEditBox); - QSize sizeHint() const; + QSize sizeHint() const override; protected: - void paintEvent(QPaintEvent* event); + void paintEvent(QPaintEvent* event) override; private: ScriptEditBox* _scriptEditBox; diff --git a/interface/src/ui/ScriptsTableWidget.h b/interface/src/ui/ScriptsTableWidget.h index 4b54130e82..f5e3407e97 100644 --- a/interface/src/ui/ScriptsTableWidget.h +++ b/interface/src/ui/ScriptsTableWidget.h @@ -21,8 +21,8 @@ public: explicit ScriptsTableWidget(QWidget* parent); protected: - virtual void paintEvent(QPaintEvent* event); - virtual void keyPressEvent(QKeyEvent* event); + virtual void paintEvent(QPaintEvent* event) override; + virtual void keyPressEvent(QKeyEvent* event) override; }; #endif // hifi__ScriptsTableWidget_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ec4b2280b6..7fdf5cd57d 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -136,6 +136,9 @@ void Stats::updateStats(bool force) { STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f); STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); + STAT_UPDATE_FLOAT(assetMbpsIn, (float)bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AssetServer) / 1000.0f, 0.01f); + STAT_UPDATE_FLOAT(assetMbpsOut, (float)bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AssetServer) / 1000.0f, 0.01f); + // Second column: ping SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index f6643a1a7a..4be2d88d9e 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -43,6 +43,8 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, packetOutCount, 0) STATS_PROPERTY(float, mbpsIn, 0) STATS_PROPERTY(float, mbpsOut, 0) + STATS_PROPERTY(float, assetMbpsIn, 0) + STATS_PROPERTY(float, assetMbpsOut, 0) STATS_PROPERTY(int, audioPing, 0) STATS_PROPERTY(int, avatarPing, 0) STATS_PROPERTY(int, entitiesPing, 0) @@ -128,6 +130,8 @@ signals: void packetOutCountChanged(); void mbpsInChanged(); void mbpsOutChanged(); + void assetMbpsInChanged(); + void assetMbpsOutChanged(); void audioPingChanged(); void avatarPingChanged(); void entitiesPingChanged(); diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 94533137ad..8f2149f02d 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -21,6 +21,7 @@ const bool DEFAULT_IS_SOLID = false; const bool DEFAULT_IS_DASHED_LINE = false; Base3DOverlay::Base3DOverlay() : + SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), _lineWidth(DEFAULT_LINE_WIDTH), _isSolid(DEFAULT_IS_SOLID), _isDashedLine(DEFAULT_IS_DASHED_LINE), @@ -31,15 +32,85 @@ Base3DOverlay::Base3DOverlay() : Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : Overlay(base3DOverlay), - _transform(base3DOverlay->_transform), + SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), _lineWidth(base3DOverlay->_lineWidth), _isSolid(base3DOverlay->_isSolid), _isDashedLine(base3DOverlay->_isDashedLine), _ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection), _drawInFront(base3DOverlay->_drawInFront) { + setTransform(base3DOverlay->getTransform()); } -void Base3DOverlay::setProperties(const QVariantMap& properties) { + +QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties) { + // the position and rotation in _transform are relative to the parent (aka local). The versions coming from + // scripts are in world-frame, unless localPosition or localRotation are used. Patch up the properties + // so that "position" and "rotation" are relative-to-parent values. + QVariantMap result = properties; + QUuid parentID = result["parentID"].isValid() ? QUuid(result["parentID"].toString()) : QUuid(); + int parentJointIndex = result["parentJointIndex"].isValid() ? result["parentJointIndex"].toInt() : -1; + bool success; + + // make "position" and "orientation" be relative-to-parent + if (result["localPosition"].isValid()) { + result["position"] = result["localPosition"]; + } else if (result["position"].isValid()) { + glm::vec3 localPosition = SpatiallyNestable::worldToLocal(vec3FromVariant(result["position"]), + parentID, parentJointIndex, success); + result["position"] = vec3toVariant(localPosition); + } + + if (result["localOrientation"].isValid()) { + result["orientation"] = result["localOrientation"]; + } else if (result["orientation"].isValid()) { + glm::quat localOrientation = SpatiallyNestable::worldToLocal(quatFromVariant(result["orientation"]), + parentID, parentJointIndex, success); + result["orientation"] = quatToVariant(localOrientation); + } + + return result; +} + +void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { + QVariantMap properties = originalProperties; + + // carry over some legacy keys + if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { + if (properties["p1"].isValid()) { + properties["position"] = properties["p1"]; + } else if (properties["point"].isValid()) { + properties["position"] = properties["point"]; + } else if (properties["start"].isValid()) { + properties["position"] = properties["start"]; + } + } + if (!properties["orientation"].isValid() && properties["rotation"].isValid()) { + properties["orientation"] = properties["rotation"]; + } + if (!properties["localOrientation"].isValid() && properties["localRotation"].isValid()) { + properties["localOrientation"] = properties["localRotation"]; + } + + // All of parentID, parentJointIndex, position, orientation are needed to make sense of any of them. + // If any of these changed, pull any missing properties from the overlay. + if (properties["parentID"].isValid() || properties["parentJointIndex"].isValid() || + properties["position"].isValid() || properties["localPosition"].isValid() || + properties["orientation"].isValid() || properties["localOrientation"].isValid()) { + if (!properties["parentID"].isValid()) { + properties["parentID"] = getParentID(); + } + if (!properties["parentJointIndex"].isValid()) { + properties["parentJointIndex"] = getParentJointIndex(); + } + if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { + properties["position"] = vec3toVariant(getPosition()); + } + if (!properties["orientation"].isValid() && !properties["localOrientation"].isValid()) { + properties["orientation"] = quatToVariant(getOrientation()); + } + } + + properties = convertOverlayLocationFromScriptSemantics(properties); Overlay::setProperties(properties); bool needRenderItemUpdate = false; @@ -52,17 +123,12 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) { needRenderItemUpdate = true; } - auto position = properties["position"]; - - // if "position" property was not there, check to see if they included aliases: point, p1 - if (!position.isValid()) { - position = properties["p1"]; - if (!position.isValid()) { - position = properties["point"]; - } + if (properties["position"].isValid()) { + setLocalPosition(vec3FromVariant(properties["position"])); + needRenderItemUpdate = true; } - if (position.isValid()) { - setPosition(vec3FromVariant(position)); + if (properties["orientation"].isValid()) { + setLocalOrientation(quatFromVariant(properties["orientation"])); needRenderItemUpdate = true; } @@ -71,13 +137,6 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) { needRenderItemUpdate = true; } - auto rotation = properties["rotation"]; - - if (rotation.isValid()) { - setRotation(quatFromVariant(rotation)); - needRenderItemUpdate = true; - } - if (properties["isSolid"].isValid()) { setIsSolid(properties["isSolid"].toBool()); } @@ -107,6 +166,15 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) { setIgnoreRayIntersection(properties["ignoreRayIntersection"].toBool()); } + if (properties["parentID"].isValid()) { + setParentID(QUuid(properties["parentID"].toString())); + needRenderItemUpdate = true; + } + if (properties["parentJointIndex"].isValid()) { + setParentJointIndex(properties["parentJointIndex"].toInt()); + needRenderItemUpdate = true; + } + // Communicate changes to the renderItem if needed if (needRenderItemUpdate) { auto itemID = getRenderItemID(); @@ -123,12 +191,18 @@ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "position" || property == "start" || property == "p1" || property == "point") { return vec3toVariant(getPosition()); } + if (property == "localPosition") { + return vec3toVariant(getLocalPosition()); + } + if (property == "rotation" || property == "orientation") { + return quatToVariant(getOrientation()); + } + if (property == "localRotation" || property == "localOrientation") { + return quatToVariant(getLocalOrientation()); + } if (property == "lineWidth") { return _lineWidth; } - if (property == "rotation") { - return quatToVariant(getRotation()); - } if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") { return _isSolid; } @@ -144,6 +218,12 @@ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "drawInFront") { return _drawInFront; } + if (property == "parentID") { + return getParentID(); + } + if (property == "parentJointIndex") { + return getParentJointIndex(); + } return Overlay::getProperty(property); } @@ -152,3 +232,19 @@ bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3 float& distance, BoxFace& face, glm::vec3& surfaceNormal) { return false; } + +void Base3DOverlay::locationChanged(bool tellPhysics) { + auto itemID = getRenderItemID(); + if (render::Item::isValidID(itemID)) { + render::ScenePointer scene = qApp->getMain3DScene(); + render::PendingChanges pendingChanges; + pendingChanges.updateItem(itemID); + scene->enqueuePendingChanges(pendingChanges); + } + // Overlays can't currently have children. + // SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also +} + +void Base3DOverlay::parentDeleted() { + qApp->getOverlays().deleteOverlay(getOverlayID()); +} diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index e602dec48c..1860af4e85 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -12,10 +12,11 @@ #define hifi_Base3DOverlay_h #include +#include #include "Overlay.h" -class Base3DOverlay : public Overlay { +class Base3DOverlay : public Overlay, public SpatiallyNestable { Q_OBJECT public: @@ -24,12 +25,9 @@ public: // getters virtual bool is3D() const override { return true; } - const glm::vec3& getPosition() const { return _transform.getTranslation(); } - const glm::quat& getRotation() const { return _transform.getRotation(); } - const glm::vec3& getScale() const { return _transform.getScale(); } // TODO: consider implementing registration points in this class - const glm::vec3& getCenter() const { return getPosition(); } + glm::vec3 getCenter() const { return getPosition(); } float getLineWidth() const { return _lineWidth; } bool getIsSolid() const { return _isSolid; } @@ -38,12 +36,6 @@ public: bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; } bool getDrawInFront() const { return _drawInFront; } - // setters - void setPosition(const glm::vec3& value) { _transform.setTranslation(value); } - void setRotation(const glm::quat& value) { _transform.setRotation(value); } - void setScale(float value) { _transform.setScale(value); } - void setScale(const glm::vec3& value) { _transform.setScale(value); } - void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } void setIsSolid(bool isSolid) { _isSolid = isSolid; } void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; } @@ -64,7 +56,8 @@ public: } protected: - Transform _transform; + virtual void locationChanged(bool tellPhysics = true) override; + virtual void parentDeleted() override; float _lineWidth; bool _isSolid; diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 6ebfd5c71c..e9ee997aac 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -69,17 +69,17 @@ void Circle3DOverlay::render(RenderArgs* args) { // FIXME: THe line width of _lineWidth is not supported anymore, we ll need a workaround - auto transform = _transform; + auto transform = getTransform(); transform.postScale(glm::vec3(getDimensions(), 1.0f)); batch.setModelTransform(transform); - + // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise // we just draw a line... if (getIsSolid()) { if (!_quadVerticesID) { _quadVerticesID = geometryCache->allocateID(); } - + if (geometryChanged) { QVector points; QVector colors; diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index f72fb8d920..a61e442436 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -47,7 +47,7 @@ void Cube3DOverlay::render(RenderArgs* args) { auto geometryCache = DependencyManager::get(); auto pipeline = args->_pipeline; if (!pipeline) { - pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline(); + pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline(); } if (_isSolid) { @@ -55,7 +55,7 @@ void Cube3DOverlay::render(RenderArgs* args) { batch->setModelTransform(transform); geometryCache->renderSolidCubeInstance(*batch, cubeColor, pipeline); } else { - geometryCache->bindSimpleProgram(*batch, false, false, true, true); + geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); if (getIsDashedLine()) { transform.setScale(1.0f); batch->setModelTransform(transform); diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index d59e552779..a384c992ad 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -37,7 +37,11 @@ Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) : } void Image3DOverlay::update(float deltatime) { - applyTransformTo(_transform); + if (usecTimestampNow() > _transformExpiry) { + Transform transform = getTransform(); + applyTransformTo(transform); + setTransform(transform); + } } void Image3DOverlay::render(RenderArgs* args) { @@ -86,13 +90,14 @@ void Image3DOverlay::render(RenderArgs* args) { xColor color = getColor(); float alpha = getAlpha(); - applyTransformTo(_transform, true); - Transform transform = _transform; + Transform transform = getTransform(); + applyTransformTo(transform, true); + setTransform(transform); transform.postScale(glm::vec3(getDimensions(), 1.0f)); batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); - + DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) @@ -187,7 +192,10 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec float& distance, BoxFace& face, glm::vec3& surfaceNormal) { if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. - applyTransformTo(_transform, true); + Transform transform = getTransform(); + // XXX this code runs too often for this... + // applyTransformTo(transform, true); + // setTransform(transform); // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); @@ -197,7 +205,10 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize); // FIXME - face and surfaceNormal not being set - return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance); + return findRayRectangleIntersection(origin, direction, + transform.getRotation(), + transform.getTranslation(), + dimensions, distance); } return false; diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index c9a8b19f6a..c3a6c5920e 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -23,6 +23,7 @@ Line3DOverlay::Line3DOverlay() : Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : Base3DOverlay(line3DOverlay), + _start(line3DOverlay->_start), _end(line3DOverlay->_end), _geometryCacheID(DependencyManager::get()->allocateID()) { @@ -31,12 +32,46 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : Line3DOverlay::~Line3DOverlay() { } +glm::vec3 Line3DOverlay::getStart() const { + bool success; + glm::vec3 worldStart = localToWorld(_start, _parentID, _parentJointIndex, success); + if (!success) { + qDebug() << "Line3DOverlay::getStart failed"; + } + return worldStart; +} + +glm::vec3 Line3DOverlay::getEnd() const { + bool success; + glm::vec3 worldEnd = localToWorld(_end, _parentID, _parentJointIndex, success); + if (!success) { + qDebug() << "Line3DOverlay::getEnd failed"; + } + return worldEnd; +} + +void Line3DOverlay::setStart(const glm::vec3& start) { + bool success; + _start = worldToLocal(start, _parentID, _parentJointIndex, success); + if (!success) { + qDebug() << "Line3DOverlay::setStart failed"; + } +} + +void Line3DOverlay::setEnd(const glm::vec3& end) { + bool success; + _end = worldToLocal(end, _parentID, _parentJointIndex, success); + if (!success) { + qDebug() << "Line3DOverlay::setEnd failed"; + } +} + AABox Line3DOverlay::getBounds() const { auto extents = Extents{}; extents.addPoint(_start); extents.addPoint(_end); - extents.transform(_transform); - + extents.transform(getTransform()); + return AABox(extents); } @@ -52,17 +87,17 @@ void Line3DOverlay::render(RenderArgs* args) { auto batch = args->_batch; if (batch) { - batch->setModelTransform(_transform); + batch->setModelTransform(getTransform()); auto geometryCache = DependencyManager::get(); if (getIsDashedLine()) { // TODO: add support for color to renderDashedLine() - geometryCache->bindSimpleProgram(*batch, false, false, true, true); + geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); geometryCache->renderDashedLine(*batch, _start, _end, colorv4, _geometryCacheID); } else if (_glow > 0.0f) { geometryCache->renderGlowLine(*batch, _start, _end, colorv4, _glow, _glowWidth, _geometryCacheID); } else { - geometryCache->bindSimpleProgram(*batch, false, false, true, true); + geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); geometryCache->renderLine(*batch, _start, _end, colorv4, _geometryCacheID); } } @@ -76,8 +111,8 @@ const render::ShapeKey Line3DOverlay::getShapeKey() { return builder.build(); } -void Line3DOverlay::setProperties(const QVariantMap& properties) { - Base3DOverlay::setProperties(properties); +void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { + QVariantMap properties = originalProperties; auto start = properties["start"]; // if "start" property was not there, check to see if they included aliases: startPoint @@ -87,6 +122,7 @@ void Line3DOverlay::setProperties(const QVariantMap& properties) { if (start.isValid()) { setStart(vec3FromVariant(start)); } + properties.remove("start"); // so that Base3DOverlay doesn't respond to it auto end = properties["end"]; // if "end" property was not there, check to see if they included aliases: endPoint @@ -109,14 +145,16 @@ void Line3DOverlay::setProperties(const QVariantMap& properties) { if (glowWidth.isValid()) { setGlow(glowWidth.toFloat()); } + + Base3DOverlay::setProperties(properties); } QVariant Line3DOverlay::getProperty(const QString& property) { if (property == "start" || property == "startPoint" || property == "p1") { - return vec3toVariant(_start); + return vec3toVariant(getStart()); } if (property == "end" || property == "endPoint" || property == "p2") { - return vec3toVariant(_end); + return vec3toVariant(getEnd()); } return Base3DOverlay::getProperty(property); @@ -125,3 +163,8 @@ QVariant Line3DOverlay::getProperty(const QString& property) { Line3DOverlay* Line3DOverlay::createClone() const { return new Line3DOverlay(this); } + + +void Line3DOverlay::locationChanged(bool tellPhysics) { + // do nothing +} diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index d066677c70..b4e2ba8168 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -28,14 +28,15 @@ public: virtual AABox getBounds() const override; // getters - const glm::vec3& getStart() const { return _start; } - const glm::vec3& getEnd() const { return _end; } + glm::vec3 getStart() const; + glm::vec3 getEnd() const; const float& getGlow() const { return _glow; } const float& getGlowWidth() const { return _glowWidth; } // setters - void setStart(const glm::vec3& start) { _start = start; } - void setEnd(const glm::vec3& end) { _end = end; } + void setStart(const glm::vec3& start); + void setEnd(const glm::vec3& end); + void setGlow(const float& glow) { _glow = glow; } void setGlowWidth(const float& glowWidth) { _glowWidth = glowWidth; } @@ -44,6 +45,8 @@ public: virtual Line3DOverlay* createClone() const override; + virtual void locationChanged(bool tellPhysics = true) override; + protected: glm::vec3 _start; glm::vec3 _end; diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h index 011f3dfb11..6bab7f5f86 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.h +++ b/interface/src/ui/overlays/LocalModelsOverlay.h @@ -20,15 +20,15 @@ class LocalModelsOverlay : public Volume3DOverlay { Q_OBJECT public: static QString const TYPE; - virtual QString getType() const { return TYPE; } + virtual QString getType() const override { return TYPE; } LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer); LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay); - - virtual void update(float deltatime); - virtual void render(RenderArgs* args); - virtual LocalModelsOverlay* createClone() const; + virtual void update(float deltatime) override; + virtual void render(RenderArgs* args) override; + + virtual LocalModelsOverlay* createClone() const override; private: EntityTreeRenderer* _entityTreeRenderer; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 9c203c0129..0a89268f6b 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -178,3 +178,12 @@ bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const g ModelOverlay* ModelOverlay::createClone() const { return new ModelOverlay(this); } + +void ModelOverlay::locationChanged(bool tellPhysics) { + Base3DOverlay::locationChanged(tellPhysics); + + if (_model && _model->isActive()) { + _model->setRotation(getRotation()); + _model->setTranslation(getPosition()); + } +} diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 091cab44c9..d5f709c2db 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -39,6 +39,8 @@ public: virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) override; virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) override; + void locationChanged(bool tellPhysics) override; + private: ModelPointer _model; diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 5d2c315a92..82b90d228c 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -77,7 +77,7 @@ void Overlay::setProperties(const QVariantMap& properties) { if (properties["pulsePeriod"].isValid()) { setPulsePeriod(properties["pulsePeriod"].toFloat()); } - + if (properties["alphaPulse"].isValid()) { setAlphaPulse(properties["alphaPulse"].toFloat()); } @@ -90,7 +90,7 @@ void Overlay::setProperties(const QVariantMap& properties) { bool visible = properties["visible"].toBool(); setVisible(visible); } - + if (properties["anchor"].isValid()) { QString property = properties["anchor"].toString(); if (property == "MyAvatar") { diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 466ec0e913..51792b24b3 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -17,7 +17,7 @@ class Overlay : public QObject { Q_OBJECT - + public: enum Anchor { NO_ANCHOR, @@ -31,9 +31,13 @@ public: Overlay(); Overlay(const Overlay* overlay); ~Overlay(); + + unsigned int getOverlayID() { return _overlayID; } + void setOverlayID(unsigned int overlayID) { _overlayID = overlayID; } + virtual void update(float deltatime) {} virtual void render(RenderArgs* args) = 0; - + virtual AABox getBounds() const = 0; virtual bool supportsGetProperty() const { return true; } @@ -85,6 +89,8 @@ protected: render::ItemID _renderItemID{ render::Item::INVALID_ITEM_ID }; + unsigned int _overlayID; // what Overlays.cpp knows this instance as + bool _isLoaded; float _alpha; diff --git a/interface/src/ui/overlays/OverlayPanel.h b/interface/src/ui/overlays/OverlayPanel.h index df553883f1..b0b8cdb989 100644 --- a/interface/src/ui/overlays/OverlayPanel.h +++ b/interface/src/ui/overlays/OverlayPanel.h @@ -62,7 +62,7 @@ public: void setProperties(const QVariantMap& properties); QVariant getProperty(const QString& property); - virtual void applyTransformTo(Transform& transform, bool force = false); + virtual void applyTransformTo(Transform& transform, bool force = false) override; private: Transform _anchorTransform; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 0b4bcc8652..242821234a 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -121,7 +121,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) { batch.setResourceTexture(0, textureCache->getWhiteTexture()); // FIXME - do we really need to do this?? batch.setProjectionTransform(legacyProjection); batch.setModelTransform(Transform()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); thisOverlay->render(renderArgs); } @@ -192,6 +192,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QVariant& propertie unsigned int Overlays::addOverlay(Overlay::Pointer overlay) { QWriteLocker lock(&_lock); unsigned int thisID = _nextOverlayID; + overlay->setOverlayID(thisID); _nextOverlayID++; if (overlay->is3D()) { _overlaysWorld[thisID] = overlay; diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 99f74fa0f9..e19a6b36a9 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -93,7 +93,7 @@ public slots: /// successful edit, if the input id is for an unknown overlay this function will have no effect bool editOverlays(const QVariant& propertiesById); - /// deletes a particle + /// deletes an overlay void deleteOverlay(unsigned int id); /// get the string type of the overlay used in addOverlay diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index c580464e16..58d72b100b 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -28,10 +28,10 @@ Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) : AABox Planar3DOverlay::getBounds() const { auto halfDimensions = glm::vec3{_dimensions / 2.0f, 0.01f}; - + auto extents = Extents{-halfDimensions, halfDimensions}; - extents.transform(_transform); - + extents.transform(getTransform()); + return AABox(extents); } diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 75d7ec565c..35c479dce6 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -61,7 +61,7 @@ void Rectangle3DOverlay::render(RenderArgs* args) { geometryCache->bindSimpleProgram(*batch); geometryCache->renderQuad(*batch, topLeft, bottomRight, rectangleColor); } else { - geometryCache->bindSimpleProgram(*batch, false, false, true, true); + geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); if (getIsDashedLine()) { glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); diff --git a/interface/src/ui/overlays/RectangleOverlay.h b/interface/src/ui/overlays/RectangleOverlay.h index 06e2fb228c..c429210c42 100644 --- a/interface/src/ui/overlays/RectangleOverlay.h +++ b/interface/src/ui/overlays/RectangleOverlay.h @@ -14,14 +14,13 @@ class RectangleOverlay : public QmlOverlay { public: static QString const TYPE; - virtual QString getType() const { return TYPE; } + virtual QString getType() const override { return TYPE; } static QUrl const URL; RectangleOverlay(); RectangleOverlay(const RectangleOverlay* RectangleOverlay); - virtual RectangleOverlay* createClone() const; + virtual RectangleOverlay* createClone() const override; }; - #endif // hifi_RectangleOverlay_h diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index cd07385aab..2556bc84aa 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -47,7 +47,7 @@ void Shape3DOverlay::render(RenderArgs* args) { auto geometryCache = DependencyManager::get(); auto pipeline = args->_pipeline; if (!pipeline) { - pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline(); + pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline(); } transform.setScale(dimensions); diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index bbdd886d11..07c2861f16 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -39,14 +39,14 @@ void Sphere3DOverlay::render(RenderArgs* args) { auto batch = args->_batch; if (batch) { - Transform transform = _transform; + Transform transform = getTransform(); transform.postScale(getDimensions() * SPHERE_OVERLAY_SCALE); batch->setModelTransform(transform); auto geometryCache = DependencyManager::get(); auto pipeline = args->_pipeline; if (!pipeline) { - pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline(); + pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline(); } if (_isSolid) { diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 0ae1c306ba..153f787fc7 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -65,44 +65,49 @@ xColor Text3DOverlay::getBackgroundColor() { } void Text3DOverlay::update(float deltatime) { - applyTransformTo(_transform); + if (usecTimestampNow() > _transformExpiry) { + Transform transform = getTransform(); + applyTransformTo(transform); + setTransform(transform); + } } void Text3DOverlay::render(RenderArgs* args) { if (!_visible || !getParentVisible()) { return; // do nothing if we're not visible } - + Q_ASSERT(args->_batch); auto& batch = *args->_batch; - - applyTransformTo(_transform, true); - batch.setModelTransform(_transform); + + Transform transform = getTransform(); + applyTransformTo(transform, true); + setTransform(transform); + batch.setModelTransform(transform); const float MAX_COLOR = 255.0f; xColor backgroundColor = getBackgroundColor(); glm::vec4 quadColor(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, getBackgroundAlpha()); - + glm::vec2 dimensions = getDimensions(); glm::vec2 halfDimensions = dimensions * 0.5f; - + const float SLIGHTLY_BEHIND = -0.001f; - + glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, quadColor); - + // Same font properties as textSize() float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; - + float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; - + glm::vec2 clipMinimum(0.0f, 0.0f); glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor, (dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor); - Transform transform = _transform; transform.postTranslate(glm::vec3(-(halfDimensions.x - _leftMargin), halfDimensions.y - _topMargin, 0.001f)); transform.setScale(scaleFactor); @@ -222,6 +227,8 @@ QSizeF Text3DOverlay::textSize(const QString& text) const { bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance, BoxFace &face, glm::vec3& surfaceNormal) { - applyTransformTo(_transform, true); + Transform transform = getTransform(); + applyTransformTo(transform, true); + setTransform(transform); return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal); } diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index 563198c976..ad61e28bc7 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -56,7 +56,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve float& distance, BoxFace& face, glm::vec3& surfaceNormal) { // extents is the entity relative, scaled, centered extents of the entity glm::mat4 worldToEntityMatrix; - _transform.getInverseMatrix(worldToEntityMatrix); + getTransform().getInverseMatrix(worldToEntityMatrix); glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 overlayFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)); diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 1c84e71fa7..6ca3e49569 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -47,7 +47,7 @@ Web3DOverlay::~Web3DOverlay() { _webSurface->disconnect(_connection); // The lifetime of the QML surface MUST be managed by the main thread // Additionally, we MUST use local variables copied by value, rather than - // member variables, since they would implicitly refer to a this that + // member variables, since they would implicitly refer to a this that // is no longer valid auto webSurface = _webSurface; AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] { @@ -57,7 +57,11 @@ Web3DOverlay::~Web3DOverlay() { } void Web3DOverlay::update(float deltatime) { - applyTransformTo(_transform); + if (usecTimestampNow() > _transformExpiry) { + Transform transform = getTransform(); + applyTransformTo(transform); + setTransform(transform); + } } void Web3DOverlay::render(RenderArgs* args) { @@ -85,8 +89,9 @@ void Web3DOverlay::render(RenderArgs* args) { vec2 halfSize = size / 2.0f; vec4 color(toGlm(getColor()), getAlpha()); - applyTransformTo(_transform, true); - Transform transform = _transform; + Transform transform = getTransform(); + applyTransformTo(transform, true); + setTransform(transform); if (glm::length2(getDimensions()) != 1.0f) { transform.postScale(vec3(getDimensions(), 1.0f)); } @@ -165,7 +170,10 @@ bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& // FIXME - face and surfaceNormal not being returned // Make sure position and rotation is updated. - applyTransformTo(_transform, true); + Transform transform; + applyTransformTo(transform, true); + setTransform(transform); + vec2 size = _resolution / _dpi * INCHES_TO_METERS * vec2(getDimensions()); // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), size, distance); diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index 9da649e66e..a7d8700fed 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -34,9 +34,9 @@ public: Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url); protected: - + virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra); + const void* extra) override; private: explicit AnimationCache(QObject* parent = NULL); virtual ~AnimationCache() { } @@ -82,7 +82,7 @@ class AnimationReader : public QObject, public QRunnable { public: AnimationReader(const QUrl& url, const QByteArray& data); - virtual void run(); + virtual void run() override; signals: void onSuccess(FBXGeometry::Pointer geometry); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 171014edda..b83547ce00 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -86,7 +86,7 @@ public: using Mutex = std::mutex; using Lock = std::unique_lock; - + class AudioOutputIODevice : public QIODevice { public: AudioOutputIODevice(MixedProcessedAudioStream& receivedAudioStream, AudioClient* audio) : @@ -94,8 +94,8 @@ public: void start() { open(QIODevice::ReadOnly); } void stop() { close(); } - qint64 readData(char * data, qint64 maxSize); - qint64 writeData(const char * data, qint64 maxSize) { return 0; } + qint64 readData(char * data, qint64 maxSize) override; + qint64 writeData(const char * data, qint64 maxSize) override { return 0; } int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; } private: MixedProcessedAudioStream& _receivedAudioStream; @@ -136,7 +136,7 @@ public: void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } - + QVector& getActiveLocalAudioInjectors() { return _activeLocalAudioInjectors; } static const float CALLBACK_ACCELERATOR_RATIO; @@ -163,7 +163,7 @@ public slots: void audioMixerKilled(); void toggleMute(); - virtual void setIsStereoInput(bool stereo); + virtual void setIsStereoInput(bool stereo) override; void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; } @@ -175,7 +175,7 @@ public slots: int setOutputBufferSize(int numFrames, bool persist = true); - virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector); + virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector) override; bool switchInputToAudioDevice(const QString& inputDeviceName); bool switchOutputToAudioDevice(const QString& outputDeviceName); @@ -215,7 +215,7 @@ protected: AudioClient(); ~AudioClient(); - virtual void customDeleter() { + virtual void customDeleter() override { deleteLater(); } @@ -316,7 +316,7 @@ private: void checkDevices(); bool _hasReceivedFirstPacket = false; - + QVector _activeLocalAudioInjectors; CodecPluginPointer _codec; diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index 32a1221b78..988b622ce7 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -20,27 +20,27 @@ class AudioInjectorLocalBuffer : public QIODevice { Q_OBJECT public: AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent); - + void stop(); - - bool seek(qint64 pos); - - qint64 readData(char* data, qint64 maxSize); - qint64 writeData(const char* data, qint64 maxSize) { return 0; } - + + bool seek(qint64 pos) override; + + qint64 readData(char* data, qint64 maxSize) override; + qint64 writeData(const char* data, qint64 maxSize) override { return 0; } + void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; } void setCurrentOffset(int currentOffset) { _currentOffset = currentOffset; } void setVolume(float volume) { _volume = glm::clamp(volume, 0.0f, 1.0f); } - + private: qint64 recursiveReadFromFront(char* data, qint64 maxSize); - + QByteArray _rawAudioArray; bool _shouldLoop; bool _isStopped; - + int _currentOffset; float _volume; }; -#endif // hifi_AudioInjectorLocalBuffer_h \ No newline at end of file +#endif // hifi_AudioInjectorLocalBuffer_h diff --git a/libraries/audio/src/AudioLimiter.cpp b/libraries/audio/src/AudioLimiter.cpp index d9257b7df5..7bbaca62ca 100644 --- a/libraries/audio/src/AudioLimiter.cpp +++ b/libraries/audio/src/AudioLimiter.cpp @@ -567,7 +567,7 @@ class LimiterMono : public LimiterImpl { public: LimiterMono(int sampleRate) : LimiterImpl(sampleRate) {} - void process(float* input, int16_t* output, int numFrames); + void process(float* input, int16_t* output, int numFrames) override; }; template @@ -619,7 +619,7 @@ public: LimiterStereo(int sampleRate) : LimiterImpl(sampleRate) {} // interleaved stereo input/output - void process(float* input, int16_t* output, int numFrames); + void process(float* input, int16_t* output, int numFrames) override; }; template diff --git a/libraries/audio/src/MixedProcessedAudioStream.h b/libraries/audio/src/MixedProcessedAudioStream.h index 2f9a691278..b2127abcf7 100644 --- a/libraries/audio/src/MixedProcessedAudioStream.h +++ b/libraries/audio/src/MixedProcessedAudioStream.h @@ -22,7 +22,7 @@ public: MixedProcessedAudioStream(int numFrameSamples, int numFramesCapacity, const InboundAudioStream::Settings& settings); signals: - + void addedSilence(int silentSamplesPerChannel); void addedLastFrameRepeatedWithFade(int samplesPerChannel); void addedStereoSamples(const QByteArray& samples); @@ -33,9 +33,9 @@ public: void outputFormatChanged(int outputFormatChannelCountTimesSampleRate); protected: - int writeDroppableSilentSamples(int silentSamples); - int writeLastFrameRepeatedWithFade(int samples); - int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties); + int writeDroppableSilentSamples(int silentSamples) override; + int writeLastFrameRepeatedWithFade(int samples) override; + int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) override; private: int networkToDeviceSamples(int networkSamples); diff --git a/libraries/audio/src/PositionalAudioStream.h b/libraries/audio/src/PositionalAudioStream.h index 5b50cfa60e..a330fbdd3f 100644 --- a/libraries/audio/src/PositionalAudioStream.h +++ b/libraries/audio/src/PositionalAudioStream.h @@ -32,9 +32,9 @@ public: const QUuid DEFAULT_STREAM_IDENTIFIER = QUuid(); virtual const QUuid& getStreamIdentifier() const { return DEFAULT_STREAM_IDENTIFIER; } - virtual void resetStats(); + virtual void resetStats() override; - virtual AudioStreamStats getAudioStreamStats() const; + virtual AudioStreamStats getAudioStreamStats() const override; void updateLastPopOutputLoudnessAndTrailingLoudness(); float getLastPopOutputTrailingLoudness() const { return _lastPopOutputTrailingLoudness; } @@ -46,7 +46,7 @@ public: PositionalAudioStream::Type getType() const { return _type; } const glm::vec3& getPosition() const { return _position; } const glm::quat& getOrientation() const { return _orientation; } - + protected: // disallow copying of PositionalAudioStream objects diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h index 59a25dd847..97d5f659d8 100644 --- a/libraries/audio/src/SoundCache.h +++ b/libraries/audio/src/SoundCache.h @@ -23,12 +23,12 @@ class SoundCache : public ResourceCache, public Dependency { public: Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); - + protected: virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra); + const void* extra) override; private: SoundCache(QObject* parent = NULL); }; -#endif // hifi_SoundCache_h \ No newline at end of file +#endif // hifi_SoundCache_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index dee0d1cb20..d0c7b3912c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1638,7 +1638,9 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) { for (const auto& attachmentVar : variant) { AttachmentData attachment; attachment.fromVariant(attachmentVar); - newAttachments.append(attachment); + if (!attachment.modelURL.isEmpty()) { + newAttachments.append(attachment); + } } setAttachmentData(newAttachments); } diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h index 958914264e..24d5ec93e9 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h @@ -24,11 +24,11 @@ public: : Endpoint(Input::INVALID_INPUT), _callable(callable) { } - virtual float peek() const { + virtual float peek() const override { return (float)const_cast(this)->_callable.call().toNumber(); } - virtual void apply(float newValue, const Pointer& source) { + virtual void apply(float newValue, const Pointer& source) override { _callable.call(QJSValueList({ QJSValue(newValue) })); } diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index fe08647074..315e7510a5 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -1,6 +1,7 @@ set(TARGET_NAME display-plugins) +AUTOSCRIBE_SHADER_LIB(gpu display-plugins) setup_hifi_library(OpenGL) -link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui) +link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui render-utils) target_opengl() diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index f488a805c6..588c43d534 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -33,18 +33,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() { return Parent::internalActivate(); } -void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) { - _wantVsync = true; // always - Parent::submitSceneTexture(frameIndex, sceneTexture); -} - -void Basic2DWindowOpenGLDisplayPlugin::internalPresent() { - if (_wantVsync != isVsyncEnabled()) { - enableVsync(_wantVsync); - } - Parent::internalPresent(); -} - static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60; bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 6375425243..2e4e57e15a 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -24,10 +24,6 @@ public: virtual bool internalActivate() override; - virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; - - virtual void internalPresent() override; - virtual bool isThrottled() const override; protected: @@ -40,5 +36,4 @@ private: QAction* _vsyncAction { nullptr }; uint32_t _framerateTarget { 0 }; int _fullscreenTarget{ -1 }; - bool _wantVsync { true }; }; diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 08368597d0..3e090dc7b3 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -12,6 +12,7 @@ #include "NullDisplayPlugin.h" #include "stereo/SideBySideStereoDisplayPlugin.h" #include "stereo/InterleavedStereoDisplayPlugin.h" +#include "hmd/DebugHmdDisplayPlugin.h" #include "Basic2DWindowOpenGLDisplayPlugin.h" const QString& DisplayPlugin::MENU_PATH() { @@ -23,6 +24,7 @@ const QString& DisplayPlugin::MENU_PATH() { DisplayPluginList getDisplayPlugins() { DisplayPlugin* PLUGIN_POOL[] = { new Basic2DWindowOpenGLDisplayPlugin(), + new DebugHmdDisplayPlugin(), #ifdef DEBUG new NullDisplayPlugin(), #endif diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 4fadbdb94b..5ee05fa2e3 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -11,6 +11,9 @@ #include #include +#include +#include +#include const QString NullDisplayPlugin::NAME("NullDisplayPlugin"); @@ -22,12 +25,10 @@ bool NullDisplayPlugin::hasFocus() const { return false; } -void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) { - _container->releaseSceneTexture(sceneTexture); -} - -void NullDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) { - _container->releaseOverlayTexture(overlayTexture); +void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) { + if (frame) { + _gpuContext->consumeFrameUpdates(frame); + } } QImage NullDisplayPlugin::getScreenshot() const { diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index dfa4232a86..198c89ae78 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -11,16 +11,14 @@ class NullDisplayPlugin : public DisplayPlugin { public: + ~NullDisplayPlugin() final {} + const QString& getName() const override { return NAME; } + grouping getGrouping() const override { return DEVELOPER; } - virtual ~NullDisplayPlugin() final {} - virtual const QString& getName() const override { return NAME; } - virtual grouping getGrouping() const override { return DEVELOPER; } - - virtual glm::uvec2 getRecommendedRenderSize() const override; - virtual bool hasFocus() const override; - virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; - virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override; - virtual QImage getScreenshot() const override; + glm::uvec2 getRecommendedRenderSize() const override; + bool hasFocus() const override; + void submitFrame(const gpu::FramePointer& newFrame) override; + QImage getScreenshot() const override; private: static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index e0c87fbbed..7a57e1d0f2 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -8,6 +8,7 @@ #include "OpenGLDisplayPlugin.h" #include +#include #include #include @@ -19,26 +20,56 @@ #if defined(Q_OS_MAC) #include #endif -#include -#include -#include + #include #include -#include -#include +#include + +#include +#include #include #include -#include -#include -#include "CompositorHelper.h" + +#include +#include +#include +#include +#include + +#include +#include +#include #include +#include +#include "CompositorHelper.h" -#if THREADED_PRESENT +const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE( -// FIXME, for display plugins that don't block on something like vsync, just -// cap the present rate at 200 -// const static unsigned int MAX_PRESENT_RATE = 200; +uniform sampler2D colorMap; + +in vec2 varTexCoord0; + +out vec4 outFragColor; + +float sRGBFloatToLinear(float value) { + const float SRGB_ELBOW = 0.04045; + + return (value <= SRGB_ELBOW) ? value / 12.92 : pow((value + 0.055) / 1.055, 2.4); +} + +vec3 colorToLinearRGB(vec3 srgb) { + return vec3(sRGBFloatToLinear(srgb.r), sRGBFloatToLinear(srgb.g), sRGBFloatToLinear(srgb.b)); +} + +void main(void) { + outFragColor.a = 1.0; + outFragColor.rgb = colorToLinearRGB(texture(colorMap, varTexCoord0).rgb); +} + +)SCRIBE"; + +extern QThread* RENDER_THREAD; class PresentThread : public QThread, public Dependency { using Mutex = std::mutex; @@ -86,13 +117,22 @@ public: virtual void run() override { + // FIXME determine the best priority balance between this and the main thread... + // It may be dependent on the display plugin being used, since VR plugins should + // have higher priority on rendering (although we could say that the Oculus plugin + // doesn't need that since it has async timewarp). + // A higher priority here + setPriority(QThread::HighPriority); OpenGLDisplayPlugin* currentPlugin{ nullptr }; - thread()->setPriority(QThread::HighestPriority); Q_ASSERT(_context); + _context->makeCurrent(); + Q_ASSERT(isCurrentContext(_context->contextHandle())); while (!_shutdown) { if (_pendingMainThreadOperation) { + PROFILE_RANGE("MainThreadOp") { Lock lock(_mutex); + _context->doneCurrent(); // Move the context to the main thread _context->moveToThread(qApp->thread()); _pendingMainThreadOperation = false; @@ -111,33 +151,46 @@ public: // Check for a new display plugin { Lock lock(_mutex); - _context->makeCurrent(); // Check if we have a new plugin to activate while (!_newPluginQueue.empty()) { auto newPlugin = _newPluginQueue.front(); if (newPlugin != currentPlugin) { // Deactivate the old plugin if (currentPlugin != nullptr) { - try { - currentPlugin->uncustomizeContext(); - } catch (const oglplus::Error& error) { - qWarning() << "OpenGL error in uncustomizeContext: " << error.what(); - } + _context->makeCurrent(); + currentPlugin->uncustomizeContext(); + CHECK_GL_ERROR(); + _context->doneCurrent(); } if (newPlugin) { - try { - newPlugin->customizeContext(); - } catch (const oglplus::Error& error) { - qWarning() << "OpenGL error in customizeContext: " << error.what(); - } + bool hasVsync = true; + bool wantVsync = newPlugin->wantVsync(); + _context->makeCurrent(); +#if defined(Q_OS_WIN) + wglSwapIntervalEXT(wantVsync ? 1 : 0); + hasVsync = wglGetSwapIntervalEXT() != 0; +#elif defined(Q_OS_MAC) + GLint interval = wantVsync ? 1 : 0; + newPlugin->swapBuffers(); + CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); + newPlugin->swapBuffers(); + CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); + hasVsync = interval != 0; +#else + // TODO: Fill in for linux + Q_UNUSED(wantVsync); +#endif + newPlugin->setVsyncEnabled(hasVsync); + newPlugin->customizeContext(); + CHECK_GL_ERROR(); + _context->doneCurrent(); } currentPlugin = newPlugin; _newPluginQueue.pop(); _condition.notify_one(); } } - _context->doneCurrent(); } // If there's no active plugin, just sleep @@ -147,18 +200,14 @@ public: continue; } - // take the latest texture and present it + // Execute the frame and present it to the display device. _context->makeCurrent(); - if (isCurrentContext(_context->contextHandle())) { - try { - currentPlugin->present(); - } catch (const oglplus::Error& error) { - qWarning() << "OpenGL error in presentation: " << error.what(); - } - _context->doneCurrent(); - } else { - qWarning() << "Makecurrent failed"; + { + PROFILE_RANGE("PluginPresent") + currentPlugin->present(); + CHECK_GL_ERROR(); } + _context->doneCurrent(); } Lock lock(_mutex); @@ -204,27 +253,6 @@ private: QGLContext* _context { nullptr }; }; -#endif - - -OpenGLDisplayPlugin::OpenGLDisplayPlugin() { - _sceneTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture){ - cleanupForSceneTexture(texture); - _container->releaseSceneTexture(texture); - }); - _overlayTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture) { - _container->releaseOverlayTexture(texture); - }); -} - -void OpenGLDisplayPlugin::cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture) { - withRenderThreadLock([&] { - Q_ASSERT(_sceneTextureToFrameIndexMap.contains(sceneTexture)); - _sceneTextureToFrameIndexMap.remove(sceneTexture); - }); -} - - bool OpenGLDisplayPlugin::activate() { if (!_cursorsData.size()) { auto& cursorManager = Cursor::Manager::instance(); @@ -242,9 +270,7 @@ bool OpenGLDisplayPlugin::activate() { if (!_container) { return false; } - _vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported(); -#if THREADED_PRESENT // Start the present thread if necessary QSharedPointer presentThread; if (DependencyManager::isSet()) { @@ -259,7 +285,9 @@ bool OpenGLDisplayPlugin::activate() { presentThread->start(); } _presentThread = presentThread.data(); -#endif + if (!RENDER_THREAD) { + RENDER_THREAD = _presentThread; + } // Child classes may override this in order to do things like initialize // libraries, etc @@ -267,17 +295,10 @@ bool OpenGLDisplayPlugin::activate() { return false; } -#if THREADED_PRESENT // This should not return until the new context has been customized // and the old context (if any) has been uncustomized presentThread->setNewDisplayPlugin(this); -#else - static auto widget = _container->getPrimaryWidget(); - widget->makeCurrent(); - customizeContext(); - _container->makeRenderingContextCurrent(); -#endif auto compositorHelper = DependencyManager::get(); connect(compositorHelper.data(), &CompositorHelper::alphaChanged, [this] { @@ -296,20 +317,12 @@ bool OpenGLDisplayPlugin::activate() { } void OpenGLDisplayPlugin::deactivate() { - auto compositorHelper = DependencyManager::get(); disconnect(compositorHelper.data()); -#if THREADED_PRESENT auto presentThread = DependencyManager::get(); // Does not return until the GL transition has completeed presentThread->setNewDisplayPlugin(nullptr); -#else - static auto widget = _container->getPrimaryWidget(); - widget->makeCurrent(); - uncustomizeContext(); - _container->makeRenderingContextCurrent(); -#endif internalDeactivate(); _container->showDisplayPluginsTools(false); @@ -323,58 +336,99 @@ void OpenGLDisplayPlugin::deactivate() { Parent::deactivate(); } - void OpenGLDisplayPlugin::customizeContext() { -#if THREADED_PRESENT auto presentThread = DependencyManager::get(); Q_ASSERT(thread() == presentThread->thread()); -#endif - enableVsync(); + + getGLBackend()->setCameraCorrection(mat4()); for (auto& cursorValue : _cursorsData) { auto& cursorData = cursorValue.second; if (!cursorData.texture) { - const auto& image = cursorData.image; - glGenTextures(1, &cursorData.texture); - glBindTexture(GL_TEXTURE_2D, cursorData.texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glGenerateMipmap(GL_TEXTURE_2D); + auto image = cursorData.image; + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + if ((image.width() > 0) && (image.height() > 0)) { + + cursorData.texture.reset( + gpu::Texture::create2D( + gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), + image.width(), image.height(), + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); + cursorData.texture->setUsage(usage.build()); + cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); + cursorData.texture->autoGenerateMips(-1); + } } - glBindTexture(GL_TEXTURE_2D, 0); } - using namespace oglplus; - Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha); - Context::Disable(Capability::Blend); - Context::Disable(Capability::DepthTest); - Context::Disable(Capability::CullFace); - - _program = loadDefaultShader(); - - auto uniforms = _program->ActiveUniforms(); - while (!uniforms.Empty()) { - auto uniform = uniforms.Front(); - if (uniform.Name() == "mvp") { - _mvpUniform = uniform.Index(); + if (!_presentPipeline) { + { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::StandardShaderLib::getDrawTexturePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + state->setScissorEnable(true); + _simplePipeline = gpu::Pipeline::create(program, state); } - if (uniform.Name() == "alpha") { - _alphaUniform = uniform.Index(); + + { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + state->setScissorEnable(true); + _presentPipeline = gpu::Pipeline::create(program, state); + } + + { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::StandardShaderLib::getDrawTexturePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + 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); + _overlayPipeline = gpu::Pipeline::create(program, state); + } + + { + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); + auto ps = gpu::StandardShaderLib::getDrawTexturePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + 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); + _cursorPipeline = gpu::Pipeline::create(program, state); } - uniforms.Next(); } - - _plane = loadPlane(_program); - - _compositeFramebuffer = std::make_shared(); - _compositeFramebuffer->Init(getRecommendedRenderSize()); + auto renderSize = getRecommendedRenderSize(); + _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); } void OpenGLDisplayPlugin::uncustomizeContext() { + _presentPipeline.reset(); + _cursorPipeline.reset(); + _overlayPipeline.reset(); _compositeFramebuffer.reset(); - _program.reset(); - _plane.reset(); + withPresentThreadLock([&] { + _currentFrame.reset(); + while (!_newFrameQueue.empty()) { + _gpuContext->consumeFrameUpdates(_newFrameQueue.front()); + _newFrameQueue.pop(); + } + }); } @@ -420,172 +474,161 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { return false; } -void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) { +void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) { if (_lockCurrentTexture) { - _container->releaseSceneTexture(sceneTexture); return; } - withRenderThreadLock([&] { - _sceneTextureToFrameIndexMap[sceneTexture] = frameIndex; + withNonPresentThreadLock([&] { + _newFrameQueue.push(newFrame); }); - - // Submit it to the presentation thread via escrow - _sceneTextureEscrow.submit(sceneTexture); - -#if THREADED_PRESENT -#else - static auto widget = _container->getPrimaryWidget(); - widget->makeCurrent(); - present(); - _container->makeRenderingContextCurrent(); -#endif -} - -void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) { - // Submit it to the presentation thread via escrow - _overlayTextureEscrow.submit(overlayTexture); -} - -void OpenGLDisplayPlugin::updateTextures() { - // FIXME intrduce a GPU wait instead of a CPU/GPU sync point? -#if THREADED_PRESENT - if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) { -#else - if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) { -#endif - updateFrameData(); - _newFrameRate.increment(); - } - - _overlayTextureEscrow.fetchSignaledAndRelease(_currentOverlayTexture); } void OpenGLDisplayPlugin::updateFrameData() { withPresentThreadLock([&] { - auto previousFrameIndex = _currentPresentFrameIndex; - _currentPresentFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture]; - auto skippedCount = (_currentPresentFrameIndex - previousFrameIndex) - 1; + gpu::FramePointer oldFrame = _currentFrame; + uint32_t skippedCount = 0; + if (!_newFrameQueue.empty()) { + // We're changing frames, so we can cleanup any GL resources that might have been used by the old frame + _gpuContext->recycle(); + } + while (!_newFrameQueue.empty()) { + _currentFrame = _newFrameQueue.front(); + _newFrameQueue.pop(); + _gpuContext->consumeFrameUpdates(_currentFrame); + if (_currentFrame && oldFrame) { + skippedCount += (_currentFrame->frameIndex - oldFrame->frameIndex) - 1; + } + } _droppedFrameRate.increment(skippedCount); }); } void OpenGLDisplayPlugin::compositeOverlay() { - using namespace oglplus; - - auto compositorHelper = DependencyManager::get(); - - useProgram(_program); - // set the alpha - Uniform(*_program, _alphaUniform).Set(_compositeOverlayAlpha); - // check the alpha - // Overlay draw - if (isStereo()) { - Uniform(*_program, _mvpUniform).Set(mat4()); - for_each_eye([&](Eye eye) { - eyeViewport(eye); - drawUnitQuad(); - }); - } else { - // Overlay draw - Uniform(*_program, _mvpUniform).Set(mat4()); - drawUnitQuad(); - } - // restore the alpha - Uniform(*_program, _alphaUniform).Set(1.0); -} - -void OpenGLDisplayPlugin::compositePointer() { - using namespace oglplus; - auto compositorHelper = DependencyManager::get(); - - useProgram(_program); - // set the alpha - Uniform(*_program, _alphaUniform).Set(_compositeOverlayAlpha); - Uniform(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4())); - if (isStereo()) { - for_each_eye([&](Eye eye) { - eyeViewport(eye); - drawUnitQuad(); - }); - } else { - drawUnitQuad(); - } - Uniform(*_program, _mvpUniform).Set(mat4()); - // restore the alpha - Uniform(*_program, _alphaUniform).Set(1.0); -} - -void OpenGLDisplayPlugin::compositeScene() { - using namespace oglplus; - useProgram(_program); - Uniform(*_program, _mvpUniform).Set(mat4()); - drawUnitQuad(); -} - -void OpenGLDisplayPlugin::compositeLayers() { - using namespace oglplus; - auto targetRenderSize = getRecommendedRenderSize(); - if (!_compositeFramebuffer || _compositeFramebuffer->size != targetRenderSize) { - _compositeFramebuffer = std::make_shared(); - _compositeFramebuffer->Init(targetRenderSize); - } - _compositeFramebuffer->Bound(Framebuffer::Target::Draw, [&] { - Context::Viewport(targetRenderSize.x, targetRenderSize.y); - auto sceneTextureId = getSceneTextureId(); - auto overlayTextureId = getOverlayTextureId(); - glBindTexture(GL_TEXTURE_2D, sceneTextureId); - compositeScene(); - if (overlayTextureId) { - glBindTexture(GL_TEXTURE_2D, overlayTextureId); - Context::Enable(Capability::Blend); - Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha); - compositeOverlay(); - - auto compositorHelper = DependencyManager::get(); - if (compositorHelper->getReticleVisible()) { - auto& cursorManager = Cursor::Manager::instance(); - const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; - glBindTexture(GL_TEXTURE_2D, cursorData.texture); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, overlayTextureId); - compositePointer(); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - } - glBindTexture(GL_TEXTURE_2D, 0); - Context::Disable(Capability::Blend); + render([&](gpu::Batch& batch){ + batch.enableStereo(false); + batch.setFramebuffer(_compositeFramebuffer); + batch.setPipeline(_overlayPipeline); + batch.setResourceTexture(0, _currentFrame->overlay); + if (isStereo()) { + for_each_eye([&](Eye eye) { + batch.setViewportTransform(eyeViewport(eye)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + } else { + batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize())); + batch.draw(gpu::TRIANGLE_STRIP, 4); } - compositeExtra(); }); } +void OpenGLDisplayPlugin::compositePointer() { + auto& cursorManager = Cursor::Manager::instance(); + const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; + auto cursorTransform = DependencyManager::get()->getReticleTransform(glm::mat4()); + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setProjectionTransform(mat4()); + batch.setFramebuffer(_compositeFramebuffer); + batch.setPipeline(_cursorPipeline); + batch.setResourceTexture(0, cursorData.texture); + batch.resetViewTransform(); + batch.setModelTransform(cursorTransform); + if (isStereo()) { + for_each_eye([&](Eye eye) { + batch.setViewportTransform(eyeViewport(eye)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + } else { + batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize())); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } + }); +} + +void OpenGLDisplayPlugin::compositeScene() { + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setFramebuffer(_compositeFramebuffer); + batch.setViewportTransform(ivec4(uvec2(), _compositeFramebuffer->getSize())); + batch.setStateScissorRect(ivec4(uvec2(), _compositeFramebuffer->getSize())); + batch.resetViewTransform(); + batch.setProjectionTransform(mat4()); + batch.setPipeline(_simplePipeline); + batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); +} + +void OpenGLDisplayPlugin::compositeLayers() { + auto renderSize = getRecommendedRenderSize(); + if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) { + _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); + } + + { + PROFILE_RANGE_EX("compositeScene", 0xff0077ff, (uint64_t)presentCount()) + compositeScene(); + } + + { + PROFILE_RANGE_EX("compositeOverlay", 0xff0077ff, (uint64_t)presentCount()) + compositeOverlay(); + } + auto compositorHelper = DependencyManager::get(); + if (compositorHelper->getReticleVisible()) { + PROFILE_RANGE_EX("compositePointer", 0xff0077ff, (uint64_t)presentCount()) + compositePointer(); + } + + { + PROFILE_RANGE_EX("compositeExtra", 0xff0077ff, (uint64_t)presentCount()) + compositeExtra(); + } +} + void OpenGLDisplayPlugin::internalPresent() { - using namespace oglplus; - const uvec2& srcSize = _compositeFramebuffer->size; - uvec2 dstSize = getSurfacePixels(); - _compositeFramebuffer->Bound(FramebufferTarget::Read, [&] { - Context::BlitFramebuffer( - 0, 0, srcSize.x, srcSize.y, - 0, 0, dstSize.x, dstSize.y, - BufferSelectBit::ColorBuffer, BlitFilter::Nearest); + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(gpu::FramebufferPointer()); + batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels())); + batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); + batch.setPipeline(_presentPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); }); swapBuffers(); } void OpenGLDisplayPlugin::present() { + PROFILE_RANGE_EX(__FUNCTION__, 0xffffff00, (uint64_t)presentCount()) + updateFrameData(); incrementPresentCount(); - PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) + { + PROFILE_RANGE_EX("recycle", 0xff00ff00, (uint64_t)presentCount()) + _gpuContext->recycle(); + } + + if (_currentFrame) { + { + // Execute the frame rendering commands + PROFILE_RANGE_EX("execute", 0xff00ff00, (uint64_t)presentCount()) + _gpuContext->executeFrame(_currentFrame); + } - updateTextures(); - if (_currentSceneTexture) { // Write all layers to a local framebuffer - compositeLayers(); + { + PROFILE_RANGE_EX("composite", 0xff00ffff, (uint64_t)presentCount()) + compositeLayers(); + } + // Take the composite framebuffer and send it to the output device - internalPresent(); + { + PROFILE_RANGE_EX("internalPresent", 0xff00ffff, (uint64_t)presentCount()) + internalPresent(); + } _presentRate.increment(); - _activeProgram.reset(); } } @@ -595,7 +638,7 @@ float OpenGLDisplayPlugin::newFramePresentRate() const { float OpenGLDisplayPlugin::droppedFrameRate() const { float result; - withRenderThreadLock([&] { + withNonPresentThreadLock([&] { result = _droppedFrameRate.rate(); }); return result; @@ -605,97 +648,27 @@ float OpenGLDisplayPlugin::presentRate() const { return _presentRate.rate(); } -void OpenGLDisplayPlugin::drawUnitQuad() { - useProgram(_program); - _plane->Use(); - _plane->Draw(); -} - -void OpenGLDisplayPlugin::enableVsync(bool enable) { - if (!_vsyncSupported) { - return; - } -#if defined(Q_OS_WIN) - wglSwapIntervalEXT(enable ? 1 : 0); -#elif defined(Q_OS_MAC) - GLint interval = enable ? 1 : 0; - CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); -#else - // TODO: Fill in for linux - return; -#endif -} - -bool OpenGLDisplayPlugin::isVsyncEnabled() { - if (!_vsyncSupported) { - return true; - } -#if defined(Q_OS_WIN) - return wglGetSwapIntervalEXT() != 0; -#elif defined(Q_OS_MAC) - GLint interval; - CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); - return interval != 0; -#else - // TODO: Fill in for linux - return true; -#endif -} - void OpenGLDisplayPlugin::swapBuffers() { static auto widget = _container->getPrimaryWidget(); widget->swapBuffers(); } void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const { -#if THREADED_PRESENT static auto presentThread = DependencyManager::get(); presentThread->withMainThreadContext(f); _container->makeRenderingContextCurrent(); -#else - static auto widget = _container->getPrimaryWidget(); - widget->makeCurrent(); - f(); - _container->makeRenderingContextCurrent(); -#endif } QImage OpenGLDisplayPlugin::getScreenshot() const { - using namespace oglplus; - QImage screenshot(_compositeFramebuffer->size.x, _compositeFramebuffer->size.y, QImage::Format_RGBA8888); + auto size = _compositeFramebuffer->getSize(); + auto glBackend = const_cast(*this).getGLBackend(); + QImage screenshot(size.x, size.y, QImage::Format_ARGB32); withMainThreadContext([&] { - Framebuffer::Bind(Framebuffer::Target::Read, _compositeFramebuffer->fbo); - Context::ReadPixels(0, 0, _compositeFramebuffer->size.x, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits()); + glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(uvec2(0), size), screenshot); }); return screenshot.mirrored(false, true); } -uint32_t OpenGLDisplayPlugin::getSceneTextureId() const { - if (!_currentSceneTexture) { - return 0; - } - - return _currentSceneTexture->getHardwareId(); -} - -uint32_t OpenGLDisplayPlugin::getOverlayTextureId() const { - if (!_currentOverlayTexture) { - return 0; - } - return _currentOverlayTexture->getHardwareId(); -} - -void OpenGLDisplayPlugin::eyeViewport(Eye eye) const { - using namespace oglplus; - uvec2 vpSize = _compositeFramebuffer->size; - vpSize.x /= 2; - uvec2 vpPos; - if (eye == Eye::Right) { - vpPos.x = vpSize.x; - } - Context::Viewport(vpPos.x, vpPos.y, vpSize.x, vpSize.y); -} - glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const { uvec2 result; auto window = _container->getPrimaryWidget(); @@ -719,14 +692,7 @@ bool OpenGLDisplayPlugin::hasFocus() const { return window ? window->hasFocus() : false; } -void OpenGLDisplayPlugin::useProgram(const ProgramPtr& program) { - if (_activeProgram != program) { - program->Bind(); - _activeProgram = program; - } -} - -void OpenGLDisplayPlugin::assertIsRenderThread() const { +void OpenGLDisplayPlugin::assertNotPresentThread() const { Q_ASSERT(QThread::currentThread() != _presentThread); } @@ -735,8 +701,39 @@ void OpenGLDisplayPlugin::assertIsPresentThread() const { } bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) { - withRenderThreadLock([&] { + withNonPresentThreadLock([&] { _compositeOverlayAlpha = _overlayAlpha; }); return Parent::beginFrameRender(frameIndex); } + +ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const { + uvec2 vpSize = _currentFrame->framebuffer->getSize(); + vpSize.x /= 2; + uvec2 vpPos; + if (eye == Eye::Right) { + vpPos.x = vpSize.x; + } + return ivec4(vpPos, vpSize); +} + +gpu::gl::GLBackend* OpenGLDisplayPlugin::getGLBackend() { + if (!_gpuContext || !_gpuContext->getBackend()) { + return nullptr; + } + auto backend = _gpuContext->getBackend().get(); +#if defined(Q_OS_MAC) + // Should be dynamic_cast, but that doesn't work in plugins on OSX + auto glbackend = static_cast(backend); +#else + auto glbackend = dynamic_cast(backend); +#endif + + return glbackend; +} + +void OpenGLDisplayPlugin::render(std::function f) { + gpu::Batch batch; + f(batch); + _gpuContext->executeBatch(batch); +} diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 068b236289..48f9a78eda 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -11,17 +11,21 @@ #include #include +#include #include #include #include #include -#include #include #include -#define THREADED_PRESENT 1 +namespace gpu { + namespace gl { + class GLBackend; + } +} class OpenGLDisplayPlugin : public DisplayPlugin { Q_OBJECT @@ -33,19 +37,14 @@ protected: using Condition = std::condition_variable; using TextureEscrow = GLEscrow; public: - OpenGLDisplayPlugin(); - // These must be final to ensure proper ordering of operations // between the main thread and the presentation thread bool activate() override final; void deactivate() override final; - bool eventFilter(QObject* receiver, QEvent* event) override; bool isDisplayVisible() const override { return true; } - - void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; - void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override; + void submitFrame(const gpu::FramePointer& newFrame) override; glm::uvec2 getRecommendedRenderSize() const override { return getSurfacePixels(); @@ -64,17 +63,18 @@ public: float droppedFrameRate() const override; bool beginFrameRender(uint32_t frameIndex) override; + + virtual bool wantVsync() const { return true; } + void setVsyncEnabled(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; } + bool isVsyncEnabled() const { return _vsyncEnabled; } + protected: -#if THREADED_PRESENT friend class PresentThread; -#endif - uint32_t getSceneTextureId() const; - uint32_t getOverlayTextureId() const; glm::uvec2 getSurfaceSize() const; glm::uvec2 getSurfacePixels() const; - void compositeLayers(); + virtual void compositeLayers(); virtual void compositeScene(); virtual void compositeOverlay(); virtual void compositePointer(); @@ -82,10 +82,6 @@ protected: virtual bool hasFocus() const override; - // FIXME make thread safe? - virtual bool isVsyncEnabled(); - virtual void enableVsync(bool enable = true); - // These functions must only be called on the presentation thread virtual void customizeContext(); virtual void uncustomizeContext(); @@ -93,54 +89,46 @@ protected: // Returns true on successful activation virtual bool internalActivate() { return true; } virtual void internalDeactivate() {} - virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture); + // Plugin specific functionality to send the composed scene to the output window or device virtual void internalPresent(); - void withMainThreadContext(std::function f) const; - - void useProgram(const ProgramPtr& program); - void present(); - void updateTextures(); - void drawUnitQuad(); - void swapBuffers(); - void eyeViewport(Eye eye) const; - virtual void updateFrameData(); - QThread* _presentThread{ nullptr }; - ProgramPtr _program; - int32_t _mvpUniform { -1 }; - int32_t _alphaUniform { -1 }; - ShapeWrapperPtr _plane; + void withMainThreadContext(std::function f) const; + void present(); + virtual void swapBuffers(); + ivec4 eyeViewport(Eye eye) const; + + void render(std::function f); + + bool _vsyncEnabled { true }; + QThread* _presentThread{ nullptr }; + std::queue _newFrameQueue; RateCounter<> _droppedFrameRate; RateCounter<> _newFrameRate; RateCounter<> _presentRate; - QMap _sceneTextureToFrameIndexMap; - uint32_t _currentPresentFrameIndex { 0 }; - float _compositeOverlayAlpha{ 1.0f }; - gpu::TexturePointer _currentSceneTexture; - gpu::TexturePointer _currentOverlayTexture; - - TextureEscrow _sceneTextureEscrow; - TextureEscrow _overlayTextureEscrow; - - bool _vsyncSupported { false }; + gpu::FramePointer _currentFrame; + gpu::FramebufferPointer _compositeFramebuffer; + gpu::PipelinePointer _overlayPipeline; + gpu::PipelinePointer _simplePipeline; + gpu::PipelinePointer _presentPipeline; + gpu::PipelinePointer _cursorPipeline; + float _compositeOverlayAlpha { 1.0f }; struct CursorData { QImage image; vec2 hotSpot; uvec2 size; - uint32_t texture { 0 }; + gpu::TexturePointer texture; }; std::map _cursorsData; - BasicFramebufferWrapperPtr _compositeFramebuffer; bool _lockCurrentTexture { false }; - void assertIsRenderThread() const; + void assertNotPresentThread() const; void assertIsPresentThread() const; template @@ -151,17 +139,17 @@ protected: } template - void withRenderThreadLock(F f) const { - assertIsRenderThread(); + void withNonPresentThreadLock(F f) const { + assertNotPresentThread(); Lock lock(_presentMutex); f(); } -private: + gpu::gl::GLBackend* getGLBackend(); + // Any resource shared by the main thread and the presentation thread must // be serialized through this mutex mutable Mutex _presentMutex; - ProgramPtr _activeProgram; float _overlayAlpha{ 1.0f }; }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp new file mode 100644 index 0000000000..fa267e2c68 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp @@ -0,0 +1,90 @@ +// +// Created by Bradley Austin Davis on 2016/07/31 +// 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 "DebugHmdDisplayPlugin.h" + +#include + +#include +#include +#include + +const QString DebugHmdDisplayPlugin::NAME("HMD Simulator"); + +static const QString DEBUG_FLAG("HIFI_DEBUG_HMD"); +static bool enableDebugHmd = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); + + +bool DebugHmdDisplayPlugin::isSupported() const { + return enableDebugHmd; +} + +void DebugHmdDisplayPlugin::resetSensors() { + _currentRenderFrameInfo.renderPose = glm::mat4(); // identity +} + +bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) { + _currentRenderFrameInfo = FrameInfo(); + _currentRenderFrameInfo.sensorSampleTime = secTimestampNow(); + _currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime; + // FIXME simulate head movement + //_currentRenderFrameInfo.renderPose = ; + //_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose; + + withNonPresentThreadLock([&] { + _uiModelTransform = DependencyManager::get()->getModelTransform(); + _frameInfos[frameIndex] = _currentRenderFrameInfo; + + _handPoses[0] = glm::translate(mat4(), vec3(-0.3f, 0.0f, 0.0f)); + _handLasers[0].color = vec4(1, 0, 0, 1); + _handLasers[0].mode = HandLaserMode::Overlay; + + _handPoses[1] = glm::translate(mat4(), vec3(0.3f, 0.0f, 0.0f)); + _handLasers[1].color = vec4(0, 1, 1, 1); + _handLasers[1].mode = HandLaserMode::Overlay; + }); + return Parent::beginFrameRender(frameIndex); +} + +// DLL based display plugins MUST initialize GLEW inside the DLL code. +void DebugHmdDisplayPlugin::customizeContext() { + glewExperimental = true; + glewInit(); + glGetError(); // clear the potential error from glewExperimental + Parent::customizeContext(); +} + +bool DebugHmdDisplayPlugin::internalActivate() { + _ipd = 0.0327499993f * 2.0f; + _eyeProjections[0][0] = vec4{ 0.759056330, 0.000000000, 0.000000000, 0.000000000 }; + _eyeProjections[0][1] = vec4{ 0.000000000, 0.682773232, 0.000000000, 0.000000000 }; + _eyeProjections[0][2] = vec4{ -0.0580431037, -0.00619550655, -1.00000489, -1.00000000 }; + _eyeProjections[0][3] = vec4{ 0.000000000, 0.000000000, -0.0800003856, 0.000000000 }; + _eyeProjections[1][0] = vec4{ 0.752847493, 0.000000000, 0.000000000, 0.000000000 }; + _eyeProjections[1][1] = vec4{ 0.000000000, 0.678060353, 0.000000000, 0.000000000 }; + _eyeProjections[1][2] = vec4{ 0.0578232110, -0.00669418881, -1.00000489, -1.000000000 }; + _eyeProjections[1][3] = vec4{ 0.000000000, 0.000000000, -0.0800003856, 0.000000000 }; + _eyeInverseProjections[0] = glm::inverse(_eyeProjections[0]); + _eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]); + _eyeOffsets[0][3] = vec4{ -0.0327499993, 0.0, 0.0149999997, 1.0 }; + _eyeOffsets[0][3] = vec4{ 0.0327499993, 0.0, 0.0149999997, 1.0 }; + _renderTargetSize = { 3024, 1680 }; + _cullingProjection = _eyeProjections[0]; + // This must come after the initialization, so that the values calculated + // above are available during the customizeContext call (when not running + // in threaded present mode) + return Parent::internalActivate(); +} + +void DebugHmdDisplayPlugin::updatePresentPose() { + float yaw = sinf(secTimestampNow()) * 0.25f; + float pitch = cosf(secTimestampNow()) * 0.25f; + // Simulates head pose latency correction + _currentPresentFrameInfo.presentPose = + glm::mat4_cast(glm::angleAxis(yaw, Vectors::UP)) * + glm::mat4_cast(glm::angleAxis(pitch, Vectors::RIGHT)); +} diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h new file mode 100644 index 0000000000..509e13eda7 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h @@ -0,0 +1,33 @@ +// +// Created by Bradley Austin Davis on 2016/07/31 +// 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 +// +#pragma once + +#include "HmdDisplayPlugin.h" + +class DebugHmdDisplayPlugin : public HmdDisplayPlugin { + using Parent = HmdDisplayPlugin; + +public: + const QString& getName() const override { return NAME; } + grouping getGrouping() const override { return DEVELOPER; } + + bool isSupported() const override; + void resetSensors() override final; + bool beginFrameRender(uint32_t frameIndex) override; + float getTargetFrameRate() const override { return 90; } + + +protected: + void updatePresentPose() override; + void hmdPresent() override {} + bool isHmdMounted() const override { return true; } + void customizeContext() override; + bool internalActivate() override; +private: + static const QString NAME; +}; diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 306bc26a17..6904700be5 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -1,10 +1,11 @@ -// +// // Created by Bradley Austin Davis on 2016/02/15 // 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 "HmdDisplayPlugin.h" #include @@ -22,9 +23,10 @@ #include #include #include - -#include -#include +#include +#include +#include +#include #include @@ -32,94 +34,18 @@ #include "../CompositorHelper.h" static const QString MONO_PREVIEW = "Mono Preview"; -static const QString REPROJECTION = "Allow Reprojection"; +static const QString DISABLE_PREVIEW = "Disable Preview"; static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; static const QString DEVELOPER_MENU_PATH = "Developer>" + DisplayPlugin::MENU_PATH(); static const bool DEFAULT_MONO_VIEW = true; -static const int NUMBER_OF_HANDS = 2; -static const glm::mat4 IDENTITY_MATRIX; - - -glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const { - return CompositorHelper::VIRTUAL_SCREEN_SIZE; -} - -QRect HmdDisplayPlugin::getRecommendedOverlayRect() const { - return CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT; -} - -bool HmdDisplayPlugin::internalActivate() { - _monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW); - - _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), MONO_PREVIEW, - [this](bool clicked) { - _monoPreview = clicked; - _container->setBoolSetting("monoPreview", _monoPreview); - }, true, _monoPreview); - _container->removeMenu(FRAMERATE); - _container->addMenu(DEVELOPER_MENU_PATH); - _container->addMenuItem(PluginType::DISPLAY_PLUGIN, DEVELOPER_MENU_PATH, REPROJECTION, - [this](bool clicked) { - _enableReprojection = clicked; - _container->setBoolSetting("enableReprojection", _enableReprojection); - }, true, _enableReprojection); - - for_each_eye([&](Eye eye) { - _eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]); - }); - - if (_previewTextureID == 0) { - QImage previewTexture(PathUtils::resourcesPath() + "images/preview.png"); - if (!previewTexture.isNull()) { - glGenTextures(1, &_previewTextureID); - glBindTexture(GL_TEXTURE_2D, _previewTextureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, previewTexture.width(), previewTexture.height(), 0, - GL_BGRA, GL_UNSIGNED_BYTE, previewTexture.mirrored(false, true).bits()); - using namespace oglplus; - Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear); - Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear); - glBindTexture(GL_TEXTURE_2D, 0); - _previewAspect = ((float)previewTexture.width())/((float)previewTexture.height()); - _firstPreview = true; - } - } - - return Parent::internalActivate(); -} - -void HmdDisplayPlugin::internalDeactivate() { - if (_previewTextureID != 0) { - glDeleteTextures(1, &_previewTextureID); - _previewTextureID = 0; - } - Parent::internalDeactivate(); -} - -void HmdDisplayPlugin::customizeContext() { - Parent::customizeContext(); - // Only enable mirroring if we know vsync is disabled - // On Mac, this won't work due to how the contexts are handled, so don't try #if !defined(Q_OS_MAC) - enableVsync(false); +static const bool DEFAULT_DISABLE_PREVIEW = false; #endif - _enablePreview = !isVsyncEnabled(); - _sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO); - using namespace oglplus; - if (!_enablePreview) { - const std::string version("#version 410 core\n"); - compileProgram(_previewProgram, version + DrawUnitQuadTexcoord_vert, version + DrawTexture_frag); - _previewUniforms.previewTexture = Uniform(*_previewProgram, "colorMap").Location(); - } - - updateReprojectionProgram(); - updateOverlayProgram(); -#ifdef HMD_HAND_LASER_SUPPORT - updateLaserProgram(); - _laserGeometry = loadLaser(_laserProgram); -#endif -} +static const glm::mat4 IDENTITY_MATRIX; +static const size_t NUMBER_OF_HANDS = 2; //#define LIVE_SHADER_RELOAD 1 +extern glm::vec3 getPoint(float yaw, float pitch); static QString readFile(const QString& filename) { QFile file(filename); @@ -129,183 +55,220 @@ static QString readFile(const QString& filename) { return result; } -void HmdDisplayPlugin::updateReprojectionProgram() { - static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert"; - static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag"; -#if LIVE_SHADER_RELOAD - static qint64 vsBuiltAge = 0; - static qint64 fsBuiltAge = 0; - QFileInfo vsInfo(vsFile); - QFileInfo fsInfo(fsFile); - auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch(); - auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch(); - if (!_reprojectionProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) { - vsBuiltAge = vsAge; - fsBuiltAge = fsAge; -#else - if (!_reprojectionProgram) { -#endif - QString vsSource = readFile(vsFile); - QString fsSource = readFile(fsFile); - ProgramPtr program; - try { - compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString()); - if (program) { - using namespace oglplus; - _reprojectionUniforms.reprojectionMatrix = Uniform(*program, "reprojection").Location(); - _reprojectionUniforms.inverseProjectionMatrix = Uniform(*program, "inverseProjections").Location(); - _reprojectionUniforms.projectionMatrix = Uniform(*program, "projections").Location(); - _reprojectionProgram = program; - } - } catch (std::runtime_error& error) { - qWarning() << "Error building reprojection shader " << error.what(); - } - } - +glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const { + return CompositorHelper::VIRTUAL_SCREEN_SIZE; } -#ifdef HMD_HAND_LASER_SUPPORT -void HmdDisplayPlugin::updateLaserProgram() { - static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert"; - static const QString gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom"; - static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.frag"; - -#if LIVE_SHADER_RELOAD - static qint64 vsBuiltAge = 0; - static qint64 gsBuiltAge = 0; - static qint64 fsBuiltAge = 0; - QFileInfo vsInfo(vsFile); - QFileInfo fsInfo(fsFile); - QFileInfo gsInfo(fsFile); - auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch(); - auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch(); - auto gsAge = gsInfo.lastModified().toMSecsSinceEpoch(); - if (!_laserProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge || gsAge > gsBuiltAge) { - vsBuiltAge = vsAge; - gsBuiltAge = gsAge; - fsBuiltAge = fsAge; -#else - if (!_laserProgram) { -#endif - - QString vsSource = readFile(vsFile); - QString fsSource = readFile(fsFile); - QString gsSource = readFile(gsFile); - ProgramPtr program; - try { - compileProgram(program, vsSource.toLocal8Bit().toStdString(), gsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString()); - if (program) { - using namespace oglplus; - _laserUniforms.color = Uniform(*program, "color").Location(); - _laserUniforms.mvp = Uniform(*program, "mvp").Location(); - _laserProgram = program; - } - } catch (std::runtime_error& error) { - qWarning() << "Error building hand laser composite shader " << error.what(); - } - } +QRect HmdDisplayPlugin::getRecommendedOverlayRect() const { + return CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT; } -#endif -void HmdDisplayPlugin::updateOverlayProgram() { - static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert"; - static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag"; - -#if LIVE_SHADER_RELOAD - static qint64 vsBuiltAge = 0; - static qint64 fsBuiltAge = 0; - QFileInfo vsInfo(vsFile); - QFileInfo fsInfo(fsFile); - auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch(); - auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch(); - if (!_overlayProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) { - vsBuiltAge = vsAge; - fsBuiltAge = fsAge; -#else - if (!_overlayProgram) { -#endif - QString vsSource = readFile(vsFile); - QString fsSource = readFile(fsFile); - ProgramPtr program; - try { - compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString()); - if (program) { - using namespace oglplus; - _overlayUniforms.mvp = Uniform(*program, "mvp").Location(); - _overlayUniforms.alpha = Uniform(*program, "alpha").Location(); - _overlayUniforms.glowColors = Uniform(*program, "glowColors").Location(); - _overlayUniforms.glowPoints = Uniform(*program, "glowPoints").Location(); - _overlayUniforms.resolution = Uniform(*program, "resolution").Location(); - _overlayUniforms.radius = Uniform(*program, "radius").Location(); - _overlayProgram = program; - useProgram(_overlayProgram); - Uniform(*_overlayProgram, _overlayUniforms.resolution).Set(CompositorHelper::VIRTUAL_SCREEN_SIZE); - } - } catch (std::runtime_error& error) { - qWarning() << "Error building overlay composite shader " << error.what(); - } +bool HmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) { + if (!_vsyncEnabled && !_disablePreviewItemAdded) { + _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), DISABLE_PREVIEW, + [this](bool clicked) { + _disablePreview = clicked; + _container->setBoolSetting("disableHmdPreview", _disablePreview); + if (_disablePreview) { + _clearPreviewFlag = true; + } + }, true, _disablePreview); + _disablePreviewItemAdded = true; } + return Parent::beginFrameRender(frameIndex); +} + + +bool HmdDisplayPlugin::internalActivate() { + _disablePreviewItemAdded = false; + _monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW); + _clearPreviewFlag = true; + _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), MONO_PREVIEW, + [this](bool clicked) { + _monoPreview = clicked; + _container->setBoolSetting("monoPreview", _monoPreview); + }, true, _monoPreview); +#if defined(Q_OS_MAC) + _disablePreview = true; +#else + _disablePreview = _container->getBoolSetting("disableHmdPreview", DEFAULT_DISABLE_PREVIEW || _vsyncEnabled); +#endif + + _container->removeMenu(FRAMERATE); + for_each_eye([&](Eye eye) { + _eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]); + }); + + _clearPreviewFlag = true; + + return Parent::internalActivate(); +} + +void HmdDisplayPlugin::internalDeactivate() { + Parent::internalDeactivate(); +} + +void HmdDisplayPlugin::customizeContext() { + Parent::customizeContext(); + _overlayRenderer.build(); } void HmdDisplayPlugin::uncustomizeContext() { - _overlayProgram.reset(); - _sphereSection.reset(); - _compositeFramebuffer.reset(); - _previewProgram.reset(); - _reprojectionProgram.reset(); -#ifdef HMD_HAND_LASER_SUPPORT - _laserProgram.reset(); - _laserGeometry.reset(); -#endif + // This stops the weirdness where if the preview was disabled, on switching back to 2D, + // the vsync was stuck in the disabled state. No idea why that happens though. + _disablePreview = false; + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(_compositeFramebuffer); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + }); + internalPresent(); + _overlayRenderer = OverlayRenderer(); Parent::uncustomizeContext(); } -// By default assume we'll present with the same pose as the render +ivec4 HmdDisplayPlugin::getViewportForSourceSize(const uvec2& size) const { + // screen preview mirroring + auto window = _container->getPrimaryWidget(); + auto devicePixelRatio = window->devicePixelRatio(); + auto windowSize = toGlm(window->size()); + windowSize *= devicePixelRatio; + float windowAspect = aspect(windowSize); + float sceneAspect = aspect(size); + float aspectRatio = sceneAspect / windowAspect; + uvec2 targetViewportSize = windowSize; + if (aspectRatio < 1.0f) { + targetViewportSize.x *= aspectRatio; + } else { + targetViewportSize.y /= aspectRatio; + } + uvec2 targetViewportPosition; + if (targetViewportSize.x < windowSize.x) { + targetViewportPosition.x = (windowSize.x - targetViewportSize.x) / 2; + } else if (targetViewportSize.y < windowSize.y) { + targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2; + } + return ivec4(targetViewportPosition, targetViewportSize); +} + +void HmdDisplayPlugin::internalPresent() { + PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) + + // Composite together the scene, overlay and mouse cursor + hmdPresent(); + + if (!_disablePreview) { + // screen preview mirroring + auto sourceSize = _renderTargetSize; + if (_monoPreview) { + sourceSize.x >>= 1; + } + auto viewport = getViewportForSourceSize(sourceSize); + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(gpu::FramebufferPointer()); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + batch.setStateScissorRect(viewport); + if (_monoPreview) { + viewport.z *= 2; + } + batch.setViewportTransform(viewport); + batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); + batch.setPipeline(_presentPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + swapBuffers(); + } else if (_clearPreviewFlag) { + auto image = QImage(PathUtils::resourcesPath() + "images/preview.png"); + image = image.mirrored(); + image = image.convertToFormat(QImage::Format_RGBA8888); + if (!_previewTexture) { + _previewTexture.reset( + gpu::Texture::create2D( + gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), + image.width(), image.height(), + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + _previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build()); + _previewTexture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); + _previewTexture->autoGenerateMips(-1); + } + + if (getGLBackend()->isTextureReady(_previewTexture)) { + auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(gpu::FramebufferPointer()); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + batch.setStateScissorRect(viewport); + batch.setViewportTransform(viewport); + batch.setResourceTexture(0, _previewTexture); + batch.setPipeline(_presentPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + _clearPreviewFlag = false; + swapBuffers(); + } + } + postPreview(); +} + +// HMD specific stuff + +glm::mat4 HmdDisplayPlugin::getHeadPose() const { + return _currentRenderFrameInfo.renderPose; +} + void HmdDisplayPlugin::updatePresentPose() { + // By default assume we'll present with the same pose as the render _currentPresentFrameInfo.presentPose = _currentPresentFrameInfo.renderPose; } -void HmdDisplayPlugin::compositeScene() { - updatePresentPose(); +void HmdDisplayPlugin::updateFrameData() { + // Check if we have old frame data to discard + static const uint32_t INVALID_FRAME = (uint32_t)(~0); + uint32_t oldFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME; - if (!_enableReprojection || glm::mat3() == _currentPresentFrameInfo.presentReprojection) { - // No reprojection required - Parent::compositeScene(); - return; + Parent::updateFrameData(); + uint32_t newFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME; + + if (oldFrameIndex != newFrameIndex) { + withPresentThreadLock([&] { + if (oldFrameIndex != INVALID_FRAME) { + auto itr = _frameInfos.find(oldFrameIndex); + if (itr != _frameInfos.end()) { + _frameInfos.erase(itr); + } + } + if (newFrameIndex != INVALID_FRAME) { + _currentPresentFrameInfo = _frameInfos[newFrameIndex]; + } + }); } -#ifdef DEBUG_REPROJECTION_SHADER - _reprojectionProgram = getReprojectionProgram(); -#endif - useProgram(_reprojectionProgram); + updatePresentPose(); - using namespace oglplus; - Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear); - Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear); - Uniform(*_reprojectionProgram, _reprojectionUniforms.reprojectionMatrix).Set(_currentPresentFrameInfo.presentReprojection); - //Uniform(*_reprojectionProgram, PROJECTION_MATRIX_LOCATION).Set(_eyeProjections); - //Uniform(*_reprojectionProgram, INVERSE_PROJECTION_MATRIX_LOCATION).Set(_eyeInverseProjections); - // FIXME what's the right oglplus mechanism to do this? It's not that ^^^ ... better yet, switch to a uniform buffer - glUniformMatrix4fv(_reprojectionUniforms.inverseProjectionMatrix, 2, GL_FALSE, &(_eyeInverseProjections[0][0][0])); - glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_eyeProjections[0][0][0])); - _plane->UseInProgram(*_reprojectionProgram); - _plane->Draw(); -} - -void HmdDisplayPlugin::compositeOverlay() { - using namespace oglplus; - auto compositorHelper = DependencyManager::get(); - glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix(); + if (_currentFrame) { + auto batchPose = _currentFrame->pose; + auto currentPose = _currentPresentFrameInfo.presentPose; + auto correction = glm::inverse(batchPose) * currentPose; + getGLBackend()->setCameraCorrection(correction); + } withPresentThreadLock([&] { _presentHandLasers = _handLasers; _presentHandPoses = _handPoses; _presentUiModelTransform = _uiModelTransform; }); - std::array handGlowPoints { { vec2(-1), vec2(-1) } }; + + auto compositorHelper = DependencyManager::get(); + glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix(); + std::array handGlowPoints{ { vec2(-1), vec2(-1) } }; // compute the glow point interesections - for (int i = 0; i < NUMBER_OF_HANDS; ++i) { + for (size_t i = 0; i < NUMBER_OF_HANDS; ++i) { if (_presentHandPoses[i] == IDENTITY_MATRIX) { continue; } @@ -331,6 +294,9 @@ void HmdDisplayPlugin::compositeOverlay() { continue; } + _presentHandLaserPoints[i].first = vec3(_presentHandPoses[i][3]); + _presentHandLaserPoints[i].second = _presentHandLaserPoints[i].first + (castDirection * distance); + vec3 intersectionPosition = vec3(_presentHandPoses[i][3]) + (castDirection * distance) - _presentUiModelTransform.getTranslation(); intersectionPosition = glm::inverse(_presentUiModelTransform.getRotation()) * intersectionPosition; @@ -339,7 +305,7 @@ void HmdDisplayPlugin::compositeOverlay() { { vec2 xdir = glm::normalize(vec2(intersectionPosition.x, -intersectionPosition.z)); yawPitch.x = glm::atan(xdir.x, xdir.y); - yawPitch.y = (acosf(intersectionPosition.y) * -1.0f) + M_PI_2; + yawPitch.y = (acosf(intersectionPosition.y) * -1.0f) + (float)M_PI_2; } vec2 halfFov = CompositorHelper::VIRTUAL_UI_TARGET_FOV / 2.0f; @@ -353,154 +319,174 @@ void HmdDisplayPlugin::compositeOverlay() { handGlowPoints[i] = yawPitch; } - updateOverlayProgram(); - if (!_overlayProgram) { - return; - } - useProgram(_overlayProgram); + for_each_eye([&](Eye eye) { + auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat; + _overlayRenderer.mvps[eye] = _eyeProjections[eye] * modelView; + }); + // Setup the uniforms { - if (_overlayUniforms.alpha >= 0) { - Uniform(*_overlayProgram, _overlayUniforms.alpha).Set(_compositeOverlayAlpha); - } - if (_overlayUniforms.glowPoints >= 0) { - vec4 glowPoints(handGlowPoints[0], handGlowPoints[1]); - Uniform(*_overlayProgram, _overlayUniforms.glowPoints).Set(glowPoints); - } - if (_overlayUniforms.glowColors >= 0) { - std::array glowColors; - glowColors[0] = _presentHandLasers[0].color; - glowColors[1] = _presentHandLasers[1].color; - glProgramUniform4fv(GetName(*_overlayProgram), _overlayUniforms.glowColors, 2, &glowColors[0].r); + auto& uniforms = _overlayRenderer.uniforms; + uniforms.alpha = _compositeOverlayAlpha; + uniforms.glowPoints = vec4(handGlowPoints[0], handGlowPoints[1]); + uniforms.glowColors[0] = _presentHandLasers[0].color; + uniforms.glowColors[1] = _presentHandLasers[1].color; + } +} + +void HmdDisplayPlugin::OverlayRenderer::build() { + vertices = std::make_shared(); + indices = std::make_shared(); + + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm + + static const float fov = CompositorHelper::VIRTUAL_UI_TARGET_FOV.y; + static const float aspectRatio = CompositorHelper::VIRTUAL_UI_ASPECT_RATIO; + static const uint16_t stacks = 128; + static const uint16_t slices = 64; + + Vertex vertex; + + // Compute vertices positions and texture UV coordinate + // Create and write to buffer + for (int i = 0; i < stacks; i++) { + vertex.uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f + // abs(theta) <= fov / 2.0f + float pitch = -fov * (vertex.uv.y - 0.5f); + for (int j = 0; j < slices; j++) { + vertex.uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f + // abs(phi) <= fov * aspectRatio / 2.0f + float yaw = -fov * aspectRatio * (vertex.uv.x - 0.5f); + vertex.pos = getPoint(yaw, pitch); + vertices->append(sizeof(Vertex), (gpu::Byte*)&vertex); } } - _sphereSection->Use(); - for_each_eye([&](Eye eye) { - eyeViewport(eye); - auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat; - auto mvp = _eyeProjections[eye] * modelView; - Uniform(*_overlayProgram, _overlayUniforms.mvp).Set(mvp); - _sphereSection->Draw(); + // Compute number of indices needed + static const int VERTEX_PER_TRANGLE = 3; + static const int TRIANGLE_PER_RECTANGLE = 2; + int numberOfRectangles = (slices - 1) * (stacks - 1); + indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; + + // Compute indices order + std::vector indices; + for (int i = 0; i < stacks - 1; i++) { + for (int j = 0; j < slices - 1; j++) { + GLushort bottomLeftIndex = i * slices + j; + GLushort bottomRightIndex = bottomLeftIndex + 1; + GLushort topLeftIndex = bottomLeftIndex + slices; + GLushort topRightIndex = topLeftIndex + 1; + // FIXME make a z-order curve for better vertex cache locality + indices.push_back(topLeftIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(topRightIndex); + + indices.push_back(topRightIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(bottomRightIndex); + } + } + this->indices->append(indices); + format = std::make_shared(); // 1 for everyone + format->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + format->setAttribute(gpu::Stream::TEXCOORD, gpu::Stream::TEXCOORD, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + uniformBuffers[0] = std::make_shared(sizeof(Uniforms), nullptr); + uniformBuffers[1] = std::make_shared(sizeof(Uniforms), nullptr); + updatePipeline(); +} + +void HmdDisplayPlugin::OverlayRenderer::updatePipeline() { + static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert"; + static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag"; + +#if LIVE_SHADER_RELOAD + static qint64 vsBuiltAge = 0; + static qint64 fsBuiltAge = 0; + QFileInfo vsInfo(vsFile); + QFileInfo fsInfo(fsFile); + auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch(); + auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch(); + if (!pipeline || vsAge > vsBuiltAge || fsAge > fsBuiltAge) { + vsBuiltAge = vsAge; + fsBuiltAge = fsAge; +#else + if (!pipeline) { +#endif + QString vsSource = readFile(vsFile); + QString fsSource = readFile(fsFile); + auto vs = gpu::Shader::createVertex(vsSource.toLocal8Bit().toStdString()); + auto ps = gpu::Shader::createPixel(fsSource.toLocal8Bit().toStdString()); + auto program = gpu::Shader::createProgram(vs, ps); + gpu::gl::GLBackend::makeProgram(*program, gpu::Shader::BindingSet()); + this->uniformsLocation = program->getBuffers().findLocation("overlayBuffer"); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + 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); + + pipeline = gpu::Pipeline::create(program, state); + } +} + +void HmdDisplayPlugin::OverlayRenderer::render(HmdDisplayPlugin& plugin) { + updatePipeline(); + for_each_eye([&](Eye eye){ + uniforms.mvp = mvps[eye]; + uniformBuffers[eye]->setSubData(0, uniforms); + }); + plugin.render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setFramebuffer(plugin._compositeFramebuffer); + batch.setPipeline(pipeline); + batch.setInputFormat(format); + gpu::BufferView posView(vertices, VERTEX_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::POSITION)._element); + gpu::BufferView uvView(vertices, TEXTURE_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::TEXCOORD)._element); + batch.setInputBuffer(gpu::Stream::POSITION, posView); + batch.setInputBuffer(gpu::Stream::TEXCOORD, uvView); + batch.setIndexBuffer(gpu::UINT16, indices, 0); + batch.setResourceTexture(0, plugin._currentFrame->overlay); + // FIXME use stereo information input to set both MVPs in the uniforms + for_each_eye([&](Eye eye) { + batch.setUniformBuffer(uniformsLocation, uniformBuffers[eye]); + batch.setViewportTransform(plugin.eyeViewport(eye)); + batch.drawIndexed(gpu::TRIANGLES, indexCount); + }); }); } void HmdDisplayPlugin::compositePointer() { - using namespace oglplus; - + auto& cursorManager = Cursor::Manager::instance(); + const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; auto compositorHelper = DependencyManager::get(); - - useProgram(_program); - // set the alpha - Uniform(*_program, _alphaUniform).Set(_compositeOverlayAlpha); - - // Mouse pointer - _plane->Use(); // Reconstruct the headpose from the eye poses auto headPosition = vec3(_currentPresentFrameInfo.presentPose[3]); - for_each_eye([&](Eye eye) { - eyeViewport(eye); - auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye); - auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition); - auto mvp = _eyeProjections[eye] * reticleTransform; - Uniform(*_program, _mvpUniform).Set(mvp); - _plane->Draw(); - }); - // restore the alpha - Uniform(*_program, _alphaUniform).Set(1.0); -} - - -void HmdDisplayPlugin::internalPresent() { - - PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) - - // Composite together the scene, overlay and mouse cursor - hmdPresent(); - - // screen preview mirroring - auto window = _container->getPrimaryWidget(); - auto devicePixelRatio = window->devicePixelRatio(); - auto windowSize = toGlm(window->size()); - windowSize *= devicePixelRatio; - float windowAspect = aspect(windowSize); - float sceneAspect = _enablePreview ? aspect(_renderTargetSize) : _previewAspect; - if (_enablePreview && _monoPreview) { - sceneAspect /= 2.0f; - } - float aspectRatio = sceneAspect / windowAspect; - - uvec2 targetViewportSize = windowSize; - if (aspectRatio < 1.0f) { - targetViewportSize.x *= aspectRatio; - } else { - targetViewportSize.y /= aspectRatio; - } - - uvec2 targetViewportPosition; - if (targetViewportSize.x < windowSize.x) { - targetViewportPosition.x = (windowSize.x - targetViewportSize.x) / 2; - } else if (targetViewportSize.y < windowSize.y) { - targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2; - } - - if (_enablePreview) { - using namespace oglplus; - Context::Clear().ColorBuffer(); - auto sourceSize = _compositeFramebuffer->size; - if (_monoPreview) { - sourceSize.x /= 2; - } - _compositeFramebuffer->Bound(Framebuffer::Target::Read, [&] { - Context::BlitFramebuffer( - 0, 0, sourceSize.x, sourceSize.y, - targetViewportPosition.x, targetViewportPosition.y, - targetViewportPosition.x + targetViewportSize.x, targetViewportPosition.y + targetViewportSize.y, - BufferSelectBit::ColorBuffer, BlitFilter::Nearest); + render([&](gpu::Batch& batch) { + // FIXME use standard gpu stereo rendering for this. + batch.enableStereo(false); + batch.setFramebuffer(_compositeFramebuffer); + batch.setPipeline(_cursorPipeline); + batch.setResourceTexture(0, cursorData.texture); + batch.resetViewTransform(); + for_each_eye([&](Eye eye) { + batch.setViewportTransform(eyeViewport(eye)); + batch.setProjectionTransform(_eyeProjections[eye]); + auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye); + auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition); + batch.setModelTransform(reticleTransform); + batch.draw(gpu::TRIANGLE_STRIP, 4); }); - swapBuffers(); - } else if (_firstPreview || windowSize != _prevWindowSize || devicePixelRatio != _prevDevicePixelRatio) { - useProgram(_previewProgram); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glViewport(targetViewportPosition.x, targetViewportPosition.y, targetViewportSize.x, targetViewportSize.y); - glUniform1i(_previewUniforms.previewTexture, 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, _previewTextureID); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - swapBuffers(); - _firstPreview = false; - _prevWindowSize = windowSize; - _prevDevicePixelRatio = devicePixelRatio; + }); +} + +void HmdDisplayPlugin::compositeOverlay() { + if (!_currentFrame || !_currentFrame->overlay) { + return; } - postPreview(); -} - -void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) { -} - -void HmdDisplayPlugin::updateFrameData() { - // Check if we have old frame data to discard - withPresentThreadLock([&] { - auto itr = _frameInfos.find(_currentPresentFrameIndex); - if (itr != _frameInfos.end()) { - _frameInfos.erase(itr); - } - }); - - Parent::updateFrameData(); - - withPresentThreadLock([&] { - _currentPresentFrameInfo = _frameInfos[_currentPresentFrameIndex]; - }); -} - -glm::mat4 HmdDisplayPlugin::getHeadPose() const { - return _currentRenderFrameInfo.renderPose; + _overlayRenderer.render(*this); } bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const vec4& color, const vec3& direction) { @@ -508,7 +494,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve info.mode = mode; info.color = color; info.direction = direction; - withRenderThreadLock([&] { + withNonPresentThreadLock([&] { if (hands & Hand::LeftHand) { _handLasers[0] = info; } @@ -522,7 +508,6 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve } void HmdDisplayPlugin::compositeExtra() { -#ifdef HMD_HAND_LASER_SUPPORT // If neither hand laser is activated, exit if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) { return; @@ -532,64 +517,20 @@ void HmdDisplayPlugin::compositeExtra() { return; } - updateLaserProgram(); - - // Render hand lasers - using namespace oglplus; - useProgram(_laserProgram); - _laserGeometry->Use(); - std::array handLaserModelMatrices; - - for (int i = 0; i < NUMBER_OF_HANDS; ++i) { - if (_presentHandPoses[i] == IDENTITY_MATRIX) { - continue; - } - const auto& handLaser = _presentHandLasers[i]; - if (!handLaser.valid()) { - continue; - } - - const auto& laserDirection = handLaser.direction; - auto model = _presentHandPoses[i]; - auto castDirection = glm::quat_cast(model) * laserDirection; - if (glm::abs(glm::length2(castDirection) - 1.0f) > EPSILON) { - castDirection = glm::normalize(castDirection); - } - - // FIXME fetch the actual UI radius from... somewhere? - float uiRadius = 1.0f; - - // Find the intersection of the laser with he UI and use it to scale the model matrix - float distance; - if (!glm::intersectRaySphere(vec3(_presentHandPoses[i][3]), castDirection, _presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) { - continue; - } - - // Make sure we rotate to match the desired laser direction - if (laserDirection != Vectors::UNIT_NEG_Z) { - auto rotation = glm::rotation(Vectors::UNIT_NEG_Z, laserDirection); - model = model * glm::mat4_cast(rotation); - } - - model = glm::scale(model, vec3(distance)); - handLaserModelMatrices[i] = model; - } - - glEnable(GL_BLEND); - for_each_eye([&](Eye eye) { - eyeViewport(eye); - auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye); - auto view = glm::inverse(eyePose); - const auto& projection = _eyeProjections[eye]; - for (int i = 0; i < NUMBER_OF_HANDS; ++i) { - if (handLaserModelMatrices[i] == IDENTITY_MATRIX) { - continue; + auto geometryCache = DependencyManager::get(); + render([&](gpu::Batch& batch) { + batch.setFramebuffer(_compositeFramebuffer); + batch.setViewportTransform(ivec4(uvec2(0), _renderTargetSize)); + batch.setViewTransform(_currentPresentFrameInfo.presentPose, false); + bilateral::for_each_side([&](bilateral::Side side){ + auto index = bilateral::index(side); + if (_presentHandPoses[index] == IDENTITY_MATRIX) { + return; } - Uniform(*_laserProgram, "mvp").Set(projection * view * handLaserModelMatrices[i]); - Uniform(*_laserProgram, "color").Set(_presentHandLasers[i].color); - _laserGeometry->Draw(); - } + const auto& points = _presentHandLaserPoints[index]; + const auto& lasers = _presentHandLasers[index]; + geometryCache->renderGlowLine(batch, points.first, points.second, lasers.color); + }); }); - glDisable(GL_BLEND); -#endif } + diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 79e52f1406..a5710b6077 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -9,14 +9,16 @@ #include +#include + #include #include -#include "../OpenGLDisplayPlugin.h" +#include +#include -#ifdef Q_OS_WIN -#define HMD_HAND_LASER_SUPPORT -#endif +#include "../CompositorHelper.h" +#include "../OpenGLDisplayPlugin.h" class HmdDisplayPlugin : public OpenGLDisplayPlugin { using Parent = OpenGLDisplayPlugin; @@ -28,7 +30,6 @@ public: glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; } glm::uvec2 getRecommendedUiSize() const override final; glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; } - void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final; bool isDisplayVisible() const override { return isHmdMounted(); } QRect getRecommendedOverlayRect() const override final; @@ -37,15 +38,19 @@ public: bool setHandLaser(uint32_t hands, HandLaserMode mode, const vec4& color, const vec3& direction) override; + bool wantVsync() const override { + return false; + } + protected: virtual void hmdPresent() = 0; virtual bool isHmdMounted() const = 0; virtual void postPreview() {}; virtual void updatePresentPose(); + bool beginFrameRender(uint32_t frameIndex) override; bool internalActivate() override; void internalDeactivate() override; - void compositeScene() override; void compositeOverlay() override; void compositePointer() override; void internalPresent() override; @@ -67,82 +72,74 @@ protected: Transform _uiModelTransform; std::array _handLasers; - std::array _handPoses; + std::array _handPoses; Transform _presentUiModelTransform; std::array _presentHandLasers; std::array _presentHandPoses; + std::array, 2> _presentHandLaserPoints; - std::array _eyeOffsets; - std::array _eyeProjections; - std::array _eyeInverseProjections; + std::array _eyeOffsets; + std::array _eyeProjections; + std::array _eyeInverseProjections; - glm::mat4 _cullingProjection; - glm::uvec2 _renderTargetSize; + mat4 _cullingProjection; + uvec2 _renderTargetSize; float _ipd { 0.064f }; struct FrameInfo { - glm::mat4 rawRenderPose; - glm::mat4 renderPose; - glm::mat4 rawPresentPose; - glm::mat4 presentPose; + mat4 renderPose; + mat4 presentPose; double sensorSampleTime { 0 }; double predictedDisplayTime { 0 }; - glm::mat3 presentReprojection; + mat3 presentReprojection; }; QMap _frameInfos; FrameInfo _currentPresentFrameInfo; FrameInfo _currentRenderFrameInfo; + bool _disablePreview{ true }; private: - void updateOverlayProgram(); -#ifdef HMD_HAND_LASER_SUPPORT - void updateLaserProgram(); -#endif - void updateReprojectionProgram(); + ivec4 getViewportForSourceSize(const uvec2& size) const; - bool _enablePreview { false }; + bool _disablePreviewItemAdded { false }; bool _monoPreview { true }; - bool _enableReprojection { true }; - bool _firstPreview { true }; + bool _clearPreviewFlag { false }; + gpu::TexturePointer _previewTexture; - ProgramPtr _overlayProgram; - struct OverlayUniforms { - int32_t mvp { -1 }; - int32_t alpha { -1 }; - int32_t glowColors { -1 }; - int32_t glowPoints { -1 }; - int32_t resolution { -1 }; - int32_t radius { -1 }; - } _overlayUniforms; + struct OverlayRenderer { + gpu::Stream::FormatPointer format; + gpu::BufferPointer vertices; + gpu::BufferPointer indices; + uint32_t indexCount { 0 }; + gpu::PipelinePointer pipeline; + int32_t uniformsLocation { -1 }; - ProgramPtr _previewProgram; - struct PreviewUniforms { - int32_t previewTexture { -1 }; - } _previewUniforms; + // FIXME this is stupid, use the built in transformation pipeline + std::array uniformBuffers; + std::array mvps; - float _previewAspect { 0 }; - GLuint _previewTextureID { 0 }; - glm::uvec2 _prevWindowSize { 0, 0 }; - qreal _prevDevicePixelRatio { 0 }; + struct Uniforms { + mat4 mvp; + vec4 glowPoints { -1 }; + vec4 glowColors[2]; + vec2 resolution { CompositorHelper::VIRTUAL_SCREEN_SIZE }; + float radius { 0.005f }; + float alpha { 1.0f }; + } uniforms; + + struct Vertex { + vec3 pos; + vec2 uv; + } vertex; - ProgramPtr _reprojectionProgram; - struct ReprojectionUniforms { - int32_t reprojectionMatrix { -1 }; - int32_t inverseProjectionMatrix { -1 }; - int32_t projectionMatrix { -1 }; - } _reprojectionUniforms; + static const size_t VERTEX_OFFSET { offsetof(Vertex, pos) }; + static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) }; + static const int VERTEX_STRIDE { sizeof(Vertex) }; - ShapeWrapperPtr _sphereSection; - -#ifdef HMD_HAND_LASER_SUPPORT - ProgramPtr _laserProgram; - struct LaserUniforms { - int32_t mvp { -1 }; - int32_t color { -1 }; - } _laserUniforms; - ShapeWrapperPtr _laserGeometry; -#endif + void build(); + void updatePipeline(); + void render(HmdDisplayPlugin& plugin); + } _overlayRenderer; }; - diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 62268afb47..0b20d0bf30 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -8,52 +8,56 @@ #include "InterleavedStereoDisplayPlugin.h" -static const char * INTERLEAVED_TEXTURED_VS = R"VS(#version 410 core -#pragma line __LINE__ +#include +#include +#include +#include -in vec3 Position; -in vec2 TexCoord; +static const char* INTERLEAVED_SRGB_TO_LINEAR_FRAG = R"SCRIBE( -out vec2 vTexCoord; +struct TextureData { + ivec2 textureSize; +}; -void main() { - gl_Position = vec4(Position, 1); - vTexCoord = TexCoord; -} +layout(std140) uniform textureDataBuffer { + TextureData textureData; +}; -)VS"; +uniform sampler2D colorMap; -static const char * INTERLEAVED_TEXTURED_FS = R"FS(#version 410 core -#pragma line __LINE__ +in vec2 varTexCoord0; -uniform sampler2D sampler; -uniform ivec2 textureSize; +out vec4 outFragColor; -in vec2 vTexCoord; -out vec4 FragColor; - -void main() { - ivec2 texCoord = ivec2(floor(vTexCoord * textureSize)); +void main(void) { + ivec2 texCoord = ivec2(floor(varTexCoord0 * textureData.textureSize)); texCoord.x /= 2; int row = int(floor(gl_FragCoord.y)); if (row % 2 > 0) { - texCoord.x += (textureSize.x / 2); + texCoord.x += (textureData.textureSize.x / 2); } - FragColor = texelFetch(sampler, texCoord, 0); //texture(sampler, texCoord); + outFragColor = vec4(pow(texelFetch(colorMap, texCoord, 0).rgb, vec3(2.2)), 1.0); } -)FS"; +)SCRIBE"; const QString InterleavedStereoDisplayPlugin::NAME("3D TV - Interleaved"); void InterleavedStereoDisplayPlugin::customizeContext() { StereoDisplayPlugin::customizeContext(); - // Set up the stencil buffers? Or use a custom shader? - compileProgram(_interleavedProgram, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS); + if (!_interleavedPresentPipeline) { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(INTERLEAVED_SRGB_TO_LINEAR_FRAG)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + _interleavedPresentPipeline = gpu::Pipeline::create(program, state); + } } void InterleavedStereoDisplayPlugin::uncustomizeContext() { - _interleavedProgram.reset(); + _interleavedPresentPipeline.reset(); StereoDisplayPlugin::uncustomizeContext(); } @@ -65,15 +69,14 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const { } void InterleavedStereoDisplayPlugin::internalPresent() { - using namespace oglplus; - auto sceneSize = getRecommendedRenderSize(); - _interleavedProgram->Bind(); - Uniform(*_interleavedProgram, "textureSize").SetValue(sceneSize); - auto surfaceSize = getSurfacePixels(); - Context::Viewport(0, 0, surfaceSize.x, surfaceSize.y); - glBindTexture(GL_TEXTURE_2D, GetName(_compositeFramebuffer->color)); - _plane->Use(); - _plane->Draw(); + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(gpu::FramebufferPointer()); + batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels())); + batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); + batch.setPipeline(_interleavedPresentPipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); swapBuffers(); } - diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h index 5eeda951e5..8c3ebcaa6d 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h @@ -24,6 +24,7 @@ protected: void internalPresent() override; private: - ProgramPtr _interleavedProgram; static const QString NAME; + gpu::PipelinePointer _interleavedPresentPipeline; + gpu::BufferPointer _textureDataBuffer; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index 5d9f812edf..aac9b9584f 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -7,11 +7,6 @@ // #include "SideBySideStereoDisplayPlugin.h" -#include -#include -#include -#include -#include "../CompositorHelper.h" const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo"); diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index cfdfb1fc21..ae8f9ec039 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -101,4 +101,3 @@ void StereoDisplayPlugin::internalDeactivate() { float StereoDisplayPlugin::getRecommendedAspectRatio() const { return aspect(Parent::getRecommendedRenderSize()); } - diff --git a/libraries/display-plugins/src/hmd_ui_glow.slf b/libraries/display-plugins/src/hmd_ui_glow.slf new file mode 100644 index 0000000000..9270842092 --- /dev/null +++ b/libraries/display-plugins/src/hmd_ui_glow.slf @@ -0,0 +1,75 @@ +// +// Created by Bradley Austin Davis on 2016/07/11 +// Copyright 2013-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 +// + +uniform sampler2D sampler; + +struct OverlayData { + mat4 mvp; + vec4 glowPoints; + vec4 glowColors[2]; + vec4 resolutionRadiusAlpha; +}; + +layout(std140) uniform overlayBuffer { + OverlayData overlay; +}; + +vec2 resolution = overlay.resolutionRadiusAlpha.xy; +float radius = overlay.resolutionRadiusAlpha.z; +float alpha = overlay.resolutionRadiusAlpha.w; +vec4 glowPoints = overlay.glowPoints; +vec4 glowColors[2] = overlay.glowColors; + +in vec3 vPosition; +in vec2 vTexCoord; + +out vec4 FragColor; + +float easeInOutCubic(float f) { + const float d = 1.0; + const float b = 0.0; + const float c = 1.0; + float t = f; + if ((t /= d / 2.0) < 1.0) return c / 2.0 * t * t * t + b; + return c / 2.0 * ((t -= 2.0) * t * t + 2.0) + b; +} + +void main() { + FragColor = texture(sampler, vTexCoord); + + vec2 aspect = resolution; + aspect /= resolution.x; + + float glowIntensity = 0.0; + float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect); + float dist2 = distance(vTexCoord * aspect, glowPoints.zw * aspect); + float dist = min(dist1, dist2); + vec3 glowColor = glowColors[0].rgb; + if (dist2 < dist1) { + glowColor = glowColors[1].rgb; + } + + if (dist <= radius) { + glowIntensity = 1.0 - (dist / radius); + glowColor.rgb = pow(glowColor, vec3(1.0 - glowIntensity)); + glowIntensity = easeInOutCubic(glowIntensity); + glowIntensity = pow(glowIntensity, 0.5); + } + + if (alpha <= 0.0) { + if (glowIntensity <= 0.0) { + discard; + } + + FragColor = vec4(glowColor, glowIntensity); + return; + } + + FragColor.rgb = mix(FragColor.rgb, glowColor.rgb, glowIntensity); + FragColor.a *= alpha; +} \ No newline at end of file diff --git a/libraries/display-plugins/src/hmd_ui_glow.slv b/libraries/display-plugins/src/hmd_ui_glow.slv new file mode 100644 index 0000000000..54eb062590 --- /dev/null +++ b/libraries/display-plugins/src/hmd_ui_glow.slv @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis on 2016/07/11 +// Copyright 2013-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 +// + +struct OverlayData { + mat4 mvp; + vec4 glowPoints; + vec4 glowColors[2]; + vec4 resolutionRadiusAlpha; +}; + +layout(std140) uniform overlayBuffer { + OverlayData overlay; +}; + +mat4 mvp = overlay.mvp; + +layout(location = 0) in vec3 Position; +layout(location = 3) in vec2 TexCoord; + +out vec3 vPosition; +out vec2 vTexCoord; + +void main() { + gl_Position = mvp * vec4(Position, 1); + vTexCoord = TexCoord; + vPosition = Position; +} diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index c560c43a5e..cb76eed9f2 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -35,7 +35,7 @@ public: /// Initializes the manager. HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0); - bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; private slots: void isTcpServerListening(); @@ -46,7 +46,7 @@ private: protected: /// Accepts all pending connections - virtual void incomingConnection(qintptr socketDescriptor); + virtual void incomingConnection(qintptr socketDescriptor) override; virtual bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url); QHostAddress _listenAddress; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 1ec934be92..e36563681a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -57,7 +57,10 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory) - REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, RenderableWebEntityItem::factory) + // Offscreen web surfaces are incompatible with nSight + if (!nsightActive()) { + REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, RenderableWebEntityItem::factory) + } REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Line, RenderableLineEntityItem::factory) @@ -159,7 +162,9 @@ void EntityTreeRenderer::init() { } void EntityTreeRenderer::shutdown() { - _entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential + if (_entitiesScriptEngine) { + _entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential + } _shuttingDown = true; clear(); // always clear() on shutdown @@ -341,15 +346,13 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetStage(); auto skyStage = scene->getSkyStage(); auto sceneKeyLight = sceneStage->getKeyLight(); - auto sceneLocation = sceneStage->getLocation(); - auto sceneTime = sceneStage->getTime(); // Skybox and procedural skybox data auto skybox = std::dynamic_pointer_cast(skyStage->getSkybox()); - static QString userData; + // If there is no zone, use the default background if (!zone) { - userData = QString(); + _zoneUserData = QString(); skybox->clear(); _pendingSkyboxTexture = false; @@ -358,51 +361,33 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrresetAmbientSphere(); - sceneKeyLight->setAmbientMap(nullptr); - sceneKeyLight->setColor(_previousKeyLightColor); - sceneKeyLight->setIntensity(_previousKeyLightIntensity); - sceneKeyLight->setAmbientIntensity(_previousKeyLightAmbientIntensity); - sceneKeyLight->setDirection(_previousKeyLightDirection); - sceneStage->setSunModelEnable(_previousStageSunModelEnabled); - sceneStage->setLocation(_previousStageLongitude, _previousStageLatitude, - _previousStageAltitude); - sceneTime->setHour(_previousStageHour); - sceneTime->setDay(_previousStageDay); + sceneKeyLight->resetAmbientSphere(); + sceneKeyLight->setAmbientMap(nullptr); - _hasPreviousZone = false; - } - - skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME); // let the application background through - - return; // Early exit - } - - if (!_hasPreviousZone) { - _previousKeyLightColor = sceneKeyLight->getColor(); - _previousKeyLightIntensity = sceneKeyLight->getIntensity(); - _previousKeyLightAmbientIntensity = sceneKeyLight->getAmbientIntensity(); - _previousKeyLightDirection = sceneKeyLight->getDirection(); - _previousStageSunModelEnabled = sceneStage->isSunModelEnabled(); - _previousStageLongitude = sceneLocation->getLongitude(); - _previousStageLatitude = sceneLocation->getLatitude(); - _previousStageAltitude = sceneLocation->getAltitude(); - _previousStageHour = sceneTime->getHour(); - _previousStageDay = sceneTime->getDay(); - _hasPreviousZone = true; + skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT); + return; } + // Set the keylight sceneKeyLight->setColor(ColorUtils::toVec3(zone->getKeyLightProperties().getColor())); sceneKeyLight->setIntensity(zone->getKeyLightProperties().getIntensity()); sceneKeyLight->setAmbientIntensity(zone->getKeyLightProperties().getAmbientIntensity()); sceneKeyLight->setDirection(zone->getKeyLightProperties().getDirection()); - sceneStage->setSunModelEnable(zone->getStageProperties().getSunModelEnabled()); - sceneStage->setLocation(zone->getStageProperties().getLongitude(), zone->getStageProperties().getLatitude(), - zone->getStageProperties().getAltitude()); - sceneTime->setHour(zone->getStageProperties().calculateHour()); - sceneTime->setDay(zone->getStageProperties().calculateDay()); + // Set the stage + bool isSunModelEnabled = zone->getStageProperties().getSunModelEnabled(); + sceneStage->setSunModelEnable(isSunModelEnabled); + if (isSunModelEnabled) { + sceneStage->setLocation(zone->getStageProperties().getLongitude(), + zone->getStageProperties().getLatitude(), + zone->getStageProperties().getAltitude()); + + auto sceneTime = sceneStage->getTime(); + sceneTime->setHour(zone->getStageProperties().calculateHour()); + sceneTime->setDay(zone->getStageProperties().calculateDay()); + } + + // Set the ambient texture bool isAmbientTextureSet = false; if (zone->getKeyLightProperties().getAmbientURL().isEmpty()) { _pendingAmbientTexture = false; @@ -425,12 +410,13 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetBackgroundMode()) { case BACKGROUND_MODE_SKYBOX: { skybox->setColor(zone->getSkyboxProperties().getColorVec3()); - if (userData != zone->getUserData()) { - userData = zone->getUserData(); - skybox->parse(userData); + if (_zoneUserData != zone->getUserData()) { + _zoneUserData = zone->getUserData(); + skybox->parse(_zoneUserData); } if (zone->getSkyboxProperties().getURL().isEmpty()) { skybox->setCubemap(nullptr); @@ -467,14 +453,18 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrclear(); _skyboxTexture.clear(); _pendingSkyboxTexture = false; // Let the application background through - skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME); + if (isAmbientTextureSet) { + skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_TEXTURE); + } else { + skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE); + } break; } @@ -529,7 +519,7 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl) { +ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority) { ModelPointer model = nullptr; // Only create and delete models on the thread that owns the EntityTreeRenderer @@ -543,6 +533,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString } model = std::make_shared(std::make_shared()); + model->setLoadingPriority(loadingPriority); model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index b0d0d2bacc..0e24fe2d8f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -28,11 +28,14 @@ class AbstractViewStateInterface; class Model; class ScriptEngine; class ZoneEntityItem; +class EntityItem; class Model; using ModelPointer = std::shared_ptr; using ModelWeakPointer = std::weak_ptr; +using CalculateEntityLoadingPriority = std::function; + // Generic client side Octree renderer class. class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency { Q_OBJECT @@ -41,10 +44,14 @@ public: AbstractScriptingServicesInterface* scriptingServices); virtual ~EntityTreeRenderer(); - virtual char getMyNodeType() const { return NodeType::EntityServer; } - virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; } - virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } - virtual void setTree(OctreePointer newTree); + virtual char getMyNodeType() const override { return NodeType::EntityServer; } + virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; } + virtual PacketType getExpectedPacketType() const override { return PacketType::EntityData; } + virtual void setTree(OctreePointer newTree) override; + + // Returns the priority at which an entity should be loaded. Higher values indicate higher priority. + float getEntityLoadingPriority(const EntityItem& item) const { return _calculateEntityLoadingPriorityFunc(item); } + void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { this->_calculateEntityLoadingPriorityFunc = fn; } void shutdown(); void update(); @@ -53,29 +60,29 @@ public: void processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); - virtual void init(); + virtual void init() override; + + virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) override; + virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) override; + virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) override; - virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem); - virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem); - virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem); - /// clears the tree - virtual void clear(); + virtual void clear() override; /// reloads the entity scripts, calling unload and preload void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl); - + Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority = 0.0f); + /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl); /// if a renderable entity item is done with a model, it should return it to us void releaseModel(ModelPointer model); - + void deleteReleasedModels(); - + // event handles which may generate entity related events void mouseReleaseEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); @@ -121,7 +128,7 @@ public slots: void setDontDoPrecisionPicking(bool value) { _dontDoPrecisionPicking = value; } protected: - virtual OctreePointer createTree() { + virtual OctreePointer createTree() override { EntityTreePointer newTree = EntityTreePointer(new EntityTree(true)); newTree->createRootElement(); return newTree; @@ -173,15 +180,16 @@ private: AbstractScriptingServicesInterface* _scriptingServices; bool _displayModelBounds; bool _dontDoPrecisionPicking; - + bool _shuttingDown { false }; QMultiMap _waitingOnPreload; - bool _hasPreviousZone { false }; std::shared_ptr _bestZone; float _bestZoneVolume; + QString _zoneUserData; + quint64 _lastZoneCheck { 0 }; const quint64 ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz const float ZONE_CHECK_DISTANCE = 0.001f; @@ -196,12 +204,16 @@ private: float _previousStageAltitude; float _previousStageHour; int _previousStageDay; - + QHash _entitiesInScene; // For Scene.shouldRenderEntities QList _entityIDsLastInScene; static int _entitiesScriptEngineCount; + + CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc = [](const EntityItem& item) -> float { + return 0.0f; + }; }; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 011675fc82..359b050803 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -19,7 +19,7 @@ namespace render { if (payload->entity->getType() == EntityTypes::Light) { return ItemKey::Builder::light(); } - if (payload && payload->entity->getType() == EntityTypes::PolyLine) { + if (payload && payload->entity->isTransparent()) { return ItemKey::Builder::transparentShape(); } } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 9840bf3150..22b6264520 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -96,8 +96,17 @@ public: \ virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override { _renderHelper.removeFromScene(self, scene, pendingChanges); } \ virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \ virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \ + void checkFading() { \ + bool transparent = isTransparent(); \ + if (transparent != _prevIsTransparent) { \ + _renderHelper.notifyChanged(); \ + _isFading = false; \ + _prevIsTransparent = transparent; \ + } \ + } \ private: \ - SimpleRenderableEntityItem _renderHelper; + SimpleRenderableEntityItem _renderHelper; \ + bool _prevIsTransparent { isTransparent() }; #endif // hifi_RenderableEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index fb6061e94f..b7f32cca65 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -28,6 +28,8 @@ EntityItemPointer RenderableLightEntityItem::factory(const EntityItemID& entityI void RenderableLightEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableLightEntityItem::render"); assert(getType() == EntityTypes::Light); + checkFading(); + glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); glm::quat rotation = getRotation(); @@ -35,7 +37,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) { glm::vec3 color = toGlm(getXColor()); - float intensity = getIntensity(); + float intensity = getIntensity() * (_isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f); float falloffRadius = getFalloffRadius(); float exponent = getExponent(); float cutoff = glm::radians(getCutoff()); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index eba2d4cf4b..ac447417aa 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -176,25 +176,6 @@ void RenderableModelEntityItem::doInitialModelSimulation() { _needsInitialSimulation = false; } - -// TODO: we need a solution for changes to the postion/rotation/etc of a model... -// this current code path only addresses that in this setup case... not the changing/moving case -bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { - if (!_model && renderArgs) { - // TODO: this getModel() appears to be about 3% of model render time. We should optimize - PerformanceTimer perfTimer("getModel"); - EntityTreeRenderer* renderer = static_cast(renderArgs->_renderer); - getModel(renderer); - } - if (renderArgs && _model && _needsInitialSimulation && _model->isActive() && _model->isLoaded()) { - // make sure to simulate so everything gets set up correctly for rendering - doInitialModelSimulation(); - _model->renderSetup(renderArgs); - } - bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(renderArgs); - return ready; -} - class RenderableModelEntityItemMeta { public: RenderableModelEntityItemMeta(EntityItemPointer entity) : entity(entity){ } @@ -371,6 +352,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); + // When the individual mesh parts of a model finish fading, they will mark their Model as needing updating + // we will watch for that and ask the model to update it's render items + if (_model && _model->getRenderItemsNeedUpdate()) { + _model->updateRenderItems(); + } + if (hasModel()) { // Prepare the current frame { @@ -484,7 +471,7 @@ ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { if (!getModelURL().isEmpty()) { // If we don't have a model, allocate one *immediately* if (!_model) { - _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL()); + _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL(), renderer->getEntityLoadingPriority(*this)); _needsInitialSimulation = true; // If we need to change URLs, update it *after rendering* (to avoid access violations) } else if ((QUrl(getModelURL()) != _model->getURL() || QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) { @@ -608,6 +595,9 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { } void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { + const uint32_t TRIANGLE_STRIDE = 3; + const uint32_t QUAD_STRIDE = 4; + ShapeType type = getShapeType(); glm::vec3 dimensions = getDimensions(); if (type == SHAPE_TYPE_COMPOUND) { @@ -624,8 +614,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. - const uint32_t TRIANGLE_STRIDE = 3; - const uint32_t QUAD_STRIDE = 4; foreach (const FBXMesh& mesh, collisionGeometry.meshes) { // each meshPart is a convex hull foreach (const FBXMeshPart &meshPart, mesh.parts) { @@ -634,7 +622,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // run through all the triangles and (uniquely) add each point to the hull uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); - assert(numIndices % TRIANGLE_STRIDE == 0); + // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + //assert(numIndices % TRIANGLE_STRIDE == 0); + numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader + for (uint32_t j = 0; j < numIndices; j += TRIANGLE_STRIDE) { glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]]; glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]]; @@ -652,7 +643,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // run through all the quads and (uniquely) add each point to the hull numIndices = (uint32_t)meshPart.quadIndices.size(); - assert(numIndices % QUAD_STRIDE == 0); + // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + //assert(numIndices % QUAD_STRIDE == 0); + numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXReader + for (uint32_t j = 0; j < numIndices; j += QUAD_STRIDE) { glm::vec3 p0 = mesh.vertices[meshPart.quadIndices[j]]; glm::vec3 p1 = mesh.vertices[meshPart.quadIndices[j + 1]]; @@ -748,6 +742,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { int32_t meshCount = 0; int32_t pointListIndex = 0; for (auto& mesh : meshes) { + if (!mesh) { + continue; + } const gpu::BufferView& vertices = mesh->getVertexBuffer(); const gpu::BufferView& indices = mesh->getIndexBuffer(); const gpu::BufferView& parts = mesh->getPartBuffer(); @@ -781,24 +778,30 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements())); gpu::BufferView::Iterator partItr = parts.cbegin(); while (partItr != parts.cend()) { + auto numIndices = partItr->_numIndices; if (partItr->_topology == model::Mesh::TRIANGLES) { - assert(partItr->_numIndices % 3 == 0); + // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + //assert(numIndices % TRIANGLE_STRIDE == 0); + numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader + auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + partItr->_numIndices; + auto indexEnd = indexItr + numIndices; while (indexItr != indexEnd) { triangleIndices.push_back(*indexItr + meshIndexOffset); ++indexItr; } } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) { - assert(partItr->_numIndices > 2); - uint32_t approxNumIndices = 3 * partItr->_numIndices; + // TODO: resurrect assert after we start sanitizing FBXMesh higher up + //assert(numIndices > 2); + + uint32_t approxNumIndices = TRIANGLE_STRIDE * numIndices; if (approxNumIndices > (uint32_t)(triangleIndices.capacity() - triangleIndices.size())) { // we underestimated the final size of triangleIndices so we pre-emptively expand it triangleIndices.reserve(triangleIndices.size() + approxNumIndices); } auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + (partItr->_numIndices - 2); + auto indexEnd = indexItr + (numIndices - 2); // first triangle uses the first three indices triangleIndices.push_back(*(indexItr++) + meshIndexOffset); @@ -832,18 +835,24 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { while (partItr != parts.cend()) { // collect unique list of indices for this part std::set uniqueIndices; + auto numIndices = partItr->_numIndices; if (partItr->_topology == model::Mesh::TRIANGLES) { - assert(partItr->_numIndices % 3 == 0); + // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + //assert(numIndices% TRIANGLE_STRIDE == 0); + numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader + auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + partItr->_numIndices; + auto indexEnd = indexItr + numIndices; while (indexItr != indexEnd) { uniqueIndices.insert(*indexItr); ++indexItr; } } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) { - assert(partItr->_numIndices > 2); + // TODO: resurrect assert after we start sanitizing FBXMesh higher up + //assert(numIndices > TRIANGLE_STRIDE - 1); + auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + (partItr->_numIndices - 2); + auto indexEnd = indexItr + (numIndices - 2); // first triangle uses the first three indices uniqueIndices.insert(*(indexItr++)); @@ -855,11 +864,11 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { while (indexItr != indexEnd) { if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { if (triangleCount % 2 == 0) { - // even triangles use first two indices in order + // EVEN triangles use first two indices in order uniqueIndices.insert(*(indexItr - 2)); uniqueIndices.insert(*(indexItr - 1)); } else { - // odd triangles swap order of first two indices + // ODD triangles swap order of first two indices uniqueIndices.insert(*(indexItr - 1)); uniqueIndices.insert(*(indexItr - 2)); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index f487e79880..a3d9e4db98 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -40,7 +40,6 @@ public: void doInitialModelSimulation(); - virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr); virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override; virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override; @@ -52,7 +51,6 @@ public: bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const override; - ModelPointer getModel(EntityTreeRenderer* renderer); virtual bool needsToCallUpdate() const override; @@ -93,6 +91,9 @@ public: render::ItemID getMetaRenderItem() { return _myMetaItem; } + // Transparency is handled in ModelMeshPartPayload + bool isTransparent() override { return false; } + private: QVariantMap parseTexturesToMap(QString textures); void remapTextures(); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 600b876d39..dc2545b956 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -167,6 +167,8 @@ void RenderablePolyLineEntityItem::update(const quint64& now) { } void RenderablePolyLineEntityItem::render(RenderArgs* args) { + checkFading(); + QWriteLocker lock(&_quadReadWriteLock); if (_points.size() < 2 || _normals.size () < 2 || _strokeWidths.size() < 2) { return; @@ -204,5 +206,9 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); + if (_isFading) { + batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime)); + } + batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0); }; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index dfde97c407..75b2bcd58a 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -30,7 +30,9 @@ public: virtual void render(RenderArgs* args) override; virtual void update(const quint64& now) override; - virtual bool needsToCallUpdate() const override { return true; }; + virtual bool needsToCallUpdate() const override { return true; } + + bool isTransparent() override { return true; } SIMPLE_RENDERABLE(); @@ -47,7 +49,6 @@ protected: gpu::BufferView _uniformBuffer; unsigned int _numVertices; QVector _vertices; - }; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index c46a26deb5..44186073b2 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -50,7 +50,7 @@ public: void initializePolyVox(); - virtual void somethingChangedNotification() { + virtual void somethingChangedNotification() override { // This gets called from EnityItem::readEntityDataFromBuffer every time a packet describing // this entity comes from the entity-server. It gets called even if nothing has actually changed // (see the comment in EntityItem.cpp). If that gets fixed, this could be used to know if we @@ -58,19 +58,19 @@ public: // _needsModelReload = true; } - virtual uint8_t getVoxel(int x, int y, int z); - virtual bool setVoxel(int x, int y, int z, uint8_t toValue); + virtual uint8_t getVoxel(int x, int y, int z) override; + virtual bool setVoxel(int x, int y, int z, uint8_t toValue) override; - void render(RenderArgs* args); - virtual bool supportsDetailedRayIntersection() const { return true; } + void render(RenderArgs* args) override; + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const; + void** intersectedObject, bool precisionPicking) const override; - virtual void setVoxelData(QByteArray voxelData); - virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); - virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle); + virtual void setVoxelData(QByteArray voxelData) override; + virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize) override; + virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) override; glm::vec3 getSurfacePositionAdjustment() const; glm::mat4 voxelToWorldMatrix() const; @@ -78,45 +78,45 @@ public: glm::mat4 voxelToLocalMatrix() const; glm::mat4 localToVoxelMatrix() const; - virtual ShapeType getShapeType() const; - virtual bool shouldBePhysical() const { return !isDead(); } - virtual bool isReadyToComputeShape(); - virtual void computeShapeInfo(ShapeInfo& info); + virtual ShapeType getShapeType() const override; + virtual bool shouldBePhysical() const override { return !isDead(); } + virtual bool isReadyToComputeShape() override; + virtual void computeShapeInfo(ShapeInfo& info) override; - virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const; - virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const; - virtual glm::vec3 voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const; - virtual glm::vec3 localCoordsToVoxelCoords(glm::vec3& localCoords) const; + virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const override; + virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const override; + virtual glm::vec3 voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const override; + virtual glm::vec3 localCoordsToVoxelCoords(glm::vec3& localCoords) const override; // coords are in voxel-volume space - virtual bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue); - virtual bool setVoxelInVolume(glm::vec3 position, uint8_t toValue); + virtual bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) override; + virtual bool setVoxelInVolume(glm::vec3 position, uint8_t toValue) override; // coords are in world-space - virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue); - virtual bool setAll(uint8_t toValue); - virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue); + virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue) override; + virtual bool setAll(uint8_t toValue) override; + virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue) override; - virtual void setXTextureURL(QString xTextureURL); - virtual void setYTextureURL(QString yTextureURL); - virtual void setZTextureURL(QString zTextureURL); + virtual void setXTextureURL(QString xTextureURL) override; + virtual void setYTextureURL(QString yTextureURL) override; + virtual void setZTextureURL(QString zTextureURL) override; virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, - render::PendingChanges& pendingChanges); + render::PendingChanges& pendingChanges) override; virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, - render::PendingChanges& pendingChanges); + render::PendingChanges& pendingChanges) override; - virtual void setXNNeighborID(const EntityItemID& xNNeighborID); - virtual void setYNNeighborID(const EntityItemID& yNNeighborID); - virtual void setZNNeighborID(const EntityItemID& zNNeighborID); + virtual void setXNNeighborID(const EntityItemID& xNNeighborID) override; + virtual void setYNNeighborID(const EntityItemID& yNNeighborID) override; + virtual void setZNNeighborID(const EntityItemID& zNNeighborID) override; - virtual void setXPNeighborID(const EntityItemID& xPNeighborID); - virtual void setYPNeighborID(const EntityItemID& yPNeighborID); - virtual void setZPNeighborID(const EntityItemID& zPNeighborID); + virtual void setXPNeighborID(const EntityItemID& xPNeighborID) override; + virtual void setYPNeighborID(const EntityItemID& yPNeighborID) override; + virtual void setZPNeighborID(const EntityItemID& zPNeighborID) override; - virtual void updateRegistrationPoint(const glm::vec3& value); + virtual void updateRegistrationPoint(const glm::vec3& value) override; void setVoxelsFromData(QByteArray uncompressedData, quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize); void forEachVoxelValue(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize, @@ -131,6 +131,9 @@ public: void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; }); } + // Transparent polyvox didn't seem to be working so disable for now + bool isTransparent() override { return false; } + private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. @@ -161,7 +164,7 @@ private: // these are run off the main thread void decompressVolumeData(); void compressVolumeDataAndSendEditPacket(); - virtual void getMesh(); // recompute mesh + virtual void getMesh() override; // recompute mesh void computeShapeInfoWorker(); // these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 48ad05a714..c3e097382c 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -40,7 +40,6 @@ static std::array MAPPING { { GeometryCache::Cylinder, } }; - RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { Pointer entity = std::make_shared(entityID); entity->setProperties(properties); @@ -72,18 +71,29 @@ void RenderableShapeEntityItem::setUserData(const QString& value) { } } +bool RenderableShapeEntityItem::isTransparent() { + if (_procedural && _procedural->isFading()) { + float isFading = Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f; + _procedural->setIsFading(isFading); + return isFading; + } else { + return getLocalRenderAlpha() < 1.0f || EntityItem::isTransparent(); + } +} + void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); Q_ASSERT(args->_batch); + checkFading(); if (!_procedural) { _procedural.reset(new Procedural(getUserData())); _procedural->_vertexSource = simple_vert; _procedural->_fragmentSource = simple_frag; - _procedural->_state->setCullMode(gpu::State::CULL_NONE); - _procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL); - _procedural->_state->setBlendFunction(false, + _procedural->_opaqueState->setCullMode(gpu::State::CULL_NONE); + _procedural->_opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL); + _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); } @@ -102,14 +112,17 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { if (_procedural->ready()) { _procedural->prepare(batch, getPosition(), getDimensions(), getOrientation()); auto outColor = _procedural->getColor(color); + outColor.a *= _procedural->isFading() ? Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) : 1.0f; batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); DependencyManager::get()->renderShape(batch, MAPPING[_shape]); } else { // FIXME, support instanced multi-shape rendering using multidraw indirect - DependencyManager::get()->renderSolidShapeInstance(batch, MAPPING[_shape], color); + color.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; + auto geometryCache = DependencyManager::get(); + auto pipeline = color.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); + geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline); } - static const auto triCount = DependencyManager::get()->getShapeTriangleCount(MAPPING[_shape]); args->_details._trianglesRendered += (int)triCount; } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index b18370b13c..7eefe0e7a4 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -26,10 +26,12 @@ public: void render(RenderArgs* args) override; void setUserData(const QString& value) override; - SIMPLE_RENDERABLE(); + bool isTransparent() override; private: - QSharedPointer _procedural; + std::unique_ptr _procedural { nullptr }; + + SIMPLE_RENDERABLE(); }; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 6773a906fe..20adff83df 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -15,8 +15,6 @@ #include #include - - #include "RenderableTextEntityItem.h" #include "GLMHelpers.h" @@ -29,10 +27,13 @@ EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID void RenderableTextEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableTextEntityItem::render"); Q_ASSERT(getType() == EntityTypes::Text); + checkFading(); static const float SLIGHTLY_BEHIND = -0.005f; - glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), 1.0f); - glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f); + float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; + bool transparent = fadeRatio < 1.0f; + glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), fadeRatio); + glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), fadeRatio); glm::vec3 dimensions = getDimensions(); // Render background @@ -62,7 +63,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) { batch.setModelTransform(transformToTopLeft); - DependencyManager::get()->bindSimpleProgram(batch, false, false, false, true); + DependencyManager::get()->bindSimpleProgram(batch, false, transparent, false, false, true); DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor); float scale = _lineHeight / _textRenderer->getFontSize(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 82c142db37..c712ee506e 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -164,6 +164,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { } void RenderableWebEntityItem::render(RenderArgs* args) { + checkFading(); + #ifdef WANT_EXTRA_DEBUGGING { gpu::Batch& batch = *args->_batch; @@ -181,6 +183,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { if (!buildWebSurface(static_cast(args->_renderer))) { return; } + _fadeStartTime = usecTimestampNow(); #endif } @@ -207,8 +210,11 @@ void RenderableWebEntityItem::render(RenderArgs* args) { batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture); } + float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; + batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); + DependencyManager::get()->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch); - DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); + DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio)); } void RenderableWebEntityItem::setSourceUrl(const QString& value) { diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 9b7193bbfc..bfbe6d7e5a 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -39,7 +39,7 @@ void main(void) { vec3 color = varColor.rgb; packDeferredFragmentTranslucent( interpolatedNormal * frontCondition, - texel.a, + texel.a * varColor.a, polyline.color * texel.rgb, vec3(0.01, 0.01, 0.01), 10.0); diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf index b7682913a7..bebefa9434 100644 --- a/libraries/entities-renderer/src/polyvox.slf +++ b/libraries/entities-renderer/src/polyvox.slf @@ -1,7 +1,7 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// model.frag +// polyvox.frag // fragment shader // // Created by Seth Alves on 2015-8-3 @@ -41,5 +41,13 @@ void main(void) { vec3 yzDiffuseScaled = yzDiffuse.rgb * abs(worldNormal.x); vec4 diffuse = vec4(xyDiffuseScaled + xzDiffuseScaled + yzDiffuseScaled, 1.0); - packDeferredFragment(_normal, 1.0, vec3(diffuse), DEFAULT_ROUGHNESS, DEFAULT_METALLIC, DEFAULT_EMISSIVE, DEFAULT_OCCLUSION, DEFAULT_SCATTERING); + packDeferredFragment( + _normal, + 1.0, + vec3(diffuse), + DEFAULT_ROUGHNESS, + DEFAULT_METALLIC, + DEFAULT_EMISSIVE, + DEFAULT_OCCLUSION, + DEFAULT_SCATTERING); } diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index 79f75187c5..cab76227c4 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -44,7 +44,7 @@ out vec4 varColor; out vec2 varTexcoord; const int NUM_VERTICES_PER_PARTICLE = 4; -// This ordering ensures that un-rotated particles render upright in the wiewer. +// This ordering ensures that un-rotated particles render upright in the viewer. const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE]( vec4(-1.0, 1.0, 0.0, 0.0), vec4(-1.0, -1.0, 0.0, 0.0), diff --git a/libraries/entities/src/AddEntityOperator.h b/libraries/entities/src/AddEntityOperator.h index 6aa2d5f727..0f33cacae3 100644 --- a/libraries/entities/src/AddEntityOperator.h +++ b/libraries/entities/src/AddEntityOperator.h @@ -16,9 +16,9 @@ class AddEntityOperator : public RecurseOctreeOperator { public: AddEntityOperator(EntityTreePointer tree, EntityItemPointer newEntity); - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; + virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override; private: EntityTreePointer _tree; EntityItemPointer _newEntity; diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index 3ee452cc5f..4284be1eda 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -34,30 +34,33 @@ public: void associateWithAnimationLoop(AnimationLoop* animationLoop) { _animationLoop = animationLoop; } // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); - virtual void debugDump() const; - virtual void listChangedProperties(QList& out); + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes); - virtual void markAllChanged(); - virtual EntityPropertyFlags getChangedProperties() const; + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; // EntityItem related helpers // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const; + virtual void getProperties(EntityItemProperties& propertiesOut) const override; /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties); + virtual bool setProperties(const EntityItemProperties& properties) override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, @@ -65,12 +68,12 @@ public: EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, ""); DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f); diff --git a/libraries/entities/src/DeleteEntityOperator.h b/libraries/entities/src/DeleteEntityOperator.h index ae2ec3621e..245d331743 100644 --- a/libraries/entities/src/DeleteEntityOperator.h +++ b/libraries/entities/src/DeleteEntityOperator.h @@ -36,8 +36,8 @@ public: ~DeleteEntityOperator(); void addEntityIDToDeleteList(const EntityItemID& searchEntityID); - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; const RemovedEntities& getEntities() const { return _entitiesToDelete; } private: diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 1991142f3f..9150748a68 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -42,8 +42,8 @@ public: void queueEraseEntityMessage(const EntityItemID& entityItemID); // My server type is the model server - virtual char getMyNodeType() const { return NodeType::EntityServer; } - virtual void adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew); + virtual char getMyNodeType() const override { return NodeType::EntityServer; } + virtual void adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew) override; public slots: void processEntityEditNackPacket(QSharedPointer message, SharedNodePointer sendingNode); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f774d52274..29cbfd79e6 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -35,6 +35,7 @@ int EntityItem::_maxActionsDataSize = 800; quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND; +std::function EntityItem::_entitiesShouldFadeFunction = [](){ return true; }; EntityItem::EntityItem(const EntityItemID& entityItemID) : SpatiallyNestable(NestableType::Entity, entityItemID), @@ -2212,4 +2213,4 @@ void EntityItem::globalizeProperties(EntityItemProperties& properties, const QSt } QUuid empty; properties.setParentID(empty); -} +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 9fa13690f1..45e178a8dc 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "EntityItemID.h" #include "EntityItemPropertiesDefaults.h" @@ -52,7 +53,7 @@ namespace render { } #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; -#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { }; +#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { }; #define debugTime(T, N) qPrintable(QString("%1 [ %2 ago]").arg(T, 16, 10).arg(formatUsecTime(N - T), 15)) #define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10)) @@ -321,10 +322,6 @@ public: /// return preferred shape type (actual physical shape may differ) virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } - // these are only needed because the names don't match - virtual const glm::quat getRotation() const { return getOrientation(); } - virtual void setRotation(glm::quat orientation) { setOrientation(orientation); } - // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags virtual void updateRegistrationPoint(const glm::vec3& value); void updatePosition(const glm::vec3& value); @@ -435,6 +432,9 @@ public: QUuid getOwningAvatarID() const { return _owningAvatarID; } void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } + static void setEntitiesShouldFadeFunction(std::function func) { _entitiesShouldFadeFunction = func; } + static std::function getEntitiesShouldFadeFunction() { return _entitiesShouldFadeFunction; } + virtual bool isTransparent() { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; } protected: @@ -565,7 +565,9 @@ protected: quint64 _lastUpdatedAngularVelocityTimestamp { 0 }; quint64 _lastUpdatedAccelerationTimestamp { 0 }; - + quint64 _fadeStartTime { usecTimestampNow() }; + static std::function _entitiesShouldFadeFunction; + bool _isFading { _entitiesShouldFadeFunction() }; }; #endif // hifi_EntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index dcd7e25bc1..b3d810c0eb 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -325,6 +325,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_QUERY_AA_CUBE, queryAACube); CHECK_PROPERTY_CHANGE(PROP_LOCAL_POSITION, localPosition); CHECK_PROPERTY_CHANGE(PROP_LOCAL_ROTATION, localRotation); + CHECK_PROPERTY_CHANGE(PROP_LOCAL_VELOCITY, localVelocity); + CHECK_PROPERTY_CHANGE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity); CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed); CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed); @@ -570,6 +572,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_VELOCITY, localVelocity); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); @@ -707,6 +711,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(localPosition, glmVec3, setLocalPosition); COPY_PROPERTY_FROM_QSCRIPTVALUE(localRotation, glmQuat, setLocalRotation); + COPY_PROPERTY_FROM_QSCRIPTVALUE(localVelocity, glmVec3, setLocalVelocity); + COPY_PROPERTY_FROM_QSCRIPTVALUE(localAngularVelocity, glmVec3, setLocalAngularVelocity); COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotationsSet, qVectorBool, setJointRotationsSet); COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotations, qVectorQuat, setJointRotations); @@ -864,6 +870,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_LOCAL_POSITION, LocalPosition, localPosition, glm::vec3); ADD_PROPERTY_TO_MAP(PROP_LOCAL_ROTATION, LocalRotation, localRotation, glm::quat); + ADD_PROPERTY_TO_MAP(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glm::vec3); ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector); ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector); @@ -1982,7 +1990,9 @@ QList EntityItemProperties::listChangedProperties() { } bool EntityItemProperties::parentDependentPropertyChanged() const { - return localPositionChanged() || positionChanged() || localRotationChanged() || rotationChanged(); + return localPositionChanged() || positionChanged() || + localRotationChanged() || rotationChanged() || + localVelocityChanged() || localAngularVelocityChanged(); } bool EntityItemProperties::parentRelatedPropertyChanged() const { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6018ba793f..22d740e0dd 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -201,6 +201,8 @@ public: // these are used when bouncing location data into and out of scripts DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3); DEFINE_PROPERTY_REF(PROP_LOCAL_ROTATION, LocalRotation, localRotation, glmQuat, ENTITY_ITEM_DEFAULT_ROTATION); + DEFINE_PROPERTY_REF(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, glmVec3, ENTITY_ITEM_ZERO_VEC3); + DEFINE_PROPERTY_REF(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glmVec3, ENTITY_ITEM_ZERO_VEC3); DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector, QVector()); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 36bb37c8f3..0baef0fa25 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -177,6 +177,9 @@ enum EntityPropertyList { PROP_SHAPE, + PROP_LOCAL_VELOCITY, // only used to convert values to and from scripts + PROP_LOCAL_ANGULAR_VELOCITY, // only used to convert values to and from scripts + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 653b37c3bb..1961742c2e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -40,6 +40,12 @@ void EntityScriptingInterface::queueEntityMessage(PacketType packetType, getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties); } +void EntityScriptingInterface::resetActivityTracking() { + _activityTracking.addedEntityCount = 0; + _activityTracking.deletedEntityCount = 0; + _activityTracking.editedEntityCount = 0; +} + bool EntityScriptingInterface::canAdjustLocks() { auto nodeList = DependencyManager::get(); return nodeList->isAllowedEditor(); @@ -78,6 +84,8 @@ EntityItemProperties convertLocationToScriptSemantics(const EntityItemProperties EntityItemProperties scriptSideProperties = entitySideProperties; scriptSideProperties.setLocalPosition(entitySideProperties.getPosition()); scriptSideProperties.setLocalRotation(entitySideProperties.getRotation()); + scriptSideProperties.setLocalVelocity(entitySideProperties.getLocalVelocity()); + scriptSideProperties.setLocalAngularVelocity(entitySideProperties.getLocalAngularVelocity()); bool success; glm::vec3 worldPosition = SpatiallyNestable::localToWorld(entitySideProperties.getPosition(), @@ -88,10 +96,19 @@ EntityItemProperties convertLocationToScriptSemantics(const EntityItemProperties entitySideProperties.getParentID(), entitySideProperties.getParentJointIndex(), success); - // TODO -- handle velocity and angularVelocity + glm::vec3 worldVelocity = SpatiallyNestable::localToWorldVelocity(entitySideProperties.getVelocity(), + entitySideProperties.getParentID(), + entitySideProperties.getParentJointIndex(), + success); + glm::vec3 worldAngularVelocity = SpatiallyNestable::localToWorldAngularVelocity(entitySideProperties.getAngularVelocity(), + entitySideProperties.getParentID(), + entitySideProperties.getParentJointIndex(), + success); scriptSideProperties.setPosition(worldPosition); scriptSideProperties.setRotation(worldRotation); + scriptSideProperties.setVelocity(worldVelocity); + scriptSideProperties.setAngularVelocity(worldAngularVelocity); return scriptSideProperties; } @@ -125,11 +142,34 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti entitySideProperties.setRotation(localRotation); } + if (scriptSideProperties.localVelocityChanged()) { + entitySideProperties.setVelocity(scriptSideProperties.getLocalVelocity()); + } else if (scriptSideProperties.velocityChanged()) { + glm::vec3 localVelocity = SpatiallyNestable::worldToLocalVelocity(entitySideProperties.getVelocity(), + entitySideProperties.getParentID(), + entitySideProperties.getParentJointIndex(), + success); + entitySideProperties.setVelocity(localVelocity); + } + + if (scriptSideProperties.localAngularVelocityChanged()) { + entitySideProperties.setAngularVelocity(scriptSideProperties.getLocalAngularVelocity()); + } else if (scriptSideProperties.angularVelocityChanged()) { + glm::vec3 localAngularVelocity = + SpatiallyNestable::worldToLocalAngularVelocity(entitySideProperties.getAngularVelocity(), + entitySideProperties.getParentID(), + entitySideProperties.getParentJointIndex(), + success); + entitySideProperties.setAngularVelocity(localAngularVelocity); + } + return entitySideProperties; } QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { + _activityTracking.addedEntityCount++; + EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); @@ -200,6 +240,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, bool dynamic, const glm::vec3& position, const glm::vec3& gravity) { + _activityTracking.addedEntityCount++; + EntityItemProperties properties; properties.setType(EntityTypes::Model); properties.setName(name); @@ -263,6 +305,8 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit } QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) { + _activityTracking.editedEntityCount++; + EntityItemProperties properties = scriptSideProperties; auto dimensions = properties.getDimensions(); @@ -406,6 +450,8 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } void EntityScriptingInterface::deleteEntity(QUuid id) { + _activityTracking.deletedEntityCount++; + EntityItemID entityID(id); bool shouldDelete = true; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 5aa0f5907e..e3c659cf6b 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -59,20 +59,30 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT - + Q_PROPERTY(float currentAvatarEnergy READ getCurrentAvatarEnergy WRITE setCurrentAvatarEnergy) Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier) public: EntityScriptingInterface(bool bidOnSimulationOwnership); + class ActivityTracking { + public: + int addedEntityCount { 0 }; + int deletedEntityCount { 0 }; + int editedEntityCount { 0 }; + }; + EntityEditPacketSender* getEntityPacketSender() const { return (EntityEditPacketSender*)getPacketSender(); } - virtual NodeType_t getServerNodeType() const { return NodeType::EntityServer; } - virtual OctreeEditPacketSender* createPacketSender() { return new EntityEditPacketSender(); } + virtual NodeType_t getServerNodeType() const override { return NodeType::EntityServer; } + virtual OctreeEditPacketSender* createPacketSender() override { return new EntityEditPacketSender(); } void setEntityTree(EntityTreePointer modelTree); EntityTreePointer getEntityTree() { return _entityTree; } void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine); float calculateCost(float mass, float oldVelocity, float newVelocity); + + void resetActivityTracking(); + ActivityTracking getActivityTracking() const { return _activityTracking; } public slots: // returns true if the DomainServer will allow this Node/Avatar to make changes @@ -221,12 +231,13 @@ private: std::recursive_mutex _entitiesScriptEngineLock; EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr }; - + bool _bidOnSimulationOwnership { false }; float _currentAvatarEnergy = { FLT_MAX }; float getCurrentAvatarEnergy() { return _currentAvatarEnergy; } void setCurrentAvatarEnergy(float energy); - + + ActivityTracking _activityTracking; float costMultiplier = { 0.01f }; float getCostMultiplier(); void setCostMultiplier(float value); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 848d473321..4cdebc364c 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1304,8 +1304,8 @@ void EntityTree::debugDumpMap() { class ContentsDimensionOperator : public RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element) { return true; } + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override { return true; } glm::vec3 getDimensions() const { return _contentExtents.size(); } float getLargestDimension() const { return _contentExtents.largestDimension(); } private: @@ -1332,8 +1332,8 @@ float EntityTree::getContentsLargestDimension() { class DebugOperator : public RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element) { return true; } + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override { return true; } }; bool DebugOperator::preRecursion(OctreeElementPointer element) { @@ -1350,8 +1350,8 @@ void EntityTree::dumpTree() { class PruneOperator : public RecurseOctreeOperator { public: - virtual bool preRecursion(OctreeElementPointer element) { return true; } - virtual bool postRecursion(OctreeElementPointer element); + virtual bool preRecursion(OctreeElementPointer element) override { return true; } + virtual bool postRecursion(OctreeElementPointer element) override; }; bool PruneOperator::postRecursion(OctreeElementPointer element) { diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 0523933ee6..657e0b286b 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -634,8 +634,8 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con } } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results - // Never intersect with particle effect entities - if (localDistance < distance && EntityTypes::getEntityTypeName(entity->getType()) != "ParticleEffect") { + // Never intersect with particle entities + if (localDistance < distance && entity->getType() != EntityTypes::ParticleEffect) { distance = localDistance; face = localFace; surfaceNormal = localSurfaceNormal; diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index aa05438bde..4875e258da 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -80,7 +80,7 @@ class EntityTreeElement : public OctreeElement, ReadWriteLockable { EntityTreeElement(unsigned char* octalCode = NULL); - virtual OctreeElementPointer createNewElement(unsigned char* octalCode = NULL); + virtual OctreeElementPointer createNewElement(unsigned char* octalCode = NULL) override; public: virtual ~EntityTreeElement(); @@ -93,67 +93,69 @@ public: // methods you can and should override to implement your tree functionality /// Adds a child to the current element. Override this if there is additional child initialization your class needs. - virtual OctreeElementPointer addChildAtIndex(int index); + virtual OctreeElementPointer addChildAtIndex(int index) override; /// Override this to implement LOD averaging on changes to the tree. - virtual void calculateAverageFromChildren(); + virtual void calculateAverageFromChildren() override; /// Override this to implement LOD collapsing and identical child pruning on changes to the tree. - virtual bool collapseChildren(); + virtual bool collapseChildren() override; /// Should this element be considered to have content in it. This will be used in collision and ray casting methods. /// By default we assume that only leaves are actual content, but some octrees may have different semantics. - virtual bool hasContent() const { return hasEntities(); } + virtual bool hasContent() const override { return hasEntities(); } /// Should this element be considered to have detailed content in it. Specifically should it be rendered. /// By default we assume that only leaves have detailed content, but some octrees may have different semantics. - virtual bool hasDetailedContent() const { return hasEntities(); } + virtual bool hasDetailedContent() const override { return hasEntities(); } /// Override this to break up large octree elements when an edit operation is performed on a smaller octree element. /// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the /// meaningful split would be to break the larger cube into smaller cubes of the same color/texture. - virtual void splitChildren() { } + virtual void splitChildren() override { } /// Override to indicate that this element requires a split before editing lower elements in the octree - virtual bool requiresSplit() const { return false; } + virtual bool requiresSplit() const override { return false; } - virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const; - virtual void initializeExtraEncodeData(EncodeBitstreamParams& params); - virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const; - virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const; - virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const; - virtual void elementEncodeComplete(EncodeBitstreamParams& params) const; + virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const override; + virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) override; + virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const override; + virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const override; + virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const override; + virtual void elementEncodeComplete(EncodeBitstreamParams& params) const override; bool alreadyFullyEncoded(EncodeBitstreamParams& params) const; /// Override to serialize the state of this element. This is used for persistance and for transmission across the network. - virtual OctreeElement::AppendState appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const; + virtual OctreeElement::AppendState appendElementData(OctreePacketData* packetData, + EncodeBitstreamParams& params) const override; /// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading /// from the network. - virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); + virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args) override; /// Override to indicate that the item is currently rendered in the rendering engine. By default we assume that if /// the element should be rendered, then your rendering engine is rendering. But some rendering engines my have cases /// where an element is not actually rendering all should render elements. If the isRendered() state doesn't match the /// shouldRender() state, the tree will remark elements as changed even in cases there the elements have not changed. - virtual bool isRendered() const { return getShouldRender(); } - virtual bool deleteApproved() const { return !hasEntities(); } + virtual bool isRendered() const override { return getShouldRender(); } + virtual bool deleteApproved() const override { return !hasEntities(); } - virtual bool canRayIntersect() const { return hasEntities(); } + virtual bool canRayIntersect() const override { return hasEntities(); } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& node, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, void** intersectedObject = NULL, bool precisionPicking = false); virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, + bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, void** intersectedObject, bool precisionPicking, float distanceToElementCube); virtual bool findSpherePenetration(const glm::vec3& center, float radius, - glm::vec3& penetration, void** penetratedObject) const; + glm::vec3& penetration, void** penetratedObject) const override; template @@ -232,7 +234,7 @@ public: } protected: - virtual void init(unsigned char * octalCode); + virtual void init(unsigned char * octalCode) override; EntityTreePointer _myTree; EntityItems _entityItems; }; diff --git a/libraries/entities/src/EntityTreeHeadlessViewer.h b/libraries/entities/src/EntityTreeHeadlessViewer.h index 0e0d4f9726..fe05323a0b 100644 --- a/libraries/entities/src/EntityTreeHeadlessViewer.h +++ b/libraries/entities/src/EntityTreeHeadlessViewer.h @@ -30,9 +30,9 @@ public: EntityTreeHeadlessViewer(); virtual ~EntityTreeHeadlessViewer(); - virtual char getMyNodeType() const { return NodeType::EntityServer; } - virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; } - virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } + virtual char getMyNodeType() const override { return NodeType::EntityServer; } + virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; } + virtual PacketType getExpectedPacketType() const override { return PacketType::EntityData; } void update(); @@ -40,10 +40,10 @@ public: void processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); - virtual void init(); + virtual void init() override; protected: - virtual OctreePointer createTree() { + virtual OctreePointer createTree() override { EntityTreePointer newTree = EntityTreePointer(new EntityTree(true)); newTree->createRootElement(); return newTree; diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index b905a5a997..029a257bf1 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -30,57 +30,57 @@ class ReadBitstreamToTreeParams; class KeyLightPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); - virtual void debugDump() const; - virtual void listChangedProperties(QList& out); + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; - virtual bool appendToEditPacket(OctreePacketData* packetData, + virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes); - virtual void markAllChanged(); - virtual EntityPropertyFlags getChangedProperties() const; + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; // EntityItem related helpers // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties); + virtual void getProperties(EntityItemProperties& propertiesOut) const override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); - + bool& somethingChanged) override; + static const xColor DEFAULT_KEYLIGHT_COLOR; static const float DEFAULT_KEYLIGHT_INTENSITY; static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION; - + DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, xColor, DEFAULT_KEYLIGHT_COLOR); DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY); DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, AmbientIntensity, ambientIntensity, float, DEFAULT_KEYLIGHT_AMBIENT_INTENSITY); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_AMBIENT_URL, AmbientURL, ambientURL, QString, ""); - -protected: - }; #endif // hifi_KeyLightPropertyGroup_h diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h index 4c84d3204c..31ef012f6a 100644 --- a/libraries/entities/src/LightEntityItem.h +++ b/libraries/entities/src/LightEntityItem.h @@ -25,30 +25,30 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); LightEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - virtual void setDimensions(const glm::vec3& value); - + virtual void setDimensions(const glm::vec3& value) override; + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; const rgbColor& getColor() const { return _color; } xColor getXColor() const { diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h index 6a5ef20bac..575d987605 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h @@ -12,35 +12,35 @@ #ifndef hifi_LineEntityItem_h #define hifi_LineEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class LineEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); LineEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated - + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } @@ -51,25 +51,26 @@ class LineEntityItem : public EntityItem { _color[GREEN_INDEX] = value.green; _color[BLUE_INDEX] = value.blue; } - + void setLineWidth(float lineWidth){ _lineWidth = lineWidth; } float getLineWidth() const{ return _lineWidth; } - + bool setLinePoints(const QVector& points); bool appendPoint(const glm::vec3& point); - + const QVector& getLinePoints() const{ return _points; } - - virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } + + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; } // never have a ray intersection pick a LineEntityItem. - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const { return false; } + bool& keepSearching, OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + void** intersectedObject, + bool precisionPicking) const override { return false; } - virtual void debugDump() const; + virtual void debugDump() const override; static const float DEFAULT_LINE_WIDTH; static const int MAX_POINTS_PER_LINE; diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 7b7edaf945..6f2a0e1b31 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -26,11 +26,11 @@ public: ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, @@ -38,20 +38,20 @@ public: EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; - virtual void update(const quint64& now); - virtual bool needsToCallUpdate() const; - virtual void debugDump() const; + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const override; + virtual void debugDump() const override; - void setShapeType(ShapeType type); - virtual ShapeType getShapeType() const; + void setShapeType(ShapeType type) override; + virtual ShapeType getShapeType() const override; // TODO: Move these to subclasses, or other appropriate abstraction @@ -115,7 +115,7 @@ public: const QString getTextures() const; void setTextures(const QString& textures); - virtual bool shouldBePhysical() const; + virtual bool shouldBePhysical() const override; virtual glm::vec3 getJointPosition(int jointIndex) const { return glm::vec3(); } virtual glm::quat getJointRotation(int jointIndex) const { return glm::quat(); } diff --git a/libraries/entities/src/MovingEntitiesOperator.h b/libraries/entities/src/MovingEntitiesOperator.h index 5d16c41543..27ecb340a8 100644 --- a/libraries/entities/src/MovingEntitiesOperator.h +++ b/libraries/entities/src/MovingEntitiesOperator.h @@ -38,9 +38,9 @@ public: ~MovingEntitiesOperator(); void addEntityToMoveList(EntityItemPointer entity, const AACube& newCube); - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; + virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override; bool hasMovingEntities() const { return _entitiesToMove.size() > 0; } private: EntityTreePointer _tree; diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 9ddda62c8b..36bc5e6247 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -26,10 +26,10 @@ public: ParticleEffectEntityItem(const EntityItemID& entityItemID); // methods for getting/setting all properties of this entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, @@ -37,15 +37,15 @@ public: EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; - virtual void update(const quint64& now); - virtual bool needsToCallUpdate() const; + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const override; const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } @@ -95,10 +95,10 @@ public: void setAlphaSpread(float alphaSpread); float getAlphaSpread() const { return _alphaSpread; } - void setShapeType(ShapeType type); - virtual ShapeType getShapeType() const { return _shapeType; } + void setShapeType(ShapeType type) override; + virtual ShapeType getShapeType() const override { return _shapeType; } - virtual void debugDump() const; + virtual void debugDump() const override; bool isEmittingParticles() const; /// emitting enabled, and there are particles alive bool getIsEmitting() const { return _isEmitting; } @@ -219,7 +219,7 @@ public: _emitterShouldTrail = emitterShouldTrail; } - virtual bool supportsDetailedRayIntersection() const { return false; } + virtual bool supportsDetailedRayIntersection() const override { return false; } protected: struct Particle; diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 3231e7c5e1..95522fe57f 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -12,35 +12,35 @@ #ifndef hifi_PolyLineEntityItem_h #define hifi_PolyLineEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class PolyLineEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); PolyLineEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated - + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } @@ -49,22 +49,21 @@ class PolyLineEntityItem : public EntityItem { memcpy(_color, value, sizeof(_color)); } void setColor(const xColor& value) { - _color[RED_INDEX] = value.red; _color[GREEN_INDEX] = value.green; _color[BLUE_INDEX] = value.blue; } - + void setLineWidth(float lineWidth){ _lineWidth = lineWidth; } float getLineWidth() const{ return _lineWidth; } - + bool setLinePoints(const QVector& points); bool appendPoint(const glm::vec3& point); const QVector& getLinePoints() const{ return _points; } - + bool setNormals(const QVector& normals); const QVector& getNormals() const{ return _normals; } - + bool setStrokeWidths(const QVector& strokeWidths); const QVector& getStrokeWidths() const{ return _strokeWidths; } @@ -76,18 +75,18 @@ class PolyLineEntityItem : public EntityItem { } } - virtual bool needsToCallUpdate() const { return true; } + virtual bool needsToCallUpdate() const override { return true; } - virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; } // never have a ray intersection pick a PolyLineEntityItem. - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const { return false;} + bool& keepSearching, OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + void** intersectedObject, bool precisionPicking) const override { return false; } - virtual void debugDump() const; + virtual void debugDump() const override; static const float DEFAULT_LINE_WIDTH; static const int MAX_POINTS_PER_LINE; diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 7441b34c9c..b2d64b60b1 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -23,11 +23,11 @@ class PolyVoxEntityItem : public EntityItem { ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, @@ -35,21 +35,21 @@ class PolyVoxEntityItem : public EntityItem { EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const; + OctreeElement::AppendState& appendState) const override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; // never have a ray intersection pick a PolyVoxEntityItem. - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const { return false; } + bool& keepSearching, OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + void** intersectedObject, bool precisionPicking) const override { return false; } - virtual void debugDump() const; + virtual void debugDump() const override; virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); virtual glm::vec3 getVoxelVolumeSize() const; diff --git a/libraries/entities/src/PropertyGroup.h b/libraries/entities/src/PropertyGroup.h index 138ee020c1..d2d48c4cd0 100644 --- a/libraries/entities/src/PropertyGroup.h +++ b/libraries/entities/src/PropertyGroup.h @@ -63,7 +63,7 @@ public: EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, + int& propertyCount, OctreeElement::AppendState& appendState) const = 0; virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) = 0; diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h index c64cf91b61..dbf8dbd15b 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.h +++ b/libraries/entities/src/RecurseOctreeToMapOperator.h @@ -15,8 +15,8 @@ class RecurseOctreeToMapOperator : public RecurseOctreeOperator { public: RecurseOctreeToMapOperator(QVariantMap& map, OctreeElementPointer top, QScriptEngine* engine, bool skipDefaultValues, bool skipThoseWithBadParents); - bool preRecursion(OctreeElementPointer element); - bool postRecursion(OctreeElementPointer element); + bool preRecursion(OctreeElementPointer element) override; + bool postRecursion(OctreeElementPointer element) override; private: QVariantMap& _map; OctreeElementPointer _top; diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h index 745f762821..8b485353b8 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ b/libraries/entities/src/SkyboxPropertyGroup.h @@ -30,44 +30,47 @@ class ReadBitstreamToTreeParams; class SkyboxPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); - virtual void debugDump() const; - virtual void listChangedProperties(QList& out); + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes); - virtual void markAllChanged(); - virtual EntityPropertyFlags getChangedProperties() const; + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; // EntityItem related helpers // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties); + virtual void getProperties(EntityItemProperties& propertiesOut) const override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); - + bool& somethingChanged) override; + glm::vec3 getColorVec3() const { const quint8 MAX_COLOR = 255; glm::vec3 color = { (float)_color.red / (float)MAX_COLOR, diff --git a/libraries/entities/src/StagePropertyGroup.h b/libraries/entities/src/StagePropertyGroup.h index c25a1e629e..80f02f851a 100644 --- a/libraries/entities/src/StagePropertyGroup.h +++ b/libraries/entities/src/StagePropertyGroup.h @@ -30,54 +30,57 @@ class ReadBitstreamToTreeParams; class StagePropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers - virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; - virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); - virtual void debugDump() const; - virtual void listChangedProperties(QList& out); + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes); - virtual void markAllChanged(); - virtual EntityPropertyFlags getChangedProperties() const; + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; // EntityItem related helpers // methods for getting/setting all properties of an entity - virtual void getProperties(EntityItemProperties& propertiesOut) const; - - /// returns true if something changed - virtual bool setProperties(const EntityItemProperties& properties); + virtual void getProperties(EntityItemProperties& propertiesOut) const override; - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); - + bool& somethingChanged) override; + static const bool DEFAULT_STAGE_SUN_MODEL_ENABLED; static const float DEFAULT_STAGE_LATITUDE; static const float DEFAULT_STAGE_LONGITUDE; static const float DEFAULT_STAGE_ALTITUDE; static const quint16 DEFAULT_STAGE_DAY; static const float DEFAULT_STAGE_HOUR; - + float calculateHour() const; uint16_t calculateDay() const; - + DEFINE_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, SunModelEnabled, sunModelEnabled, bool, DEFAULT_STAGE_SUN_MODEL_ENABLED); DEFINE_PROPERTY(PROP_STAGE_LATITUDE, Latitude, latitude, float, DEFAULT_STAGE_LATITUDE); DEFINE_PROPERTY(PROP_STAGE_LONGITUDE, Longitude, longitude, float, DEFAULT_STAGE_LONGITUDE); diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 1caceee085..65d3d8bb21 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -12,45 +12,45 @@ #ifndef hifi_TextEntityItem_h #define hifi_TextEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class TextEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); TextEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - virtual void setDimensions(const glm::vec3& value); - virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } - + virtual void setDimensions(const glm::vec3& value) override; + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; } + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, + bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const; + void** intersectedObject, bool precisionPicking) const override; static const QString DEFAULT_TEXT; void setText(const QString& value) { _text = value; } @@ -81,7 +81,7 @@ public: _backgroundColor[GREEN_INDEX] = value.green; _backgroundColor[BLUE_INDEX] = value.blue; } - + static const bool DEFAULT_FACE_CAMERA; bool getFaceCamera() const { return _faceCamera; } void setFaceCamera(bool value) { _faceCamera = value; } diff --git a/libraries/entities/src/UpdateEntityOperator.h b/libraries/entities/src/UpdateEntityOperator.h index 8b5bbdf135..b33d6c6c3b 100644 --- a/libraries/entities/src/UpdateEntityOperator.h +++ b/libraries/entities/src/UpdateEntityOperator.h @@ -25,9 +25,9 @@ public: ~UpdateEntityOperator(); - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); - virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; + virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override; private: EntityTreePointer _tree; EntityItemPointer _existingEntity; diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 8e9d924cde..7c4e9f801a 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -9,7 +9,7 @@ #ifndef hifi_WebEntityItem_h #define hifi_WebEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class WebEntityItem : public EntityItem { public: @@ -18,38 +18,38 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); WebEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - virtual void setDimensions(const glm::vec3& value); - virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } - + virtual void setDimensions(const glm::vec3& value) override; + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; } + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const; + void** intersectedObject, bool precisionPicking) const override; virtual void setSourceUrl(const QString& value); const QString& getSourceUrl() const; diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index f0f2a91d63..cad8a3baac 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -23,28 +23,28 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); ZoneEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated - + // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; + int& propertyCount, + OctreeElement::AppendState& appendState) const override; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); + bool& somethingChanged) override; @@ -53,11 +53,11 @@ public: static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; } static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; } - - virtual bool isReadyToComputeShape() { return false; } - void setShapeType(ShapeType type) { _shapeType = type; } - virtual ShapeType getShapeType() const; - + + virtual bool isReadyToComputeShape() override { return false; } + void setShapeType(ShapeType type) override { _shapeType = type; } + virtual ShapeType getShapeType() const override; + virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); } const QString getCompoundShapeURL() const { return _compoundShapeURL; } virtual void setCompoundShapeURL(const QString& url); @@ -75,13 +75,13 @@ public: bool getGhostingAllowed() const { return _ghostingAllowed; } void setGhostingAllowed(bool value) { _ghostingAllowed = value; } - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const; + void** intersectedObject, bool precisionPicking) const override; - virtual void debugDump() const; + virtual void debugDump() const override; static const ShapeType DEFAULT_SHAPE_TYPE; static const QString DEFAULT_COMPOUND_SHAPE_URL; diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index eb25f1d8a2..8c0f4b34ac 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -9,7 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "FBXReader.h" + #include +#include + #include #include #include @@ -20,9 +24,8 @@ #include #include #include -#include "FBXReader.h" -#include +#include "ModelFormatLogging.h" bool FBXMaterial::needTangentSpace() const { return !normalTexture.isNull(); @@ -258,11 +261,11 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { } } } - qDebug() << " fbx material Name:" << material.name; + qCDebug(modelformat) << " fbx material Name:" << material.name; if (materialMap.contains(material.name)) { QJsonObject materialOptions = materialMap.value(material.name).toObject(); - qDebug() << "Mapping fbx material:" << material.name << " with HifiMaterial: " << materialOptions; + qCDebug(modelformat) << "Mapping fbx material:" << material.name << " with HifiMaterial: " << materialOptions; if (materialOptions.contains("scattering")) { float scattering = (float) materialOptions.value("scattering").toDouble(); diff --git a/libraries/gl/src/gl/GLEscrow.h b/libraries/gl/src/gl/GLEscrow.h index 357398c79b..9482f88683 100644 --- a/libraries/gl/src/gl/GLEscrow.h +++ b/libraries/gl/src/gl/GLEscrow.h @@ -20,6 +20,8 @@ #include #include +#include "Config.h" + // The GLEscrow class provides a simple mechanism for producer GL contexts to provide // content to a consumer where the consumer is assumed to be connected to a display and // therefore must never be blocked. diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 2781d5b9b0..633c4ef0eb 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -2,18 +2,16 @@ #include -#include -#include -#include +#include +#include #include #include -#ifdef DEBUG -static bool enableDebug = true; -#else -static const QString DEBUG_FLAG("HIFI_ENABLE_OPENGL_45"); -static bool enableDebug = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); -#endif +#include +#include +#include + +#include const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { static QSurfaceFormat format; @@ -23,7 +21,8 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); setGLFormatVersion(format); - if (enableDebug) { + if (GLDebug::enabled()) { + qDebug() << "Enabling debug context"; format.setOption(QSurfaceFormat::DebugContext); } format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); @@ -72,3 +71,36 @@ QJsonObject getGLContextData() { { "renderer", glRenderer }, }; } + +QThread* RENDER_THREAD = nullptr; + +bool isRenderThread() { + return QThread::currentThread() == RENDER_THREAD; +} + + +#ifdef DEBUG +static bool enableDebugLogger = true; +#else +static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL"); +static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); +#endif + +bool GLDebug::enabled() { + return enableDebugLogger; +} + +void GLDebug::log(const QOpenGLDebugMessage & debugMessage) { + qDebug() << debugMessage; +} + +void GLDebug::setupLogger(QObject* window) { + if (enabled()) { + QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(window); + logger->initialize(); // initializes in the current context, i.e. ctx + logger->enableMessages(); + QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, window, [&](const QOpenGLDebugMessage & debugMessage) { + GLDebug::log(debugMessage); + }); + } +} \ No newline at end of file diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index 477bf7abc8..9e185f5845 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -18,6 +18,8 @@ // but GL implementations usually just come with buffer sizes in multiples of 8) #define DEFAULT_GL_STENCIL_BUFFER_BITS 8 +class QObject; +class QOpenGLDebugMessage; class QSurfaceFormat; class QGLFormat; @@ -29,4 +31,15 @@ const QGLFormat& getDefaultGLFormat(); QJsonObject getGLContextData(); int glVersionToInteger(QString glVersion); +bool isRenderThread(); + + +class GLDebug { +public: + static bool enabled(); + static void log(const QOpenGLDebugMessage& debugMessage); + static void setupLogger(QObject* window = nullptr); +}; + + #endif diff --git a/libraries/gl/src/gl/GLWindow.cpp b/libraries/gl/src/gl/GLWindow.cpp index 78c7666d25..e553290a04 100644 --- a/libraries/gl/src/gl/GLWindow.cpp +++ b/libraries/gl/src/gl/GLWindow.cpp @@ -8,8 +8,8 @@ #include "GLWindow.h" +#include #include -#include #include "GLHelpers.h" diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index a6b5a03ff6..e7f821a49f 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -13,30 +13,16 @@ #include "OffscreenGLCanvas.h" #include +#include #include -#include #include #include "GLHelpers.h" -#ifdef DEBUG -static bool enableDebugLogger = true; -#else -static const QString DEBUG_FLAG("HIFI_ENABLE_OPENGL_45"); -static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); -#endif - - OffscreenGLCanvas::OffscreenGLCanvas() : _context(new QOpenGLContext), _offscreenSurface(new QOffscreenSurface){ } OffscreenGLCanvas::~OffscreenGLCanvas() { - if (_logger) { - makeCurrent(); - delete _logger; - _logger = nullptr; - } - _context->doneCurrent(); delete _context; _context = nullptr; @@ -68,25 +54,14 @@ bool OffscreenGLCanvas::makeCurrent() { bool result = _context->makeCurrent(_offscreenSurface); Q_ASSERT(result); - std::call_once(_reportOnce, []{ + std::call_once(_reportOnce, [this]{ qDebug() << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); qDebug() << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); qDebug() << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR)); qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); + GLDebug::setupLogger(this); }); - - if (result && !_logger) { - _logger = new QOpenGLDebugLogger(this); - if (_logger->initialize()) { - connect(_logger, &QOpenGLDebugLogger::messageLogged, [](const QOpenGLDebugMessage& message) { - qDebug() << message; - }); - _logger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::NotificationSeverity); - _logger->startLogging(QOpenGLDebugLogger::LoggingMode::SynchronousLogging); - } - } - return result; } @@ -101,4 +76,4 @@ QObject* OffscreenGLCanvas::getContextObject() { void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) { moveToThread(thread); _context->moveToThread(thread); -} \ No newline at end of file +} diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index 8def09796d..583aa7c60f 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -36,7 +36,6 @@ protected: std::once_flag _reportOnce; QOpenGLContext* _context{ nullptr }; QOffscreenSurface* _offscreenSurface{ nullptr }; - QOpenGLDebugLogger* _logger{ nullptr }; }; #endif // hifi_OffscreenGLCanvas_h diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 8c167fafdc..0470766eb9 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "OffscreenGLCanvas.h" #include "GLEscrow.h" @@ -55,6 +56,22 @@ private: friend class OffscreenQmlSurface; }; +class QmlNetworkAccessManager : public NetworkAccessManager { +public: + friend class QmlNetworkAccessManagerFactory; +protected: + QmlNetworkAccessManager(QObject* parent) : NetworkAccessManager(parent) { } +}; + +class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory { +public: + QNetworkAccessManager* create(QObject* parent) override; +}; + +QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { + return new QmlNetworkAccessManager(parent); +} + Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") @@ -402,6 +419,8 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { // Create a QML engine. _qmlEngine = new QQmlEngine; + _qmlEngine->setNetworkAccessManagerFactory(new QmlNetworkAccessManagerFactory); + auto importList = _qmlEngine->importPathList(); importList.insert(importList.begin(), PathUtils::resourcesPath()); _qmlEngine->setImportPathList(importList); diff --git a/libraries/gl/src/gl/OglplusHelpers.h b/libraries/gl/src/gl/OglplusHelpers.h index c453fbad28..fe5822c4be 100644 --- a/libraries/gl/src/gl/OglplusHelpers.h +++ b/libraries/gl/src/gl/OglplusHelpers.h @@ -33,6 +33,13 @@ #pragma clang diagnostic ignored "-Wpessimizing-move" #endif +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif +#endif + #include #include @@ -42,6 +49,10 @@ #include #include +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + #ifdef _WIN32 #pragma warning(pop) #elif defined(Q_OS_MAC) diff --git a/libraries/gl/src/gl/QOpenGLDebugLoggerWrapper.cpp b/libraries/gl/src/gl/QOpenGLDebugLoggerWrapper.cpp deleted file mode 100644 index 2a351ead7e..0000000000 --- a/libraries/gl/src/gl/QOpenGLDebugLoggerWrapper.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// -// QOpenGLDebugLoggerWrapper.cpp -// -// -// Created by Clement on 12/4/15. -// 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 "QOpenGLDebugLoggerWrapper.h" - -#include -#include - -void OpenGLDebug::log(const QOpenGLDebugMessage & debugMessage) { - qDebug() << debugMessage; -} - -void setupDebugLogger(QObject* window) { - QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(window); - logger->initialize(); // initializes in the current context, i.e. ctx - logger->enableMessages(); - QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, window, [&](const QOpenGLDebugMessage & debugMessage) { - OpenGLDebug::log(debugMessage); - - }); -} \ No newline at end of file diff --git a/libraries/gl/src/gl/QOpenGLDebugLoggerWrapper.h b/libraries/gl/src/gl/QOpenGLDebugLoggerWrapper.h deleted file mode 100644 index 2a378a712a..0000000000 --- a/libraries/gl/src/gl/QOpenGLDebugLoggerWrapper.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// QOpenGLDebugLoggerWrapper.h -// -// -// Created by Clement on 12/4/15. -// 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 -// - -#ifndef hifi_QOpenGLDebugLoggerWrapper_h -#define hifi_QOpenGLDebugLoggerWrapper_h - -class QObject; -class QOpenGLDebugMessage; - -void setupDebugLogger(QObject* window); - -class OpenGLDebug { -public: - static void log(const QOpenGLDebugMessage & debugMessage); -}; - -#endif // hifi_QOpenGLDebugLoggerWrapper_h \ No newline at end of file diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index ce2f4c8d66..a37ac37adb 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -32,30 +32,44 @@ using namespace gpu; using namespace gpu::gl; -static const QString DEBUG_FLAG("HIFI_ENABLE_OPENGL_45"); -static bool enableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); +static const QString DEBUG_FLAG("HIFI_DISABLE_OPENGL_45"); +static bool disableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); -Backend* GLBackend::createBackend() { +static GLBackend* INSTANCE{ nullptr }; +static const char* GL_BACKEND_PROPERTY_NAME = "com.highfidelity.gl.backend"; + +BackendPointer GLBackend::createBackend() { // FIXME provide a mechanism to override the backend for testing // Where the gpuContext is initialized and where the TRUE Backend is created and assigned auto version = QOpenGLContextWrapper::currentContextVersion(); - GLBackend* result; - if (enableOpenGL45 && version >= 0x0405) { + std::shared_ptr result; + if (!disableOpenGL45 && version >= 0x0405) { qDebug() << "Using OpenGL 4.5 backend"; - result = new gpu::gl45::GL45Backend(); + result = std::make_shared(); } else { qDebug() << "Using OpenGL 4.1 backend"; - result = new gpu::gl41::GL41Backend(); + result = std::make_shared(); } result->initInput(); result->initTransform(); + + INSTANCE = result.get(); + void* voidInstance = &(*result); + qApp->setProperty(GL_BACKEND_PROPERTY_NAME, QVariant::fromValue(voidInstance)); + gl::GLTexture::initTextureTransferHelper(); return result; } +GLBackend& getBackend() { + if (!INSTANCE) { + INSTANCE = static_cast(qApp->property(GL_BACKEND_PROPERTY_NAME).value()); + } + return *INSTANCE; +} bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { - return GLShader::makeProgram(shader, slotBindings); + return GLShader::makeProgram(getBackend(), shader, slotBindings); } GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = @@ -120,24 +134,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_popProfileRange), }; -extern std::function TEXTURE_ID_RESOLVER; - void GLBackend::init() { static std::once_flag once; std::call_once(once, [] { - - TEXTURE_ID_RESOLVER = [](const Texture& texture)->uint32 { - auto object = Backend::getGPUObject(texture); - if (!object) { - return 0; - } - - if (object->getSyncState() != GLSyncState::Idle) { - return object->_downsampleSource._texture; - } - return object->_texture; - }; - QString vendor{ (const char*)glGetString(GL_VENDOR) }; QString renderer{ (const char*)glGetString(GL_RENDERER) }; qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); @@ -178,6 +177,7 @@ void GLBackend::init() { } GLBackend::GLBackend() { + _pipeline._cameraCorrectionBuffer._buffer->flush(); glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment); } @@ -189,7 +189,7 @@ GLBackend::~GLBackend() { killTransform(); } -void GLBackend::renderPassTransfer(Batch& batch) { +void GLBackend::renderPassTransfer(const Batch& batch) { const size_t numCommands = batch.getCommands().size(); const Batch::Commands::value_type* command = batch.getCommands().data(); const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); @@ -245,7 +245,7 @@ void GLBackend::renderPassTransfer(Batch& batch) { _inRenderTransferPass = false; } -void GLBackend::renderPassDraw(Batch& batch) { +void GLBackend::renderPassDraw(const Batch& batch) { _currentDraw = -1; _transform._camerasItr = _transform._cameraOffsets.begin(); const size_t numCommands = batch.getCommands().size(); @@ -290,11 +290,8 @@ void GLBackend::renderPassDraw(Batch& batch) { } } -void GLBackend::render(Batch& batch) { - // Finalize the batch by moving all the instanced rendering into the command buffer - batch.preExecute(); - - _stereo._skybox = batch.isSkyboxEnabled(); +void GLBackend::render(const Batch& batch) { + _transform._skybox = _stereo._skybox = batch.isSkyboxEnabled(); // Allow the batch to override the rendering stereo settings // for things like full framebuffer copy operations (deferred lighting passes) bool savedStereo = _stereo._enable; @@ -318,6 +315,7 @@ void GLBackend::render(Batch& batch) { void GLBackend::syncCache() { + recycle(); syncTransformStateCache(); syncPipelineStateCache(); syncInputStateCache(); @@ -334,21 +332,21 @@ void GLBackend::setupStereoSide(int side) { _transform.bindCurrentCamera(side); } -void GLBackend::do_resetStages(Batch& batch, size_t paramOffset) { +void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { resetStages(); } -void GLBackend::do_runLambda(Batch& batch, size_t paramOffset) { +void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) { std::function f = batch._lambdas.get(batch._params[paramOffset]._uint); f(); } -void GLBackend::do_startNamedCall(Batch& batch, size_t paramOffset) { +void GLBackend::do_startNamedCall(const Batch& batch, size_t paramOffset) { batch._currentNamedCall = batch._names.get(batch._params[paramOffset]._uint); _currentDraw = -1; } -void GLBackend::do_stopNamedCall(Batch& batch, size_t paramOffset) { +void GLBackend::do_stopNamedCall(const Batch& batch, size_t paramOffset) { batch._currentNamedCall.clear(); } @@ -365,14 +363,16 @@ void GLBackend::resetStages() { } -void GLBackend::do_pushProfileRange(Batch& batch, size_t paramOffset) { -#if defined(NSIGHT_FOUND) +void GLBackend::do_pushProfileRange(const Batch& batch, size_t paramOffset) { auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); + profileRanges.push_back(name); +#if defined(NSIGHT_FOUND) nvtxRangePush(name.c_str()); #endif } -void GLBackend::do_popProfileRange(Batch& batch, size_t paramOffset) { +void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { + profileRanges.pop_back(); #if defined(NSIGHT_FOUND) nvtxRangePop(); #endif @@ -385,7 +385,7 @@ void GLBackend::do_popProfileRange(Batch& batch, size_t paramOffset) { // As long as we don;t use several versions of shaders we can avoid this more complex code path // #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, isStereo()); #define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc -void GLBackend::do_glActiveBindTexture(Batch& batch, size_t paramOffset) { +void GLBackend::do_glActiveBindTexture(const Batch& batch, size_t paramOffset) { glActiveTexture(batch._params[paramOffset + 2]._uint); glBindTexture( GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._uint), @@ -394,7 +394,7 @@ void GLBackend::do_glActiveBindTexture(Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniform1i(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -408,7 +408,7 @@ void GLBackend::do_glUniform1i(Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniform1f(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniform1f(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -422,7 +422,7 @@ void GLBackend::do_glUniform1f(Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniform2f(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniform2f(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -436,7 +436,7 @@ void GLBackend::do_glUniform2f(Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniform3f(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniform3f(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -451,7 +451,7 @@ void GLBackend::do_glUniform3f(Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniform4f(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniform4f(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -467,7 +467,7 @@ void GLBackend::do_glUniform4f(Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniform3fv(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniform3fv(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -477,12 +477,12 @@ void GLBackend::do_glUniform3fv(Batch& batch, size_t paramOffset) { glUniform3fv( GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), batch._params[paramOffset + 1]._uint, - (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint)); + (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniform4fv(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniform4fv(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -492,13 +492,13 @@ void GLBackend::do_glUniform4fv(Batch& batch, size_t paramOffset) { GLint location = GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int); GLsizei count = batch._params[paramOffset + 1]._uint; - const GLfloat* value = (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint); + const GLfloat* value = (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint); glUniform4fv(location, count, value); (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniform4iv(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniform4iv(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -508,12 +508,12 @@ void GLBackend::do_glUniform4iv(Batch& batch, size_t paramOffset) { glUniform4iv( GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), batch._params[paramOffset + 1]._uint, - (const GLint*)batch.editData(batch._params[paramOffset + 0]._uint)); + (const GLint*)batch.readData(batch._params[paramOffset + 0]._uint)); (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniformMatrix3fv(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -525,11 +525,11 @@ void GLBackend::do_glUniformMatrix3fv(Batch& batch, size_t paramOffset) { GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), batch._params[paramOffset + 2]._uint, batch._params[paramOffset + 1]._uint, - (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint)); + (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); (void)CHECK_GL_ERROR(); } -void GLBackend::do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) { +void GLBackend::do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that // because these uniform setters are deprecated and we don;t want to create side effect @@ -541,11 +541,11 @@ void GLBackend::do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) { GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), batch._params[paramOffset + 2]._uint, batch._params[paramOffset + 1]._uint, - (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint)); + (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); (void)CHECK_GL_ERROR(); } -void GLBackend::do_glColor4f(Batch& batch, size_t paramOffset) { +void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { glm::vec4 newColor( batch._params[paramOffset + 3]._float, @@ -559,3 +559,132 @@ void GLBackend::do_glColor4f(Batch& batch, size_t paramOffset) { } (void)CHECK_GL_ERROR(); } + +void GLBackend::releaseBuffer(GLuint id, Size size) const { + Lock lock(_trashMutex); + _buffersTrash.push_back({ id, size }); +} + +void GLBackend::releaseTexture(GLuint id, Size size) const { + Lock lock(_trashMutex); + _texturesTrash.push_back({ id, size }); +} + +void GLBackend::releaseFramebuffer(GLuint id) const { + Lock lock(_trashMutex); + _framebuffersTrash.push_back(id); +} + +void GLBackend::releaseShader(GLuint id) const { + Lock lock(_trashMutex); + _shadersTrash.push_back(id); +} + +void GLBackend::releaseProgram(GLuint id) const { + Lock lock(_trashMutex); + _shadersTrash.push_back(id); +} + +void GLBackend::releaseQuery(GLuint id) const { + Lock lock(_trashMutex); + _queriesTrash.push_back(id); +} + +void GLBackend::recycle() const { + { + std::vector ids; + std::list> buffersTrash; + { + Lock lock(_trashMutex); + std::swap(_buffersTrash, buffersTrash); + } + ids.reserve(buffersTrash.size()); + for (auto pair : buffersTrash) { + ids.push_back(pair.first); + decrementBufferGPUCount(); + updateBufferGPUMemoryUsage(pair.second, 0); + } + if (!ids.empty()) { + glDeleteBuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + std::list framebuffersTrash; + { + Lock lock(_trashMutex); + std::swap(_framebuffersTrash, framebuffersTrash); + } + ids.reserve(framebuffersTrash.size()); + for (auto id : framebuffersTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteFramebuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + std::list> texturesTrash; + { + Lock lock(_trashMutex); + std::swap(_texturesTrash, texturesTrash); + } + ids.reserve(texturesTrash.size()); + for (auto pair : texturesTrash) { + ids.push_back(pair.first); + decrementTextureGPUCount(); + updateTextureGPUMemoryUsage(pair.second, 0); + } + if (!ids.empty()) { + glDeleteTextures((GLsizei)ids.size(), ids.data()); + } + } + + { + std::list programsTrash; + { + Lock lock(_trashMutex); + std::swap(_programsTrash, programsTrash); + } + for (auto id : programsTrash) { + glDeleteProgram(id); + } + } + + { + std::list shadersTrash; + { + Lock lock(_trashMutex); + std::swap(_shadersTrash, shadersTrash); + } + for (auto id : shadersTrash) { + glDeleteShader(id); + } + } + + { + std::vector ids; + std::list queriesTrash; + { + Lock lock(_trashMutex); + std::swap(_queriesTrash, queriesTrash); + } + ids.reserve(queriesTrash.size()); + for (auto id : queriesTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteQueries((GLsizei)ids.size(), ids.data()); + } + } +} + +void GLBackend::setCameraCorrection(const Mat4& correction) { + _transform._correction.correction = correction; + _transform._correction.correctionInverse = glm::inverse(correction); + _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); + _pipeline._cameraCorrectionBuffer._buffer->flush(); +} diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index d27ec3808b..bc348e3c3f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -30,30 +31,33 @@ namespace gpu { namespace gl { -class GLBackend : public Backend { +class GLBackend : public Backend, public std::enable_shared_from_this { // Context Backend static interface required friend class gpu::Context; static void init(); - static Backend* createBackend(); - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings); + static BackendPointer createBackend(); protected: explicit GLBackend(bool syncCache); GLBackend(); public: + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); + ~GLBackend(); - void render(Batch& batch) final; + void setCameraCorrection(const Mat4& correction); + void render(const Batch& batch) final override; // This call synchronize the Full Backend cache with the current GLState // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync // the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls // Let's try to avoid to do that as much as possible! - void syncCache() final; + void syncCache() final override; // This is the ugly "download the pixels to sysmem for taking a snapshot" // Just avoid using it, it's ugly and will break performances - virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) final; + virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, + const Vec4i& region, QImage& destImage) final override; static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; @@ -72,74 +76,74 @@ public: size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } // Draw Stage - virtual void do_draw(Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexed(Batch& batch, size_t paramOffset) = 0; - virtual void do_drawInstanced(Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexedInstanced(Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndirect(Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) = 0; + virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; + virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0; + virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0; // Input Stage - virtual void do_setInputFormat(Batch& batch, size_t paramOffset) final; - virtual void do_setInputBuffer(Batch& batch, size_t paramOffset) final; - virtual void do_setIndexBuffer(Batch& batch, size_t paramOffset) final; - virtual void do_setIndirectBuffer(Batch& batch, size_t paramOffset) final; - virtual void do_generateTextureMips(Batch& batch, size_t paramOffset) final; + virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final; + virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; // Transform Stage - virtual void do_setModelTransform(Batch& batch, size_t paramOffset) final; - virtual void do_setViewTransform(Batch& batch, size_t paramOffset) final; - virtual void do_setProjectionTransform(Batch& batch, size_t paramOffset) final; - virtual void do_setViewportTransform(Batch& batch, size_t paramOffset) final; - virtual void do_setDepthRangeTransform(Batch& batch, size_t paramOffset) final; + virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final; // Uniform Stage - virtual void do_setUniformBuffer(Batch& batch, size_t paramOffset) final; + virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final; // Resource Stage - virtual void do_setResourceTexture(Batch& batch, size_t paramOffset) final; + virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; // Pipeline Stage - virtual void do_setPipeline(Batch& batch, size_t paramOffset) final; + virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final; // Output stage - virtual void do_setFramebuffer(Batch& batch, size_t paramOffset) final; - virtual void do_clearFramebuffer(Batch& batch, size_t paramOffset) final; - virtual void do_blit(Batch& batch, size_t paramOffset) = 0; + virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_blit(const Batch& batch, size_t paramOffset) = 0; // Query section - virtual void do_beginQuery(Batch& batch, size_t paramOffset) final; - virtual void do_endQuery(Batch& batch, size_t paramOffset) final; - virtual void do_getQuery(Batch& batch, size_t paramOffset) final; + virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final; + virtual void do_endQuery(const Batch& batch, size_t paramOffset) final; + virtual void do_getQuery(const Batch& batch, size_t paramOffset) final; // Reset stages - virtual void do_resetStages(Batch& batch, size_t paramOffset) final; + virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; - virtual void do_runLambda(Batch& batch, size_t paramOffset) final; + virtual void do_runLambda(const Batch& batch, size_t paramOffset) final; - virtual void do_startNamedCall(Batch& batch, size_t paramOffset) final; - virtual void do_stopNamedCall(Batch& batch, size_t paramOffset) final; + virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final; + virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final; - virtual void do_pushProfileRange(Batch& batch, size_t paramOffset) final; - virtual void do_popProfileRange(Batch& batch, size_t paramOffset) final; + virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final; + virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final; // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API - virtual void do_glActiveBindTexture(Batch& batch, size_t paramOffset) final; + virtual void do_glActiveBindTexture(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform1i(Batch& batch, size_t paramOffset) final; - virtual void do_glUniform1f(Batch& batch, size_t paramOffset) final; - virtual void do_glUniform2f(Batch& batch, size_t paramOffset) final; - virtual void do_glUniform3f(Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4f(Batch& batch, size_t paramOffset) final; - virtual void do_glUniform3fv(Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4fv(Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4iv(Batch& batch, size_t paramOffset) final; - virtual void do_glUniformMatrix3fv(Batch& batch, size_t paramOffset) final; - virtual void do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) final; + virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glColor4f(Batch& batch, size_t paramOffset) final; + virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final; // The State setters called by the GLState::Commands when a new state is assigned virtual void do_setStateFillMode(int32 mode) final; @@ -156,21 +160,28 @@ public: virtual void do_setStateSampleMask(uint32 mask) final; virtual void do_setStateBlend(State::BlendFunction blendFunction) final; virtual void do_setStateColorWriteMask(uint32 mask) final; - virtual void do_setStateBlendFactor(Batch& batch, size_t paramOffset) final; - virtual void do_setStateScissorRect(Batch& batch, size_t paramOffset) final; + virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final; + virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; + + virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; + virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0; + virtual GLuint getBufferID(const Buffer& buffer) = 0; + virtual GLuint getQueryID(const QueryPointer& query) = 0; + virtual bool isTextureReady(const TexturePointer& texture); + + virtual void releaseBuffer(GLuint id, Size size) const; + virtual void releaseTexture(GLuint id, Size size) const; + virtual void releaseFramebuffer(GLuint id) const; + virtual void releaseShader(GLuint id) const; + virtual void releaseProgram(GLuint id) const; + virtual void releaseQuery(GLuint id) const; protected: - virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; + void recycle() const override; virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; - - virtual GLuint getBufferID(const Buffer& buffer) = 0; virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; - - virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0; virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0; - - virtual GLuint getQueryID(const QueryPointer& query) = 0; virtual GLQuery* syncGPUObject(const Query& query) = 0; static const size_t INVALID_OFFSET = (size_t)-1; @@ -178,8 +189,17 @@ protected: int32_t _uboAlignment { 0 }; int _currentDraw { -1 }; - void renderPassTransfer(Batch& batch); - void renderPassDraw(Batch& batch); + std::list profileRanges; + mutable Mutex _trashMutex; + mutable std::list> _buffersTrash; + mutable std::list> _texturesTrash; + mutable std::list _framebuffersTrash; + mutable std::list _shadersTrash; + mutable std::list _programsTrash; + mutable std::list _queriesTrash; + + void renderPassTransfer(const Batch& batch); + void renderPassDraw(const Batch& batch); void setupStereoSide(int side); virtual void initInput() final; @@ -229,6 +249,14 @@ protected: void updateTransform(const Batch& batch); void resetTransformStage(); + // Allows for correction of the camera pose to account for changes + // between the time when a was recorded and the time(s) when it is + // executed + struct CameraCorrection { + Mat4 correction; + Mat4 correctionInverse; + }; + struct TransformStageState { using CameraBufferElement = TransformCamera; using TransformCameras = std::vector; @@ -243,7 +271,11 @@ protected: GLuint _drawCallInfoBuffer { 0 }; GLuint _objectBufferTexture { 0 }; size_t _cameraUboSize { 0 }; + bool _viewIsCamera{ false }; + bool _skybox { false }; Transform _view; + CameraCorrection _correction; + Mat4 _projection; Vec4i _viewport { 0, 0, 1, 1 }; Vec2 _depthRange { 0.0f, 1.0f }; @@ -297,14 +329,21 @@ protected: PipelinePointer _pipeline; GLuint _program { 0 }; + GLint _cameraCorrectionLocation { -1 }; GLShader* _programShader { nullptr }; bool _invalidProgram { false }; - State::Data _stateCache { State::DEFAULT }; + BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; + + State::Data _stateCache{ State::DEFAULT }; State::Signature _stateSignatureCache { 0 }; GLState* _state { nullptr }; bool _invalidState { false }; + + PipelineStageState() { + _cameraCorrectionBuffer.edit() = CameraCorrection(); + } } _pipeline; // Synchronize the state cache of this Backend with the actual real state of the GL Context @@ -323,7 +362,7 @@ protected: void resetStages(); - typedef void (GLBackend::*CommandCall)(Batch&, size_t); + typedef void (GLBackend::*CommandCall)(const Batch&, size_t); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; friend class GLState; }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp index 99c1ff0438..9256a42b80 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp @@ -14,7 +14,7 @@ using namespace gpu; using namespace gpu::gl; -void GLBackend::do_setInputFormat(Batch& batch, size_t paramOffset) { +void GLBackend::do_setInputFormat(const Batch& batch, size_t paramOffset) { Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint); if (format != _input._format) { @@ -23,7 +23,7 @@ void GLBackend::do_setInputFormat(Batch& batch, size_t paramOffset) { } } -void GLBackend::do_setInputBuffer(Batch& batch, size_t paramOffset) { +void GLBackend::do_setInputBuffer(const Batch& batch, size_t paramOffset) { Offset stride = batch._params[paramOffset + 0]._uint; Offset offset = batch._params[paramOffset + 1]._uint; BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); @@ -116,7 +116,7 @@ void GLBackend::resetInputStage() { } -void GLBackend::do_setIndexBuffer(Batch& batch, size_t paramOffset) { +void GLBackend::do_setIndexBuffer(const Batch& batch, size_t paramOffset) { _input._indexBufferType = (Type)batch._params[paramOffset + 2]._uint; _input._indexBufferOffset = batch._params[paramOffset + 0]._uint; @@ -133,7 +133,7 @@ void GLBackend::do_setIndexBuffer(Batch& batch, size_t paramOffset) { (void) CHECK_GL_ERROR(); } -void GLBackend::do_setIndirectBuffer(Batch& batch, size_t paramOffset) { +void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) { _input._indirectBufferOffset = batch._params[paramOffset + 1]._uint; _input._indirectBufferStride = batch._params[paramOffset + 2]._uint; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp index 1d46078b5b..2eadd4976a 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp @@ -35,7 +35,7 @@ void GLBackend::resetOutputStage() { glEnable(GL_FRAMEBUFFER_SRGB); } -void GLBackend::do_setFramebuffer(Batch& batch, size_t paramOffset) { +void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { auto framebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); if (_output._framebuffer != framebuffer) { auto newFBO = getFramebufferID(framebuffer); @@ -47,7 +47,7 @@ void GLBackend::do_setFramebuffer(Batch& batch, size_t paramOffset) { } } -void GLBackend::do_clearFramebuffer(Batch& batch, size_t paramOffset) { +void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { if (_stereo._enable && !_pipeline._stateCache.scissorEnable) { qWarning("Clear without scissor in stereo mode"); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 04f56ba0f5..906144e013 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -19,7 +19,7 @@ using namespace gpu; using namespace gpu::gl; -void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) { +void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { PipelinePointer pipeline = batch._pipelines.get(batch._params[paramOffset + 0]._uint); if (_pipeline._pipeline == pipeline) { @@ -34,13 +34,14 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) { _pipeline._pipeline.reset(); _pipeline._program = 0; + _pipeline._cameraCorrectionLocation = -1; _pipeline._programShader = nullptr; _pipeline._invalidProgram = true; _pipeline._state = nullptr; _pipeline._invalidState = true; } else { - auto pipelineObject = GLPipeline::sync(*pipeline); + auto pipelineObject = GLPipeline::sync(*this, *pipeline); if (!pipelineObject) { return; } @@ -53,6 +54,7 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) { _pipeline._program = glprogram; _pipeline._programShader = pipelineObject->_program; _pipeline._invalidProgram = true; + _pipeline._cameraCorrectionLocation = pipelineObject->_cameraCorrection; } // Now for the state @@ -68,6 +70,10 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) { // THis should be done on Pipeline::update... if (_pipeline._invalidProgram) { glUseProgram(_pipeline._program); + if (_pipeline._cameraCorrectionLocation != -1) { + auto cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); + glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection)); + } (void) CHECK_GL_ERROR(); _pipeline._invalidProgram = false; } @@ -135,7 +141,7 @@ void GLBackend::resetUniformStage() { } } -void GLBackend::do_setUniformBuffer(Batch& batch, size_t paramOffset) { +void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 3]._uint; BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); GLintptr rangeStart = batch._params[paramOffset + 1]._uint; @@ -184,7 +190,7 @@ void GLBackend::resetResourceStage() { } } -void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) { +void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp index 463cff9a6c..60b204ba60 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp @@ -21,7 +21,7 @@ static bool timeElapsed = true; static bool timeElapsed = false; #endif -void GLBackend::do_beginQuery(Batch& batch, size_t paramOffset) { +void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -34,7 +34,7 @@ void GLBackend::do_beginQuery(Batch& batch, size_t paramOffset) { } } -void GLBackend::do_endQuery(Batch& batch, size_t paramOffset) { +void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -47,7 +47,7 @@ void GLBackend::do_endQuery(Batch& batch, size_t paramOffset) { } } -void GLBackend::do_getQuery(Batch& batch, size_t paramOffset) { +void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp index b1e4f427db..f41570ef7b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp @@ -290,7 +290,7 @@ void GLBackend::do_setStateColorWriteMask(uint32 mask) { } -void GLBackend::do_setStateBlendFactor(Batch& batch, size_t paramOffset) { +void GLBackend::do_setStateBlendFactor(const Batch& batch, size_t paramOffset) { Vec4 factor(batch._params[paramOffset + 0]._float, batch._params[paramOffset + 1]._float, batch._params[paramOffset + 2]._float, @@ -300,9 +300,9 @@ void GLBackend::do_setStateBlendFactor(Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GLBackend::do_setStateScissorRect(Batch& batch, size_t paramOffset) { +void GLBackend::do_setStateScissorRect(const Batch& batch, size_t paramOffset) { Vec4i rect; - memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + memcpy(&rect, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); if (_stereo._enable) { rect.z /= 2; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp index 6d9b5fd2c7..f51eac0e33 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp @@ -14,7 +14,14 @@ using namespace gpu; using namespace gpu::gl; -void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { +bool GLBackend::isTextureReady(const TexturePointer& texture) { + // DO not transfer the texture, this call is expected for rendering texture + GLTexture* object = syncGPUObject(texture, true); + return object && object->isReady(); +} + + +void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); if (!resourceTexture) { return; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp index 6120df0bfc..7f821078cd 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp @@ -14,21 +14,22 @@ using namespace gpu; using namespace gpu::gl; // Transform Stage -void GLBackend::do_setModelTransform(Batch& batch, size_t paramOffset) { +void GLBackend::do_setModelTransform(const Batch& batch, size_t paramOffset) { } -void GLBackend::do_setViewTransform(Batch& batch, size_t paramOffset) { +void GLBackend::do_setViewTransform(const Batch& batch, size_t paramOffset) { _transform._view = batch._transforms.get(batch._params[paramOffset]._uint); + _transform._viewIsCamera = batch._params[paramOffset + 1]._uint != 0; _transform._invalidView = true; } -void GLBackend::do_setProjectionTransform(Batch& batch, size_t paramOffset) { - memcpy(&_transform._projection, batch.editData(batch._params[paramOffset]._uint), sizeof(Mat4)); +void GLBackend::do_setProjectionTransform(const Batch& batch, size_t paramOffset) { + memcpy(&_transform._projection, batch.readData(batch._params[paramOffset]._uint), sizeof(Mat4)); _transform._invalidProj = true; } -void GLBackend::do_setViewportTransform(Batch& batch, size_t paramOffset) { - memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); +void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) { + memcpy(&_transform._viewport, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); if (!_inRenderTransferPass && !isStereo()) { ivec4& vp = _transform._viewport; @@ -39,7 +40,7 @@ void GLBackend::do_setViewportTransform(Batch& batch, size_t paramOffset) { _transform._invalidViewport = true; } -void GLBackend::do_setDepthRangeTransform(Batch& batch, size_t paramOffset) { +void GLBackend::do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) { Vec2 depthRange(batch._params[paramOffset + 1]._float, batch._params[paramOffset + 0]._float); @@ -82,6 +83,16 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo } if (_invalidView) { + // Apply the correction + if (_viewIsCamera && _correction.correction != glm::mat4()) { + // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? + Transform result; + _view.mult(result, _view, _correction.correction); + if (_skybox) { + result.setTranslation(vec3()); + } + _view = result; + } // This is when the _view matrix gets assigned _view.getInverseMatrix(_camera._view); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBuffer.cpp b/libraries/gpu-gl/src/gpu/gl/GLBuffer.cpp index cd0f86a410..f05e7341c9 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBuffer.cpp @@ -7,20 +7,24 @@ // #include "GLBuffer.h" +#include "GLBackend.h" using namespace gpu; using namespace gpu::gl; GLBuffer::~GLBuffer() { - glDeleteBuffers(1, &_id); - Backend::decrementBufferGPUCount(); - Backend::updateBufferGPUMemoryUsage(_size, 0); + if (_id) { + auto backend = _backend.lock(); + if (backend) { + backend->releaseBuffer(_id, _size); + } + } } -GLBuffer::GLBuffer(const Buffer& buffer, GLuint id) : - GLObject(buffer, id), - _size((GLuint)buffer._sysmem.getSize()), - _stamp(buffer._sysmem.getStamp()) +GLBuffer::GLBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLuint id) : + GLObject(backend, buffer, id), + _size((GLuint)buffer._renderSysmem.getSize()), + _stamp(buffer._renderSysmem.getStamp()) { Backend::incrementBufferGPUCount(); Backend::updateBufferGPUMemoryUsage(0, _size); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBuffer.h b/libraries/gpu-gl/src/gpu/gl/GLBuffer.h index 4783541b11..182014e764 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBuffer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBuffer.h @@ -9,21 +9,30 @@ #define hifi_gpu_gl_GLBuffer_h #include "GLShared.h" +#include "GLBackend.h" namespace gpu { namespace gl { class GLBuffer : public GLObject { public: template - static GLBufferType* sync(const Buffer& buffer) { + static GLBufferType* sync(GLBackend& backend, const Buffer& buffer) { + if (buffer.getSysmem().getSize() != 0) { + if (buffer._getUpdateCount == 0) { + qWarning() << "Unsynced buffer"; + } + if (buffer._getUpdateCount < buffer._applyUpdateCount) { + qWarning() << "Unsynced buffer " << buffer._getUpdateCount << " " << buffer._applyUpdateCount; + } + } GLBufferType* object = Backend::getGPUObject(buffer); // Has the storage size changed? - if (!object || object->_stamp != buffer.getSysmem().getStamp()) { - object = new GLBufferType(buffer, object); + if (!object || object->_stamp != buffer._renderSysmem.getStamp()) { + object = new GLBufferType(backend.shared_from_this(), buffer, object); } - if (0 != (buffer._flags & Buffer::DIRTY)) { + if (0 != (buffer._renderPages._flags & PageManager::DIRTY)) { object->transfer(); } @@ -31,8 +40,8 @@ public: } template - static GLuint getId(const Buffer& buffer) { - GLBuffer* bo = sync(buffer); + static GLuint getId(GLBackend& backend, const Buffer& buffer) { + GLBuffer* bo = sync(backend, buffer); if (bo) { return bo->_buffer; } else { @@ -49,7 +58,7 @@ public: virtual void transfer() = 0; protected: - GLBuffer(const Buffer& buffer, GLuint id); + GLBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLuint id); }; } } diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp index 91f7fbd494..eaca31ebdc 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp @@ -7,10 +7,19 @@ // #include "GLFramebuffer.h" +#include "GLBackend.h" using namespace gpu; using namespace gpu::gl; +GLFramebuffer::~GLFramebuffer() { + if (_id) { + auto backend = _backend.lock(); + if (backend) { + backend->releaseFramebuffer(_id); + } + } +} bool GLFramebuffer::checkStatus(GLenum target) const { bool result = false; diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h index d54c181c20..9b4f9703fc 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h @@ -9,13 +9,14 @@ #define hifi_gpu_gl_GLFramebuffer_h #include "GLShared.h" +#include "GLBackend.h" namespace gpu { namespace gl { class GLFramebuffer : public GLObject { public: template - static GLFramebufferType* sync(const Framebuffer& framebuffer) { + static GLFramebufferType* sync(GLBackend& backend, const Framebuffer& framebuffer) { GLFramebufferType* object = Backend::getGPUObject(framebuffer); bool needsUpate { false }; @@ -36,7 +37,7 @@ public: // need to have a gpu object? if (!object) { // All is green, assign the gpuobject to the Framebuffer - object = new GLFramebufferType(framebuffer); + object = new GLFramebufferType(backend.shared_from_this(), framebuffer); Backend::setGPUObject(framebuffer, object); (void)CHECK_GL_ERROR(); } @@ -46,8 +47,8 @@ public: } template - static GLuint getId(const Framebuffer& framebuffer) { - GLFramebufferType* fbo = sync(framebuffer); + static GLuint getId(GLBackend& backend, const Framebuffer& framebuffer) { + GLFramebufferType* fbo = sync(backend, framebuffer); if (fbo) { return fbo->_id; } else { @@ -65,8 +66,8 @@ protected: virtual void update() = 0; bool checkStatus(GLenum target) const; - GLFramebuffer(const Framebuffer& framebuffer, GLuint id) : GLObject(framebuffer, id) {} - ~GLFramebuffer() { if (_id) { glDeleteFramebuffers(1, &_id); } }; + GLFramebuffer(const std::weak_ptr& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {} + ~GLFramebuffer(); }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp index fa54e7c8fe..bcc8e70c89 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp @@ -14,7 +14,7 @@ using namespace gpu; using namespace gpu::gl; -GLPipeline* GLPipeline::sync(const Pipeline& pipeline) { +GLPipeline* GLPipeline::sync(GLBackend& backend, const Pipeline& pipeline) { GLPipeline* object = Backend::getGPUObject(pipeline); // If GPU object already created then good @@ -30,7 +30,7 @@ GLPipeline* GLPipeline::sync(const Pipeline& pipeline) { return nullptr; } - GLShader* programObject = GLShader::sync(*shader); + GLShader* programObject = GLShader::sync(backend, *shader); if (programObject == nullptr) { shader->setCompilationHasFailed(true); return nullptr; @@ -48,6 +48,10 @@ GLPipeline* GLPipeline::sync(const Pipeline& pipeline) { Backend::setGPUObject(pipeline, object); } + // Special case for view correction matrices, any pipeline that declares the correction buffer + // uniform will automatically have it provided without any client code necessary. + // Required for stable lighting in the HMD. + object->_cameraCorrection = shader->getBuffers().findLocation("cameraCorrectionBuffer"); object->_program = programObject; object->_state = stateObject; diff --git a/libraries/gpu-gl/src/gpu/gl/GLPipeline.h b/libraries/gpu-gl/src/gpu/gl/GLPipeline.h index 9ade2bb830..a298f149d9 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLPipeline.h +++ b/libraries/gpu-gl/src/gpu/gl/GLPipeline.h @@ -14,10 +14,13 @@ namespace gpu { namespace gl { class GLPipeline : public GPUObject { public: - static GLPipeline* sync(const Pipeline& pipeline); + static GLPipeline* sync(GLBackend& backend, const Pipeline& pipeline); GLShader* _program { nullptr }; GLState* _state { nullptr }; + // Bit of a hack, any pipeline can need the camera correction buffer at execution time, so + // we store whether a given pipeline has declared the uniform buffer for it. + int32 _cameraCorrection { -1 }; }; } } diff --git a/libraries/gpu-gl/src/gpu/gl/GLQuery.h b/libraries/gpu-gl/src/gpu/gl/GLQuery.h index 93f5ab20b3..4bed659ba3 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLQuery.h +++ b/libraries/gpu-gl/src/gpu/gl/GLQuery.h @@ -9,6 +9,7 @@ #define hifi_gpu_gl_GLQuery_h #include "GLShared.h" +#include "GLBackend.h" namespace gpu { namespace gl { @@ -16,13 +17,13 @@ class GLQuery : public GLObject { using Parent = gpu::gl::GLObject; public: template - static GLQueryType* sync(const Query& query) { + static GLQueryType* sync(GLBackend& backend, const Query& query) { GLQueryType* object = Backend::getGPUObject(query); // need to have a gpu object? if (!object) { // All is green, assign the gpuobject to the Query - object = new GLQueryType(query); + object = new GLQueryType(backend.shared_from_this(), query); (void)CHECK_GL_ERROR(); Backend::setGPUObject(query, object); } @@ -31,12 +32,12 @@ public: } template - static GLuint getId(const QueryPointer& query) { + static GLuint getId(GLBackend& backend, const QueryPointer& query) { if (!query) { return 0; } - GLQuery* object = sync(*query); + GLQuery* object = sync(backend, *query); if (!object) { return 0; } @@ -49,7 +50,7 @@ public: GLuint64 _result { (GLuint64)-1 }; protected: - GLQuery(const Query& query, GLuint endId, GLuint beginId) : Parent(query, endId), _beginqo(beginId){} + GLQuery(const std::weak_ptr& backend, const Query& query, GLuint endId, GLuint beginId) : Parent(backend, query, endId), _beginqo(beginId) {} ~GLQuery() { if (_id) { GLuint ids[2] = { _endqo, _beginqo }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp index 8dc3854b41..a2f44b9938 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp @@ -11,16 +11,19 @@ using namespace gpu; using namespace gpu::gl; -GLShader::GLShader() { +GLShader::GLShader(const std::weak_ptr& backend) : _backend(backend) { } GLShader::~GLShader() { for (auto& so : _shaderObjects) { - if (so.glshader != 0) { - glDeleteShader(so.glshader); - } - if (so.glprogram != 0) { - glDeleteProgram(so.glprogram); + auto backend = _backend.lock(); + if (backend) { + if (so.glshader != 0) { + backend->releaseShader(so.glshader); + } + if (so.glprogram != 0) { + backend->releaseProgram(so.glprogram); + } } } } @@ -54,7 +57,7 @@ static const std::array VERSION_DEFINES { { "" } }; -GLShader* compileBackendShader(const Shader& shader) { +GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) { // Any GLSLprogram ? normally yes... const std::string& shaderSource = shader.getSource().getCode(); GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; @@ -72,13 +75,13 @@ GLShader* compileBackendShader(const Shader& shader) { } // So far so good, the shader is created successfully - GLShader* object = new GLShader(); + GLShader* object = new GLShader(backend.shared_from_this()); object->_shaderObjects = shaderObjects; return object; } -GLShader* compileBackendProgram(const Shader& program) { +GLShader* compileBackendProgram(GLBackend& backend, const Shader& program) { if (!program.isProgram()) { return nullptr; } @@ -91,7 +94,7 @@ GLShader* compileBackendProgram(const Shader& program) { // Let's go through every shaders and make sure they are ready to go std::vector< GLuint > shaderGLObjects; for (auto subShader : program.getShaders()) { - auto object = GLShader::sync(*subShader); + auto object = GLShader::sync(backend, *subShader); if (object) { shaderGLObjects.push_back(object->_shaderObjects[version].glshader); } else { @@ -111,13 +114,13 @@ GLShader* compileBackendProgram(const Shader& program) { } // So far so good, the program versions have all been created successfully - GLShader* object = new GLShader(); + GLShader* object = new GLShader(backend.shared_from_this()); object->_shaderObjects = programObjects; return object; } -GLShader* GLShader::sync(const Shader& shader) { +GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { GLShader* object = Backend::getGPUObject(shader); // If GPU object already created then good @@ -126,26 +129,27 @@ GLShader* GLShader::sync(const Shader& shader) { } // need to have a gpu object? if (shader.isProgram()) { - GLShader* tempObject = compileBackendProgram(shader); + GLShader* tempObject = compileBackendProgram(backend, shader); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); } } else if (shader.isDomain()) { - GLShader* tempObject = compileBackendShader(shader); + GLShader* tempObject = compileBackendShader(backend, shader); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); } } + glFinish(); return object; } -bool GLShader::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { +bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings) { // First make sure the Shader has been compiled - GLShader* object = sync(shader); + GLShader* object = sync(backend, shader); if (!object) { return false; } @@ -181,7 +185,6 @@ bool GLShader::makeProgram(Shader& shader, const Shader::BindingSet& slotBinding } } - return true; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.h b/libraries/gpu-gl/src/gpu/gl/GLShader.h index ca583e6d74..e75e96cf16 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.h @@ -14,8 +14,8 @@ namespace gpu { namespace gl { class GLShader : public GPUObject { public: - static GLShader* sync(const Shader& shader); - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings); + static GLShader* sync(GLBackend& backend, const Shader& shader); + static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings); enum Version { Mono = 0, @@ -28,7 +28,7 @@ public: using UniformMapping = std::map; using UniformMappingVersions = std::vector; - GLShader(); + GLShader(const std::weak_ptr& backend); ~GLShader(); ShaderObjects _shaderObjects; @@ -44,6 +44,7 @@ public: return srcLoc; } + const std::weak_ptr _backend; }; } } diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp index 8f234ca6b4..fd6857c4c0 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp @@ -557,46 +557,67 @@ int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindin glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); + struct UniformBlockInfo { + using Vector = std::vector; + const GLuint index{ 0 }; + const std::string name; + GLint binding{ -1 }; + GLint size{ 0 }; + + static std::string getName(GLuint glprogram, GLuint i) { + static const GLint NAME_LENGTH = 256; + GLint length = 0; + GLchar nameBuffer[NAME_LENGTH]; + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); + glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); + return std::string(nameBuffer); + } + + UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + } + }; + + UniformBlockInfo::Vector uniformBlocks; + uniformBlocks.reserve(buffersCount); for (int i = 0; i < buffersCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLint binding = -1; + uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); + } - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); - glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, name); - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_BINDING, &binding); - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - - GLuint blockIndex = glGetUniformBlockIndex(glprogram, name); - - // CHeck if there is a requested binding for this block - auto requestedBinding = slotBindings.find(std::string(name)); + for (auto& info : uniformBlocks) { + auto requestedBinding = slotBindings.find(info.name); if (requestedBinding != slotBindings.end()) { - // If yes force it - if (binding != (*requestedBinding)._location) { - binding = (*requestedBinding)._location; - glUniformBlockBinding(glprogram, blockIndex, binding); - } - } else if (binding == 0) { + info.binding = (*requestedBinding)._location; + glUniformBlockBinding(glprogram, info.index, info.binding); + uniformBufferSlotMap[info.binding] = info.index; + } + } + + for (auto& info : uniformBlocks) { + if (slotBindings.count(info.name)) { + continue; + } + + // If the binding is 0, or the binding maps to an already used binding + if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) { // If no binding was assigned then just do it finding a free slot auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot); if (slotIt != uniformBufferSlotMap.end()) { - binding = slotIt - uniformBufferSlotMap.begin(); - glUniformBlockBinding(glprogram, blockIndex, binding); + info.binding = slotIt - uniformBufferSlotMap.begin(); + glUniformBlockBinding(glprogram, info.index, info.binding); } else { // This should neve happen, an active ubo cannot find an available slot among the max available?! - binding = -1; + info.binding = -1; } } - // If binding is valid record it - if (binding >= 0) { - uniformBufferSlotMap[binding] = blockIndex; - } - Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); - buffers.insert(Shader::Slot(name, binding, element, Resource::BUFFER)); + uniformBufferSlotMap[info.binding] = info.index; + } + + for (auto& info : uniformBlocks) { + static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); + buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); } return buffersCount; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl/src/gpu/gl/GLShared.h index 3220eafef4..676d3910ff 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.h @@ -121,15 +121,19 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = { bool checkGLError(const char* name = nullptr); bool checkGLErrorDebug(const char* name = nullptr); +class GLBackend; + template struct GLObject : public GPUObject { public: - GLObject(const GPUType& gpuObject, GLuint id) : _gpuObject(gpuObject), _id(id) {} + GLObject(const std::weak_ptr& backend, const GPUType& gpuObject, GLuint id) : _gpuObject(gpuObject), _id(id), _backend(backend) {} virtual ~GLObject() { } const GPUType& _gpuObject; const GLuint _id; +protected: + const std::weak_ptr _backend; }; class GlBuffer; diff --git a/libraries/gpu-gl/src/gpu/gl/GLState.cpp b/libraries/gpu-gl/src/gpu/gl/GLState.cpp index 8cb2efa7b4..b6d917b928 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLState.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLState.cpp @@ -5,7 +5,22 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif +#endif + + #include "GLState.h" + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + #include "GLBackend.h" using namespace gpu; diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 74428b53f5..342b2611d5 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -11,6 +11,7 @@ #include #include "GLTextureTransfer.h" +#include "GLBackend.h" using namespace gpu; using namespace gpu::gl; @@ -117,7 +118,9 @@ float GLTexture::getMemoryPressure() { return (float)consumedGpuMemory / (float)availableTextureMemory; } -GLTexture::DownsampleSource::DownsampleSource(GLTexture* oldTexture) : +GLTexture::DownsampleSource::DownsampleSource(const std::weak_ptr& backend, GLTexture* oldTexture) : + _backend(backend), + _size(oldTexture ? oldTexture->_size : 0), _texture(oldTexture ? oldTexture->takeOwnership() : 0), _minMip(oldTexture ? oldTexture->_minMip : 0), _maxMip(oldTexture ? oldTexture->_maxMip : 0) @@ -126,20 +129,22 @@ GLTexture::DownsampleSource::DownsampleSource(GLTexture* oldTexture) : GLTexture::DownsampleSource::~DownsampleSource() { if (_texture) { - glDeleteTextures(1, &_texture); - Backend::decrementTextureGPUCount(); + auto backend = _backend.lock(); + if (backend) { + backend->releaseTexture(_texture, _size); + } } } -GLTexture::GLTexture(const gpu::Texture& texture, GLuint id, GLTexture* originalTexture, bool transferrable) : - GLObject(texture, id), +GLTexture::GLTexture(const std::weak_ptr& backend, const gpu::Texture& texture, GLuint id, GLTexture* originalTexture, bool transferrable) : + GLObject(backend, texture, id), _storageStamp(texture.getStamp()), _target(getGLTextureType(texture)), _maxMip(texture.maxMip()), _minMip(texture.minMip()), _virtualSize(texture.evalTotalSize()), _transferrable(transferrable), - _downsampleSource(originalTexture) + _downsampleSource(backend, originalTexture) { if (_transferrable) { uint16 mipCount = usedMipLevels(); @@ -156,8 +161,8 @@ GLTexture::GLTexture(const gpu::Texture& texture, GLuint id, GLTexture* original // Create the texture and allocate storage -GLTexture::GLTexture(const Texture& texture, GLuint id, bool transferrable) : - GLTexture(texture, id, nullptr, transferrable) +GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : + GLTexture(backend, texture, id, nullptr, transferrable) { // FIXME, do during allocation //Backend::updateTextureGPUMemoryUsage(0, _size); @@ -165,8 +170,8 @@ GLTexture::GLTexture(const Texture& texture, GLuint id, bool transferrable) : } // Create the texture and copy from the original higher resolution version -GLTexture::GLTexture(const gpu::Texture& texture, GLuint id, GLTexture* originalTexture) : - GLTexture(texture, id, originalTexture, originalTexture->_transferrable) +GLTexture::GLTexture(const std::weak_ptr& backend, const gpu::Texture& texture, GLuint id, GLTexture* originalTexture) : + GLTexture(backend, texture, id, originalTexture, originalTexture->_transferrable) { Q_ASSERT(_minMip >= originalTexture->_minMip); // Set the GPU object last because that implicitly destroys the originalTexture object @@ -188,11 +193,11 @@ GLTexture::~GLTexture() { } if (_id) { - glDeleteTextures(1, &_id); - const_cast(_id) = 0; - Backend::decrementTextureGPUCount(); + auto backend = _backend.lock(); + if (backend) { + backend->releaseTexture(_id, _size); + } } - Backend::updateTextureGPUMemoryUsage(_size, 0); Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index df2d38e2f3..6ac83d7116 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -10,6 +10,7 @@ #include "GLShared.h" #include "GLTextureTransfer.h" +#include "GLBackend.h" namespace gpu { namespace gl { @@ -24,7 +25,7 @@ public: static std::shared_ptr _textureTransferHelper; template - static GLTextureType* sync(const TexturePointer& texturePointer, bool needTransfer) { + static GLTextureType* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) { const Texture& texture = *texturePointer; if (!texture.isDefined()) { // NO texture definition yet so let's avoid thinking @@ -38,7 +39,7 @@ public: // for easier use of immutable storage) if (!object || object->isInvalid()) { // This automatically any previous texture - object = new GLTextureType(texture, needTransfer); + object = new GLTextureType(backend.shared_from_this(), texture, needTransfer); if (!object->_transferrable) { object->createTexture(); object->_contentStamp = texture.getDataStamp(); @@ -62,7 +63,7 @@ public: if (object->isOverMaxMemory() && texturePointer->incremementMinMip()) { // WARNING, this code path will essentially `delete this`, // so no dereferencing of this instance should be done past this point - object = new GLTextureType(texture, object); + object = new GLTextureType(backend.shared_from_this(), texture, object); _textureTransferHelper->transferTexture(texturePointer); } } else if (object->isOutdated()) { @@ -75,13 +76,13 @@ public: } template - static GLuint getId(const TexturePointer& texture, bool shouldSync) { + static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) { if (!texture) { return 0; } GLTextureType* object { nullptr }; if (shouldSync) { - object = sync(texture, shouldSync); + object = sync(backend, texture, shouldSync); } else { object = Backend::getGPUObject(*texture); } @@ -92,11 +93,13 @@ public: GLuint result = object->_id; // Don't return textures that are in transfer state - if ((object->getSyncState() != GLSyncState::Idle) || - // Don't return transferrable textures that have never completed transfer - (!object->_transferrable || 0 != object->_transferCount)) { - // Will be either 0 or the original texture being downsampled. - result = object->_downsampleSource._texture; + if (shouldSync) { + if ((object->getSyncState() != GLSyncState::Idle) || + // Don't return transferrable textures that have never completed transfer + (!object->_transferrable || 0 != object->_transferCount)) { + // Will be either 0 or the original texture being downsampled. + result = object->_downsampleSource._texture; + } } return result; @@ -123,10 +126,12 @@ public: struct DownsampleSource { using Pointer = std::shared_ptr; - DownsampleSource() : _texture(0), _minMip(0), _maxMip(0) {} - DownsampleSource(GLTexture* originalTexture); + DownsampleSource(const std::weak_ptr& backend) : _backend(backend), _size(0), _texture(0), _minMip(0), _maxMip(0) {} + DownsampleSource(const std::weak_ptr& backend, GLTexture* originalTexture); ~DownsampleSource(); void reset() const { const_cast(_texture) = 0; } + const std::weak_ptr& _backend; + const GLuint _size { 0 }; const GLuint _texture { 0 }; const uint16 _minMip { 0 }; const uint16 _maxMip { 0 }; @@ -168,8 +173,8 @@ protected: const GLuint _size { 0 }; // true size as reported by the gl api std::atomic _syncState { GLSyncState::Idle }; - GLTexture(const Texture& texture, GLuint id, bool transferrable); - GLTexture(const Texture& texture, GLuint id, GLTexture* originalTexture); + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable); + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, GLTexture* originalTexture); void setSyncState(GLSyncState syncState) { _syncState = syncState; } uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } @@ -188,7 +193,7 @@ protected: private: - GLTexture(const gpu::Texture& gpuTexture, GLuint id, GLTexture* originalTexture, bool transferrable); + GLTexture(const std::weak_ptr& backend, const gpu::Texture& gpuTexture, GLuint id, GLTexture* originalTexture, bool transferrable); friend class GLTextureTransferHelper; friend class GLBackend; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index 93d87ee6e4..6c2b2f434e 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -18,7 +18,7 @@ Q_LOGGING_CATEGORY(gpugl41logging, "hifi.gpu.gl41") using namespace gpu; using namespace gpu::gl41; -void GL41Backend::do_draw(Batch& batch, size_t paramOffset) { +void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; uint32 numVertices = batch._params[paramOffset + 1]._uint; @@ -43,7 +43,7 @@ void GL41Backend::do_draw(Batch& batch, size_t paramOffset) { (void) CHECK_GL_ERROR(); } -void GL41Backend::do_drawIndexed(Batch& batch, size_t paramOffset) { +void GL41Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; uint32 numIndices = batch._params[paramOffset + 1]._uint; @@ -72,7 +72,7 @@ void GL41Backend::do_drawIndexed(Batch& batch, size_t paramOffset) { (void) CHECK_GL_ERROR(); } -void GL41Backend::do_drawInstanced(Batch& batch, size_t paramOffset) { +void GL41Backend::do_drawInstanced(const Batch& batch, size_t paramOffset) { GLint numInstances = batch._params[paramOffset + 4]._uint; Primitive primitiveType = (Primitive)batch._params[paramOffset + 3]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; @@ -108,7 +108,7 @@ void glbackend_glDrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsize #endif } -void GL41Backend::do_drawIndexedInstanced(Batch& batch, size_t paramOffset) { +void GL41Backend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) { GLint numInstances = batch._params[paramOffset + 4]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 3]._uint]; uint32 numIndices = batch._params[paramOffset + 2]._uint; @@ -143,7 +143,7 @@ void GL41Backend::do_drawIndexedInstanced(Batch& batch, size_t paramOffset) { } -void GL41Backend::do_multiDrawIndirect(Batch& batch, size_t paramOffset) { +void GL41Backend::do_multiDrawIndirect(const Batch& batch, size_t paramOffset) { #if (GPU_INPUT_PROFILE == GPU_CORE_43) uint commandCount = batch._params[paramOffset + 0]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint]; @@ -159,7 +159,7 @@ void GL41Backend::do_multiDrawIndirect(Batch& batch, size_t paramOffset) { } -void GL41Backend::do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) { +void GL41Backend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) { #if (GPU_INPUT_PROFILE == GPU_CORE_43) uint commandCount = batch._params[paramOffset + 0]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint]; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 5695ba080e..37441c4ebb 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -40,8 +40,8 @@ public: using Parent = gpu::gl::GLTexture; GLuint allocate(); public: - GL41Texture(const Texture& buffer, bool transferrable); - GL41Texture(const Texture& buffer, GL41Texture* original); + GL41Texture(const std::weak_ptr& backend, const Texture& buffer, bool transferrable); + GL41Texture(const std::weak_ptr& backend, const Texture& buffer, GL41Texture* original); protected: void transferMip(uint16_t mipLevel, uint8_t face = 0) const; @@ -68,12 +68,12 @@ protected: gl::GLQuery* syncGPUObject(const Query& query) override; // Draw Stage - void do_draw(Batch& batch, size_t paramOffset) override; - void do_drawIndexed(Batch& batch, size_t paramOffset) override; - void do_drawInstanced(Batch& batch, size_t paramOffset) override; - void do_drawIndexedInstanced(Batch& batch, size_t paramOffset) override; - void do_multiDrawIndirect(Batch& batch, size_t paramOffset) override; - void do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) override; + void do_draw(const Batch& batch, size_t paramOffset) override; + void do_drawIndexed(const Batch& batch, size_t paramOffset) override; + void do_drawInstanced(const Batch& batch, size_t paramOffset) override; + void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; + void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) override; + void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) override; // Input Stage void updateInput() override; @@ -85,7 +85,7 @@ protected: void resetTransformStage(); // Output stage - void do_blit(Batch& batch, size_t paramOffset) override; + void do_blit(const Batch& batch, size_t paramOffset) override; }; } } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp index ac337550ca..d9d7328bc8 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp @@ -8,55 +8,60 @@ #include "GL41Backend.h" #include "../gl/GLBuffer.h" +namespace gpu { + namespace gl41 { + class GL41Buffer : public gl::GLBuffer { + using Parent = gpu::gl::GLBuffer; + static GLuint allocate() { + GLuint result; + glGenBuffers(1, &result); + return result; + } + + public: + GL41Buffer(const std::weak_ptr& backend, const Buffer& buffer, GL41Buffer* original) : Parent(backend, buffer, allocate()) { + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + glBufferData(GL_ARRAY_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + if (original && original->_size) { + glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer); + glBindBuffer(GL_COPY_READ_BUFFER, original->_buffer); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, original->_size); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + glBindBuffer(GL_COPY_READ_BUFFER, 0); + (void)CHECK_GL_ERROR(); + } + Backend::setGPUObject(buffer, this); + } + + void transfer() override { + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + (void)CHECK_GL_ERROR(); + Size offset; + Size size; + Size currentPage { 0 }; + auto data = _gpuObject._renderSysmem.readData(); + while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) { + glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset); + (void)CHECK_GL_ERROR(); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + (void)CHECK_GL_ERROR(); + _gpuObject._renderPages._flags &= ~PageManager::DIRTY; + } + }; + } +} + using namespace gpu; using namespace gpu::gl41; -class GL41Buffer : public gl::GLBuffer { - using Parent = gpu::gl::GLBuffer; - static GLuint allocate() { - GLuint result; - glGenBuffers(1, &result); - return result; - } - -public: - GL41Buffer(const Buffer& buffer, GL41Buffer* original) : Parent(buffer, allocate()) { - glBindBuffer(GL_ARRAY_BUFFER, _buffer); - glBufferData(GL_ARRAY_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - if (original && original->_size) { - glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer); - glBindBuffer(GL_COPY_READ_BUFFER, original->_buffer); - glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, original->_size); - glBindBuffer(GL_COPY_WRITE_BUFFER, 0); - glBindBuffer(GL_COPY_READ_BUFFER, 0); - (void)CHECK_GL_ERROR(); - } - Backend::setGPUObject(buffer, this); - } - - void transfer() override { - glBindBuffer(GL_ARRAY_BUFFER, _buffer); - (void)CHECK_GL_ERROR(); - Size offset; - Size size; - Size currentPage { 0 }; - auto data = _gpuObject.getSysmem().readData(); - while (_gpuObject.getNextTransferBlock(offset, size, currentPage)) { - glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset); - (void)CHECK_GL_ERROR(); - } - glBindBuffer(GL_ARRAY_BUFFER, 0); - (void)CHECK_GL_ERROR(); - _gpuObject._flags &= ~Buffer::DIRTY; - } -}; GLuint GL41Backend::getBufferID(const Buffer& buffer) { - return GL41Buffer::getId(buffer); + return GL41Buffer::getId(*this, buffer); } gl::GLBuffer* GL41Backend::syncGPUObject(const Buffer& buffer) { - return GL41Buffer::sync(buffer); + return GL41Buffer::sync(*this, buffer); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp index 53c2c75394..6d11a52035 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp @@ -56,7 +56,7 @@ public: for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = gl::GLTexture::sync(surface, false); // Grab the gltexture and don't transfer + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer } else { gltexture = nullptr; } @@ -83,7 +83,7 @@ public: if (_gpuObject.getDepthStamp() != _depthStamp) { auto surface = _gpuObject.getDepthStencilBuffer(); if (_gpuObject.hasDepthStencil() && surface) { - gltexture = gl::GLTexture::sync(surface, false); // Grab the gltexture and don't transfer + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer } if (gltexture) { @@ -115,19 +115,19 @@ public: public: - GL41Framebuffer(const gpu::Framebuffer& framebuffer) - : Parent(framebuffer, allocate()) { } + GL41Framebuffer(const std::weak_ptr& backend, const gpu::Framebuffer& framebuffer) + : Parent(backend, framebuffer, allocate()) { } }; gl::GLFramebuffer* GL41Backend::syncGPUObject(const Framebuffer& framebuffer) { - return GL41Framebuffer::sync(framebuffer); + return GL41Framebuffer::sync(*this, framebuffer); } GLuint GL41Backend::getFramebufferID(const FramebufferPointer& framebuffer) { - return framebuffer ? GL41Framebuffer::getId(*framebuffer) : 0; + return framebuffer ? GL41Framebuffer::getId(*this, *framebuffer) : 0; } -void GL41Backend::do_blit(Batch& batch, size_t paramOffset) { +void GL41Backend::do_blit(const Batch& batch, size_t paramOffset) { auto srcframebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); Vec4i srcvp; for (auto i = 0; i < 4; ++i) { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp index 8cc47a43a4..342c4ba6c2 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp @@ -24,14 +24,14 @@ public: return result; } - GL41Query(const Query& query) - : Parent(query, allocateQuery(), allocateQuery()) { } + GL41Query(const std::weak_ptr& backend, const Query& query) + : Parent(backend, query, allocateQuery(), allocateQuery()) { } }; gl::GLQuery* GL41Backend::syncGPUObject(const Query& query) { - return GL41Query::sync(query); + return GL41Query::sync(*this, query); } GLuint GL41Backend::getQueryID(const QueryPointer& query) { - return GL41Query::getId(query); + return GL41Query::getId(*this, query); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 326a63c01a..8f1248ef57 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -30,16 +30,16 @@ GLuint GL41Texture::allocate() { } GLuint GL41Backend::getTextureID(const TexturePointer& texture, bool transfer) { - return GL41Texture::getId(texture, transfer); + return GL41Texture::getId(*this, texture, transfer); } gl::GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { - return GL41Texture::sync(texture, transfer); + return GL41Texture::sync(*this, texture, transfer); } -GL41Texture::GL41Texture(const Texture& texture, bool transferrable) : gl::GLTexture(texture, allocate(), transferrable) {} +GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) : gl::GLTexture(backend, texture, allocate(), transferrable) {} -GL41Texture::GL41Texture(const Texture& texture, GL41Texture* original) : gl::GLTexture(texture, allocate(), original) {} +GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, GL41Texture* original) : gl::GLTexture(backend, texture, allocate(), original) {} void GL41Backend::GL41Texture::withPreservedTexture(std::function f) const { GLint boundTex = -1; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTransform.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTransform.cpp index 89b5db34c0..45f48df310 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTransform.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTransform.cpp @@ -38,17 +38,13 @@ void GL41Backend::transferTransformState(const Batch& batch) const { } if (!batch._objects.empty()) { - auto byteSize = batch._objects.size() * sizeof(Batch::TransformObject); - bufferData.resize(byteSize); - memcpy(bufferData.data(), batch._objects.data(), byteSize); - #ifdef GPU_SSBO_DRAW_CALL_INFO glBindBuffer(GL_SHADER_STORAGE_BUFFER, _transform._objectBuffer); - glBufferData(GL_SHADER_STORAGE_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); + glBufferData(GL_SHADER_STORAGE_BUFFER, sysmem.getSize(), sysmem.readData(), GL_STREAM_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); #else glBindBuffer(GL_TEXTURE_BUFFER, _transform._objectBuffer); - glBufferData(GL_TEXTURE_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); + glBufferData(GL_TEXTURE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_DYNAMIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); #endif } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index bb6ae67233..dad1f07ed3 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -18,7 +18,7 @@ Q_LOGGING_CATEGORY(gpugl45logging, "hifi.gpu.gl45") using namespace gpu; using namespace gpu::gl45; -void GL45Backend::do_draw(Batch& batch, size_t paramOffset) { +void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; uint32 numVertices = batch._params[paramOffset + 1]._uint; @@ -43,7 +43,7 @@ void GL45Backend::do_draw(Batch& batch, size_t paramOffset) { (void) CHECK_GL_ERROR(); } -void GL45Backend::do_drawIndexed(Batch& batch, size_t paramOffset) { +void GL45Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; uint32 numIndices = batch._params[paramOffset + 1]._uint; @@ -72,7 +72,7 @@ void GL45Backend::do_drawIndexed(Batch& batch, size_t paramOffset) { (void) CHECK_GL_ERROR(); } -void GL45Backend::do_drawInstanced(Batch& batch, size_t paramOffset) { +void GL45Backend::do_drawInstanced(const Batch& batch, size_t paramOffset) { GLint numInstances = batch._params[paramOffset + 4]._uint; Primitive primitiveType = (Primitive)batch._params[paramOffset + 3]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; @@ -100,7 +100,7 @@ void GL45Backend::do_drawInstanced(Batch& batch, size_t paramOffset) { (void) CHECK_GL_ERROR(); } -void GL45Backend::do_drawIndexedInstanced(Batch& batch, size_t paramOffset) { +void GL45Backend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) { GLint numInstances = batch._params[paramOffset + 4]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 3]._uint]; uint32 numIndices = batch._params[paramOffset + 2]._uint; @@ -129,7 +129,7 @@ void GL45Backend::do_drawIndexedInstanced(Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GL45Backend::do_multiDrawIndirect(Batch& batch, size_t paramOffset) { +void GL45Backend::do_multiDrawIndirect(const Batch& batch, size_t paramOffset) { uint commandCount = batch._params[paramOffset + 0]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint]; glMultiDrawArraysIndirect(mode, reinterpret_cast(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride); @@ -138,7 +138,7 @@ void GL45Backend::do_multiDrawIndirect(Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GL45Backend::do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) { +void GL45Backend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) { uint commandCount = batch._params[paramOffset + 0]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint]; GLenum indexType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType]; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index d0dfbd0e41..679699129f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -29,8 +29,8 @@ public: using Parent = gpu::gl::GLTexture; GLuint allocate(const Texture& texture); public: - GL45Texture(const Texture& texture, bool transferrable); - GL45Texture(const Texture& texture, GLTexture* original); + GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable); + GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLTexture* original); protected: void transferMip(uint16_t mipLevel, uint8_t face = 0) const; @@ -57,12 +57,12 @@ protected: gl::GLQuery* syncGPUObject(const Query& query) override; // Draw Stage - void do_draw(Batch& batch, size_t paramOffset) override; - void do_drawIndexed(Batch& batch, size_t paramOffset) override; - void do_drawInstanced(Batch& batch, size_t paramOffset) override; - void do_drawIndexedInstanced(Batch& batch, size_t paramOffset) override; - void do_multiDrawIndirect(Batch& batch, size_t paramOffset) override; - void do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) override; + void do_draw(const Batch& batch, size_t paramOffset) override; + void do_drawIndexed(const Batch& batch, size_t paramOffset) override; + void do_drawInstanced(const Batch& batch, size_t paramOffset) override; + void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; + void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) override; + void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) override; // Input Stage void updateInput() override; @@ -74,7 +74,7 @@ protected: void resetTransformStage(); // Output stage - void do_blit(Batch& batch, size_t paramOffset) override; + void do_blit(const Batch& batch, size_t paramOffset) override; }; } } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp index 1676b0ce1c..c7c9ec1b2e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp @@ -8,43 +8,48 @@ #include "GL45Backend.h" #include "../gl/GLBuffer.h" +namespace gpu { + namespace gl45 { + class GL45Buffer : public gl::GLBuffer { + using Parent = gpu::gl::GLBuffer; + static GLuint allocate() { + GLuint result; + glCreateBuffers(1, &result); + return result; + } + + public: + GL45Buffer(const std::weak_ptr& backend, const Buffer& buffer, GLBuffer* original) : Parent(backend, buffer, allocate()) { + glNamedBufferStorage(_buffer, _size == 0 ? 256 : _size, nullptr, GL_DYNAMIC_STORAGE_BIT); + if (original && original->_size) { + glCopyNamedBufferSubData(original->_buffer, _buffer, 0, 0, std::min(original->_size, _size)); + } + Backend::setGPUObject(buffer, this); + } + + void transfer() override { + Size offset; + Size size; + Size currentPage { 0 }; + auto data = _gpuObject._renderSysmem.readData(); + while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) { + glNamedBufferSubData(_buffer, (GLintptr)offset, (GLsizeiptr)size, data + offset); + } + (void)CHECK_GL_ERROR(); + _gpuObject._renderPages._flags &= ~PageManager::DIRTY; + } + }; + } +} + using namespace gpu; using namespace gpu::gl45; -class GL45Buffer : public gl::GLBuffer { - using Parent = gpu::gl::GLBuffer; - static GLuint allocate() { - GLuint result; - glCreateBuffers(1, &result); - return result; - } - -public: - GL45Buffer(const Buffer& buffer, GLBuffer* original) : Parent(buffer, allocate()) { - glNamedBufferStorage(_buffer, _size, nullptr, GL_DYNAMIC_STORAGE_BIT); - if (original && original->_size) { - glCopyNamedBufferSubData(original->_buffer, _buffer, 0, 0, std::min(original->_size, _size)); - } - Backend::setGPUObject(buffer, this); - } - - void transfer() override { - Size offset; - Size size; - Size currentPage { 0 }; - auto data = _gpuObject.getSysmem().readData(); - while (_gpuObject.getNextTransferBlock(offset, size, currentPage)) { - glNamedBufferSubData(_buffer, (GLintptr)offset, (GLsizeiptr)size, data + offset); - } - (void)CHECK_GL_ERROR(); - _gpuObject._flags &= ~Buffer::DIRTY; - } -}; GLuint GL45Backend::getBufferID(const Buffer& buffer) { - return GL45Buffer::getId(buffer); + return GL45Buffer::getId(*this, buffer); } gl::GLBuffer* GL45Backend::syncGPUObject(const Buffer& buffer) { - return GL45Buffer::sync(buffer); + return GL45Buffer::sync(*this, buffer); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp index b846dd4df3..c5b84b7deb 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp @@ -52,7 +52,7 @@ public: for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = gl::GLTexture::sync(surface, false); // Grab the gltexture and don't transfer + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer } else { gltexture = nullptr; } @@ -79,7 +79,7 @@ public: if (_gpuObject.getDepthStamp() != _depthStamp) { auto surface = _gpuObject.getDepthStencilBuffer(); if (_gpuObject.hasDepthStencil() && surface) { - gltexture = gl::GLTexture::sync(surface, false); // Grab the gltexture and don't transfer + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer } if (gltexture) { @@ -107,19 +107,19 @@ public: public: - GL45Framebuffer(const gpu::Framebuffer& framebuffer) - : Parent(framebuffer, allocate()) { } + GL45Framebuffer(const std::weak_ptr& backend, const gpu::Framebuffer& framebuffer) + : Parent(backend, framebuffer, allocate()) { } }; gl::GLFramebuffer* GL45Backend::syncGPUObject(const Framebuffer& framebuffer) { - return gl::GLFramebuffer::sync(framebuffer); + return gl::GLFramebuffer::sync(*this, framebuffer); } GLuint GL45Backend::getFramebufferID(const FramebufferPointer& framebuffer) { - return framebuffer ? gl::GLFramebuffer::getId(*framebuffer) : 0; + return framebuffer ? gl::GLFramebuffer::getId(*this, *framebuffer) : 0; } -void GL45Backend::do_blit(Batch& batch, size_t paramOffset) { +void GL45Backend::do_blit(const Batch& batch, size_t paramOffset) { auto srcframebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); Vec4i srcvp; for (auto i = 0; i < 4; ++i) { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp index 9e808aeb82..df81d7914e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp @@ -23,16 +23,17 @@ public: return result; } - GL45Query(const Query& query) - : Parent(query, allocateQuery(), allocateQuery()){} + GL45Query(const std::weak_ptr& backend, const Query& query) + : Parent(backend, query, allocateQuery(), allocateQuery()) { + } }; gl::GLQuery* GL45Backend::syncGPUObject(const Query& query) { - return GL45Query::sync(query); + return GL45Query::sync(*this, query); } GLuint GL45Backend::getQueryID(const QueryPointer& query) { - return GL45Query::getId(query); + return GL45Query::getId(*this, query); } } } \ No newline at end of file diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 36fb4bfde3..7c3e362834 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -30,18 +30,18 @@ GLuint GL45Texture::allocate(const Texture& texture) { } GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { - return GL45Texture::getId(texture, transfer); + return GL45Texture::getId(*this, texture, transfer); } gl::GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { - return GL45Texture::sync(texture, transfer); + return GL45Texture::sync(*this, texture, transfer); } -GL45Backend::GL45Texture::GL45Texture(const Texture& texture, bool transferrable) - : gl::GLTexture(texture, allocate(texture), transferrable) {} +GL45Backend::GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) + : gl::GLTexture(backend, texture, allocate(texture), transferrable) {} -GL45Backend::GL45Texture::GL45Texture(const Texture& texture, GLTexture* original) - : gl::GLTexture(texture, allocate(texture), original) {} +GL45Backend::GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLTexture* original) + : gl::GLTexture(backend, texture, allocate(texture), original) {} void GL45Backend::GL45Texture::withPreservedTexture(std::function f) const { f(); @@ -84,7 +84,10 @@ void GL45Backend::GL45Texture::transferMip(uint16_t mipLevel, uint8_t face) cons if (GL_TEXTURE_2D == _target) { glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); } else if (GL_TEXTURE_CUBE_MAP == _target) { - glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); + // DSA ARB does not work on AMD, so use EXT + // glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); + auto target = CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); } else { Q_ASSERT(false); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTransform.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTransform.cpp index 96afb4cc71..314e7d79be 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTransform.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTransform.cpp @@ -38,10 +38,7 @@ void GL45Backend::transferTransformState(const Batch& batch) const { } if (!batch._objects.empty()) { - auto byteSize = batch._objects.size() * sizeof(Batch::TransformObject); - bufferData.resize(byteSize); - memcpy(bufferData.data(), batch._objects.data(), byteSize); - glNamedBufferData(_transform._objectBuffer, bufferData.size(), bufferData.data(), GL_STREAM_DRAW); + glNamedBufferData(_transform._objectBuffer, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_DYNAMIC_DRAW); } if (!batch._namedData.empty()) { @@ -59,9 +56,9 @@ void GL45Backend::transferTransformState(const Batch& batch) const { #ifdef GPU_SSBO_DRAW_CALL_INFO glBindBufferBase(GL_SHADER_STORAGE_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._objectBuffer); #else - glTextureBuffer(_transform._objectBufferTexture, GL_RGBA32F, _transform._objectBuffer); glActiveTexture(GL_TEXTURE0 + TRANSFORM_OBJECT_SLOT); glBindTexture(GL_TEXTURE_BUFFER, _transform._objectBufferTexture); + glTextureBuffer(_transform._objectBufferTexture, GL_RGBA32F, _transform._objectBuffer); #endif CHECK_GL_ERROR(); diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index ab1337070c..8d3f019168 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -232,10 +232,11 @@ void Batch::setModelTransform(const Transform& model) { _invalidModel = true; } -void Batch::setViewTransform(const Transform& view) { +void Batch::setViewTransform(const Transform& view, bool camera) { ADD_COMMAND(setViewTransform); - + uint cameraFlag = camera ? 1 : 0; _params.emplace_back(_transforms.cache(view)); + _params.emplace_back(cameraFlag); } void Batch::setProjectionTransform(const Mat4& proj) { @@ -491,17 +492,6 @@ void Batch::captureNamedDrawCallInfo(std::string name) { std::swap(_currentNamedCall, name); // Restore _currentNamedCall } -void Batch::preExecute() { - for (auto& mapItem : _namedData) { - auto& name = mapItem.first; - auto& instance = mapItem.second; - - startNamedCall(name); - instance.process(*this); - stopNamedCall(); - } -} - // Debugging void Batch::pushProfileRange(const char* name) { #if defined(NSIGHT_FOUND) @@ -628,3 +618,66 @@ void Batch::_glColor4f(float red, float green, float blue, float alpha) { _params.emplace_back(green); _params.emplace_back(red); } + +void Batch::finishFrame(BufferUpdates& updates) { + for (auto& mapItem : _namedData) { + auto& name = mapItem.first; + auto& instance = mapItem.second; + + startNamedCall(name); + instance.process(*this); + stopNamedCall(); + } + + for (auto& namedCallData : _namedData) { + for (auto& buffer : namedCallData.second.buffers) { + if (!buffer || !buffer->isDirty()) { + continue; + } + updates.emplace_back(buffer->getUpdate()); + } + } + + for (auto& bufferCacheItem : _buffers._items) { + const BufferPointer& buffer = bufferCacheItem._data; + if (!buffer || !buffer->isDirty()) { + continue; + } + updates.emplace_back(buffer->getUpdate()); + } +} + +void Batch::flush() { + for (auto& mapItem : _namedData) { + auto& name = mapItem.first; + auto& instance = mapItem.second; + + auto& self = const_cast(*this); + self.startNamedCall(name); + instance.process(self); + self.stopNamedCall(); + } + + for (auto& namedCallData : _namedData) { + for (auto& buffer : namedCallData.second.buffers) { + if (!buffer) { + continue; + } + if (!buffer->isDirty()) { + continue; + } + buffer->flush(); + } + } + + for (auto& bufferCacheItem : _buffers._items) { + const BufferPointer& buffer = bufferCacheItem._data; + if (!buffer) { + continue; + } + if (!buffer->isDirty()) { + continue; + } + buffer->flush(); + } +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 9cf1ca8269..8a52eef4ea 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -75,7 +75,7 @@ public: Function function; DrawCallInfoBuffer drawCallInfos; - size_t count() const { return drawCallInfos.size(); } + size_t count() const { return drawCallInfos.size(); } void process(Batch& batch) { if (function) { @@ -89,7 +89,7 @@ public: DrawCallInfoBuffer _drawCallInfos; static size_t _drawCallInfosMax; - std::string _currentNamedCall; + mutable std::string _currentNamedCall; const DrawCallInfoBuffer& getDrawCallInfoBuffer() const; DrawCallInfoBuffer& getDrawCallInfoBuffer(); @@ -102,8 +102,6 @@ public: ~Batch(); void clear(); - - void preExecute(); // Batches may need to override the context level stereo settings // if they're performing framebuffer copy operations, like the @@ -172,7 +170,8 @@ public: // WARNING: ViewTransform transform from eye space to world space, its inverse is composed // with the ModelTransform to create the equivalent of the gl ModelViewMatrix void setModelTransform(const Transform& model); - void setViewTransform(const Transform& view); + void resetViewTransform() { setViewTransform(Transform(), false); } + void setViewTransform(const Transform& view, bool camera = true); void setProjectionTransform(const Mat4& proj); // Viewport is xy = low left corner in framebuffer, zw = width height of the viewport, expressed in pixels void setViewportTransform(const Vec4i& viewport); @@ -394,7 +393,7 @@ public: return offset; } - Data get(uint32 offset) { + Data get(uint32 offset) const { if (offset >= _items.size()) { return Data(); } @@ -429,6 +428,13 @@ public: return (_data.data() + offset); } + const Byte* readData(size_t offset) const { + if (offset >= _data.size()) { + return 0; + } + return (_data.data() + offset); + } + Commands _commands; static size_t _commandsMax; @@ -471,6 +477,18 @@ public: bool _enableSkybox{ false }; protected: + friend class Context; + friend class Frame; + + // Apply all the named calls to the end of the batch + // and prepare updates for the render shadow copies of the buffers + void finishFrame(BufferUpdates& updates); + + // Directly copy from the main data to the render thread shadow copy + // MUST only be called on the render thread + // MUST only be called on batches created on the render thread + void flush(); + void startNamedCall(const std::string& name); void stopNamedCall(); diff --git a/libraries/gpu/src/gpu/Buffer.cpp b/libraries/gpu/src/gpu/Buffer.cpp new file mode 100644 index 0000000000..f4cd9e41ba --- /dev/null +++ b/libraries/gpu/src/gpu/Buffer.cpp @@ -0,0 +1,204 @@ +// +// Created by Sam Gateau on 10/8/2014. +// Split from Resource.h/Resource.cpp by Bradley Austin Davis on 2016/08/07 +// 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 "Buffer.h" +#include "Context.h" + +using namespace gpu; + +std::atomic Buffer::_bufferCPUCount{ 0 }; +std::atomic Buffer::_bufferCPUMemoryUsage{ 0 }; + +void Buffer::updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { + if (prevObjectSize == newObjectSize) { + return; + } + if (prevObjectSize > newObjectSize) { + _bufferCPUMemoryUsage.fetch_sub(prevObjectSize - newObjectSize); + } else { + _bufferCPUMemoryUsage.fetch_add(newObjectSize - prevObjectSize); + } +} + +uint32_t Buffer::getBufferCPUCount() { + return _bufferCPUCount.load(); +} + +Buffer::Size Buffer::getBufferCPUMemoryUsage() { + return _bufferCPUMemoryUsage.load(); +} + +uint32_t Buffer::getBufferGPUCount() { + return Context::getBufferGPUCount(); +} + +Buffer::Size Buffer::getBufferGPUMemoryUsage() { + return Context::getBufferGPUMemoryUsage(); +} + +Buffer::Buffer(Size pageSize) : + _pages(pageSize) { + _bufferCPUCount++; +} + +Buffer::Buffer(Size size, const Byte* bytes, Size pageSize) : Buffer(pageSize) { + setData(size, bytes); +} + +Buffer::Buffer(const Buffer& buf) : Buffer(buf._pages._pageSize) { + setData(buf.getSize(), buf.getData()); +} + +Buffer& Buffer::operator=(const Buffer& buf) { + const_cast(_pages._pageSize) = buf._pages._pageSize; + setData(buf.getSize(), buf.getData()); + return (*this); +} + +Buffer::~Buffer() { + _bufferCPUCount--; + Buffer::updateBufferCPUMemoryUsage(_sysmem.getSize(), 0); +} + +Buffer::Size Buffer::resize(Size size) { + _end = size; + auto prevSize = _sysmem.getSize(); + if (prevSize < size) { + _sysmem.resize(_pages.accommodate(_end)); + Buffer::updateBufferCPUMemoryUsage(prevSize, _sysmem.getSize()); + } + return _end; +} + +void Buffer::markDirty(Size offset, Size bytes) { + if (!bytes) { + return; + } + + _pages.markRegion(offset, bytes); +} + +extern bool isRenderThread(); + +Buffer::Update::Update(const Update& other) : + buffer(other.buffer), + updateNumber(other.updateNumber), + size(other.size), + dirtyPages(other.dirtyPages), + dirtyData(other.dirtyData) { } + +Buffer::Update::Update(Update&& other) : + buffer(other.buffer), + updateNumber(other.updateNumber), + size(other.size), + dirtyPages(std::move(other.dirtyPages)), + dirtyData(std::move(other.dirtyData)) { } + +Buffer::Update::Update(const Buffer& parent) : buffer(parent) { + const auto pageSize = buffer._pages._pageSize; + updateNumber = ++buffer._getUpdateCount; + size = buffer._sysmem.getSize(); + dirtyPages = buffer._pages.getMarkedPages(); + dirtyData.resize(dirtyPages.size() * pageSize, 0); + for (Size i = 0; i < dirtyPages.size(); ++i) { + Size page = dirtyPages[i]; + Size sourceOffset = page * pageSize; + Size destOffset = i * pageSize; + assert(dirtyData.size() >= (destOffset + pageSize)); + assert(buffer._sysmem.getSize() >= (sourceOffset + pageSize)); + memcpy(dirtyData.data() + destOffset, buffer._sysmem.readData() + sourceOffset, pageSize); + } +} + +void Buffer::Update::apply() const { + // Make sure we're loaded in order + ++buffer._applyUpdateCount; + assert(buffer._applyUpdateCount.load() == updateNumber); + const auto pageSize = buffer._pages._pageSize; + buffer._renderSysmem.resize(size); + buffer._renderPages.accommodate(size); + for (Size i = 0; i < dirtyPages.size(); ++i) { + Size page = dirtyPages[i]; + Size sourceOffset = i * pageSize; + assert(dirtyData.size() >= (sourceOffset + pageSize)); + Size destOffset = page * pageSize; + assert(buffer._renderSysmem.getSize() >= (destOffset + pageSize)); + memcpy(buffer._renderSysmem.editData() + destOffset, dirtyData.data() + sourceOffset, pageSize); + buffer._renderPages.markPage(page); + } +} + +Buffer::Update Buffer::getUpdate() const { + return Update(*this); +} + +void Buffer::applyUpdate(const Update& update) { + update.apply(); +} + +void Buffer::flush() const { + ++_getUpdateCount; + ++_applyUpdateCount; + _renderPages = _pages; + _renderSysmem.resize(_sysmem.getSize()); + auto dirtyPages = _pages.getMarkedPages(); + for (Size page : dirtyPages) { + Size offset = page * _pages._pageSize; + memcpy(_renderSysmem.editData() + offset, _sysmem.readData() + offset, _pages._pageSize); + } +} + +Buffer::Size Buffer::setData(Size size, const Byte* data) { + resize(size); + setSubData(0, size, data); + return _end; +} + +Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) { + auto changedBytes = editSysmem().setSubData(offset, size, data); + if (changedBytes) { + markDirty(offset, changedBytes); + } + return changedBytes; +} + +Buffer::Size Buffer::append(Size size, const Byte* data) { + auto offset = _end; + resize(_end + size); + setSubData(offset, size, data); + return _end; +} + +Buffer::Size Buffer::getSize() const { + Q_ASSERT(getSysmem().getSize() >= _end); + return _end; +} + +const Element BufferView::DEFAULT_ELEMENT = Element( gpu::SCALAR, gpu::UINT8, gpu::RAW ); + +BufferView::BufferView() : +BufferView(DEFAULT_ELEMENT) {} + +BufferView::BufferView(const Element& element) : + BufferView(BufferPointer(), element) {} + +BufferView::BufferView(Buffer* newBuffer, const Element& element) : + BufferView(BufferPointer(newBuffer), element) {} + +BufferView::BufferView(const BufferPointer& buffer, const Element& element) : + BufferView(buffer, DEFAULT_OFFSET, buffer ? buffer->getSize() : 0, element.getSize(), element) {} + +BufferView::BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element) : + BufferView(buffer, offset, size, element.getSize(), element) {} + +BufferView::BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element) : + _buffer(buffer), + _offset(offset), + _size(size), + _element(element), + _stride(stride) {} diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h new file mode 100644 index 0000000000..22307ed697 --- /dev/null +++ b/libraries/gpu/src/gpu/Buffer.h @@ -0,0 +1,385 @@ +// +// Created by Sam Gateau on 10/8/2014. +// Split from Resource.h/Resource.cpp by Bradley Austin Davis on 2016/08/07 +// 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 +// +#ifndef hifi_gpu_Buffer_h +#define hifi_gpu_Buffer_h + +#include + +#if _DEBUG +#include +#endif + +#include "Forward.h" +#include "Format.h" +#include "Resource.h" +#include "Sysmem.h" +#include "PageManager.h" + +namespace gpu { + +class Buffer : public Resource { + static std::atomic _bufferCPUCount; + static std::atomic _bufferCPUMemoryUsage; + static void updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); + +public: + using Flag = PageManager::Flag; + + class Update { + public: + Update(const Buffer& buffer); + Update(const Update& other); + Update(Update&& other); + void apply() const; + + private: + const Buffer& buffer; + size_t updateNumber; + Size size; + PageManager::Pages dirtyPages; + std::vector dirtyData; + }; + + static uint32_t getBufferCPUCount(); + static Size getBufferCPUMemoryUsage(); + static uint32_t getBufferGPUCount(); + static Size getBufferGPUMemoryUsage(); + + Buffer(Size pageSize = PageManager::DEFAULT_PAGE_SIZE); + Buffer(Size size, const Byte* bytes, Size pageSize = PageManager::DEFAULT_PAGE_SIZE); + Buffer(const Buffer& buf); // deep copy of the sysmem buffer + Buffer& operator=(const Buffer& buf); // deep copy of the sysmem buffer + ~Buffer(); + + // The size in bytes of data stored in the buffer + Size getSize() const override; + template + Size getTypedSize() const { return getSize() / sizeof(T); }; + + const Byte* getData() const { return getSysmem().readData(); } + + // Resize the buffer + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + // \return the size of the buffer + Size setData(Size size, const Byte* data); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + // \return the number of bytes copied + Size setSubData(Size offset, Size size, const Byte* data); + + template + Size setSubData(Size index, const T& t) { + Size offset = index * sizeof(T); + Size size = sizeof(T); + return setSubData(offset, size, reinterpret_cast(&t)); + } + + template + Size setSubData(Size index, const std::vector& t) { + if (t.empty()) { + return 0; + } + Size offset = index * sizeof(T); + Size size = t.size() * sizeof(T); + return setSubData(offset, size, reinterpret_cast(&t[0])); + } + + // Append new data at the end of the current buffer + // do a resize( size + getSize) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + template + Size append(const T& t) { + return append(sizeof(t), reinterpret_cast(&t)); + } + + template + Size append(const std::vector& t) { + if (t.empty()) { + return _end; + } + return append(sizeof(T) * t.size(), reinterpret_cast(&t[0])); + } + + + const GPUObjectPointer gpuObject {}; + + // Access the sysmem object, limited to ourselves and GPUObject derived classes + const Sysmem& getSysmem() const { return _sysmem; } + + bool isDirty() const { + return _pages(PageManager::DIRTY); + } + + void applyUpdate(const Update& update); + + // Main thread operation to say that the buffer is ready to be used as a frame + Update getUpdate() const; + +protected: + // For use by the render thread to avoid the intermediate step of getUpdate/applyUpdate + void flush() const; + + // FIXME don't maintain a second buffer continuously. We should be able to apply updates + // directly to the GL object and discard _renderSysmem and _renderPages + mutable PageManager _renderPages; + mutable Sysmem _renderSysmem; + + mutable std::atomic _getUpdateCount { 0 }; + mutable std::atomic _applyUpdateCount { 0 }; + + void markDirty(Size offset, Size bytes); + + template + void markDirty(Size index, Size count = 1) { + markDirty(sizeof(T) * index, sizeof(T) * count); + } + + Sysmem& editSysmem() { return _sysmem; } + Byte* editData() { return editSysmem().editData(); } + + mutable PageManager _pages; + Size _end{ 0 }; + Sysmem _sysmem; + + + friend class BufferView; + friend class Frame; + friend class Batch; + + // FIXME find a more generic way to do this. + friend class gl::GLBackend; + friend class gl::GLBuffer; + friend class gl41::GL41Buffer; + friend class gl45::GL45Buffer; +}; + +using BufferUpdates = std::vector; + +typedef std::shared_ptr BufferPointer; +typedef std::vector< BufferPointer > Buffers; + + +class BufferView { +protected: + static const Resource::Size DEFAULT_OFFSET{ 0 }; + static const Element DEFAULT_ELEMENT; + +public: + using Size = Resource::Size; + using Index = int; + + BufferPointer _buffer; + Size _offset; + Size _size; + Element _element; + uint16 _stride; + + BufferView(const BufferView& view) = default; + BufferView& operator=(const BufferView& view) = default; + + BufferView(); + BufferView(const Element& element); + BufferView(Buffer* newBuffer, const Element& element = DEFAULT_ELEMENT); + BufferView(const BufferPointer& buffer, const Element& element = DEFAULT_ELEMENT); + BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT); + BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT); + + Size getNumElements() const { return _size / _element.getSize(); } + + //Template iterator with random access on the buffer sysmem + template + class Iterator : public std::iterator + { + public: + + Iterator(T* ptr = NULL, int stride = sizeof(T)): _ptr(ptr), _stride(stride) { } + Iterator(const Iterator& iterator) = default; + ~Iterator() {} + + Iterator& operator=(const Iterator& iterator) = default; + Iterator& operator=(T* ptr) { + _ptr = ptr; + // stride is left unchanged + return (*this); + } + + operator bool() const + { + if(_ptr) + return true; + else + return false; + } + + bool operator==(const Iterator& iterator) const { return (_ptr == iterator.getConstPtr()); } + bool operator!=(const Iterator& iterator) const { return (_ptr != iterator.getConstPtr()); } + bool operator<(const Iterator& iterator) const { return (_ptr < iterator.getConstPtr()); } + bool operator>(const Iterator& iterator) const { return (_ptr > iterator.getConstPtr()); } + + void movePtr(const Index& movement) { + auto byteptr = ((Byte*)_ptr); + byteptr += _stride * movement; + _ptr = (T*)byteptr; + } + + Iterator& operator+=(const Index& movement) { + movePtr(movement); + return (*this); + } + Iterator& operator-=(const Index& movement) { + movePtr(-movement); + return (*this); + } + Iterator& operator++() { + movePtr(1); + return (*this); + } + Iterator& operator--() { + movePtr(-1); + return (*this); + } + Iterator operator++(Index) { + auto temp(*this); + movePtr(1); + return temp; + } + Iterator operator--(Index) { + auto temp(*this); + movePtr(-1); + return temp; + } + Iterator operator+(const Index& movement) { + auto oldPtr = _ptr; + movePtr(movement); + auto temp(*this); + _ptr = oldPtr; + return temp; + } + Iterator operator-(const Index& movement) { + auto oldPtr = _ptr; + movePtr(-movement); + auto temp(*this); + _ptr = oldPtr; + return temp; + } + + Index operator-(const Iterator& iterator) { return (iterator.getPtr() - this->getPtr())/sizeof(T); } + + T& operator*(){return *_ptr;} + const T& operator*()const{return *_ptr;} + T* operator->(){return _ptr;} + + T* getPtr()const{return _ptr;} + const T* getConstPtr()const{return _ptr;} + + protected: + + T* _ptr; + int _stride; + }; + +#if 0 + // Direct memory access to the buffer contents is incompatible with the paging memory scheme + template Iterator begin() { return Iterator(&edit(0), _stride); } + template Iterator end() { return Iterator(&edit(getNum()), _stride); } +#else + template Iterator begin() const { return Iterator(&get(), _stride); } + template Iterator end() const { + // reimplement get without bounds checking + Resource::Size elementOffset = getNum() * _stride + _offset; + return Iterator((reinterpret_cast (_buffer->getData() + elementOffset)), _stride); + } +#endif + template Iterator cbegin() const { return Iterator(&get(), _stride); } + template Iterator cend() const { + // reimplement get without bounds checking + Resource::Size elementOffset = getNum() * _stride + _offset; + return Iterator((reinterpret_cast (_buffer->getData() + elementOffset)), _stride); + } + + // the number of elements of the specified type fitting in the view size + template Index getNum() const { + return Index(_size / _stride); + } + + template const T& get() const { + #if _DEBUG + if (!_buffer) { + qDebug() << "Accessing null gpu::buffer!"; + } + if (sizeof(T) > (_buffer->getSize() - _offset)) { + qDebug() << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset); + } + if (sizeof(T) > _size) { + qDebug() << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size; + } + #endif + const T* t = (reinterpret_cast (_buffer->getData() + _offset)); + return *(t); + } + + template T& edit() { + #if _DEBUG + if (!_buffer) { + qDebug() << "Accessing null gpu::buffer!"; + } + if (sizeof(T) > (_buffer->getSize() - _offset)) { + qDebug() << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset); + } + if (sizeof(T) > _size) { + qDebug() << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size; + } + #endif + _buffer->markDirty(_offset, sizeof(T)); + T* t = (reinterpret_cast (_buffer->editData() + _offset)); + return *(t); + } + + template const T& get(const Index index) const { + Resource::Size elementOffset = index * _stride + _offset; + #if _DEBUG + if (!_buffer) { + qDebug() << "Accessing null gpu::buffer!"; + } + if (sizeof(T) > (_buffer->getSize() - elementOffset)) { + qDebug() << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset); + } + if (index > getNum()) { + qDebug() << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum(); + } + #endif + return *(reinterpret_cast (_buffer->getData() + elementOffset)); + } + + template T& edit(const Index index) const { + Resource::Size elementOffset = index * _stride + _offset; + #if _DEBUG + if (!_buffer) { + qDebug() << "Accessing null gpu::buffer!"; + } + if (sizeof(T) > (_buffer->getSize() - elementOffset)) { + qDebug() << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset); + } + if (index > getNum()) { + qDebug() << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum(); + } + #endif + _buffer->markDirty(elementOffset, sizeof(T)); + return *(reinterpret_cast (_buffer->editData() + elementOffset)); + } +}; + +}; + +#endif diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index ff43491133..68a6be1fe5 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -18,15 +18,8 @@ std::once_flag Context::_initialized; Context::Context() { if (_createBackendCallback) { - _backend.reset(_createBackendCallback()); + _backend = _createBackendCallback(); } - - _frameHandler = [this](Frame& frame){ - for (size_t i = 0; i < frame.batches.size(); ++i) { - _backend->_stereo = frame.stereoStates[i]; - _backend->render(frame.batches[i]); - } - }; } Context::Context(const Context& context) { @@ -35,42 +28,56 @@ Context::Context(const Context& context) { Context::~Context() { } -void Context::setFrameHandler(FrameHandler handler) { - _frameHandler = handler; -} - -#define DEFERRED_RENDERING - -void Context::beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose) { - _currentFrame = Frame(); - _currentFrame.framebuffer = outputFramebuffer; - _currentFrame.pose = renderPose; +void Context::beginFrame(const glm::mat4& renderPose) { + assert(!_frameActive); _frameActive = true; + _currentFrame = std::make_shared(); + _currentFrame->pose = renderPose; } -void Context::append(Batch& batch) { +void Context::appendFrameBatch(Batch& batch) { if (!_frameActive) { qWarning() << "Batch executed outside of frame boundaries"; + return; } -#ifdef DEFERRED_RENDERING - _currentFrame.batches.emplace_back(batch); - _currentFrame.stereoStates.emplace_back(_stereo); -#else - _backend->_stereo = _stereo; - _backend->render(batch); -#endif + _currentFrame->batches.push_back(batch); } -void Context::endFrame() { -#ifdef DEFERRED_RENDERING - if (_frameHandler) { - _frameHandler(_currentFrame); - } -#endif - _currentFrame = Frame(); +FramePointer Context::endFrame() { + assert(_frameActive); + auto result = _currentFrame; + _currentFrame.reset(); _frameActive = false; + + result->stereoState = _stereo; + result->finish(); + return result; } +void Context::executeBatch(Batch& batch) const { + batch.flush(); + _backend->render(batch); +} + +void Context::recycle() const { + _backend->recycle(); +} + +void Context::consumeFrameUpdates(const FramePointer& frame) const { + frame->preRender(); +} + +void Context::executeFrame(const FramePointer& frame) const { + // FIXME? probably not necessary, but safe + consumeFrameUpdates(frame); + _backend->setStereoState(frame->stereoState); + { + // Execute the frame rendering commands + for (auto& batch : frame->batches) { + _backend->render(batch); + } + } +} bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { if (shader.isProgram() && _makeProgramCallback) { @@ -111,16 +118,10 @@ void Context::getStereoViews(mat4* eyeViews) const { } } -void Context::syncCache() { - PROFILE_RANGE(__FUNCTION__); - _backend->syncCache(); -} - void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) { _backend->downloadFramebuffer(srcFramebuffer, region, destImage); } - void Context::getStats(ContextStats& stats) const { _backend->getStats(stats); } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 5f894318f2..42b81606db 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -51,9 +51,11 @@ class Backend { public: virtual~ Backend() {}; - virtual void render(Batch& batch) = 0; + void setStereoState(const StereoState& stereo) { _stereo = stereo; } + virtual void render(const Batch& batch) = 0; virtual void syncCache() = 0; + virtual void recycle() const = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; // UBO class... layout MUST match the layout in Transform.slh @@ -122,7 +124,7 @@ protected: class Context { public: using Size = Resource::Size; - typedef Backend* (*CreateBackend)(); + typedef BackendPointer (*CreateBackend)(); typedef bool (*MakeProgram)(Shader& shader, const Shader::BindingSet& bindings); @@ -139,10 +141,46 @@ public: Context(); ~Context(); - void setFrameHandler(FrameHandler handler); - void beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose = glm::mat4()); - void append(Batch& batch); - void endFrame(); + void beginFrame(const glm::mat4& renderPose = glm::mat4()); + void appendFrameBatch(Batch& batch); + FramePointer endFrame(); + + // MUST only be called on the rendering thread + // + // Handle any pending operations to clean up (recycle / deallocate) resources no longer in use + void recycle() const; + + // MUST only be called on the rendering thread + // + // Execute a batch immediately, rather than as part of a frame + void executeBatch(Batch& batch) const; + + // MUST only be called on the rendering thread + // + // Executes a frame, applying any updates contained in the frame batches to the rendering + // thread shadow copies. Either executeFrame or consumeFrameUpdates MUST be called on every frame + // generated, IN THE ORDER they were generated. + void executeFrame(const FramePointer& frame) const; + + // MUST only be called on the rendering thread. + // + // Consuming a frame applies any updates queued from the recording thread and applies them to the + // shadow copy used by the rendering thread. + // + // EVERY frame generated MUST be consumed, regardless of whether the frame is actually executed, + // or the buffer shadow copies can become unsynced from the recording thread copies. + // + // Consuming a frame is idempotent, as the frame encapsulates the updates and clears them out as + // it applies them, so calling it more than once on a given frame will have no effect after the + // first time + // + // + // This is automatically called by executeFrame, so you only need to call it if you + // have frames you aren't going to otherwise execute, for instance when a display plugin is + // being disabled, or in the null display plugin where no rendering actually occurs + void consumeFrameUpdates(const FramePointer& frame) const; + + const BackendPointer& getBackend() const { return _backend; } void enableStereo(bool enable = true); bool isStereo(); @@ -150,7 +188,6 @@ public: void setStereoViews(const mat4 eyeViews[2]); void getStereoProjections(mat4* eyeProjections) const; void getStereoViews(mat4* eyeViews) const; - void syncCache(); // Downloading the Framebuffer is a synchronous action that is not efficient. // It s here for convenience to easily capture a snapshot @@ -171,10 +208,9 @@ public: protected: Context(const Context& context); - std::unique_ptr _backend; + std::shared_ptr _backend; bool _frameActive { false }; - Frame _currentFrame; - FrameHandler _frameHandler; + FramePointer _currentFrame; StereoState _stereo; // This function can only be called by "static Shader::makeProgram()" @@ -219,7 +255,7 @@ template void doInBatch(std::shared_ptr context, F f) { gpu::Batch batch; f(batch); - context->append(batch); + context->appendFrameBatch(batch); } }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index bec218d1fd..ad630e8e43 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -20,23 +20,6 @@ namespace gpu { class Backend; -class GPUObject { -public: - virtual ~GPUObject() = default; -}; - -class GPUObjectPointer { -private: - using GPUObjectUniquePointer = std::unique_ptr; - - // This shouldn't be used by anything else than the Backend class with the proper casting. - mutable GPUObjectUniquePointer _gpuObject; - void setGPUObject(GPUObject* gpuObject) const { _gpuObject.reset(gpuObject); } - GPUObject* getGPUObject() const { return _gpuObject.get(); } - - friend class Backend; -}; - // Description of a scalar type enum Type { diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index 3b04b17d87..b3e2d6f8a8 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -11,20 +11,23 @@ #include #include +#include #include -#include #include namespace gpu { + using Mutex = std::mutex; + using Lock = std::unique_lock; + class Batch; class Backend; + using BackendPointer = std::shared_ptr; class Context; using ContextPointer = std::shared_ptr; class GPUObject; class Frame; using FramePointer = std::shared_ptr; - using FrameHandler = std::function; using Stamp = int; using uint32 = uint32_t; @@ -36,6 +39,8 @@ namespace gpu { using Byte = uint8; using Size = size_t; + static const Size INVALID_SIZE = (Size)-1; + using Offset = size_t; using Offsets = std::vector; @@ -95,16 +100,36 @@ namespace gpu { Mat4 _eyeProjections[2]; }; + class GPUObject { + public: + virtual ~GPUObject() = default; + }; + + class GPUObjectPointer { + private: + using GPUObjectUniquePointer = std::unique_ptr; + + // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObjectUniquePointer _gpuObject; + void setGPUObject(GPUObject* gpuObject) const { _gpuObject.reset(gpuObject); } + GPUObject* getGPUObject() const { return _gpuObject.get(); } + + friend class Backend; + }; + namespace gl { + class GLBackend; class GLBuffer; } namespace gl41 { class GL41Backend; + class GL41Buffer; } namespace gl45 { class GL45Backend; + class GL45Buffer; } } diff --git a/libraries/gpu/src/gpu/Frame.cpp b/libraries/gpu/src/gpu/Frame.cpp new file mode 100644 index 0000000000..4854559d61 --- /dev/null +++ b/libraries/gpu/src/gpu/Frame.cpp @@ -0,0 +1,37 @@ +// +// Created by Bradley Austin Davis on 2016/07/26 +// Copyright 2013-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 "Frame.h" +#include + +using namespace gpu; + +Frame::~Frame() { + if (framebuffer && framebufferRecycler) { + framebufferRecycler(framebuffer); + framebuffer.reset(); + } + + assert(bufferUpdates.empty()); + if (!bufferUpdates.empty()) { + qFatal("Buffer sync error... frame destroyed without buffer updates being applied"); + } +} + +void Frame::finish() { + for (Batch& batch : batches) { + batch.finishFrame(bufferUpdates); + } +} + +void Frame::preRender() { + for (auto& update : bufferUpdates) { + update.apply(); + } + bufferUpdates.clear(); +} diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index ed5e2ea179..3c6fed9393 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -8,20 +8,44 @@ #ifndef hifi_gpu_Frame_h #define hifi_gpu_Frame_h +#include + #include "Forward.h" +#include "Batch.h" +#include "Resource.h" namespace gpu { -class Frame { -public: - /// The sensor pose used for rendering the frame, only applicable for HMDs - glm::mat4 pose; - /// The collection of batches which make up the frame - std::vector batches; - std::vector stereoStates; - /// The destination framebuffer in which the results will be placed - FramebufferPointer framebuffer; -}; + class Frame { + friend class Context; + + public: + virtual ~Frame(); + + using Batches = std::vector; + using FramebufferRecycler = std::function; + using OverlayRecycler = std::function; + + StereoState stereoState; + uint32_t frameIndex{ 0 }; + /// The sensor pose used for rendering the frame, only applicable for HMDs + Mat4 pose; + /// The collection of batches which make up the frame + Batches batches; + /// The main thread updates to buffers that are applicable for this frame. + BufferUpdates bufferUpdates; + /// The destination framebuffer in which the results will be placed + FramebufferPointer framebuffer; + /// The destination texture containing the 2D overlay + TexturePointer overlay; + /// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE + FramebufferRecycler framebufferRecycler; + + protected: + // Should be called once per frame, on the recording thred + void finish(); + void preRender(); + }; }; diff --git a/libraries/gpu/src/gpu/PageManager.cpp b/libraries/gpu/src/gpu/PageManager.cpp new file mode 100644 index 0000000000..ff7ab799ba --- /dev/null +++ b/libraries/gpu/src/gpu/PageManager.cpp @@ -0,0 +1,139 @@ +// +// Created by Sam Gateau on 10/8/2014. +// Split from Resource.h/Resource.cpp by Bradley Austin Davis on 2016/08/07 +// 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 "PageManager.h" + +using namespace gpu; + +PageManager::PageManager(Size pageSize) : _pageSize(pageSize) {} + +PageManager& PageManager::operator=(const PageManager& other) { + assert(other._pageSize == _pageSize); + _pages = other._pages; + _flags = other._flags; + return *this; +} + +PageManager::operator bool() const { + return (*this)(DIRTY); +} + +bool PageManager::operator()(uint8 desiredFlags) const { + return (desiredFlags == (_flags & desiredFlags)); +} + +void PageManager::markPage(Size index, uint8 markFlags) { + assert(_pages.size() > index); + _pages[index] |= markFlags; + _flags |= markFlags; +} + +void PageManager::markRegion(Size offset, Size bytes, uint8 markFlags) { + if (!bytes) { + return; + } + _flags |= markFlags; + // Find the starting page + Size startPage = (offset / _pageSize); + // Non-zero byte count, so at least one page is dirty + Size pageCount = 1; + // How much of the page is after the offset? + Size remainder = _pageSize - (offset % _pageSize); + // If there are more bytes than page space remaining, we need to increase the page count + if (bytes > remainder) { + // Get rid of the amount that will fit in the current page + bytes -= remainder; + + pageCount += (bytes / _pageSize); + if (bytes % _pageSize) { + ++pageCount; + } + } + + // Mark the pages dirty + for (Size i = 0; i < pageCount; ++i) { + _pages[i + startPage] |= markFlags; + } +} + +Size PageManager::getPageCount(uint8_t desiredFlags) const { + Size result = 0; + for (auto pageFlags : _pages) { + if (desiredFlags == (pageFlags & desiredFlags)) { + ++result; + } + } + return result; +} + +Size PageManager::getSize(uint8_t desiredFlags) const { + return getPageCount(desiredFlags) * _pageSize; +} + +void PageManager::setPageCount(Size count) { + _pages.resize(count); +} + +Size PageManager::getRequiredPageCount(Size size) const { + Size result = size / _pageSize; + if (size % _pageSize) { + ++result; + } + return result; +} + +Size PageManager::getRequiredSize(Size size) const { + return getRequiredPageCount(size) * _pageSize; +} + +Size PageManager::accommodate(Size size) { + Size newPageCount = getRequiredPageCount(size); + Size newSize = newPageCount * _pageSize; + _pages.resize(newPageCount, 0); + return newSize; +} + +// Get pages with the specified flags, optionally clearing the flags as we go +PageManager::Pages PageManager::getMarkedPages(uint8_t desiredFlags, bool clear) { + Pages result; + if (desiredFlags == (_flags & desiredFlags)) { + _flags &= ~desiredFlags; + result.reserve(_pages.size()); + for (Size i = 0; i < _pages.size(); ++i) { + if (desiredFlags == (_pages[i] & desiredFlags)) { + result.push_back(i); + if (clear) { + _pages[i] &= ~desiredFlags; + } + } + } + } + return result; +} + +bool PageManager::getNextTransferBlock(Size& outOffset, Size& outSize, Size& currentPage) { + Size pageCount = _pages.size(); + // Advance to the first dirty page + while (currentPage < pageCount && (0 == (DIRTY & _pages[currentPage]))) { + ++currentPage; + } + + // If we got to the end, we're done + if (currentPage >= pageCount) { + return false; + } + + // Advance to the next clean page + outOffset = static_cast(currentPage * _pageSize); + while (currentPage < pageCount && (0 != (DIRTY & _pages[currentPage]))) { + _pages[currentPage] &= ~DIRTY; + ++currentPage; + } + outSize = static_cast((currentPage * _pageSize) - outOffset); + return true; +} diff --git a/libraries/gpu/src/gpu/PageManager.h b/libraries/gpu/src/gpu/PageManager.h new file mode 100644 index 0000000000..5eb8a133bf --- /dev/null +++ b/libraries/gpu/src/gpu/PageManager.h @@ -0,0 +1,57 @@ +// +// Created by Sam Gateau on 10/8/2014. +// Split from Resource.h/Resource.cpp by Bradley Austin Davis on 2016/08/07 +// 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 +// +#ifndef hifi_gpu_PageManager_h +#define hifi_gpu_PageManager_h + +#include "Forward.h" + +#include + +namespace gpu { + +struct PageManager { + static const Size DEFAULT_PAGE_SIZE = 4096; + + // Currently only one flag... 'dirty' + enum Flag { + DIRTY = 0x01, + }; + + using FlagType = uint8_t; + + // A list of flags + using Vector = std::vector; + // A list of pages + using Pages = std::vector; + + Vector _pages; + uint8 _flags{ 0 }; + const Size _pageSize; + + PageManager(Size pageSize = DEFAULT_PAGE_SIZE); + PageManager& operator=(const PageManager& other); + + operator bool() const; + bool operator()(uint8 desiredFlags) const; + void markPage(Size index, uint8 markFlags = DIRTY); + void markRegion(Size offset, Size bytes, uint8 markFlags = DIRTY); + Size getPageCount(uint8_t desiredFlags = DIRTY) const; + Size getSize(uint8_t desiredFlags = DIRTY) const; + void setPageCount(Size count); + Size getRequiredPageCount(Size size) const; + Size getRequiredSize(Size size) const; + Size accommodate(Size size); + // Get pages with the specified flags, optionally clearing the flags as we go + Pages getMarkedPages(uint8_t desiredFlags = DIRTY, bool clear = true); + bool getNextTransferBlock(Size& outOffset, Size& outSize, Size& currentPage); +}; + +}; + +#endif diff --git a/libraries/gpu/src/gpu/Resource.cpp b/libraries/gpu/src/gpu/Resource.cpp index 7dbe662cbc..34a14dd3e1 100644 --- a/libraries/gpu/src/gpu/Resource.cpp +++ b/libraries/gpu/src/gpu/Resource.cpp @@ -10,357 +10,7 @@ // #include "Resource.h" -#include - -#include -#include -#include - -#include "Context.h" - using namespace gpu; -class AllocationDebugger { -public: - void operator+=(size_t size) { - _allocatedMemory += size; - maybeReport(); - } - - void operator-=(size_t size) { - _allocatedMemory -= size; - maybeReport(); - } - -private: - QString formatSize(size_t size) { - float num = size; - QStringList list; - list << "KB" << "MB" << "GB" << "TB"; - - QStringListIterator i(list); - QString unit("bytes"); - - while (num >= K && i.hasNext()) { - unit = i.next(); - num /= K; - } - return QString().setNum(num, 'f', 2) + " " + unit; - } - - void maybeReport() { - auto now = usecTimestampNow(); - if (now - _lastReportTime < MAX_REPORT_FREQUENCY) { - return; - } - size_t current = _allocatedMemory; - size_t last = _lastReportedMemory; - size_t delta = (current > last) ? (current - last) : (last - current); - if (delta > MIN_REPORT_DELTA) { - _lastReportTime = now; - _lastReportedMemory = current; - qDebug() << "Total allocation " << formatSize(current); - } - } - - std::atomic _allocatedMemory; - std::atomic _lastReportedMemory; - std::atomic _lastReportTime; - - static const float K; - // Report changes of 5 megabytes - static const size_t MIN_REPORT_DELTA = 1024 * 1024 * 5; - // Report changes no more frequently than every 15 seconds - static const uint64_t MAX_REPORT_FREQUENCY = USECS_PER_SECOND * 15; -}; - -const float AllocationDebugger::K = 1024.0f; - -static AllocationDebugger allocationDebugger; - -Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { - allocationDebugger += size; - if ( !dataAllocated ) { - qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; - return NOT_ALLOCATED; - } - - // Try to allocate if needed - Size newSize = 0; - if (size > 0) { - // Try allocating as much as the required size + one block of memory - newSize = size; - (*dataAllocated) = new (std::nothrow) Byte[newSize]; - // Failed? - if (!(*dataAllocated)) { - qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; - return NOT_ALLOCATED; - } - } - - // Return what's actually allocated - return newSize; -} - -void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { - allocationDebugger -= size; - if (dataAllocated) { - delete[] dataAllocated; - } -} - -Resource::Sysmem::Sysmem() {} - -Resource::Sysmem::Sysmem(Size size, const Byte* bytes) { - if (size > 0 && bytes) { - setData(_size, bytes); - } -} - -Resource::Sysmem::Sysmem(const Sysmem& sysmem) { - if (sysmem.getSize() > 0) { - allocate(sysmem._size); - setData(_size, sysmem._data); - } -} - -Resource::Sysmem& Resource::Sysmem::operator=(const Sysmem& sysmem) { - setData(sysmem.getSize(), sysmem.readData()); - return (*this); -} - -Resource::Sysmem::~Sysmem() { - deallocateMemory( _data, _size ); - _data = NULL; - _size = 0; -} - -Resource::Size Resource::Sysmem::allocate(Size size) { - if (size != _size) { - Byte* newData = NULL; - Size newSize = 0; - if (size > 0) { - Size allocated = allocateMemory(&newData, size); - if (allocated == NOT_ALLOCATED) { - // early exit because allocation failed - return 0; - } - newSize = allocated; - } - // Allocation was successful, can delete previous data - deallocateMemory(_data, _size); - _data = newData; - _size = newSize; - _stamp++; - } - return _size; -} - -Resource::Size Resource::Sysmem::resize(Size size) { - if (size != _size) { - Byte* newData = NULL; - Size newSize = 0; - if (size > 0) { - Size allocated = allocateMemory(&newData, size); - if (allocated == NOT_ALLOCATED) { - // early exit because allocation failed - return _size; - } - newSize = allocated; - // Restore back data from old buffer in the new one - if (_data) { - Size copySize = ((newSize < _size)? newSize: _size); - memcpy( newData, _data, copySize); - } - } - // Reallocation was successful, can delete previous data - deallocateMemory(_data, _size); - _data = newData; - _size = newSize; - _stamp++; - } - return _size; -} - -Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) { - if (allocate(size) == size) { - if (size && bytes) { - memcpy( _data, bytes, _size ); - } - } - return _size; -} - -Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { - if (size && ((offset + size) <= getSize()) && bytes) { - memcpy( _data + offset, bytes, size ); - return size; - } - return 0; -} - -Resource::Size Resource::Sysmem::append(Size size, const Byte* bytes) { - if (size > 0) { - Size oldSize = getSize(); - Size totalSize = oldSize + size; - if (resize(totalSize) == totalSize) { - return setSubData(oldSize, size, bytes); - } - } - return 0; -} - -std::atomic Buffer::_bufferCPUCount{ 0 }; -std::atomic Buffer::_bufferCPUMemoryUsage{ 0 }; - -void Buffer::updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { - if (prevObjectSize == newObjectSize) { - return; - } - if (prevObjectSize > newObjectSize) { - _bufferCPUMemoryUsage.fetch_sub(prevObjectSize - newObjectSize); - } else { - _bufferCPUMemoryUsage.fetch_add(newObjectSize - prevObjectSize); - } -} - -uint32_t Buffer::getBufferCPUCount() { - return _bufferCPUCount.load(); -} - -Buffer::Size Buffer::getBufferCPUMemoryUsage() { - return _bufferCPUMemoryUsage.load(); -} - -uint32_t Buffer::getBufferGPUCount() { - return Context::getBufferGPUCount(); -} - -Buffer::Size Buffer::getBufferGPUMemoryUsage() { - return Context::getBufferGPUMemoryUsage(); -} - -Buffer::Buffer(Size pageSize) : - _pageSize(pageSize) { - _bufferCPUCount++; -} - -Buffer::Buffer(Size size, const Byte* bytes, Size pageSize) : Buffer(pageSize) { - setData(size, bytes); -} - -Buffer::Buffer(const Buffer& buf) : Buffer(buf._pageSize) { - setData(buf.getSize(), buf.getData()); -} - -Buffer& Buffer::operator=(const Buffer& buf) { - const_cast(_pageSize) = buf._pageSize; - setData(buf.getSize(), buf.getData()); - return (*this); -} - -Buffer::~Buffer() { - _bufferCPUCount--; - Buffer::updateBufferCPUMemoryUsage(_sysmem.getSize(), 0); -} - -Buffer::Size Buffer::resize(Size size) { - _end = size; - auto prevSize = editSysmem().getSize(); - if (prevSize < size) { - auto newPages = getRequiredPageCount(); - auto newSize = newPages * _pageSize; - editSysmem().resize(newSize); - // All new pages start off as clean, because they haven't been populated by data - _pages.resize(newPages, 0); - Buffer::updateBufferCPUMemoryUsage(prevSize, newSize); - } - return _end; -} - -void Buffer::markDirty(Size offset, Size bytes) { - if (!bytes) { - return; - } - _flags |= DIRTY; - // Find the starting page - Size startPage = (offset / _pageSize); - // Non-zero byte count, so at least one page is dirty - Size pageCount = 1; - // How much of the page is after the offset? - Size remainder = _pageSize - (offset % _pageSize); - // If there are more bytes than page space remaining, we need to increase the page count - if (bytes > remainder) { - // Get rid of the amount that will fit in the current page - bytes -= remainder; - - pageCount += (bytes / _pageSize); - if (bytes % _pageSize) { - ++pageCount; - } - } - - // Mark the pages dirty - for (Size i = 0; i < pageCount; ++i) { - _pages[i + startPage] |= DIRTY; - } -} - - -Buffer::Size Buffer::setData(Size size, const Byte* data) { - resize(size); - setSubData(0, size, data); - return _end; -} - -Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) { - auto changedBytes = editSysmem().setSubData(offset, size, data); - if (changedBytes) { - markDirty(offset, changedBytes); - } - return changedBytes; -} - -Buffer::Size Buffer::append(Size size, const Byte* data) { - auto offset = _end; - resize(_end + size); - setSubData(offset, size, data); - return _end; -} - -Buffer::Size Buffer::getSize() const { - Q_ASSERT(getSysmem().getSize() >= _end); - return _end; -} - -Buffer::Size Buffer::getRequiredPageCount() const { - Size result = _end / _pageSize; - if (_end % _pageSize) { - ++result; - } - return result; -} - -const Element BufferView::DEFAULT_ELEMENT = Element( gpu::SCALAR, gpu::UINT8, gpu::RAW ); - -BufferView::BufferView() : -BufferView(DEFAULT_ELEMENT) {} - -BufferView::BufferView(const Element& element) : - BufferView(BufferPointer(), element) {} - -BufferView::BufferView(Buffer* newBuffer, const Element& element) : - BufferView(BufferPointer(newBuffer), element) {} - -BufferView::BufferView(const BufferPointer& buffer, const Element& element) : - BufferView(buffer, DEFAULT_OFFSET, buffer ? buffer->getSize() : 0, element.getSize(), element) {} - -BufferView::BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element) : - BufferView(buffer, offset, size, element.getSize(), element) {} - -BufferView::BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element) : - _buffer(buffer), - _offset(offset), - _size(size), - _element(element), - _stride(stride) {} +Resource::Resource() {} +Resource::~Resource() {} diff --git a/libraries/gpu/src/gpu/Resource.h b/libraries/gpu/src/gpu/Resource.h index 10c83dfb0e..c65723c854 100644 --- a/libraries/gpu/src/gpu/Resource.h +++ b/libraries/gpu/src/gpu/Resource.h @@ -11,25 +11,14 @@ #ifndef hifi_gpu_Resource_h #define hifi_gpu_Resource_h -#include - -#include "Format.h" - -#include -#include - -#include -#ifdef _DEBUG -#include -#endif +#include "Forward.h" namespace gpu { class Resource { public: - typedef size_t Size; - - static const Size NOT_ALLOCATED = (Size)-1; + using Size = gpu::Size; + static const Size NOT_ALLOCATED = INVALID_SIZE; // The size in bytes of data stored in the resource virtual Size getSize() const = 0; @@ -47,398 +36,14 @@ public: }; protected: + Resource(); + virtual ~Resource(); +}; // Resource - Resource() {} - virtual ~Resource() {} +} - // Sysmem is the underneath cache for the data in ram of a resource. - class Sysmem { - public: +// FIXME Compatibility with headers that rely on Resource.h for the buffer and buffer view definitions +#include "Buffer.h" - Sysmem(); - Sysmem(Size size, const Byte* bytes); - Sysmem(const Sysmem& sysmem); // deep copy of the sysmem buffer - Sysmem& operator=(const Sysmem& sysmem); // deep copy of the sysmem buffer - ~Sysmem(); - - Size getSize() const { return _size; } - - // Allocate the byte array - // \param pSize The nb of bytes to allocate, if already exist, content is lost. - // \return The nb of bytes allocated, nothing if allready the appropriate size. - Size allocate(Size pSize); - - // Resize the byte array - // Keep previous data [0 to min(pSize, mSize)] - Size resize(Size pSize); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setData(Size size, const Byte* bytes ); - - // Update Sub data, - // doesn't allocate and only copy size * bytes at the offset location - // only if all fits in the existing allocated buffer - Size setSubData(Size offset, Size size, const Byte* bytes); - - // Append new data at the end of the current buffer - // do a resize( size + getSIze) and copy the new data - // \return the number of bytes copied - Size append(Size size, const Byte* data); - - // Access the byte array. - // The edit version allow to map data. - const Byte* readData() const { return _data; } - Byte* editData() { return _data; } - - template< typename T > const T* read() const { return reinterpret_cast< T* > ( _data ); } - template< typename T > T* edit() { return reinterpret_cast< T* > ( _data ); } - - // Access the current version of the sysmem, used to compare if copies are in sync - Stamp getStamp() const { return _stamp; } - - static Size allocateMemory(Byte** memAllocated, Size size); - static void deallocateMemory(Byte* memDeallocated, Size size); - - bool isAvailable() const { return (_data != 0); } - - private: - Stamp _stamp { 0 }; - Size _size { 0 }; - Byte* _data { nullptr }; - }; - -}; - -class Buffer : public Resource { - static std::atomic _bufferCPUCount; - static std::atomic _bufferCPUMemoryUsage; - static void updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); - -public: - enum Flag { - DIRTY = 0x01, - }; - - // Currently only one flag... 'dirty' - using PageFlags = std::vector; - static const Size DEFAULT_PAGE_SIZE = 4096; - static uint32_t getBufferCPUCount(); - static Size getBufferCPUMemoryUsage(); - static uint32_t getBufferGPUCount(); - static Size getBufferGPUMemoryUsage(); - - Buffer(Size pageSize = DEFAULT_PAGE_SIZE); - Buffer(Size size, const Byte* bytes, Size pageSize = DEFAULT_PAGE_SIZE); - Buffer(const Buffer& buf); // deep copy of the sysmem buffer - Buffer& operator=(const Buffer& buf); // deep copy of the sysmem buffer - ~Buffer(); - - // The size in bytes of data stored in the buffer - Size getSize() const; - const Byte* getData() const { return getSysmem().readData(); } - - // Resize the buffer - // Keep previous data [0 to min(pSize, mSize)] - Size resize(Size pSize); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - // \return the size of the buffer - Size setData(Size size, const Byte* data); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - // \return the number of bytes copied - Size setSubData(Size offset, Size size, const Byte* data); - - template - Size setSubData(Size index, const T& t) { - Size offset = index * sizeof(T); - Size size = sizeof(T); - return setSubData(offset, size, reinterpret_cast(&t)); - } - - template - Size setSubData(Size index, const std::vector& t) { - if (t.empty()) { - return 0; - } - Size offset = index * sizeof(T); - Size size = t.size() * sizeof(T); - return setSubData(offset, size, reinterpret_cast(&t[0])); - } - - // Append new data at the end of the current buffer - // do a resize( size + getSize) and copy the new data - // \return the number of bytes copied - Size append(Size size, const Byte* data); - - template - Size append(const T& t) { - return append(sizeof(t), reinterpret_cast(&t)); - } - - template - Size append(const std::vector& t) { - if (t.empty()) { - return _end; - } - return append(sizeof(T) * t.size(), reinterpret_cast(&t[0])); - } - - bool getNextTransferBlock(Size& outOffset, Size& outSize, Size& currentPage) const { - Size pageCount = _pages.size(); - // Advance to the first dirty page - while (currentPage < pageCount && (0 == (Buffer::DIRTY & _pages[currentPage]))) { - ++currentPage; - } - - // If we got to the end, we're done - if (currentPage >= pageCount) { - return false; - } - - // Advance to the next clean page - outOffset = static_cast(currentPage * _pageSize); - while (currentPage < pageCount && (0 != (Buffer::DIRTY & _pages[currentPage]))) { - _pages[currentPage] &= ~Buffer::DIRTY; - ++currentPage; - } - outSize = static_cast((currentPage * _pageSize) - outOffset); - return true; - } - - const GPUObjectPointer gpuObject {}; - - // Access the sysmem object, limited to ourselves and GPUObject derived classes - const Sysmem& getSysmem() const { return _sysmem; } - // FIXME find a better access mechanism for clearing this - mutable uint8_t _flags; -protected: - void markDirty(Size offset, Size bytes); - - template - void markDirty(Size index, Size count = 1) { - markDirty(sizeof(T) * index, sizeof(T) * count); - } - - Sysmem& editSysmem() { return _sysmem; } - Byte* editData() { return editSysmem().editData(); } - - Size getRequiredPageCount() const; - - Size _end { 0 }; - mutable PageFlags _pages; - const Size _pageSize; - Sysmem _sysmem; - - // FIXME find a more generic way to do this. - friend class gl::GLBuffer; - friend class BufferView; -}; - -typedef std::shared_ptr BufferPointer; -typedef std::vector< BufferPointer > Buffers; - - -class BufferView { -protected: - static const Resource::Size DEFAULT_OFFSET{ 0 }; - static const Element DEFAULT_ELEMENT; - -public: - using Size = Resource::Size; - using Index = int; - - BufferPointer _buffer; - Size _offset; - Size _size; - Element _element; - uint16 _stride; - - BufferView(const BufferView& view) = default; - BufferView& operator=(const BufferView& view) = default; - - BufferView(); - BufferView(const Element& element); - BufferView(Buffer* newBuffer, const Element& element = DEFAULT_ELEMENT); - BufferView(const BufferPointer& buffer, const Element& element = DEFAULT_ELEMENT); - BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT); - BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT); - - Size getNumElements() const { return _size / _element.getSize(); } - - //Template iterator with random access on the buffer sysmem - template - class Iterator : public std::iterator - { - public: - - Iterator(T* ptr = NULL, int stride = sizeof(T)): _ptr(ptr), _stride(stride) { } - Iterator(const Iterator& iterator) = default; - ~Iterator() {} - - Iterator& operator=(const Iterator& iterator) = default; - Iterator& operator=(T* ptr) { - _ptr = ptr; - // stride is left unchanged - return (*this); - } - - operator bool() const - { - if(_ptr) - return true; - else - return false; - } - - bool operator==(const Iterator& iterator) const { return (_ptr == iterator.getConstPtr()); } - bool operator!=(const Iterator& iterator) const { return (_ptr != iterator.getConstPtr()); } - - void movePtr(const Index& movement) { - auto byteptr = ((Byte*)_ptr); - byteptr += _stride * movement; - _ptr = (T*)byteptr; - } - - Iterator& operator+=(const Index& movement) { - movePtr(movement); - return (*this); - } - Iterator& operator-=(const Index& movement) { - movePtr(-movement); - return (*this); - } - Iterator& operator++() { - movePtr(1); - return (*this); - } - Iterator& operator--() { - movePtr(-1); - return (*this); - } - Iterator operator++(Index) { - auto temp(*this); - movePtr(1); - return temp; - } - Iterator operator--(Index) { - auto temp(*this); - movePtr(-1); - return temp; - } - Iterator operator+(const Index& movement) { - auto oldPtr = _ptr; - movePtr(movement); - auto temp(*this); - _ptr = oldPtr; - return temp; - } - Iterator operator-(const Index& movement) { - auto oldPtr = _ptr; - movePtr(-movement); - auto temp(*this); - _ptr = oldPtr; - return temp; - } - - Index operator-(const Iterator& iterator) { return (iterator.getPtr() - this->getPtr())/sizeof(T); } - - T& operator*(){return *_ptr;} - const T& operator*()const{return *_ptr;} - T* operator->(){return _ptr;} - - T* getPtr()const{return _ptr;} - const T* getConstPtr()const{return _ptr;} - - protected: - - T* _ptr; - int _stride; - }; - -#if 0 - // Direct memory access to the buffer contents is incompatible with the paging memory scheme - template Iterator begin() { return Iterator(&edit(0), _stride); } - template Iterator end() { return Iterator(&edit(getNum()), _stride); } -#else - template Iterator begin() const { return Iterator(&get(), _stride); } - template Iterator end() const { return Iterator(&get(getNum()), _stride); } -#endif - template Iterator cbegin() const { return Iterator(&get(), _stride); } - template Iterator cend() const { return Iterator(&get(getNum()), _stride); } - - // the number of elements of the specified type fitting in the view size - template Index getNum() const { - return Index(_size / _stride); - } - - template const T& get() const { - #if _DEBUG - if (!_buffer) { - qDebug() << "Accessing null gpu::buffer!"; - } - if (sizeof(T) > (_buffer->getSize() - _offset)) { - qDebug() << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset); - } - if (sizeof(T) > _size) { - qDebug() << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size; - } - #endif - const T* t = (reinterpret_cast (_buffer->getData() + _offset)); - return *(t); - } - - template T& edit() { - #if _DEBUG - if (!_buffer) { - qDebug() << "Accessing null gpu::buffer!"; - } - if (sizeof(T) > (_buffer->getSize() - _offset)) { - qDebug() << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset); - } - if (sizeof(T) > _size) { - qDebug() << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size; - } - #endif - _buffer->markDirty(_offset, sizeof(T)); - T* t = (reinterpret_cast (_buffer->editData() + _offset)); - return *(t); - } - - template const T& get(const Index index) const { - Resource::Size elementOffset = index * _stride + _offset; - #if _DEBUG - if (!_buffer) { - qDebug() << "Accessing null gpu::buffer!"; - } - if (sizeof(T) > (_buffer->getSize() - elementOffset)) { - qDebug() << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset); - } - if (index > getNum()) { - qDebug() << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum(); - } - #endif - return *(reinterpret_cast (_buffer->getData() + elementOffset)); - } - - template T& edit(const Index index) const { - Resource::Size elementOffset = index * _stride + _offset; - #if _DEBUG - if (!_buffer) { - qDebug() << "Accessing null gpu::buffer!"; - } - if (sizeof(T) > (_buffer->getSize() - elementOffset)) { - qDebug() << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset); - } - if (index > getNum()) { - qDebug() << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum(); - } - #endif - _buffer->markDirty(elementOffset, sizeof(T)); - return *(reinterpret_cast (_buffer->editData() + elementOffset)); - } -}; - -}; #endif diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 9072bf23a9..ebe0bc83df 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -53,19 +53,22 @@ public: int32 _location{INVALID_LOCATION}; Element _element; uint16 _resourceType{Resource::BUFFER}; + uint32 _size { 0 }; - Slot(const Slot& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType) {} - Slot(Slot&& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType) {} - Slot(const std::string& name, int32 location, const Element& element, uint16 resourceType = Resource::BUFFER) : - _name(name), _location(location), _element(element), _resourceType(resourceType) {} + Slot(const Slot& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {} + Slot(Slot&& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {} + Slot(const std::string& name, int32 location, const Element& element, uint16 resourceType = Resource::BUFFER, uint32 size = 0) : + _name(name), _location(location), _element(element), _resourceType(resourceType), _size(size) {} Slot(const std::string& name) : _name(name) {} - + Slot& operator= (const Slot& s) { _name = s._name; _location = s._location; _element = s._element; _resourceType = s._resourceType; - return (*this); } + _size = s._size; + return (*this); + } }; class Binding { diff --git a/libraries/gpu/src/gpu/Sysmem.cpp b/libraries/gpu/src/gpu/Sysmem.cpp new file mode 100644 index 0000000000..a642d40478 --- /dev/null +++ b/libraries/gpu/src/gpu/Sysmem.cpp @@ -0,0 +1,144 @@ +// +// Created by Sam Gateau on 10/8/2014. +// Split from Resource.h/Resource.cpp by Bradley Austin Davis on 2016/08/07 +// 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 "Sysmem.h" + +#include + +using namespace gpu; + +Size Sysmem::allocateMemory(Byte** dataAllocated, Size size) { + if ( !dataAllocated ) { + qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; + return NOT_ALLOCATED; + } + + // Try to allocate if needed + Size newSize = 0; + if (size > 0) { + // Try allocating as much as the required size + one block of memory + newSize = size; + (*dataAllocated) = new (std::nothrow) Byte[newSize]; + // Failed? + if (!(*dataAllocated)) { + qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; + return NOT_ALLOCATED; + } + } + + // Return what's actually allocated + return newSize; +} + +void Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { + if (dataAllocated) { + delete[] dataAllocated; + } +} + +Sysmem::Sysmem() {} + +Sysmem::Sysmem(Size size, const Byte* bytes) { + if (size > 0 && bytes) { + setData(_size, bytes); + } +} + +Sysmem::Sysmem(const Sysmem& sysmem) { + if (sysmem.getSize() > 0) { + allocate(sysmem._size); + setData(_size, sysmem._data); + } +} + +Sysmem& Sysmem::operator=(const Sysmem& sysmem) { + setData(sysmem.getSize(), sysmem.readData()); + return (*this); +} + +Sysmem::~Sysmem() { + deallocateMemory( _data, _size ); + _data = NULL; + _size = 0; +} + +Size Sysmem::allocate(Size size) { + if (size != _size) { + Byte* newData = NULL; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return 0; + } + newSize = allocated; + } + // Allocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Size Sysmem::resize(Size size) { + if (size != _size) { + Byte* newData = NULL; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return _size; + } + newSize = allocated; + // Restore back data from old buffer in the new one + if (_data) { + Size copySize = ((newSize < _size)? newSize: _size); + memcpy( newData, _data, copySize); + } + } + // Reallocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Size Sysmem::setData( Size size, const Byte* bytes ) { + if (allocate(size) == size) { + if (size && bytes) { + memcpy( _data, bytes, _size ); + } + } + return _size; +} + +Size Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { + if (size && ((offset + size) <= getSize()) && bytes) { + memcpy( _data + offset, bytes, size ); + return size; + } + return 0; +} + +Size Sysmem::append(Size size, const Byte* bytes) { + if (size > 0) { + Size oldSize = getSize(); + Size totalSize = oldSize + size; + if (resize(totalSize) == totalSize) { + return setSubData(oldSize, size, bytes); + } + } + return 0; +} + diff --git a/libraries/gpu/src/gpu/Sysmem.h b/libraries/gpu/src/gpu/Sysmem.h new file mode 100644 index 0000000000..4a8822c324 --- /dev/null +++ b/libraries/gpu/src/gpu/Sysmem.h @@ -0,0 +1,76 @@ +// +// Created by Sam Gateau on 10/8/2014. +// Split from Resource.h/Resource.cpp by Bradley Austin Davis on 2016/08/07 +// 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 +// +#ifndef hifi_gpu_Sysmem_h +#define hifi_gpu_Sysmem_h + + +#include "Forward.h" + +namespace gpu { + +// Sysmem is the underneath cache for the data in ram of a resource. +class Sysmem { +public: + static const Size NOT_ALLOCATED = INVALID_SIZE; + + Sysmem(); + Sysmem(Size size, const Byte* bytes); + Sysmem(const Sysmem& sysmem); // deep copy of the sysmem buffer + Sysmem& operator=(const Sysmem& sysmem); // deep copy of the sysmem buffer + ~Sysmem(); + + Size getSize() const { return _size; } + + // Allocate the byte array + // \param pSize The nb of bytes to allocate, if already exist, content is lost. + // \return The nb of bytes allocated, nothing if allready the appropriate size. + Size allocate(Size pSize); + + // Resize the byte array + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData(Size size, const Byte* bytes); + + // Update Sub data, + // doesn't allocate and only copy size * bytes at the offset location + // only if all fits in the existing allocated buffer + Size setSubData(Size offset, Size size, const Byte* bytes); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // Access the byte array. + // The edit version allow to map data. + const Byte* readData() const { return _data; } + Byte* editData() { return _data; } + + template< typename T > const T* read() const { return reinterpret_cast< T* > (_data); } + template< typename T > T* edit() { return reinterpret_cast< T* > (_data); } + + // Access the current version of the sysmem, used to compare if copies are in sync + Stamp getStamp() const { return _stamp; } + + static Size allocateMemory(Byte** memAllocated, Size size); + static void deallocateMemory(Byte* memDeallocated, Size size); + + bool isAvailable() const { return (_data != 0); } + +private: + Stamp _stamp{ 0 }; + Size _size{ 0 }; + Byte* _data{ nullptr }; +}; // Sysmem + +} + +#endif diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index f9a8f3b26b..93f31f61df 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -363,6 +363,7 @@ bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, co Size expectedSize = evalStoredMipSize(level, format); if (size == expectedSize) { _storage->assignMipData(level, format, size, bytes); + _maxMip = std::max(_maxMip, level); _stamp++; return true; } else if (size > expectedSize) { @@ -371,6 +372,7 @@ bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, co // We should probably consider something a bit more smart to get the correct result but for now (UI elements) // it seems to work... _storage->assignMipData(level, format, size, bytes); + _maxMip = std::max(_maxMip, level); _stamp++; return true; } @@ -700,8 +702,6 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< return false; } - const float UCHAR_TO_FLOAT = 1.0f / float(std::numeric_limits::max()); - // for each face of cube texture for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { @@ -788,12 +788,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< uint pixOffsetIndex = (x + y * width) * numComponents; // get color from texture and map to range [0, 1] - glm::vec3 clr(float(data[pixOffsetIndex]) * UCHAR_TO_FLOAT, - float(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT, - float(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT); - - // Gamma correct - clr = ColorUtils::sRGBToLinearVec3(clr); + glm::vec3 clr(ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex]), + ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex + 1]), + ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex + 2])); // scale color and add to previously accumulated coefficients sphericalHarmonicsScale(shBuffB.data(), order, @@ -885,11 +882,3 @@ Vec3u Texture::evalMipDimensions(uint16 level) const { return glm::max(dimensions, Vec3u(1)); } -std::function TEXTURE_ID_RESOLVER; - -uint32 Texture::getHardwareId() const { - if (TEXTURE_ID_RESOLVER) { - return TEXTURE_ID_RESOLVER(*this); - } - return 0; -} diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index bceb4b196a..a7ac472922 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -294,7 +294,7 @@ public: Stamp getDataStamp() const { return _storage->getStamp(); } // The theoretical size in bytes of data stored in the texture - Size getSize() const { return _size; } + Size getSize() const override { return _size; } // The actual size in bytes of data stored in the texture Size getStoredSize() const; @@ -449,8 +449,6 @@ public: const GPUObjectPointer gpuObject {}; - uint32 getHardwareId() const; - protected: std::unique_ptr< Storage > _storage; diff --git a/libraries/gpu/src/gpu/null/NullBackend.h b/libraries/gpu/src/gpu/null/NullBackend.h index 097cee27e7..c9d249aec7 100644 --- a/libraries/gpu/src/gpu/null/NullBackend.h +++ b/libraries/gpu/src/gpu/null/NullBackend.h @@ -36,7 +36,7 @@ protected: public: ~Backend() { } - void render(Batch& batch) final { } + void render(const Batch& batch) final { } // This call synchronize the Full Backend cache with the current GLState // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 962a919d6c..5c1aafdd45 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -140,7 +140,7 @@ protected: friend class GeometryMappingResource; virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra); + const void* extra) override; private: ModelCache(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 000ca67989..1cc00bb850 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -171,7 +171,8 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const } -NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type) { +NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type, + const QVariantMap& options = QVariantMap()) { using Type = NetworkTexture; switch (type) { @@ -188,7 +189,11 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t break; } case Type::CUBE_TEXTURE: { - return model::TextureUsage::createCubeTextureFromImage; + if (options.value("generateIrradiance", true).toBool()) { + return model::TextureUsage::createCubeTextureFromImage; + } else { + return model::TextureUsage::createCubeTextureFromImageWithoutIrradiance; + } break; } case Type::BUMP_TEXTURE: { @@ -225,9 +230,9 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t } /// Returns a texture version of an image file -gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type) { +gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type, QVariantMap options) { QImage image = QImage(path); - auto loader = getTextureLoaderForType(type); + auto loader = getTextureLoaderForType(type, options); return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString())); } @@ -277,7 +282,7 @@ public: ImageReader(const QWeakPointer& resource, const QByteArray& data, const QUrl& url = QUrl()); - virtual void run(); + virtual void run() override; private: static void listSupportedImageFormats(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 0108a3dd6c..e1fa119e0d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -122,24 +122,24 @@ public: const gpu::TexturePointer& getNormalFittingTexture(); /// Returns a texture version of an image file - static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE); + static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE, QVariantMap options = QVariantMap()); /// Loads a texture from the specified URL. NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE, const QByteArray& content = QByteArray()); - + protected: // Overload ResourceCache::prefetch to allow specifying texture type for loads Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type); virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra); - + const void* extra) override; + private: TextureCache(); virtual ~TextureCache(); friend class DilatableNetworkTexture; - + gpu::TexturePointer _permutationNormalTexture; gpu::TexturePointer _whiteTexture; gpu::TexturePointer _grayTexture; diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h index 8c9e7bb1d6..b2e004a2e9 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/model/src/model/Skybox.h @@ -35,6 +35,7 @@ public: void setCubemap(const gpu::TexturePointer& cubemap); const gpu::TexturePointer& getCubemap() const { return _cubemap; } + virtual bool empty() { return _schemaBuffer.get().color == vec3(0) && !_cubemap; } virtual void clear() { setCubemap(nullptr); } void prepare(gpu::Batch& batch, int textureSlot = SKYBOX_SKYMAP_SLOT, int bufferSlot = SKYBOX_CONSTANTS_SLOT) const; diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 23503ef6fb..6cfb77f2b2 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -244,21 +244,6 @@ void SunSkyStage::updateGraphicsObject() const { double originAlt = _earthSunModel.getAltitude(); _sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f)); } - - // Background - switch (getBackgroundMode()) { - case NO_BACKGROUND: { - break; - } - case SKY_DOME: { - break; - } - case SKY_BOX: { - break; - } - case NUM_BACKGROUND_MODES: - Q_UNREACHABLE(); - }; } void SunSkyStage::setBackgroundMode(BackgroundMode mode) { diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index 335fb1c758..5f48824568 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -160,8 +160,10 @@ public: enum BackgroundMode { NO_BACKGROUND = 0, - SKY_DOME, + SKY_DEFAULT, SKY_BOX, + SKY_DEFAULT_AMBIENT_TEXTURE, + SKY_DEFAULT_TEXTURE, NUM_BACKGROUND_MODES, }; @@ -173,7 +175,7 @@ public: const SkyboxPointer& getSkybox() const { valid(); return _skybox; } protected: - BackgroundMode _backgroundMode = SKY_DOME; + BackgroundMode _backgroundMode = SKY_DEFAULT; LightPointer _sunLight; mutable SkyboxPointer _skybox; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3e6016d7c0..754862aa4a 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -59,31 +59,28 @@ const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& val const uint8 OPAQUE_ALPHA = 255; const uint8 TRANSPARENT_ALPHA = 0; if (image.hasAlphaChannel()) { - std::map alphaHistogram; - if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } - // Actual alpha channel? create the histogram - for (int y = 0; y < image.height(); ++y) { - const QRgb* data = reinterpret_cast(image.constScanLine(y)); - for (int x = 0; x < image.width(); ++x) { - auto alpha = qAlpha(data[x]); - alphaHistogram[alpha] ++; - validAlpha = validAlpha || (alpha != OPAQUE_ALPHA); + // Figure out if we can use a mask for alpha or not + int numOpaques = 0; + int numTranslucents = 0; + const int NUM_PIXELS = image.width() * image.height(); + const int MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(NUM_PIXELS)); + const QRgb* data = reinterpret_cast(image.constBits()); + for (int i = 0; i < NUM_PIXELS; ++i) { + auto alpha = qAlpha(data[i]); + if (alpha == OPAQUE_ALPHA) { + numOpaques++; + } else if (alpha != TRANSPARENT_ALPHA) { + if (++numTranslucents > MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK) { + alphaAsMask = false; + break; + } } } - - // If alpha was meaningfull refine - if (validAlpha && (alphaHistogram.size() > 1)) { - auto totalNumPixels = image.height() * image.width(); - auto numOpaques = alphaHistogram[OPAQUE_ALPHA]; - auto numTransparents = alphaHistogram[TRANSPARENT_ALPHA]; - auto numTranslucents = totalNumPixels - numOpaques - numTransparents; - - alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05); - } + validAlpha = (numOpaques != NUM_PIXELS); } if (!validAlpha && image.format() != QImage::Format_RGB888) { @@ -144,6 +141,34 @@ const QImage& image, bool isLinear, bool doCompress) { } } +#define CPU_MIPMAPS 0 + +void generateMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip) { +#if CPU_MIPMAPS + auto numMips = texture->evalNumMips(); + for (uint16 level = 1; level < numMips; ++level) { + QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); + image = image.scaled(mipSize); + texture->assignStoredMip(level, formatMip, image.byteCount(), image.constBits()); + } +#else + texture->autoGenerateMips(-1); +#endif +} + +void generateFaceMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip, uint8 face) { +#if CPU_MIPMAPS + auto numMips = texture->evalNumMips(); + for (uint16 level = 1; level < numMips; ++level) { + QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); + image = image.scaled(mipSize); + texture->assignStoredMipFace(level, formatMip, image.byteCount(), image.constBits(), face); + } +#else + texture->autoGenerateMips(-1); +#endif +} + gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, bool isLinear, bool doCompress, bool generateMips) { bool validAlpha = false; bool alphaAsMask = true; @@ -170,7 +195,7 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); if (generateMips) { - theTexture->autoGenerateMips(-1); + ::generateMips(theTexture, image, formatMip); } } @@ -210,7 +235,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - theTexture->autoGenerateMips(-1); + generateMips(theTexture, image, formatMip); } return theTexture; @@ -293,7 +318,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - theTexture->autoGenerateMips(-1); + generateMips(theTexture, image, formatMip); } return theTexture; @@ -324,7 +349,7 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - theTexture->autoGenerateMips(-1); + generateMips(theTexture, image, formatMip); // FIXME queue for transfer to GPU and block on completion } @@ -361,7 +386,7 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - theTexture->autoGenerateMips(-1); + generateMips(theTexture, image, formatMip); // FIXME queue for transfer to GPU and block on completion } @@ -395,7 +420,7 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - theTexture->autoGenerateMips(-1); + generateMips(theTexture, image, formatMip); // FIXME queue for transfer to GPU and block on completion } @@ -660,13 +685,12 @@ const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = { const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout); gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance) { - - bool validAlpha = false; - bool alphaAsMask = true; - QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask); - gpu::Texture* theTexture = nullptr; - if ((image.width() > 0) && (image.height() > 0)) { + if ((srcImage.width() > 0) && (srcImage.height() > 0)) { + QImage image = srcImage; + if (image.format() != QImage::Format_RGB888) { + image = image.convertToFormat(QImage::Format_RGB888); + } gpu::Element formatGPU; gpu::Element formatMip; @@ -674,7 +698,7 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm // Find the layout of the cubemap in the 2D image int foundLayout = CubeLayout::findLayout(image.width(), image.height()); - + std::vector faces; // If found, go extract the faces as separate images if (foundLayout >= 0) { @@ -709,6 +733,9 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm int f = 0; for (auto& face : faces) { theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f); + if (generateMips) { + generateFaceMips(theTexture, face, formatMip, f); + } f++; } @@ -729,3 +756,7 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, true); } + +gpu::Texture* TextureUsage::createCubeTextureFromImageWithoutIrradiance(const QImage& srcImage, const std::string& srcImageName) { + return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, false); +} diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index daa4b0d7bb..795b685f27 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -40,6 +40,7 @@ public: static gpu::Texture* createRoughnessTextureFromGlossImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createMetallicTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName); + static gpu::Texture* createCubeTextureFromImageWithoutIrradiance(const QImage& image, const std::string& srcImageName); static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName); diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index c4bfae7cac..d89514b7cd 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -505,6 +505,29 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } +void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter()); + + QUrl grantURL = _authURL; + grantURL.setPath("/oauth/token"); + + const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner"; + + QByteArray postData; + postData.append("grant_type=password&"); + postData.append("steam_auth_ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&"); + postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); + + request.setUrl(grantURL); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + + QNetworkReply* requestReply = networkAccessManager.post(request, postData); + connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); + connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); +} void AccountManager::requestAccessTokenFinished() { QNetworkReply* requestReply = reinterpret_cast(sender()); @@ -545,6 +568,7 @@ void AccountManager::requestAccessTokenFinished() { void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) { // TODO: error handling qCDebug(networking) << "AccountManager requestError - " << error; + emit loginFailed(); } void AccountManager::requestProfile() { diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 846cdb6220..eb4d224501 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -96,6 +96,7 @@ public: public slots: void requestAccessToken(const QString& login, const QString& password); + void requestAccessTokenWithSteam(QByteArray authSessionTicket); void requestAccessTokenFinished(); void requestProfileFinished(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index ae6aad3c4f..6760d44244 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -46,7 +47,7 @@ bool AddressManager::isConnected() { return DependencyManager::get()->getDomainHandler().isConnected(); } -const QUrl AddressManager::currentAddress() const { +QUrl AddressManager::currentAddress() const { QUrl hifiURL; hifiURL.setScheme(HIFI_URL_SCHEME); @@ -61,6 +62,21 @@ const QUrl AddressManager::currentAddress() const { return hifiURL; } +QUrl AddressManager::currentFacingAddress() const { + QUrl hifiURL; + + hifiURL.setScheme(HIFI_URL_SCHEME); + hifiURL.setHost(_host); + + if (_port != 0 && _port != DEFAULT_DOMAIN_SERVER_PORT) { + hifiURL.setPort(_port); + } + + hifiURL.setPath(currentFacingPath()); + + return hifiURL; +} + void AddressManager::loadSettings(const QString& lookupString) { if (lookupString.isEmpty()) { handleUrl(currentAddressHandle.get().toString(), LookupTrigger::StartupFromSettings); @@ -97,7 +113,7 @@ void AddressManager::storeCurrentAddress() { currentAddressHandle.set(currentAddress()); } -const QString AddressManager::currentPath(bool withOrientation) const { +QString AddressManager::currentPath(bool withOrientation) const { if (_positionGetter) { QString pathString = "/" + createByteArray(_positionGetter()); @@ -121,6 +137,25 @@ const QString AddressManager::currentPath(bool withOrientation) const { } } +QString AddressManager::currentFacingPath() const { + if (_positionGetter && _orientationGetter) { + auto position = _positionGetter(); + auto orientation = _orientationGetter(); + + // move the user a couple units away + const float DISTANCE_TO_USER = 2.0f; + position += orientation * Vectors::FRONT * DISTANCE_TO_USER; + + // rotate the user by 180 degrees + orientation = orientation * glm::angleAxis(PI, Vectors::UP); + + return "/" + createByteArray(position) + "/" + createByteArray(orientation); + } else { + qCDebug(networking) << "Cannot create address path without a getter for position/orientation."; + return QString(); + } +} + const JSONCallbackParameters& AddressManager::apiCallbackParameters() { static bool hasSetupParameters = false; static JSONCallbackParameters callbackParams; diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 2e9f177137..8ccddc5975 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -57,8 +57,10 @@ public: bool isConnected(); const QString& getProtocol() { return HIFI_URL_SCHEME; }; - const QUrl currentAddress() const; - const QString currentPath(bool withOrientation = true) const; + QUrl currentAddress() const; + QUrl currentFacingAddress() const; + QString currentPath(bool withOrientation = true) const; + QString currentFacingPath() const; const QUuid& getRootPlaceID() const { return _rootPlaceID; } const QString& getPlaceName() const { return _placeName; } diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 6b94ee152a..a8311c6146 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -33,12 +33,7 @@ bool AssetResourceRequest::urlIsAssetHash() const { } void AssetResourceRequest::doSend() { - auto parts = _url.path().split(".", QString::SkipEmptyParts); - auto hash = parts.length() > 0 ? parts[0] : ""; - auto extension = parts.length() > 1 ? parts[1] : ""; - // We'll either have a hash or an ATP path to a file (that maps to a hash) - if (urlIsAssetHash()) { // We've detected that this is a hash - simply use AssetClient to request that asset auto parts = _url.path().split(".", QString::SkipEmptyParts); diff --git a/libraries/networking/src/AtpReply.cpp b/libraries/networking/src/AtpReply.cpp new file mode 100644 index 0000000000..4440995ee0 --- /dev/null +++ b/libraries/networking/src/AtpReply.cpp @@ -0,0 +1,88 @@ +// +// AtpReply.cpp +// libraries/networking/src +// +// Created by Zander Otavka on 8/4/16. +// 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 "ResourceManager.h" +#include "AtpReply.h" + +AtpReply::AtpReply(const QUrl& url, QObject* parent) : + _resourceRequest(ResourceManager::createResourceRequest(parent, url)) { + setOperation(QNetworkAccessManager::GetOperation); + + connect(_resourceRequest, &AssetResourceRequest::progress, this, &AtpReply::downloadProgress); + connect(_resourceRequest, &AssetResourceRequest::finished, this, &AtpReply::handleRequestFinish); + + _resourceRequest->send(); +} + +AtpReply::~AtpReply() { + if (_resourceRequest) { + _resourceRequest->deleteLater(); + _resourceRequest = nullptr; + } +} + +qint64 AtpReply::bytesAvailable() const { + return _content.size() - _readOffset + QIODevice::bytesAvailable(); +} + +qint64 AtpReply::readData(char* data, qint64 maxSize) { + if (_readOffset < _content.size()) { + qint64 readSize = qMin(maxSize, _content.size() - _readOffset); + memcpy(data, _content.constData() + _readOffset, readSize); + _readOffset += readSize; + return readSize; + } else { + return -1; + } +} + +void AtpReply::handleRequestFinish() { + Q_ASSERT(_resourceRequest->getState() == ResourceRequest::State::Finished); + + switch (_resourceRequest->getResult()) { + case ResourceRequest::Result::Success: + setError(NoError, "Success"); + _content = _resourceRequest->getData(); + break; + case ResourceRequest::Result::InvalidURL: + setError(ContentNotFoundError, "Invalid URL"); + break; + case ResourceRequest::Result::NotFound: + setError(ContentNotFoundError, "Not found"); + break; + case ResourceRequest::Result::ServerUnavailable: + setError(ServiceUnavailableError, "Service unavailable"); + break; + case ResourceRequest::Result::AccessDenied: + setError(ContentAccessDenied, "Access denied"); + break; + case ResourceRequest::Result::Timeout: + setError(TimeoutError, "Timeout"); + break; + default: + setError(UnknownNetworkError, "Unknown error"); + break; + } + + open(ReadOnly | Unbuffered); + setHeader(QNetworkRequest::ContentLengthHeader, QVariant(_content.size())); + + if (error() != NoError) { + emit error(error()); + } + + setFinished(true); + emit readyRead(); + emit finished(); + + _resourceRequest->deleteLater(); + _resourceRequest = nullptr; +} diff --git a/libraries/networking/src/AtpReply.h b/libraries/networking/src/AtpReply.h new file mode 100644 index 0000000000..6ed5dd8fb8 --- /dev/null +++ b/libraries/networking/src/AtpReply.h @@ -0,0 +1,40 @@ +// +// AtpReply.h +// libraries/networking/src +// +// Created by Zander Otavka on 8/4/16. +// 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 +// + +#ifndef hifi_AtpReply_h +#define hifi_AtpReply_h + +#include +#include + +#include "AssetResourceRequest.h" + +class AtpReply : public QNetworkReply { + Q_OBJECT +public: + AtpReply(const QUrl& url, QObject* parent = Q_NULLPTR); + ~AtpReply(); + qint64 bytesAvailable() const override; + void abort() override { } + bool isSequential() const override { return true; } + +protected: + qint64 readData(char* data, qint64 maxSize) override; + +private: + void handleRequestFinish(); + + ResourceRequest* _resourceRequest { nullptr }; + QByteArray _content; + qint64 _readOffset { 0 }; +}; + +#endif // hifi_AtpReply_h diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 431d372089..6d9de2dbc1 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -175,7 +175,11 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() { } bool LimitedNodeList::isPacketVerified(const udt::Packet& packet) { - return packetVersionMatch(packet) && packetSourceAndHashMatch(packet); + // We track bandwidth when doing packet verification to avoid needing to do a node lookup + // later when we already do it in packetSourceAndHashMatchAndTrackBandwidth. A node lookup + // incurs a lock, so it is ideal to avoid needing to do it 2+ times for each packet + // received. + return packetVersionMatch(packet) && packetSourceAndHashMatchAndTrackBandwidth(packet); } bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) { @@ -224,11 +228,12 @@ bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) { } } -bool LimitedNodeList::packetSourceAndHashMatch(const udt::Packet& packet) { +bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet) { PacketType headerType = NLPacket::typeInHeader(packet); if (NON_SOURCED_PACKETS.contains(headerType)) { + emit dataReceived(NodeType::Unassigned, packet.getPayloadSize()); return true; } else { QUuid sourceID = NLPacket::sourceIDInHeader(packet); @@ -260,6 +265,8 @@ bool LimitedNodeList::packetSourceAndHashMatch(const udt::Packet& packet) { // from this sending node matchingNode->setLastHeardMicrostamp(usecTimestampNow()); + emit dataReceived(matchingNode->getType(), packet.getPayloadSize()); + return true; } else { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 48379b5e39..49a3a155a2 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -243,6 +243,7 @@ public slots: signals: void dataSent(quint8 channelType, int bytes); + void dataReceived(quint8 channelType, int bytes); // QUuid might be zero for non-sourced packet types. void packetVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID); @@ -279,7 +280,7 @@ protected: void setLocalSocket(const HifiSockAddr& sockAddr); - bool packetSourceAndHashMatch(const udt::Packet& packet); + bool packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet); void processSTUNResponse(std::unique_ptr packet); void handleNodeKill(const SharedNodePointer& node); diff --git a/libraries/networking/src/NLPacketList.h b/libraries/networking/src/NLPacketList.h index 01a017f371..48ce5ef81a 100644 --- a/libraries/networking/src/NLPacketList.h +++ b/libraries/networking/src/NLPacketList.h @@ -31,7 +31,7 @@ private: NLPacketList(const NLPacketList& other) = delete; NLPacketList& operator=(const NLPacketList& other) = delete; - virtual std::unique_ptr createPacket(); + virtual std::unique_ptr createPacket() override; PacketVersion _packetVersion; diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 97171d5ad7..78a05677fd 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -1,6 +1,6 @@ // // NetworkAccessManager.cpp -// +// libraries/networking/src // // Created by Clement on 7/1/14. // Copyright 2014 High Fidelity, Inc. @@ -11,6 +11,7 @@ #include +#include "AtpReply.h" #include "NetworkAccessManager.h" QThreadStorage networkAccessManagers; @@ -23,3 +24,13 @@ QNetworkAccessManager& NetworkAccessManager::getInstance() { return *networkAccessManagers.localData(); } + +QNetworkReply* NetworkAccessManager::createRequest(Operation operation, const QNetworkRequest& request, QIODevice* device) { + if (request.url().scheme() == "atp" && operation == GetOperation) { + return new AtpReply(request.url()); + //auto url = request.url().toString(); + //return QNetworkAccessManager::createRequest(operation, request, device); + } else { + return QNetworkAccessManager::createRequest(operation, request, device); + } +} diff --git a/libraries/networking/src/NetworkAccessManager.h b/libraries/networking/src/NetworkAccessManager.h index c4b435adb6..1679ed081c 100644 --- a/libraries/networking/src/NetworkAccessManager.h +++ b/libraries/networking/src/NetworkAccessManager.h @@ -1,6 +1,6 @@ // // NetworkAccessManager.h -// +// libraries/networking/src // // Created by Clement on 7/1/14. // Copyright 2014 High Fidelity, Inc. @@ -13,12 +13,17 @@ #define hifi_NetworkAccessManager_h #include +#include +#include /// Wrapper around QNetworkAccessManager to restrict at one instance by thread -class NetworkAccessManager : public QObject { +class NetworkAccessManager : public QNetworkAccessManager { Q_OBJECT public: static QNetworkAccessManager& getInstance(); +protected: + NetworkAccessManager(QObject* parent = Q_NULLPTR) : QNetworkAccessManager(parent) {} + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR) override; }; #endif // hifi_NetworkAccessManager_h \ No newline at end of file diff --git a/libraries/networking/src/OAuthNetworkAccessManager.h b/libraries/networking/src/OAuthNetworkAccessManager.h index acfd52e18f..f4139e5cfd 100644 --- a/libraries/networking/src/OAuthNetworkAccessManager.h +++ b/libraries/networking/src/OAuthNetworkAccessManager.h @@ -12,13 +12,14 @@ #ifndef hifi_OAuthNetworkAccessManager_h #define hifi_OAuthNetworkAccessManager_h -#include +#include "NetworkAccessManager.h" -class OAuthNetworkAccessManager : public QNetworkAccessManager { +class OAuthNetworkAccessManager : public NetworkAccessManager { public: static OAuthNetworkAccessManager* getInstance(); protected: - virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData = 0); + OAuthNetworkAccessManager(QObject* parent = Q_NULLPTR) : NetworkAccessManager(parent) { } + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData = 0) override; }; -#endif // hifi_OAuthNetworkAccessManager_h \ No newline at end of file +#endif // hifi_OAuthNetworkAccessManager_h diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 530efc5fb3..21ba7129d4 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -294,7 +294,6 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer recei PacketType packetType = receivedMessage->getType(); if (matchingNode) { - emit dataReceived(matchingNode->getType(), receivedMessage->getSize()); matchingNode->recordBytesReceived(receivedMessage->getSize()); QMetaMethod metaMethod = listener.method; @@ -326,7 +325,6 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer recei } } else { // qDebug() << "Got verified unsourced packet list: " << QString(nlPacketList->getMessage()); - emit dataReceived(NodeType::Unassigned, receivedMessage->getSize()); // one final check on the QPointer before we invoke if (listener.object) { diff --git a/libraries/networking/src/PacketReceiver.h b/libraries/networking/src/PacketReceiver.h index ff4ab3e15c..4b4d260409 100644 --- a/libraries/networking/src/PacketReceiver.h +++ b/libraries/networking/src/PacketReceiver.h @@ -67,9 +67,6 @@ public: void handleVerifiedPacket(std::unique_ptr packet); void handleVerifiedMessagePacket(std::unique_ptr message); void handleMessageFailure(HifiSockAddr from, udt::Packet::MessageNumber messageNumber); - -signals: - void dataReceived(quint8 channelType, int bytes); private: struct Listener { diff --git a/libraries/networking/src/PacketSender.h b/libraries/networking/src/PacketSender.h index e177172788..68faeaca47 100644 --- a/libraries/networking/src/PacketSender.h +++ b/libraries/networking/src/PacketSender.h @@ -43,8 +43,8 @@ public: void setPacketsPerSecond(int packetsPerSecond); int getPacketsPerSecond() const { return _packetsPerSecond; } - virtual bool process(); - virtual void terminating(); + virtual bool process() override; + virtual void terminating() override; /// are there packets waiting in the send queue to be sent bool hasPacketsToSend() const { return _packets.size() > 0; } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index cbb27fa5d6..dd790a9b3d 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -49,7 +49,7 @@ public: float getIncomingPPS() const { return _incomingPPS.getAverage(); } float getProcessedPPS() const { return _processedPPS.getAverage(); } - virtual void terminating(); + virtual void terminating() override; public slots: void nodeKilled(SharedNodePointer node); @@ -61,7 +61,7 @@ protected: virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode) = 0; /// Implements generic processing behavior for this thread. - virtual bool process(); + virtual bool process() override; /// Determines the timeout of the wait when there are no packets to process. Default value means no timeout virtual unsigned long getMaxWait() const { return ULONG_MAX; } diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 36828b3992..5fb686ca6f 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -462,6 +462,7 @@ int ResourceCache::getPendingRequestCount() { } bool ResourceCache::attemptRequest(QSharedPointer resource) { + Q_ASSERT(!resource.isNull()); auto sharedItems = DependencyManager::get(); if (_requestsActive >= _requestLimit) { diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index 846c05ecbf..33b8020d3c 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -70,9 +70,9 @@ public: // QIODevice virtual functions // WARNING: Those methods all refer to the payload ONLY and NOT the entire packet - virtual bool isSequential() const { return false; } - virtual bool reset(); - virtual qint64 size() const { return _payloadCapacity; } + virtual bool isSequential() const override { return false; } + virtual bool reset() override; + virtual qint64 size() const override { return _payloadCapacity; } using QIODevice::read; // Bring QIODevice::read methods to scope, otherwise they are hidden by folling method QByteArray read(qint64 maxSize); @@ -94,8 +94,8 @@ protected: BasePacket& operator=(BasePacket&& other); // QIODevice virtual functions - virtual qint64 writeData(const char* data, qint64 maxSize); - virtual qint64 readData(char* data, qint64 maxSize); + virtual qint64 writeData(const char* data, qint64 maxSize) override; + virtual qint64 readData(char* data, qint64 maxSize) override; void adjustPayloadStartAndCapacity(qint64 headerSize, bool shouldDecreasePayloadSize = false); diff --git a/libraries/networking/src/udt/CongestionControl.h b/libraries/networking/src/udt/CongestionControl.h index cb99e6c8d4..3ab69efe52 100644 --- a/libraries/networking/src/udt/CongestionControl.h +++ b/libraries/networking/src/udt/CongestionControl.h @@ -94,7 +94,7 @@ public: template class CongestionControlFactory: public CongestionControlVirtualFactory { public: virtual ~CongestionControlFactory() {} - virtual std::unique_ptr create() { return std::unique_ptr(new T()); } + virtual std::unique_ptr create() override { return std::unique_ptr(new T()); } }; class DefaultCC: public CongestionControl { @@ -102,12 +102,12 @@ public: DefaultCC(); public: - virtual void onACK(SequenceNumber ackNum); - virtual void onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd); - virtual void onTimeout(); + virtual void onACK(SequenceNumber ackNum) override; + virtual void onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) override; + virtual void onTimeout() override; protected: - virtual void setInitialSendSequenceNumber(SequenceNumber seqNum); + virtual void setInitialSendSequenceNumber(SequenceNumber seqNum) override; private: void stopSlowStart(); // stops the slow start on loss or timeout diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index 875eb70e45..5083d77ec9 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -54,8 +54,8 @@ public: void closeCurrentPacket(bool shouldSendEmpty = false); // QIODevice virtual functions - virtual bool isSequential() const { return false; } - virtual qint64 size() const { return getDataSize(); } + virtual bool isSequential() const override { return false; } + virtual qint64 size() const override { return getDataSize(); } template qint64 readPrimitive(T* data); template qint64 writePrimitive(const T& data); @@ -68,9 +68,9 @@ protected: void preparePackets(MessageNumber messageNumber); - virtual qint64 writeData(const char* data, qint64 maxSize); + virtual qint64 writeData(const char* data, qint64 maxSize) override; // Not implemented, added an assert so that it doesn't get used by accident - virtual qint64 readData(char* data, qint64 maxSize) { Q_ASSERT(false); return 0; } + virtual qint64 readData(char* data, qint64 maxSize) override { Q_ASSERT(false); return 0; } PacketType _packetType; std::list> _packets; diff --git a/libraries/octree/src/DirtyOctreeElementOperator.h b/libraries/octree/src/DirtyOctreeElementOperator.h index a5eec780a0..a8d00a13f0 100644 --- a/libraries/octree/src/DirtyOctreeElementOperator.h +++ b/libraries/octree/src/DirtyOctreeElementOperator.h @@ -20,8 +20,8 @@ public: ~DirtyOctreeElementOperator() {} - virtual bool preRecursion(OctreeElementPointer element); - virtual bool postRecursion(OctreeElementPointer element); + virtual bool preRecursion(OctreeElementPointer element) override; + virtual bool postRecursion(OctreeElementPointer element) override; private: glm::vec3 _point; OctreeElementPointer _element; diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index 4a390814f4..fd8cc85f91 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -58,7 +58,7 @@ public: } /// if you're running in non-threaded mode, you must call this method regularly - virtual bool process(); + virtual bool process() override; /// Set the desired number of pending messages that the OctreeEditPacketSender should attempt to queue even if /// servers are not present. This only applies to how the OctreeEditPacketSender will manage messages when no diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index d48c35d542..7034790eaf 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -34,11 +34,12 @@ const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds -OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, int persistInterval, +OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, int persistInterval, bool wantBackup, const QJsonObject& settings, bool debugTimestampNow, QString persistAsFileType) : _tree(tree), _filename(filename), + _backupDirectory(backupDirectory), _persistInterval(persistInterval), _initialLoadComplete(false), _loadTimeUSecs(0), @@ -316,7 +317,7 @@ bool OctreePersistThread::getMostRecentBackup(const QString& format, // Based on our backup file name, determine the path and file name pattern for backup files QFileInfo persistFileInfo(_filename); - QString path = persistFileInfo.path(); + QString path = _backupDirectory; QString fileNamePart = persistFileInfo.fileName(); QStringList filters; @@ -369,10 +370,12 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) { if (rule.maxBackupVersions > 0) { qCDebug(octree) << "Rolling old backup versions for rule" << rule.name << "..."; + QString backupFileName = _backupDirectory + "/" + QUrl(_filename).fileName(); + // Delete maximum rolling file because rename() fails on Windows if target exists QString backupMaxExtensionN = rule.extensionFormat; backupMaxExtensionN.replace(QString("%N"), QString::number(rule.maxBackupVersions)); - QString backupMaxFilenameN = _filename + backupMaxExtensionN; + QString backupMaxFilenameN = backupFileName + backupMaxExtensionN; QFile backupMaxFileN(backupMaxFilenameN); if (backupMaxFileN.exists()) { int result = remove(qPrintable(backupMaxFilenameN)); @@ -387,8 +390,8 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) { backupExtensionN.replace(QString("%N"), QString::number(n)); backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1)); - QString backupFilenameN = findMostRecentFileExtension(_filename, PERSIST_EXTENSIONS) + backupExtensionN; - QString backupFilenameNplusOne = _filename + backupExtensionNplusOne; + QString backupFilenameN = findMostRecentFileExtension(backupFileName, PERSIST_EXTENSIONS) + backupExtensionN; + QString backupFilenameNplusOne = backupFileName + backupExtensionNplusOne; QFile backupFileN(backupFilenameN); @@ -434,21 +437,20 @@ void OctreePersistThread::backup() { struct tm* localTime = localtime(&_lastPersistTime); - QString backupFileName; + QString backupFileName = _backupDirectory + "/" + QUrl(_filename).fileName(); // check to see if they asked for version rolling format if (rule.extensionFormat.contains("%N")) { rollOldBackupVersions(rule); // rename all the old backup files accordingly QString backupExtension = rule.extensionFormat; backupExtension.replace(QString("%N"), QString("1")); - backupFileName = _filename + backupExtension; + backupFileName += backupExtension; } else { char backupExtension[256]; strftime(backupExtension, sizeof(backupExtension), qPrintable(rule.extensionFormat), localTime); - backupFileName = _filename + backupExtension; + backupFileName += backupExtension; } - if (rule.maxBackupVersions > 0) { QFile persistFile(_filename); if (persistFile.exists()) { diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 061a7a0e15..f8215fb34a 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -33,9 +33,9 @@ public: static const int DEFAULT_PERSIST_INTERVAL; - OctreePersistThread(OctreePointer tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL, - bool wantBackup = false, const QJsonObject& settings = QJsonObject(), - bool debugTimestampNow = false, QString persistAsFileType="svo"); + OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, + int persistInterval = DEFAULT_PERSIST_INTERVAL, bool wantBackup = false, + const QJsonObject& settings = QJsonObject(), bool debugTimestampNow = false, QString persistAsFileType="svo"); bool isInitialLoadComplete() const { return _initialLoadComplete; } quint64 getLoadElapsedTime() const { return _loadTimeUSecs; } @@ -51,7 +51,7 @@ signals: protected: /// Implements generic processing behavior for this thread. - virtual bool process(); + virtual bool process() override; void persist(); void backup(); @@ -64,6 +64,7 @@ protected: private: OctreePointer _tree; QString _filename; + QString _backupDirectory; int _persistInterval; bool _initialLoadComplete; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index b5ef03be0f..19207f13ed 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -36,7 +36,7 @@ public: m_collisionFilterGroup = BULLET_COLLISION_GROUP_MY_AVATAR; m_collisionFilterMask = BULLET_COLLISION_MASK_MY_AVATAR; } - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) { + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) override { if (rayResult.m_collisionObject == _me) { return 1.0f; } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 137038e133..67072d46d4 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -164,17 +164,28 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) { assert(info.getType() == SHAPE_TYPE_STATIC_MESH); // should only get here for mesh shapes const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); - assert(pointCollection.size() == 1); // should only have one mesh + if (pointCollection.size() < 1) { + // no lists of points to work with + return nullptr; + } + + // we only use the first point collection const ShapeInfo::PointList& pointList = pointCollection[0]; - assert(pointList.size() > 2); // should have at least one triangle's worth of points + if (pointList.size() < 3) { + // not enough distinct points to make a non-degenerate triangle + return nullptr; + } const ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); - assert(triangleIndices.size() > 2); // should have at least one triangle's worth of indices + int32_t numIndices = triangleIndices.size(); + if (numIndices < 3) { + // not enough indices to make a single triangle + return nullptr; + } // allocate mesh buffers btIndexedMesh mesh; - int32_t numIndices = triangleIndices.size(); const int32_t VERTICES_PER_TRIANGLE = 3; mesh.m_numTriangles = numIndices / VERTICES_PER_TRIANGLE; if (numIndices < INT16_MAX) { @@ -328,7 +339,9 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { break; case SHAPE_TYPE_STATIC_MESH: { btTriangleIndexVertexArray* dataArray = createStaticMeshArray(info); - shape = new StaticMeshShape(dataArray); + if (dataArray) { + shape = new StaticMeshShape(dataArray); + } } break; } diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index f32650df94..e7798e9f3d 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -1,3 +1,4 @@ set(TARGET_NAME plugins) setup_hifi_library(OpenGL) link_hifi_libraries(shared) +include_hifi_library_headers(gpu) diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index f0ba762ecb..49c341cdcb 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "Plugin.h" @@ -91,11 +92,6 @@ public: return glm::mat4(); } - // Needed for timewarp style features - virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) { - // NOOP - } - virtual void abandonCalibration() {} virtual void resetSensors() {} @@ -149,16 +145,8 @@ public: virtual QString getPreferredAudioOutDevice() const { return QString(); } // Rendering support - - /** - * Sends the scene texture to the display plugin. - */ - virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) = 0; - - /** - * Sends the scene texture to the display plugin. - */ - virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) = 0; + virtual void setContext(const gpu::ContextPointer& context) final { _gpuContext = context; } + virtual void submitFrame(const gpu::FramePointer& newFrame) = 0; // Does the rendering surface have current focus? virtual bool hasFocus() const = 0; @@ -212,6 +200,8 @@ signals: protected: void incrementPresentCount(); + gpu::ContextPointer _gpuContext; + private: std::atomic _presentedFrameIndex; mutable std::mutex _paintDelayMutex; diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 7dd729384c..97bfcf4fe5 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -63,7 +63,13 @@ QJsonValue Procedural::getProceduralData(const QString& proceduralJson) { return doc.object()[PROCEDURAL_USER_DATA_KEY]; } -Procedural::Procedural() : _state { std::make_shared() } { +Procedural::Procedural() { + _transparentState->setCullMode(gpu::State::CULL_NONE); + _transparentState->setDepthTest(true, true, gpu::LESS_EQUAL); + _transparentState->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); + _proceduralDataDirty = false; } @@ -103,6 +109,11 @@ bool Procedural::parseUrl(const QUrl& shaderUrl) { return false; } + // If the URL hasn't changed, don't mark the shader as dirty + if (_shaderUrl == shaderUrl) { + return true; + } + _shaderUrl = shaderUrl; _shaderDirty = true; @@ -175,6 +186,10 @@ void Procedural::parse(const QJsonObject& proceduralData) { } bool Procedural::ready() { + if (!_hasStartedFade) { + _fadeStartTime = usecTimestampNow(); + } + // Load any changes to the procedural // Check for changes atomically, in case they are currently being made if (_proceduralDataDirty) { @@ -202,6 +217,10 @@ bool Procedural::ready() { } } + if (!_hasStartedFade) { + _hasStartedFade = true; + _isFading = true; + } return true; } @@ -222,7 +241,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm _shaderSource = _networkShader->_source; } - if (!_pipeline || _shaderDirty) { + if (!_opaquePipeline || !_transparentPipeline || _shaderDirty) { if (!_vertexShader) { _vertexShader = gpu::Shader::createVertex(_vertexSource); } @@ -260,7 +279,8 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm slotBindings.insert(gpu::Shader::Binding(std::string("iChannel3"), 3)); gpu::Shader::makeProgram(*_shader, slotBindings); - _pipeline = gpu::Pipeline::create(_shader, _state); + _opaquePipeline = gpu::Pipeline::create(_shader, _opaqueState); + _transparentPipeline = gpu::Pipeline::create(_shader, _transparentState); for (size_t i = 0; i < NUM_STANDARD_UNIFORMS; ++i) { const std::string& name = STANDARD_UNIFORM_NAMES[i]; _standardUniformSlots[i] = _shader->getUniforms().findLocation(name); @@ -269,7 +289,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm _frameCount = 0; } - batch.setPipeline(_pipeline); + batch.setPipeline(isFading() ? _transparentPipeline : _opaquePipeline); if (_shaderDirty || _uniformsDirty) { setupUniforms(); diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index 6991b47946..c128da0be0 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -38,17 +38,23 @@ public: void parse(const QString& userDataJson); bool ready(); + bool enabled() { return _enabled; } void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation); const gpu::ShaderPointer& getShader() const { return _shader; } glm::vec4 getColor(const glm::vec4& entityColor); + quint64 getFadeStartTime() { return _fadeStartTime; } + bool isFading() { return _doesFade && _isFading; } + void setIsFading(bool isFading) { _isFading = isFading; } + void setDoesFade(bool doesFade) { _doesFade = doesFade; } uint8_t _version { 1 }; std::string _vertexSource; std::string _fragmentSource; - gpu::StatePointer _state; + gpu::StatePointer _opaqueState { std::make_shared() }; + gpu::StatePointer _transparentState { std::make_shared() }; enum StandardUniforms { DATE, @@ -86,7 +92,8 @@ protected: UniformLambdas _uniforms; int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS]; NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS]; - gpu::PipelinePointer _pipeline; + gpu::PipelinePointer _opaquePipeline; + gpu::PipelinePointer _transparentPipeline; gpu::ShaderPointer _vertexShader; gpu::ShaderPointer _fragmentShader; gpu::ShaderPointer _shader; @@ -106,6 +113,11 @@ private: void setupUniforms(); void setupChannels(bool shouldCreate); + + quint64 _fadeStartTime; + bool _hasStartedFade { false }; + bool _isFading { false }; + bool _doesFade { true }; }; #endif diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 9e9a26d902..83122578e7 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -22,7 +22,12 @@ ProceduralSkybox::ProceduralSkybox() : model::Skybox() { _procedural._vertexSource = skybox_vert; _procedural._fragmentSource = skybox_frag; // Adjust the pipeline state for background using the stencil test - _procedural._state->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.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)); +} + +bool ProceduralSkybox::empty() { + return !_procedural.enabled() && Skybox::empty(); } void ProceduralSkybox::clear() { diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.h b/libraries/procedural/src/procedural/ProceduralSkybox.h index b38e481317..30412d559c 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.h +++ b/libraries/procedural/src/procedural/ProceduralSkybox.h @@ -20,13 +20,14 @@ class ProceduralSkybox: public model::Skybox { public: ProceduralSkybox(); - virtual ~ProceduralSkybox() {}; + ~ProceduralSkybox() override {}; void parse(const QString& userData) { _procedural.parse(userData); } - virtual void clear() override; + bool empty() override; + void clear() override; - virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const override; + void render(gpu::Batch& batch, const ViewFrustum& frustum) const override; static void render(gpu::Batch& batch, const ViewFrustum& frustum, const ProceduralSkybox& skybox); protected: diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 2a7d33e33a..7b272f7b7d 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -1,9 +1,9 @@ set(TARGET_NAME render-utils) -AUTOSCRIBE_SHADER_LIB(gpu model render procedural) +AUTOSCRIBE_SHADER_LIB(gpu model render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) -link_hifi_libraries(shared gpu procedural model model-networking render animation fbx) +link_hifi_libraries(shared gpu model model-networking render animation fbx) target_nsight() target_oglplus() diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 4b283731d2..667e39a2d3 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -353,7 +353,7 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(glm::mat4()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); Transform model; model.setTranslation(glm::vec3(sMin, tMin, 0.0f)); diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index e4e27a1b3d..80ba9cd1b2 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -7,6 +7,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "animdebugdraw_vert.h" #include "animdebugdraw_frag.h" #include @@ -17,13 +19,14 @@ #include "AnimDebugDraw.h" -struct Vertex { - glm::vec3 pos; - uint32_t rgba; -}; - class AnimDebugDrawData { public: + + struct Vertex { + glm::vec3 pos; + uint32_t rgba; + }; + typedef render::Payload Payload; typedef Payload::DataPointer Pointer; @@ -117,18 +120,18 @@ AnimDebugDraw::AnimDebugDraw() : } // HACK: add red, green and blue axis at (1,1,1) - _animDebugDrawData->_vertexBuffer->resize(sizeof(Vertex) * 6); + _animDebugDrawData->_vertexBuffer->resize(sizeof(AnimDebugDrawData::Vertex) * 6); - static std::vector vertices({ - Vertex { glm::vec3(1.0, 1.0f, 1.0f), toRGBA(255, 0, 0, 255) }, - Vertex { glm::vec3(2.0, 1.0f, 1.0f), toRGBA(255, 0, 0, 255) }, - Vertex { glm::vec3(1.0, 1.0f, 1.0f), toRGBA(0, 255, 0, 255) }, - Vertex { glm::vec3(1.0, 2.0f, 1.0f), toRGBA(0, 255, 0, 255) }, - Vertex { glm::vec3(1.0, 1.0f, 1.0f), toRGBA(0, 0, 255, 255) }, - Vertex { glm::vec3(1.0, 1.0f, 2.0f), toRGBA(0, 0, 255, 255) }, + static std::vector vertices({ + AnimDebugDrawData::Vertex { glm::vec3(1.0, 1.0f, 1.0f), toRGBA(255, 0, 0, 255) }, + AnimDebugDrawData::Vertex { glm::vec3(2.0, 1.0f, 1.0f), toRGBA(255, 0, 0, 255) }, + AnimDebugDrawData::Vertex { glm::vec3(1.0, 1.0f, 1.0f), toRGBA(0, 255, 0, 255) }, + AnimDebugDrawData::Vertex { glm::vec3(1.0, 2.0f, 1.0f), toRGBA(0, 255, 0, 255) }, + AnimDebugDrawData::Vertex { glm::vec3(1.0, 1.0f, 1.0f), toRGBA(0, 0, 255, 255) }, + AnimDebugDrawData::Vertex { glm::vec3(1.0, 1.0f, 2.0f), toRGBA(0, 0, 255, 255) }, }); static std::vector indices({ 0, 1, 2, 3, 4, 5 }); - _animDebugDrawData->_vertexBuffer->setSubData(0, vertices); + _animDebugDrawData->_vertexBuffer->setSubData(0, vertices); _animDebugDrawData->_indexBuffer->setSubData(0, indices); } @@ -159,7 +162,7 @@ static const uint32_t blue = toRGBA(0, 0, 255, 255); const int NUM_CIRCLE_SLICES = 24; -static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius, Vertex*& v) { +static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius, AnimDebugDrawData::Vertex*& v) { const float XYZ_AXIS_LENGTH = radius * 4.0f; @@ -234,7 +237,7 @@ static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius } static void addLink(const AnimPose& rootPose, const AnimPose& pose, const AnimPose& parentPose, - float radius, const glm::vec4& colorVec, Vertex*& v) { + float radius, const glm::vec4& colorVec, AnimDebugDrawData::Vertex*& v) { uint32_t color = toRGBA(colorVec); @@ -296,7 +299,7 @@ static void addLink(const AnimPose& rootPose, const AnimPose& pose, const AnimPo } } -static void addLine(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, Vertex*& v) { +static void addLine(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, AnimDebugDrawData::Vertex*& v) { uint32_t colorInt = toRGBA(color); v->pos = start; v->rgba = colorInt; @@ -345,10 +348,10 @@ void AnimDebugDraw::update() { numVerts += (int)DebugDraw::getInstance().getRays().size() * VERTICES_PER_RAY; // allocate verts! - std::vector vertices; + std::vector vertices; vertices.resize(numVerts); //Vertex* verts = (Vertex*)data._vertexBuffer->editData(); - Vertex* v = nullptr; + AnimDebugDrawData::Vertex* v = nullptr; if (numVerts) { v = &vertices[0]; } @@ -401,8 +404,8 @@ void AnimDebugDraw::update() { } DebugDraw::getInstance().clearRays(); - data._vertexBuffer->resize(sizeof(Vertex) * numVerts); - data._vertexBuffer->setSubData(0, vertices); + data._vertexBuffer->resize(sizeof(AnimDebugDrawData::Vertex) * numVerts); + data._vertexBuffer->setSubData(0, vertices); assert((!numVerts && !v) || (numVerts == (v - &vertices[0]))); diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 2f273f6202..b7995c1b47 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -119,7 +119,7 @@ void Antialiasing::run(const render::SceneContextPointer& sceneContext, const re args->getViewFrustum().evalProjectionMatrix(projMat); args->getViewFrustum().evalViewTransform(viewMat); batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); + batch.setViewTransform(viewMat, true); batch.setModelTransform(Transform()); // FXAA step diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 8118df5435..4f6e87220e 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -394,7 +394,7 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren args->getViewFrustum().evalProjectionMatrix(projMat); args->getViewFrustum().evalViewTransform(viewMat); batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); + batch.setViewTransform(viewMat, true); batch.setModelTransform(Transform()); // TODO REMOVE: Temporary until UI diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh index 3153a851fb..aa79781c25 100755 --- a/libraries/render-utils/src/DeferredBufferWrite.slh +++ b/libraries/render-utils/src/DeferredBufferWrite.slh @@ -73,6 +73,8 @@ void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 albedo, vec3 discard; } _fragColor0 = vec4(albedo.rgb, alpha); + _fragColor1 = vec4(packNormal(normal), clamp(roughness, 0.0, 1.0)); + } <@endif@> diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 6f202f6200..d7f09583f6 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -67,6 +67,7 @@ enum DeferredShader_MapSlot { }; enum DeferredShader_BufferSlot { DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT = 0, + CAMERA_CORRECTION_BUFFER_SLOT, SCATTERING_PARAMETERS_BUFFER_SLOT, LIGHTING_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::LIGHTING_MODEL, LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT, @@ -181,10 +182,12 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo slotBindings.insert(gpu::Shader::Binding(std::string("scatteringSpecularBeckmann"), SCATTERING_SPECULAR_UNIT)); + slotBindings.insert(gpu::Shader::Binding(std::string("cameraCorrectionBuffer"), CAMERA_CORRECTION_BUFFER_SLOT)); slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT)); slotBindings.insert(gpu::Shader::Binding(std::string("lightingModelBuffer"), LIGHTING_MODEL_BUFFER_SLOT)); slotBindings.insert(gpu::Shader::Binding(std::string("subsurfaceScatteringParametersBuffer"), SCATTERING_PARAMETERS_BUFFER_SLOT)); slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT)); + gpu::Shader::makeProgram(*program, slotBindings); @@ -481,8 +484,10 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c } } else { if (keyLight->getAmbientMap()) { - program = deferredLightingEffect->_directionalSkyboxLight; - locations = deferredLightingEffect->_directionalSkyboxLightLocations; + program = deferredLightingEffect->_directionalAmbientSphereLight; + locations = deferredLightingEffect->_directionalAmbientSphereLightLocations; + //program = deferredLightingEffect->_directionalSkyboxLight; + //locations = deferredLightingEffect->_directionalSkyboxLightLocations; } else { program = deferredLightingEffect->_directionalAmbientSphereLight; locations = deferredLightingEffect->_directionalAmbientSphereLightLocations; @@ -562,7 +567,7 @@ void RenderDeferredLocals::run(const render::SceneContextPointer& sceneContext, auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), monoViewport); batch.setProjectionTransform(monoProjMat); - batch.setViewTransform(monoViewTransform); + batch.setViewTransform(monoViewTransform, true); // Splat Point lights if (points && !deferredLightingEffect->_pointLights.empty()) { diff --git a/libraries/render-utils/src/DeferredTransform.slh b/libraries/render-utils/src/DeferredTransform.slh index b3881c4c71..13c904eece 100644 --- a/libraries/render-utils/src/DeferredTransform.slh +++ b/libraries/render-utils/src/DeferredTransform.slh @@ -13,6 +13,15 @@ <@func declareDeferredFrameTransform()@> +struct CameraCorrection { + mat4 _correction; + mat4 _correctionInverse; +}; + +uniform cameraCorrectionBuffer { + CameraCorrection cameraCorrection; +}; + struct DeferredFrameTransform { vec4 _pixelInfo; vec4 _invPixelInfo; @@ -29,7 +38,10 @@ uniform deferredFrameTransformBuffer { }; DeferredFrameTransform getDeferredFrameTransform() { - return frameTransform; + DeferredFrameTransform result = frameTransform; + result._view = result._view * cameraCorrection._correctionInverse; + result._viewInverse = result._viewInverse * cameraCorrection._correction; + return result; } vec2 getWidthHeight(int resolutionLevel) { @@ -67,11 +79,11 @@ float getPosLinearDepthFar() { } mat4 getViewInverse() { - return frameTransform._viewInverse; + return frameTransform._viewInverse * cameraCorrection._correction; } mat4 getView() { - return frameTransform._view; + return frameTransform._view * cameraCorrection._correctionInverse; } bool isStereo() { diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 1251c3b5b5..01cec78eae 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -11,7 +11,7 @@ #include "GeometryCache.h" - +#include #include #include @@ -372,6 +372,7 @@ void GeometryCache::buildShapes() { extrudePolygon<6>(_shapes[Hexagon], _shapeVertices, _shapeIndices); //Octagon, extrudePolygon<8>(_shapes[Octagon], _shapeVertices, _shapeIndices); + //Quad, //Circle, //Torus, @@ -398,26 +399,36 @@ gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() { return INSTANCED_SOLID_STREAM_FORMAT; } -render::ShapePipelinePointer GeometryCache::_simplePipeline; +render::ShapePipelinePointer GeometryCache::_simpleOpaquePipeline; +render::ShapePipelinePointer GeometryCache::_simpleTransparentPipeline; render::ShapePipelinePointer GeometryCache::_simpleWirePipeline; GeometryCache::GeometryCache() : _nextID(0) { buildShapes(); - GeometryCache::_simplePipeline = - std::make_shared(getSimplePipeline(), nullptr, - [](const render::ShapePipeline&, gpu::Batch& batch) { - // Set the defaults needed for a simple program - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, - DependencyManager::get()->getWhiteTexture()); - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, - DependencyManager::get()->getNormalFittingTexture()); - } - ); + GeometryCache::_simpleOpaquePipeline = + std::make_shared(getSimplePipeline(false, false, true, false), nullptr, + [](const render::ShapePipeline&, gpu::Batch& batch) { + // Set the defaults needed for a simple program + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, + DependencyManager::get()->getWhiteTexture()); + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, + DependencyManager::get()->getNormalFittingTexture()); + } + ); + GeometryCache::_simpleTransparentPipeline = + std::make_shared(getSimplePipeline(false, true, true, false), nullptr, + [](const render::ShapePipeline&, gpu::Batch& batch) { + // Set the defaults needed for a simple program + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, + DependencyManager::get()->getWhiteTexture()); + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, + DependencyManager::get()->getNormalFittingTexture()); + } + ); GeometryCache::_simpleWirePipeline = std::make_shared(getSimplePipeline(false, false, true, true), nullptr, - [](const render::ShapePipeline&, gpu::Batch& batch) {} - ); + [](const render::ShapePipeline&, gpu::Batch& batch) {}); } GeometryCache::~GeometryCache() { @@ -1704,6 +1715,7 @@ class SimpleProgramKey { public: enum FlagBit { IS_TEXTURED_FLAG = 0, + IS_TRANSPARENT_FLAG, IS_CULLED_FLAG, IS_UNLIT_FLAG, HAS_DEPTH_BIAS_FLAG, @@ -1713,6 +1725,7 @@ public: enum Flag { IS_TEXTURED = (1 << IS_TEXTURED_FLAG), + IS_TRANSPARENT = (1 << IS_TRANSPARENT_FLAG), IS_CULLED = (1 << IS_CULLED_FLAG), IS_UNLIT = (1 << IS_UNLIT_FLAG), HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG), @@ -1722,6 +1735,7 @@ public: bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } bool isTextured() const { return isFlag(IS_TEXTURED); } + bool isTransparent() const { return isFlag(IS_TRANSPARENT); } bool isCulled() const { return isFlag(IS_CULLED); } bool isUnlit() const { return isFlag(IS_UNLIT); } bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); } @@ -1732,9 +1746,9 @@ public: int getRaw() const { return *reinterpret_cast(this); } - SimpleProgramKey(bool textured = false, bool culled = true, + SimpleProgramKey(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false) { - _flags = (textured ? IS_TEXTURED : 0) | (culled ? IS_CULLED : 0) | + _flags = (textured ? IS_TEXTURED : 0) | (transparent ? IS_TRANSPARENT : 0) | (culled ? IS_CULLED : 0) | (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0); } @@ -1771,7 +1785,7 @@ gpu::PipelinePointer GeometryCache::getSimpleSRGBTexturedUnlitNoTexAlphaPipeline auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(false, + 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); @@ -1781,8 +1795,8 @@ gpu::PipelinePointer GeometryCache::getSimpleSRGBTexturedUnlitNoTexAlphaPipeline return _simpleSRGBTexturedUnlitNoTexAlphaPipeline; } -void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, bool unlit, bool depthBiased) { - batch.setPipeline(getSimplePipeline(textured, culled, unlit, depthBiased)); +void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) { + batch.setPipeline(getSimplePipeline(textured, transparent, culled, unlit, depthBiased)); // If not textured, set a default albedo map if (!textured) { @@ -1794,8 +1808,8 @@ void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool cul DependencyManager::get()->getNormalFittingTexture()); } -gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled, bool unlit, bool depthBiased) { - SimpleProgramKey config { textured, culled, unlit, depthBiased }; +gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) { + SimpleProgramKey config { textured, transparent, culled, unlit, depthBiased }; // Compile the shaders static std::once_flag once; @@ -1831,7 +1845,7 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled state->setDepthBias(1.0f); state->setDepthBiasSlopeScale(1.0f); } - state->setBlendFunction(false, + state->setBlendFunction(config.isTransparent(), 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); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index e28e76a766..9099daf7c0 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -152,16 +152,17 @@ public: // Bind the pipeline and get the state to render static geometry - void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true, + void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false); // Get the pipeline to render static geometry - gpu::PipelinePointer getSimplePipeline(bool textured = false, bool culled = true, + gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false); void bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(gpu::Batch& batch); gpu::PipelinePointer getSimpleSRGBTexturedUnlitNoTexAlphaPipeline(); - render::ShapePipelinePointer getShapePipeline() { return GeometryCache::_simplePipeline; } + render::ShapePipelinePointer getOpaqueShapePipeline() { return GeometryCache::_simpleOpaquePipeline; } + render::ShapePipelinePointer getTransparentShapePipeline() { return GeometryCache::_simpleTransparentPipeline; } render::ShapePipelinePointer getWireShapePipeline() { return GeometryCache::_simpleWirePipeline; } // Static (instanced) geometry @@ -169,42 +170,42 @@ public: void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer); void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1), - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline) { + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderSolidShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline); } void renderWireShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1), - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderWireShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline) { + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderWireShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline); } void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline) { + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderSolidSphereInstance(batch, glm::vec4(color, 1.0f), pipeline); } void renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleWirePipeline); void renderWireSphereInstance(gpu::Batch& batch, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) { renderWireSphereInstance(batch, glm::vec4(color, 1.0f), pipeline); } void renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderSolidCubeInstance(gpu::Batch& batch, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline) { + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderSolidCubeInstance(batch, glm::vec4(color, 1.0f), pipeline); } void renderWireCubeInstance(gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleWirePipeline); void renderWireCubeInstance(gpu::Batch& batch, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) { renderWireCubeInstance(batch, glm::vec4(color, 1.0f), pipeline); @@ -416,7 +417,8 @@ private: gpu::ShaderPointer _simpleShader; gpu::ShaderPointer _unlitShader; - static render::ShapePipelinePointer _simplePipeline; + static render::ShapePipelinePointer _simpleOpaquePipeline; + static render::ShapePipelinePointer _simpleTransparentPipeline; static render::ShapePipelinePointer _simpleWirePipeline; gpu::PipelinePointer _glowLinePipeline; QHash _simplePrograms; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index cb6c73f414..63082a8995 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -15,6 +15,7 @@ #include "DeferredLightingEffect.h" #include "Model.h" +#include "EntityItem.h" using namespace render; @@ -352,7 +353,6 @@ void ModelMeshPartPayload::initCache() { } - void ModelMeshPartPayload::notifyLocationChanged() { } @@ -392,6 +392,10 @@ ItemKey ModelMeshPartPayload::getKey() const { } } + if (!_hasFinishedFade) { + builder.withTransparent(); + } + return builder.build(); } @@ -443,7 +447,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } ShapeKey::Builder builder; - if (isTranslucent) { + if (isTranslucent || !_hasFinishedFade) { builder.withTranslucent(); } if (hasTangents) { @@ -484,9 +488,9 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } - // TODO: Get rid of that extra call - if (!_hasColorAttrib) { - batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; + if (!_hasColorAttrib || fadeRatio < 1.0f) { + batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); } } @@ -513,14 +517,37 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: batch.setModelTransform(transform); } +void ModelMeshPartPayload::startFade() { + bool shouldFade = EntityItem::getEntitiesShouldFadeFunction()(); + if (shouldFade) { + _fadeStartTime = usecTimestampNow(); + _hasStartedFade = true; + _hasFinishedFade = false; + } else { + _isFading = true; + _hasStartedFade = true; + _hasFinishedFade = true; + } +} void ModelMeshPartPayload::render(RenderArgs* args) const { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); - if (!_model->_readyWhenAdded || !_model->_isVisible) { + if (!_model->_readyWhenAdded || !_model->_isVisible || !_hasStartedFade) { return; // bail asap } + // When an individual mesh parts like this finishes its fade, we will mark the Model as + // having render items that need updating + bool nextIsFading = _isFading ? isStillFading() : false; + bool startFading = !_isFading && !_hasFinishedFade && _hasStartedFade; + bool endFading = _isFading && !nextIsFading; + if (startFading || endFading) { + _isFading = startFading; + _hasFinishedFade = endFading; + _model->setRenderItemsNeedUpdate(); + } + gpu::Batch& batch = *(args->_batch); if (!getShapeKey().isValid()) { diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 41869ec7e3..29478b3b4e 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -12,6 +12,8 @@ #ifndef hifi_MeshPartPayload_h #define hifi_MeshPartPayload_h +#include + #include #include @@ -81,6 +83,11 @@ public: void notifyLocationChanged() override; void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices); + // Entity fade in + void startFade(); + bool hasStartedFade() { return _hasStartedFade; } + bool isStillFading() const { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + // Render Item interface render::ItemKey getKey() const override; render::ShapeKey getShapeKey() const override; // shape interface @@ -99,6 +106,12 @@ public: bool _isSkinned{ false }; bool _isBlendShaped{ false }; + +private: + quint64 _fadeStartTime { 0 }; + bool _hasStartedFade { false }; + mutable bool _hasFinishedFade { false }; + mutable bool _isFading { false }; }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d755dc3aca..a6e1bb53f4 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -174,6 +174,7 @@ void Model::setOffset(const glm::vec3& offset) { void Model::updateRenderItems() { _needsUpdateClusterMatrices = true; + _renderItemsNeedUpdate = false; // queue up this work for later processing, at the end of update and just before rendering. // the application will ensure only the last lambda is actually invoked. @@ -211,6 +212,9 @@ void Model::updateRenderItems() { render::PendingChanges pendingChanges; foreach (auto itemID, self->_modelMeshRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, modelMeshOffset, deleteGeometryCounter](ModelMeshPartPayload& data) { + if (!data.hasStartedFade() && data._model && data._model->isLoaded() && data._model->getGeometry()->areTexturesLoaded()) { + data.startFade(); + } // Ensure the model geometry was not reset between frames if (data._model && data._model->isLoaded() && deleteGeometryCounter == data._model->_deleteGeometryCounter) { // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. @@ -826,7 +830,9 @@ void Model::setURL(const QUrl& url) { invalidCalculatedMeshBoxes(); deleteGeometry(); - _renderWatcher.setResource(DependencyManager::get()->getGeometryResource(url)); + auto resource = DependencyManager::get()->getGeometryResource(url); + resource->setLoadPriority(this, _loadingPriority); + _renderWatcher.setResource(resource); onInvalidate(); } @@ -910,7 +916,7 @@ public: Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, const QVector& meshes, const QVector& blendshapeCoefficients); - virtual void run(); + virtual void run() override; private: diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index f7bf83ca5b..15c8e0326a 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -103,6 +103,8 @@ public: bool isVisible() const { return _isVisible; } void updateRenderItems(); + void setRenderItemsNeedUpdate() { _renderItemsNeedUpdate = true; } + bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; } AABox getRenderableMeshBound() const; bool maybeStartBlender(); @@ -238,6 +240,8 @@ public: // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); + void setLoadingPriority(float priority) { _loadingPriority = priority; } + public slots: void loadURLFinished(bool success); void loadCollisionModelURLFinished(bool success); @@ -405,6 +409,12 @@ protected: bool _visualGeometryRequestFailed { false }; bool _collisionGeometryRequestFailed { false }; + + bool _renderItemsNeedUpdate { false }; + +private: + float _loadingPriority { 0.0f }; + }; Q_DECLARE_METATYPE(ModelPointer) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 2e3901a769..237c01cf28 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -55,7 +55,7 @@ void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true); batch.setProjectionTransform(shadow.getProjection()); - batch.setViewTransform(shadow.getView()); + batch.setViewTransform(shadow.getView(), false); auto shadowPipeline = _shapePlumber->pickPipeline(args, ShapeKey()); auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, ShapeKey::Builder().withSkinned()); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index fd778d30be..6d945061d0 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -173,7 +173,7 @@ void LinearDepthPass::run(const render::SceneContextPointer& sceneContext, const batch.setViewportTransform(depthViewport); batch.setProjectionTransform(glm::mat4()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_linearDepthFramebuffer->getDepthFrameSize(), depthViewport)); batch.setUniformBuffer(DepthLinearPass_FrameTransformSlot, frameTransform->getFrameTransformBuffer()); @@ -459,7 +459,7 @@ void SurfaceGeometryPass::run(const render::SceneContextPointer& sceneContext, c batch.enableStereo(false); batch.setProjectionTransform(glm::mat4()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); batch.setViewportTransform(curvatureViewport); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_surfaceGeometryFramebuffer->getSourceFrameSize(), curvatureViewport)); diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp index b0ac40a839..24c62fb7c2 100644 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -72,7 +72,7 @@ void ToneMappingEffect::render(RenderArgs* args, const gpu::TexturePointer& ligh batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(glm::mat4()); - batch.setViewTransform(Transform()); + batch.resetViewTransform(); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); batch.setPipeline(_blitLightBuffer); diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index f5385c23b7..f578895c85 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -48,7 +48,7 @@ void main() { packDeferredFragmentTranslucent( normalize(_normal), - a, + a * Color.a, Color.rgb, DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index dd5faf40db..228560f394 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -49,11 +49,43 @@ void main(void) { #endif - if (emissiveAmount > 0.0) { - packDeferredFragmentLightmap( - normal, 1.0, diffuse, max(0, 1.0 - shininess / 128.0), DEFAULT_METALLIC, specular, specular); + const float ALPHA_THRESHOLD = 0.999; + if (_color.a < ALPHA_THRESHOLD) { + if (emissiveAmount > 0.0) { + packDeferredFragmentTranslucent( + normal, + _color.a, + specular, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } else { + packDeferredFragmentTranslucent( + normal, + _color.a, + diffuse, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } } else { - packDeferredFragment( - normal, 1.0, diffuse, max(0, 1.0 - shininess / 128.0), length(specular), DEFAULT_EMISSIVE, DEFAULT_OCCLUSION, DEFAULT_SCATTERING); + if (emissiveAmount > 0.0) { + packDeferredFragmentLightmap( + normal, + 1.0, + diffuse, + max(0, 1.0 - shininess / 128.0), + DEFAULT_METALLIC, + specular, + specular); + } else { + packDeferredFragment( + normal, + 1.0, + diffuse, + max(0, 1.0 - shininess / 128.0), + length(specular), + DEFAULT_EMISSIVE, + DEFAULT_OCCLUSION, + DEFAULT_SCATTERING); + } } } diff --git a/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf b/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf index bab18b970b..38b7e1002c 100644 --- a/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf +++ b/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf @@ -27,8 +27,18 @@ void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); texel = colorToLinearRGBA(texel); - packDeferredFragmentUnlit( - normalize(_normal), - 1.0, - _color.rgb * texel.rgb); + const float ALPHA_THRESHOLD = 0.999; + if (_color.a < ALPHA_THRESHOLD) { + packDeferredFragmentTranslucent( + normalize(_normal), + _color.a, + _color.rgb * texel.rgb, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } else { + packDeferredFragmentUnlit( + normalize(_normal), + 1.0, + _color.rgb * texel.rgb); + } } diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf index f045af2ce5..6067c81a1b 100644 --- a/libraries/render-utils/src/simple_textured.slf +++ b/libraries/render-utils/src/simple_textured.slf @@ -29,13 +29,24 @@ void main(void) { if (_color.a <= 0.0) { texel = colorToLinearRGBA(texel); } - packDeferredFragment( - normalize(_normal.xyz), - texel.a, - _color.rgb * texel.rgb, - DEFAULT_ROUGHNESS, - DEFAULT_METALLIC, - DEFAULT_EMISSIVE, - DEFAULT_OCCLUSION, - DEFAULT_SCATTERING); + + const float ALPHA_THRESHOLD = 0.999; + if (_color.a * texel.a < ALPHA_THRESHOLD) { + packDeferredFragmentTranslucent( + normalize(_normal), + _color.a * texel.a, + _color.rgb * texel.rgb, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } else { + packDeferredFragment( + normalize(_normal), + 1.0, + _color.rgb * texel.rgb, + DEFAULT_ROUGHNESS, + DEFAULT_METALLIC, + DEFAULT_EMISSIVE, + DEFAULT_OCCLUSION, + DEFAULT_SCATTERING); + } } \ No newline at end of file diff --git a/libraries/render-utils/src/simple_textured_unlit.slf b/libraries/render-utils/src/simple_textured_unlit.slf index cbfc7d7768..4f02140825 100644 --- a/libraries/render-utils/src/simple_textured_unlit.slf +++ b/libraries/render-utils/src/simple_textured_unlit.slf @@ -29,8 +29,18 @@ void main(void) { texel = colorToLinearRGBA(texel); } - packDeferredFragmentUnlit( - normalize(_normal), - texel.a, - _color.rgb * texel.rgb); + const float ALPHA_THRESHOLD = 0.999; + if (_color.a * texel.a < ALPHA_THRESHOLD) { + packDeferredFragmentTranslucent( + normalize(_normal), + _color.a * texel.a, + _color.rgb * texel.rgb, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } else { + packDeferredFragmentUnlit( + normalize(_normal), + 1.0, + _color.rgb * texel.rgb); + } } \ No newline at end of file diff --git a/libraries/render-utils/src/stars.slf b/libraries/render-utils/src/stars.slf deleted file mode 100644 index 14191f2a6a..0000000000 --- a/libraries/render-utils/src/stars.slf +++ /dev/null @@ -1,34 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// fragment shader -// -// Created by Bradley Austin Davis on 6/10/15. -// 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 -// - -in vec4 varColor; -in float varSize; - -out vec4 outFragColor; - -const float EDGE_SIZE = 0.25; -const float ALPHA_BOUNDARY = 1.0 - EDGE_SIZE; - -void main(void) { - vec2 coord = gl_PointCoord * vec2(2.0) - vec2(1.0); - coord = coord * coord; - - float l = coord.x + coord.y; - if (l > 1.0) { - discard; - } - - outFragColor = varColor; - if (l >= ALPHA_BOUNDARY) { - outFragColor.a = smoothstep(1.0, ALPHA_BOUNDARY, l); - } -} diff --git a/libraries/render-utils/src/stars.slv b/libraries/render-utils/src/stars.slv deleted file mode 100644 index d00bb8b7dd..0000000000 --- a/libraries/render-utils/src/stars.slv +++ /dev/null @@ -1,36 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// standardTransformPNTC.slv -// vertex shader -// -// Created by Sam Gateau on 6/10/2015. -// 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 gpu/Inputs.slh@> -<@include gpu/Color.slh@> -<@include gpu/Transform.slh@> -<$declareStandardTransform()$> - -// TODO we need to get the viewport resolution and FOV passed to us so we can modify the point size -// and effectively producing a points that take up a constant angular size regardless of the display resolution -// or projection matrix - -out vec4 varColor; -out float varSize; - -void main(void) { - varColor = colorToLinearRGBA(inColor); - - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> - varSize = inColor.a; - gl_PointSize = varSize; -} \ No newline at end of file diff --git a/libraries/render-utils/src/starsGrid.slf b/libraries/render-utils/src/starsGrid.slf deleted file mode 100644 index ad9a45a4f5..0000000000 --- a/libraries/render-utils/src/starsGrid.slf +++ /dev/null @@ -1,63 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// stars.frag -// fragment shader -// -// Created by Bradley Austin Davis on 2015/06/19 - -in vec2 varTexcoord; -in vec3 varNomral; -in vec3 varPosition; - -uniform float iGlobalTime; - -const float PI = 3.14159; -const float TAU = 3.14159 * 2.0; -const int latitudeCount = 5; -const float latitudeDist = PI / 2.0 / float(latitudeCount); -const int meridianCount = 4; -const float merdianDist = PI / float(meridianCount); - -out vec4 outFragColor; - -float clampLine(float val, float target) { - return clamp((1.0 - abs((val - target)) - 0.998) * 500.0, 0.0, 1.0); -} - -float latitude(vec2 pos, float angle) { - float result = clampLine(pos.y, angle); - if (angle != 0.0) { - result += clampLine(pos.y, -angle); - } - return result; -} - -float meridian(vec2 pos, float angle) { - return clampLine(pos.x, angle) + clampLine(pos.x + PI, angle); -} - -vec2 toPolar(in vec3 dir) { - vec2 polar = vec2(atan(dir.z, dir.x), asin(dir.y)); - return polar; -} - -void mainVR( out vec4 fragColor, in vec2 fragCoord, in vec3 fragRayOri, in vec3 fragRayDir ) -{ - vec2 polar = toPolar(fragRayDir); - //polar.x += mod(iGlobalTime / 12.0, PI / 4.0) - PI / 4.0; - float c = 0.0; - for (int i = 0; i < latitudeCount - 1; ++i) { - c += latitude(polar, float(i) * latitudeDist); - } - for (int i = 0; i < meridianCount; ++i) { - c += meridian(polar, float(i) * merdianDist); - } - const vec3 col_lines = vec3(102.0 / 255.0, 136.0 / 255.0, 221.0 / 255.0); - fragColor = vec4(c * col_lines, 0.2); -} - -void main(void) { - mainVR(outFragColor, gl_FragCoord.xy, vec3(0.0), normalize(varPosition)); -} - diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index c5cfdf3668..76fc8303ce 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME render) -AUTOSCRIBE_SHADER_LIB(gpu model procedural) +AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() link_hifi_libraries(shared gpu model) diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index efcb4eea37..eac3113662 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -103,7 +103,7 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); + batch.setViewTransform(viewMat, true); batch.setModelTransform(Transform()); // bind the one gpu::Pipeline we need @@ -153,7 +153,7 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, Transform crosshairModel; crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1000.0)); crosshairModel.setScale(1000.0 * tan(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO - batch.setViewTransform(Transform()); + batch.resetViewTransform(); batch.setModelTransform(crosshairModel); batch.setPipeline(getDrawLODReticlePipeline()); batch.draw(gpu::TRIANGLE_STRIP, 4, 0); @@ -211,7 +211,7 @@ void DrawItemSelection::run(const SceneContextPointer& sceneContext, batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); + batch.setViewTransform(viewMat, true); batch.setModelTransform(Transform()); // bind the one gpu::Pipeline we need diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index bfbd123382..ec0a64d7a5 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -172,7 +172,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); + batch.setViewTransform(viewMat, true); batch.setModelTransform(Transform()); // bind the one gpu::Pipeline we need diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index b0329faa3e..b955205a1a 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -53,9 +53,6 @@ void Engine::load() { } void Engine::run() { - // Sync GPU state before beginning to render - _renderContext->args->_context->syncCache(); - for (auto job : _jobs) { job.run(_sceneContext, _renderContext); } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index fb5534e3ab..9a65b48021 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -31,7 +31,7 @@ namespace render { class Context; - + // Key is the KEY to filter Items and create specialized lists class ItemKey { public: @@ -51,8 +51,8 @@ public: NUM_FLAGS, // Not a valid flag }; - typedef std::bitset Flags; - + typedef std::bitset Flags; + // The key is the Flags Flags _flags; @@ -103,7 +103,7 @@ public: bool isRigid() const { return !_flags[DEFORMED]; } bool isDeformed() const { return _flags[DEFORMED]; } - + bool isVisible() const { return !_flags[INVISIBLE]; } bool isInvisible() const { return _flags[INVISIBLE]; } @@ -125,12 +125,12 @@ using ItemKeys = std::vector; inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) { debug << "[ItemKey: isOpaque:" << itemKey.isOpaque() - << ", isStatic:" << itemKey.isStatic() - << ", isWorldSpace:" << itemKey.isWorldSpace() + << ", isStatic:" << itemKey.isStatic() + << ", isWorldSpace:" << itemKey.isWorldSpace() << "]"; return debug; } - + class ItemFilter { public: ItemKey::Flags _value{ 0 }; @@ -150,10 +150,10 @@ public: Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); } - + Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } - + Builder& withWorldSpace() { _value.reset(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } Builder& withViewSpace() { _value.set(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } @@ -224,9 +224,9 @@ public: // Bound is the AABBox fully containing this item typedef AABox Bound; - + // Status records the life history and performances of this item while performing at rendering and updating. - // This is Used for monitoring and dynamically adjust the quality + // This is Used for monitoring and dynamically adjust the quality class Status { public: @@ -243,7 +243,7 @@ public: Value() {} Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); } - // It can be scaled in the range [0, 1] + // It can be scaled in the range [0, 1] void setScale(float scale); // the color hue in the range [0, 360] representing the color wheel hue void setColor(float hue); @@ -378,7 +378,7 @@ template const ItemKey payloadGetKey(const std::shared_ptr& payload template const Item::Bound payloadGetBound(const std::shared_ptr& payloadData) { return Item::Bound(); } template int payloadGetLayer(const std::shared_ptr& payloadData) { return 0; } template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } - + // Shape type interface // This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape. // When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline, @@ -393,21 +393,23 @@ public: Payload(const DataPointer& data) : _data(data) {} // Payload general interface - virtual const ItemKey getKey() const { return payloadGetKey(_data); } - virtual const Item::Bound getBound() const { return payloadGetBound(_data); } - virtual int getLayer() const { return payloadGetLayer(_data); } + virtual const ItemKey getKey() const override { return payloadGetKey(_data); } + virtual const Item::Bound getBound() const override { return payloadGetBound(_data); } + virtual int getLayer() const override { return payloadGetLayer(_data); } - virtual void render(RenderArgs* args) { payloadRender(_data, args); } + virtual void render(RenderArgs* args) override { payloadRender(_data, args); } // Shape Type interface - virtual const ShapeKey getShapeKey() const { return shapeGetShapeKey(_data); } + virtual const ShapeKey getShapeKey() const override { return shapeGetShapeKey(_data); } protected: DataPointer _data; // Update mechanics - virtual void update(const UpdateFunctorPointer& functor) { std::static_pointer_cast(functor)->_func((*_data)); } + virtual void update(const UpdateFunctorPointer& functor) override { + std::static_pointer_cast(functor)->_func((*_data)); + } friend class Item; }; @@ -440,7 +442,7 @@ template <> const Item::Bound payloadGetBound(const FooPointer& foo) { return foo->evaluateMyBound(); } -// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing +// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing */ // End of the example @@ -448,7 +450,7 @@ template <> const Item::Bound payloadGetBound(const FooPointer& foo) { typedef Item::PayloadPointer PayloadPointer; typedef std::vector< PayloadPointer > Payloads; -// A few typedefs for standard containers of ItemIDs +// A few typedefs for standard containers of ItemIDs using ItemIDs = std::vector; using ItemIDSet = std::set; @@ -457,7 +459,7 @@ class ItemBound { public: ItemBound(ItemID id) : id(id) { } ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { } - + ItemID id; AABox bound; }; diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index 8268090997..6ba1e3c625 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -47,19 +47,19 @@ public: template T& edit() { return std::static_pointer_cast>(_concept)->_data; } template const T& get() const { return std::static_pointer_cast>(_concept)->_data; } - + // access potential sub varyings contained in this one. Varying operator[] (uint8_t index) const { return (*_concept)[index]; } uint8_t length() const { return (*_concept).length(); } template Varying getN (uint8_t index) const { return get()[index]; } template Varying editN (uint8_t index) { return edit()[index]; } - + protected: class Concept { public: virtual ~Concept() = default; - + virtual Varying operator[] (uint8_t index) const = 0; virtual uint8_t length() const = 0; }; @@ -69,12 +69,12 @@ protected: Model(const Data& data) : _data(data) {} virtual ~Model() = default; - - virtual Varying operator[] (uint8_t index) const { + + virtual Varying operator[] (uint8_t index) const override { Varying var; return var; } - virtual uint8_t length() const { return 0; } + virtual uint8_t length() const override { return 0; } Data _data; }; @@ -362,7 +362,7 @@ public: Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); } Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); } - // Running Time measurement + // Running Time measurement // The new stats signal is emitted once per run time of a job when stats (cpu runtime) are updated void setCPURunTime(quint64 ustime) { _CPURunTime = ustime; emit newStats(); } quint64 getCPUTRunTime() const { return _CPURunTime; } @@ -457,8 +457,8 @@ public: Varying _input; Varying _output; - const Varying getInput() const { return _input; } - const Varying getOutput() const { return _output; } + const Varying getInput() const override { return _input; } + const Varying getOutput() const override { return _output; } template Model(const Varying& input, A&&... args) : @@ -466,11 +466,11 @@ public: applyConfiguration(); } - void applyConfiguration() { + void applyConfiguration() override { jobConfigure(_data, *std::static_pointer_cast(_config)); } - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) override { renderContext->jobConfig = std::static_pointer_cast(_config); if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->isEnabled()) { jobRun(_data, sceneContext, renderContext, _input.get(), _output.edit()); @@ -528,8 +528,8 @@ public: Varying _input; Varying _output; - const Varying getInput() const { return _input; } - const Varying getOutput() const { return _output; } + const Varying getInput() const override { return _input; } + const Varying getOutput() const override { return _output; } template Model(const Varying& input, A&&... args) : @@ -540,11 +540,11 @@ public: applyConfiguration(); } - void applyConfiguration() { + void applyConfiguration() override { jobConfigure(_data, *std::static_pointer_cast(_config)); } - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) override { renderContext->jobConfig = std::static_pointer_cast(_config); if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { jobRun(_data, sceneContext, renderContext, _input.get(), _output.edit()); @@ -609,7 +609,7 @@ public: void configure(const QObject& configuration) { for (auto& job : _jobs) { job.applyConfiguration(); - + } } @@ -622,5 +622,4 @@ protected: } - #endif // hifi_render_Task_h diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index f7ad8ad4bd..69c2cc0799 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -28,34 +28,34 @@ public: ArrayBufferClass(ScriptEngine* scriptEngine); QScriptValue newInstance(qint32 size); QScriptValue newInstance(const QByteArray& ba); - + QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, - QueryFlags flags, uint* id); + QueryFlags flags, uint* id) override; QScriptValue property(const QScriptValue& object, - const QScriptString& name, uint id); + const QScriptString& name, uint id) override; QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id); - - QString name() const; - QScriptValue prototype() const; - + const QScriptString& name, uint id) override; + + QString name() const override; + QScriptValue prototype() const override; + ScriptEngine* getEngine() { return _scriptEngine; } - + private: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); - + static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba); static void fromScriptValue(const QScriptValue& obj, QByteArray& ba); - + QScriptValue _proto; QScriptValue _ctor; - + // JS Object attributes QScriptString _name; QScriptString _byteLength; - + ScriptEngine* _scriptEngine; }; -#endif // hifi_ArrayBufferClass_h \ No newline at end of file +#endif // hifi_ArrayBufferClass_h diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h index b673ebf280..67af4a3fc3 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.h +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -30,23 +30,23 @@ class ArrayBufferViewClass : public QObject, public QScriptClass { Q_OBJECT public: ArrayBufferViewClass(ScriptEngine* scriptEngine); - + ScriptEngine* getScriptEngine() { return _scriptEngine; } - + virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, - QueryFlags flags, uint* id); + QueryFlags flags, uint* id) override; virtual QScriptValue property(const QScriptValue& object, - const QScriptString& name, uint id); + const QScriptString& name, uint id) override; virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id); + const QScriptString& name, uint id) override; protected: // JS Object attributes QScriptString _bufferName; QScriptString _byteOffsetName; QScriptString _byteLengthName; - + ScriptEngine* _scriptEngine; }; -#endif // hifi_ArrayBufferViewClass_h \ No newline at end of file +#endif // hifi_ArrayBufferViewClass_h diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h index b87803f4b4..72c920a727 100644 --- a/libraries/script-engine/src/DataViewClass.h +++ b/libraries/script-engine/src/DataViewClass.h @@ -19,18 +19,18 @@ class DataViewClass : public ArrayBufferViewClass { public: DataViewClass(ScriptEngine* scriptEngine); QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength); - - QString name() const; - QScriptValue prototype() const; - + + QString name() const override; + QScriptValue prototype() const override; + private: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); - + QScriptValue _proto; QScriptValue _ctor; - + QScriptString _name; }; -#endif // hifi_DataViewClass_h \ No newline at end of file +#endif // hifi_DataViewClass_h diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 40234e8134..91d7f36102 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -167,6 +167,7 @@ void ScriptCache::scriptContentAvailable() { Lock lock(_containerLock); allCallbacks = _contentCallbacks.values(url); _contentCallbacks.remove(url); + Q_ASSERT(req->getState() == ResourceRequest::Finished); success = req->getResult() == ResourceRequest::Success; if (success) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index e093f0393b..38455e8fd3 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -138,7 +138,8 @@ public: static void loadEntityScript(QWeakPointer theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload); Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method Q_INVOKABLE void unloadAllEntityScripts(); - Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()); + Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, + const QStringList& params = QStringList()) override; Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); @@ -157,8 +158,8 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - These are the callback implementations for ScriptUser the get called by ScriptCache when the contents // of a script are available. - virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents); - virtual void errorInLoadingScript(const QUrl& url); + virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents) override; + virtual void errorInLoadingScript(const QUrl& url) override; // These are currently used by Application to track if a script is user loaded or not. Consider finding a solution // inside of Application so that the ScriptEngine class is not polluted by this notion diff --git a/libraries/script-engine/src/ScriptsModel.h b/libraries/script-engine/src/ScriptsModel.h index e1902f4b23..a3ca554e51 100644 --- a/libraries/script-engine/src/ScriptsModel.h +++ b/libraries/script-engine/src/ScriptsModel.h @@ -68,11 +68,11 @@ class ScriptsModel : public QAbstractItemModel { public: ScriptsModel(QObject* parent = NULL); ~ScriptsModel(); - QModelIndex index(int row, int column, const QModelIndex& parent) const; - QModelIndex parent(const QModelIndex& child) const; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - int rowCount(const QModelIndex& parent = QModelIndex()) const; - int columnCount(const QModelIndex& parent = QModelIndex()) const; + QModelIndex index(int row, int column, const QModelIndex& parent) const override; + QModelIndex parent(const QModelIndex& child) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; TreeNodeBase* getTreeNodeFromIndex(const QModelIndex& index) const; QList getFolderNodes(TreeNodeFolder* parent) const; diff --git a/libraries/script-engine/src/ScriptsModelFilter.h b/libraries/script-engine/src/ScriptsModelFilter.h index 7b7cdd974e..4854665d12 100644 --- a/libraries/script-engine/src/ScriptsModelFilter.h +++ b/libraries/script-engine/src/ScriptsModelFilter.h @@ -20,8 +20,8 @@ class ScriptsModelFilter : public QSortFilterProxyModel { public: ScriptsModelFilter(QObject *parent = NULL); protected: - bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const; - bool lessThan(const QModelIndex& left, const QModelIndex& right) const; + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; }; #endif // hifi_ScriptsModelFilter_h diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index a1a288c3c9..141e7870d9 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -34,33 +34,33 @@ public: virtual QScriptValue newInstance(quint32 length); virtual QScriptValue newInstance(QScriptValue array); virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); - + virtual QueryFlags queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id); + const QScriptString& name, + QueryFlags flags, uint* id) override; virtual QScriptValue property(const QScriptValue& object, - const QScriptString& name, uint id); - virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) = 0; + const QScriptString& name, uint id) override; + virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override = 0; virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id); - - QString name() const; - QScriptValue prototype() const; - + const QScriptString& name, uint id) override; + + QString name() const override; + QScriptValue prototype() const override; + protected: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); - + void setBytesPerElement(quint32 bytesPerElement); - + QScriptValue _proto; QScriptValue _ctor; - + QScriptString _name; QScriptString _bytesPerElementName; QScriptString _lengthName; - + quint32 _bytesPerElement; - + friend class TypedArrayPrototype; }; @@ -68,81 +68,81 @@ class Int8ArrayClass : public TypedArray { Q_OBJECT public: Int8ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Uint8ArrayClass : public TypedArray { Q_OBJECT public: Uint8ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Uint8ClampedArrayClass : public TypedArray { Q_OBJECT public: Uint8ClampedArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Int16ArrayClass : public TypedArray { Q_OBJECT public: Int16ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Uint16ArrayClass : public TypedArray { Q_OBJECT public: Uint16ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Int32ArrayClass : public TypedArray { Q_OBJECT public: Int32ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Uint32ArrayClass : public TypedArray { Q_OBJECT public: Uint32ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Float32ArrayClass : public TypedArray { Q_OBJECT public: Float32ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; class Float64ArrayClass : public TypedArray { Q_OBJECT public: Float64ArrayClass(ScriptEngine* scriptEngine); - - QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); - void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); + + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id) override; + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) override; }; -#endif // hifi_TypedArrays_h \ No newline at end of file +#endif // hifi_TypedArrays_h diff --git a/libraries/script-engine/src/UndoStackScriptingInterface.h b/libraries/script-engine/src/UndoStackScriptingInterface.h index 479648fc92..420a282c38 100644 --- a/libraries/script-engine/src/UndoStackScriptingInterface.h +++ b/libraries/script-engine/src/UndoStackScriptingInterface.h @@ -33,10 +33,10 @@ class ScriptUndoCommand : public QObject, public QUndoCommand { public: ScriptUndoCommand(QScriptValue undoFunction, QScriptValue undoData, QScriptValue redoFunction, QScriptValue redoData); - virtual void undo(); - virtual void redo(); - virtual bool mergeWith(const QUndoCommand* command) { return false; } - virtual int id() const { return -1; } + virtual void undo() override; + virtual void redo() override; + virtual bool mergeWith(const QUndoCommand* command) override { return false; } + virtual int id() const override { return -1; } public slots: void doUndo(); diff --git a/libraries/shared/src/ColorUtils.cpp b/libraries/shared/src/ColorUtils.cpp new file mode 100644 index 0000000000..f0dfc89367 --- /dev/null +++ b/libraries/shared/src/ColorUtils.cpp @@ -0,0 +1,59 @@ +// +// ColorUtils.cpp +// libraries/shared/src +// +// Created by Ryan Huffman on 8/8/16. +// 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 "ColorUtils.h" + +// Generated from python script in repository at `tools/srgb_gen.py` +const float srgbToLinearLookupTable[256] = { + 0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f, + 0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.0033465357639f, 0.00367650732405f, + 0.0040247170185f, 0.00439144203741f, 0.00477695348069f, 0.00518151670234f, 0.0056053916242f, 0.00604883302286f, + 0.00651209079259f, 0.00699541018727f, 0.00749903204323f, 0.00802319298538f, 0.00856812561807f, 0.00913405870222f, + 0.00972121732024f, 0.0103298230296f, 0.0109600940065f, 0.0116122451797f, 0.0122864883569f, 0.0129830323422f, + 0.0137020830473f, 0.0144438435961f, 0.0152085144229f, 0.0159962933655f, 0.0168073757529f, 0.0176419544884f, + 0.0185002201284f, 0.0193823609569f, 0.0202885630567f, 0.021219010376f, 0.0221738847934f, 0.0231533661781f, + 0.0241576324485f, 0.0251868596274f, 0.0262412218948f, 0.0273208916391f, 0.0284260395044f, 0.0295568344378f, + 0.030713443733f, 0.031896033073f, 0.0331047665709f, 0.0343398068087f, 0.035601314875f, 0.0368894504011f, + 0.0382043715953f, 0.0395462352767f, 0.0409151969069f, 0.0423114106208f, 0.043735029257f, 0.0451862043857f, + 0.0466650863369f, 0.0481718242269f, 0.0497065659841f, 0.051269458374f, 0.0528606470232f, 0.0544802764424f, + 0.0561284900496f, 0.0578054301911f, 0.059511238163f, 0.0612460542316f, 0.0630100176532f, 0.0648032666929f, + 0.0666259386438f, 0.0684781698444f, 0.0703600956966f, 0.0722718506823f, 0.0742135683801f, 0.0761853814813f, + 0.0781874218052f, 0.0802198203145f, 0.0822827071298f, 0.0843762115441f, 0.0865004620365f, 0.0886555862858f, + 0.0908417111834f, 0.0930589628467f, 0.095307466631f, 0.0975873471419f, 0.0998987282471f, 0.102241733088f, + 0.104616484091f, 0.107023102978f, 0.109461710778f, 0.111932427837f, 0.114435373827f, 0.116970667759f, + 0.119538427988f, 0.12213877223f, 0.124771817561f, 0.127437680436f, 0.13013647669f, 0.132868321554f, + 0.135633329655f, 0.138431615032f, 0.14126329114f, 0.144128470858f, 0.147027266498f, 0.149959789811f, + 0.152926151996f, 0.155926463708f, 0.158960835061f, 0.162029375639f, 0.165132194502f, 0.16826940019f, + 0.171441100733f, 0.174647403656f, 0.177888415984f, 0.18116424425f, 0.1844749945f, 0.187820772301f, + 0.191201682741f, 0.194617830442f, 0.19806931956f, 0.201556253794f, 0.20507873639f, 0.208636870145f, + 0.212230757414f, 0.215860500114f, 0.219526199729f, 0.223227957317f, 0.22696587351f, 0.230740048524f, + 0.234550582161f, 0.238397573812f, 0.242281122466f, 0.246201326708f, 0.25015828473f, 0.254152094331f, + 0.258182852922f, 0.26225065753f, 0.266355604803f, 0.270497791013f, 0.27467731206f, 0.278894263477f, + 0.28314874043f, 0.287440837727f, 0.291770649818f, 0.296138270798f, 0.300543794416f, 0.30498731407f, + 0.309468922818f, 0.313988713376f, 0.318546778125f, 0.323143209113f, 0.327778098057f, 0.332451536346f, + 0.337163615048f, 0.341914424909f, 0.346704056355f, 0.3515325995f, 0.356400144146f, 0.361306779784f, + 0.366252595599f, 0.371237680474f, 0.376262122991f, 0.381326011433f, 0.386429433787f, 0.39157247775f, + 0.396755230726f, 0.401977779832f, 0.407240211902f, 0.412542613484f, 0.417885070848f, 0.423267669986f, + 0.428690496614f, 0.434153636175f, 0.439657173841f, 0.445201194516f, 0.450785782838f, 0.45641102318f, + 0.462076999654f, 0.467783796112f, 0.473531496148f, 0.479320183101f, 0.485149940056f, 0.491020849848f, + 0.496932995061f, 0.502886458033f, 0.508881320855f, 0.514917665377f, 0.520995573204f, 0.527115125706f, + 0.533276404011f, 0.539479489012f, 0.54572446137f, 0.552011401512f, 0.558340389634f, 0.564711505705f, + 0.571124829465f, 0.57758044043f, 0.584078417891f, 0.590618840919f, 0.597201788364f, 0.603827338855f, + 0.610495570808f, 0.61720656242f, 0.623960391675f, 0.630757136346f, 0.637596873994f, 0.644479681971f, + 0.65140563742f, 0.658374817279f, 0.665387298282f, 0.672443156958f, 0.679542469633f, 0.686685312435f, + 0.693871761292f, 0.701101891933f, 0.708375779892f, 0.715693500506f, 0.723055128922f, 0.73046074009f, + 0.737910408773f, 0.74540420954f, 0.752942216776f, 0.760524504675f, 0.768151147248f, 0.775822218317f, + 0.783537791526f, 0.791297940333f, 0.799102738014f, 0.806952257669f, 0.814846572216f, 0.822785754396f, + 0.830769876775f, 0.838799011741f, 0.84687323151f, 0.854992608124f, 0.863157213454f, 0.871367119199f, + 0.879622396888f, 0.887923117882f, 0.896269353374f, 0.904661174391f, 0.913098651793f, 0.921581856277f, + 0.930110858375f, 0.938685728458f, 0.947306536733f, 0.955973353249f, 0.964686247894f, 0.973445290398f, + 0.982250550333f, 0.991102097114f, 1.0f +}; \ No newline at end of file diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index 5ee9254bc9..fd0bbdd8ab 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -13,10 +13,13 @@ #define hifi_ColorUtils_h #include -#include + +#include "SharedUtil.h" #include "DependencyManager.h" +extern const float srgbToLinearLookupTable[256]; + class ColorUtils { public: inline static glm::vec3 toVec3(const xColor& color); @@ -33,6 +36,7 @@ public: inline static glm::vec4 tosRGBVec4(const glm::vec4& srgb); inline static float sRGBToLinearFloat(const float& srgb); + inline static float sRGB8ToLinearFloat(const uint8_t srgb); inline static float tosRGBFloat(const float& linear); }; @@ -82,6 +86,9 @@ inline float ColorUtils::sRGBToLinearFloat(const float &srgb) { return linearValue; } +inline float ColorUtils::sRGB8ToLinearFloat(const uint8_t srgb) { + return srgbToLinearLookupTable[srgb]; +} // This is based upon the conversions found in section 17.3.9 of the OpenGL 4.4 specification. // glm::pow(color, 1.0f/2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers. diff --git a/libraries/shared/src/CrashHelpers.h b/libraries/shared/src/CrashHelpers.h index dae39d4a99..6233ab9177 100644 --- a/libraries/shared/src/CrashHelpers.h +++ b/libraries/shared/src/CrashHelpers.h @@ -30,7 +30,7 @@ private: class B : public A { public: B() : A(this) { } - virtual void virtualFunction() { } + virtual void virtualFunction() override { } }; A::~A() { diff --git a/libraries/shared/src/GenericQueueThread.h b/libraries/shared/src/GenericQueueThread.h index 7c1bdccaa7..5c16693d53 100644 --- a/libraries/shared/src/GenericQueueThread.h +++ b/libraries/shared/src/GenericQueueThread.h @@ -55,17 +55,7 @@ public: } } -protected: - virtual void queueItemInternal(const T& t) { - _items.push_back(t); - } - - virtual uint32_t getMaxWait() { - return MSECS_PER_SECOND; - } - - - virtual bool process() { + virtual bool process() override { lock(); if (!_items.size()) { unlock(); @@ -88,6 +78,17 @@ protected: return processQueueItems(processItems); } +protected: + virtual void queueItemInternal(const T& t) { + _items.push_back(t); + } + + virtual uint32_t getMaxWait() { + return MSECS_PER_SECOND; + } + + + virtual bool processQueueItems(const Queue& items) = 0; Queue _items; diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index 47f6d9dacd..09872b32cd 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -37,6 +37,11 @@ public: bool isThreaded() const { return _isThreaded; } + /// Override this function to do whatever your class actually does, return false to exit thread early. + virtual bool process() = 0; + virtual void setup() {}; + virtual void shutdown() {}; + public slots: /// If you're running in non-threaded mode, you must call this regularly void threadRoutine(); @@ -45,10 +50,6 @@ signals: void finished(); protected: - /// Override this function to do whatever your class actually does, return false to exit thread early. - virtual bool process() = 0; - virtual void setup() {}; - virtual void shutdown() {}; /// Locks all the resources of the thread. void lock() { _mutex.lock(); } diff --git a/libraries/shared/src/Interpolate.cpp b/libraries/shared/src/Interpolate.cpp index bef69c9a33..b20c7b8fac 100644 --- a/libraries/shared/src/Interpolate.cpp +++ b/libraries/shared/src/Interpolate.cpp @@ -14,6 +14,8 @@ #include #include +#include "NumericalConstants.h" + float Interpolate::bezierInterpolate(float y1, float y2, float y3, float u) { // https://en.wikipedia.org/wiki/Bezier_curve assert(0.0f <= u && u <= 1.0f); @@ -58,3 +60,13 @@ float Interpolate::interpolate3Points(float y1, float y2, float y3, float u) { } } } + +float Interpolate::calculateFadeRatio(quint64 start) { + const float FADE_TIME = 1.0f; + float t = 2.0f * std::min(((float)(usecTimestampNow() - start)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); + float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-powf(2.0f, -10.0f * (t - 1.0f)) + 2.0f); + + // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly + const float EASING_SCALE = 1.001f; + return std::min(EASING_SCALE * fadeRatio, 1.0f); +} \ No newline at end of file diff --git a/libraries/shared/src/Interpolate.h b/libraries/shared/src/Interpolate.h index 316bee1339..a9fa5baad2 100644 --- a/libraries/shared/src/Interpolate.h +++ b/libraries/shared/src/Interpolate.h @@ -12,6 +12,8 @@ #ifndef hifi_Interpolate_h #define hifi_Interpolate_h +#include "SharedUtil.h" + class Interpolate { public: @@ -22,6 +24,8 @@ public: // Interpolate at position u [0.0 - 1.0] between y values equally spaced along the x-axis such that the interpolated values // pass through all three y values. Return value lies wholly within the range of y values passed in. static float interpolate3Points(float y1, float y2, float y3, float u); + + static float calculateFadeRatio(quint64 start); }; #endif // hifi_Interpolate_h diff --git a/libraries/shared/src/Preferences.h b/libraries/shared/src/Preferences.h index 0b8140af1b..abb3f5afbd 100644 --- a/libraries/shared/src/Preferences.h +++ b/libraries/shared/src/Preferences.h @@ -105,7 +105,7 @@ public: using Lambda = std::function; ButtonPreference(const QString& category, const QString& name, Lambda triggerHandler) : Preference(category, name), _triggerHandler(triggerHandler) { } - Type getType() { return Button; } + Type getType() override { return Button; } Q_INVOKABLE void trigger() { _triggerHandler(); } protected: @@ -210,7 +210,7 @@ public: SliderPreference(const QString& category, const QString& name, Getter getter, Setter setter) : FloatPreference(category, name, getter, setter) { } - Type getType() { return Slider; } + Type getType() override { return Slider; } }; class SpinnerPreference : public FloatPreference { @@ -219,7 +219,7 @@ public: SpinnerPreference(const QString& category, const QString& name, Getter getter, Setter setter) : FloatPreference(category, name, getter, setter) { } - Type getType() { return Spinner; } + Type getType() override { return Spinner; } }; class EditPreference : public StringPreference { @@ -229,7 +229,7 @@ class EditPreference : public StringPreference { public: EditPreference(const QString& category, const QString& name, Getter getter, Setter setter) : StringPreference(category, name, getter, setter) { } - Type getType() { return Editable; } + Type getType() override { return Editable; } const QString& getPlaceholderText() const { return _placeholderText; } void setPlaceholderText(const QString& placeholderText) { _placeholderText = placeholderText; } @@ -244,7 +244,7 @@ class ComboBoxPreference : public EditPreference { public: ComboBoxPreference(const QString& category, const QString& name, Getter getter, Setter setter) : EditPreference(category, name, getter, setter) { } - Type getType() { return ComboBox; } + Type getType() override { return ComboBox; } const QStringList& getItems() { return _items; } void setItems(const QStringList& items) { _items = items; } @@ -260,7 +260,7 @@ class BrowsePreference : public EditPreference { public: BrowsePreference(const QString& category, const QString& name, Getter getter, Setter setter) : EditPreference(category, name, getter, setter) { } - Type getType() { return Browsable; } + Type getType() override { return Browsable; } const QString& getBrowseLabel() { return _browseLabel; } void setBrowseLabel(const QString& browseLabel) { _browseLabel = browseLabel; } @@ -276,7 +276,7 @@ public: : BrowsePreference(category, name, getter, setter) { _browseLabel = "Change"; } - Type getType() { return Avatar; } + Type getType() override { return Avatar; } }; @@ -285,7 +285,7 @@ class CheckPreference : public BoolPreference { public: CheckPreference(const QString& category, const QString& name, Getter getter, Setter setter) : BoolPreference(category, name, getter, setter) { } - Type getType() { return Checkbox; } + Type getType() override { return Checkbox; } }; #endif diff --git a/libraries/shared/src/SettingHandle.h b/libraries/shared/src/SettingHandle.h index 8e07d28dad..36d4f0b249 100644 --- a/libraries/shared/src/SettingHandle.h +++ b/libraries/shared/src/SettingHandle.h @@ -70,57 +70,57 @@ namespace Setting { public: Handle(const QString& key) : Interface(key) {} Handle(const QStringList& path) : Interface(path.join("/")) {} - + Handle(const QString& key, const T& defaultValue) : Interface(key), _defaultValue(defaultValue) {} Handle(const QStringList& path, const T& defaultValue) : Handle(path.join("/"), defaultValue) {} - + virtual ~Handle() { deinit(); } - + // Returns setting value, returns its default value if not found - T get() const { - return get(_defaultValue); + T get() const { + return get(_defaultValue); } // Returns setting value, returns other if not found - T get(const T& other) const { - maybeInit(); - return (_isSet) ? _value : other; + T get(const T& other) const { + maybeInit(); + return (_isSet) ? _value : other; } - const T& getDefault() const { - return _defaultValue; - } - - void reset() { - set(_defaultValue); + const T& getDefault() const { + return _defaultValue; } - void set(const T& value) { - maybeInit(); + void reset() { + set(_defaultValue); + } + + void set(const T& value) { + maybeInit(); if ((!_isSet && (value != _defaultValue)) || _value != value) { - _value = value; - _isSet = true; - save(); - } + _value = value; + _isSet = true; + save(); + } } - void remove() { - maybeInit(); - if (_isSet) { - _isSet = false; - save(); - } + void remove() { + maybeInit(); + if (_isSet) { + _isSet = false; + save(); + } } - + protected: - virtual void setVariant(const QVariant& variant); - virtual QVariant getVariant() { return QVariant::fromValue(get()); } - + virtual void setVariant(const QVariant& variant) override; + virtual QVariant getVariant() override { return QVariant::fromValue(get()); } + private: T _value; const T _defaultValue; }; - + template void Handle::setVariant(const QVariant& variant) { if (variant.canConvert() || std::is_same::value) { diff --git a/libraries/shared/src/ShutdownEventListener.h b/libraries/shared/src/ShutdownEventListener.h index f6d50401b2..8a9363e2c8 100644 --- a/libraries/shared/src/ShutdownEventListener.h +++ b/libraries/shared/src/ShutdownEventListener.h @@ -20,7 +20,7 @@ class ShutdownEventListener : public QObject, public QAbstractNativeEventFilter public: static ShutdownEventListener& getInstance(); - virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result); + virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; private: ShutdownEventListener(QObject* parent = 0); }; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 2a3cb4af47..453035de0f 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -26,6 +26,12 @@ SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) : _transform.setRotation(glm::quat()); } +SpatiallyNestable::~SpatiallyNestable() { + forEachChild([&](SpatiallyNestablePointer object) { + object->parentDeleted(); + }); +} + const QUuid SpatiallyNestable::getID() const { QUuid result; _idLock.withReadLock([&] { @@ -35,6 +41,10 @@ const QUuid SpatiallyNestable::getID() const { } void SpatiallyNestable::setID(const QUuid& id) { + // adjust the parentID of any children + forEachChild([&](SpatiallyNestablePointer object) { + object->setParentID(id); + }); _idLock.withWriteLock([&] { _id = id; }); @@ -214,6 +224,38 @@ glm::quat SpatiallyNestable::worldToLocal(const glm::quat& orientation, return result.getRotation(); } +glm::vec3 SpatiallyNestable::worldToLocalVelocity(const glm::vec3& velocity, const QUuid& parentID, + int parentJointIndex, bool& success) { + SpatiallyNestablePointer parent = SpatiallyNestable::findByID(parentID, success); + if (!success || !parent) { + return velocity; + } + Transform parentTransform = parent->getTransform(success); + if (!success) { + return velocity; + } + glm::vec3 parentVelocity = parent->getVelocity(success); + if (!success) { + return velocity; + } + + return glm::inverse(parentTransform.getRotation()) * (velocity - parentVelocity); +} + +glm::vec3 SpatiallyNestable::worldToLocalAngularVelocity(const glm::vec3& angularVelocity, const QUuid& parentID, + int parentJointIndex, bool& success) { + SpatiallyNestablePointer parent = SpatiallyNestable::findByID(parentID, success); + if (!success || !parent) { + return angularVelocity; + } + Transform parentTransform = parent->getTransform(success); + if (!success) { + return angularVelocity; + } + + return glm::inverse(parentTransform.getRotation()) * angularVelocity; +} + glm::vec3 SpatiallyNestable::localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex, bool& success) { @@ -288,6 +330,38 @@ glm::quat SpatiallyNestable::localToWorld(const glm::quat& orientation, return result.getRotation(); } +glm::vec3 SpatiallyNestable::localToWorldVelocity(const glm::vec3& velocity, const QUuid& parentID, + int parentJointIndex, bool& success) { + SpatiallyNestablePointer parent = SpatiallyNestable::findByID(parentID, success); + if (!success || !parent) { + return velocity; + } + Transform parentTransform = parent->getTransform(success); + if (!success) { + return velocity; + } + glm::vec3 parentVelocity = parent->getVelocity(success); + if (!success) { + return velocity; + } + + return parentVelocity + parentTransform.getRotation() * velocity; +} + +glm::vec3 SpatiallyNestable::localToWorldAngularVelocity(const glm::vec3& angularVelocity, const QUuid& parentID, + int parentJointIndex, bool& success) { + SpatiallyNestablePointer parent = SpatiallyNestable::findByID(parentID, success); + if (!success || !parent) { + return angularVelocity; + } + Transform parentTransform = parent->getTransform(success); + if (!success) { + return angularVelocity; + } + + return parentTransform.getRotation() * angularVelocity; +} + glm::vec3 SpatiallyNestable::getPosition(bool& success) const { return getTransform(success).getTranslation(); } @@ -313,17 +387,20 @@ void SpatiallyNestable::setPosition(const glm::vec3& position, bool& success, bo success = false; return; } + + bool changed = false; Transform parentTransform = getParentTransform(success); Transform myWorldTransform; _transformLock.withWriteLock([&] { Transform::mult(myWorldTransform, parentTransform, _transform); - myWorldTransform.setTranslation(position); - Transform::inverseMult(_transform, parentTransform, myWorldTransform); + if (myWorldTransform.getTranslation() != position) { + changed = true; + myWorldTransform.setTranslation(position); + Transform::inverseMult(_transform, parentTransform, myWorldTransform); + } }); - if (success) { + if (success && changed) { locationChanged(tellPhysics); - } else { - qDebug() << "setPosition failed for" << getID(); } } @@ -363,14 +440,18 @@ void SpatiallyNestable::setOrientation(const glm::quat& orientation, bool& succe return; } + bool changed = false; Transform parentTransform = getParentTransform(success); Transform myWorldTransform; _transformLock.withWriteLock([&] { Transform::mult(myWorldTransform, parentTransform, _transform); - myWorldTransform.setRotation(orientation); - Transform::inverseMult(_transform, parentTransform, myWorldTransform); + if (myWorldTransform.getRotation() != orientation) { + changed = true; + myWorldTransform.setRotation(orientation); + Transform::inverseMult(_transform, parentTransform, myWorldTransform); + } }); - if (success) { + if (success && changed) { locationChanged(tellPhysics); } } @@ -513,6 +594,15 @@ const Transform SpatiallyNestable::getTransform(bool& success, int depth) const return result; } +const Transform SpatiallyNestable::getTransform() const { + bool success; + Transform result = getTransform(success); + if (!success) { + qDebug() << "getTransform failed for" << getID(); + } + return result; +} + const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success, int depth) const { // this returns the world-space transform for this object. It finds its parent's transform (which may // cause this object's parent to query its parent, etc) and multiplies this object's local transform onto it. @@ -549,15 +639,27 @@ void SpatiallyNestable::setTransform(const Transform& transform, bool& success) success = false; return; } + + bool changed = false; Transform parentTransform = getParentTransform(success); _transformLock.withWriteLock([&] { + Transform beforeTransform = _transform; Transform::inverseMult(_transform, parentTransform, transform); + if (_transform != beforeTransform) { + changed = true; + } }); - if (success) { + if (success && changed) { locationChanged(); } } +bool SpatiallyNestable::setTransform(const Transform& transform) { + bool success; + setTransform(transform, success); + return success; +} + glm::vec3 SpatiallyNestable::getScale() const { // TODO: scale glm::vec3 result; @@ -575,14 +677,43 @@ glm::vec3 SpatiallyNestable::getScale(int jointIndex) const { void SpatiallyNestable::setScale(const glm::vec3& scale) { // guard against introducing NaN into the transform if (isNaN(scale)) { - qDebug() << "SpatiallyNestable::setLocalScale -- scale contains NaN"; + qDebug() << "SpatiallyNestable::setScale -- scale contains NaN"; return; } + + bool changed = false; // TODO: scale _transformLock.withWriteLock([&] { - _transform.setScale(scale); + if (_transform.getScale() != scale) { + _transform.setScale(scale); + changed = true; + } }); - dimensionsChanged(); + if (changed) { + dimensionsChanged(); + } +} + +void SpatiallyNestable::setScale(float value) { + // guard against introducing NaN into the transform + if (value <= 0.0f) { + qDebug() << "SpatiallyNestable::setScale -- scale is zero or negative value"; + return; + } + + bool changed = false; + // TODO: scale + _transformLock.withWriteLock([&] { + glm::vec3 beforeScale = _transform.getScale(); + _transform.setScale(value); + if (_transform.getScale() != beforeScale) { + changed = true; + } + }); + + if (changed) { + dimensionsChanged(); + } } const Transform SpatiallyNestable::getLocalTransform() const { @@ -599,10 +730,18 @@ void SpatiallyNestable::setLocalTransform(const Transform& transform) { qDebug() << "SpatiallyNestable::setLocalTransform -- transform contains NaN"; return; } + + bool changed = false; _transformLock.withWriteLock([&] { - _transform = transform; + if (_transform != transform) { + _transform = transform; + changed = true; + } }); - locationChanged(); + + if (changed) { + locationChanged(); + } } glm::vec3 SpatiallyNestable::getLocalPosition() const { @@ -619,10 +758,16 @@ void SpatiallyNestable::setLocalPosition(const glm::vec3& position, bool tellPhy qDebug() << "SpatiallyNestable::setLocalPosition -- position contains NaN"; return; } + bool changed = false; _transformLock.withWriteLock([&] { - _transform.setTranslation(position); + if (_transform.getTranslation() != position) { + _transform.setTranslation(position); + changed = true; + } }); - locationChanged(tellPhysics); + if (changed) { + locationChanged(tellPhysics); + } } glm::quat SpatiallyNestable::getLocalOrientation() const { @@ -639,10 +784,16 @@ void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) { qDebug() << "SpatiallyNestable::setLocalOrientation -- orientation contains NaN"; return; } + bool changed = false; _transformLock.withWriteLock([&] { - _transform.setRotation(orientation); + if (_transform.getRotation() != orientation) { + _transform.setRotation(orientation); + changed = true; + } }); - locationChanged(); + if (changed) { + locationChanged(); + } } glm::vec3 SpatiallyNestable::getLocalVelocity() const { @@ -688,9 +839,14 @@ void SpatiallyNestable::setLocalScale(const glm::vec3& scale) { qDebug() << "SpatiallyNestable::setLocalScale -- scale contains NaN"; return; } + + bool changed = false; // TODO: scale _transformLock.withWriteLock([&] { - _transform.setScale(scale); + if (_transform.getScale() != scale) { + _transform.setScale(scale); + changed = true; + } }); dimensionsChanged(); } @@ -886,12 +1042,18 @@ void SpatiallyNestable::getLocalTransformAndVelocities( } void SpatiallyNestable::setLocalTransformAndVelocities( - const Transform& localTransform, - const glm::vec3& localVelocity, - const glm::vec3& localAngularVelocity) { + const Transform& localTransform, + const glm::vec3& localVelocity, + const glm::vec3& localAngularVelocity) { + + bool changed = false; + // transform _transformLock.withWriteLock([&] { - _transform = localTransform; + if (_transform != localTransform) { + _transform = localTransform; + changed = true; + } }); // linear velocity _velocityLock.withWriteLock([&] { @@ -901,5 +1063,20 @@ void SpatiallyNestable::setLocalTransformAndVelocities( _angularVelocityLock.withWriteLock([&] { _angularVelocity = localAngularVelocity; }); - locationChanged(false); + + if (changed) { + locationChanged(false); + } +} + +SpatiallyNestablePointer SpatiallyNestable::findByID(QUuid id, bool& success) { + QSharedPointer parentFinder = DependencyManager::get(); + if (!parentFinder) { + return nullptr; + } + SpatiallyNestableWeakPointer parentWP = parentFinder->find(id, success); + if (!success) { + return nullptr; + } + return parentWP.lock(); } diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index c2563a1188..5605cc0031 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -28,13 +28,14 @@ using SpatiallyNestableConstPointer = std::shared_ptr; enum class NestableType { Entity, - Avatar + Avatar, + Overlay }; class SpatiallyNestable : public std::enable_shared_from_this { public: SpatiallyNestable(NestableType nestableType, QUuid id); - virtual ~SpatiallyNestable() { } + virtual ~SpatiallyNestable(); virtual const QUuid getID() const; virtual void setID(const QUuid& id); @@ -47,13 +48,23 @@ public: static glm::vec3 worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex, bool& success); static glm::quat worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex, bool& success); + static glm::vec3 worldToLocalVelocity(const glm::vec3& velocity, const QUuid& parentID, + int parentJointIndex, bool& success); + static glm::vec3 worldToLocalAngularVelocity(const glm::vec3& angularVelocity, const QUuid& parentID, + int parentJointIndex, bool& success); static glm::vec3 localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex, bool& success); static glm::quat localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex, bool& success); + static glm::vec3 localToWorldVelocity(const glm::vec3& velocity, + const QUuid& parentID, int parentJointIndex, bool& success); + static glm::vec3 localToWorldAngularVelocity(const glm::vec3& angularVelocity, + const QUuid& parentID, int parentJointIndex, bool& success); // world frame virtual const Transform getTransform(bool& success, int depth = 0) const; + virtual const Transform getTransform() const; virtual void setTransform(const Transform& transform, bool& success); + virtual bool setTransform(const Transform& transform); virtual Transform getParentTransform(bool& success, int depth = 0) const; @@ -68,6 +79,10 @@ public: virtual void setOrientation(const glm::quat& orientation, bool& success, bool tellPhysics = true); virtual void setOrientation(const glm::quat& orientation); + // these are here because some older code uses rotation rather than orientation + virtual const glm::quat getRotation() const { return getOrientation(); } + virtual void setRotation(glm::quat orientation) { setOrientation(orientation); } + virtual glm::vec3 getVelocity(bool& success) const; virtual glm::vec3 getVelocity() const; virtual void setVelocity(const glm::vec3& velocity, bool& success); @@ -91,6 +106,7 @@ public: virtual glm::vec3 getScale() const; virtual void setScale(const glm::vec3& scale); + virtual void setScale(float value); // get world-frame values for a specific joint virtual const Transform getTransform(int jointIndex, bool& success, int depth = 0) const; @@ -123,10 +139,10 @@ public: // this object's frame virtual const Transform getAbsoluteJointTransformInObjectFrame(int jointIndex) const; - virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const = 0; - virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const = 0; - virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) = 0; - virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) = 0; + virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const { return glm::quat(); } + virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const { return glm::vec3(); } + virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) { return false; } + virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) {return false; } SpatiallyNestablePointer getThisPointer() const; @@ -143,6 +159,8 @@ public: virtual SpatialParentTree* getParentTree() const { return nullptr; } bool hasAncestorOfType(NestableType nestableType); + SpatiallyNestablePointer getParentPointer(bool& success) const; + static SpatiallyNestablePointer findByID(QUuid id, bool& success); void getLocalTransformAndVelocities(Transform& localTransform, glm::vec3& localVelocity, @@ -158,7 +176,6 @@ protected: QUuid _id; QUuid _parentID; // what is this thing's transform relative to? quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to? - SpatiallyNestablePointer getParentPointer(bool& success) const; mutable SpatiallyNestableWeakPointer _parent; @@ -170,6 +187,7 @@ protected: virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed virtual void dimensionsChanged() { } // called when a this object's dimensions have changed + virtual void parentDeleted() { } // called on children of a deleted parent // _queryAACube is used to decide where something lives in the octree mutable AACube _queryAACube; diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 1e1d10c54b..38d47695f7 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -89,6 +89,10 @@ public: return _rotation == other._rotation && _scale == other._scale && _translation == other._translation; } + bool operator!=(const Transform& other) const { + return _rotation != other._rotation || _scale != other._scale || _translation != other._translation; + } + Transform& setIdentity(); const Vec3& getTranslation() const; diff --git a/libraries/shared/src/shared/NsightHelpers.cpp b/libraries/shared/src/shared/NsightHelpers.cpp index 2539ff8864..84fde7887b 100644 --- a/libraries/shared/src/shared/NsightHelpers.cpp +++ b/libraries/shared/src/shared/NsightHelpers.cpp @@ -8,16 +8,24 @@ #include "NsightHelpers.h" -#ifdef _WIN32 -#if defined(NSIGHT_FOUND) + +#if defined(_WIN32) && defined(NSIGHT_FOUND) + +#include #include "nvToolsExt.h" +static const QString NSIGHT_FLAG("NSIGHT_LAUNCHED"); +static const bool nsightLaunched = QProcessEnvironment::systemEnvironment().contains(NSIGHT_FLAG); + +bool nsightActive() { + return nsightLaunched; +} + ProfileRange::ProfileRange(const char *name) { - nvtxRangePush(name); + _rangeId = nvtxRangeStart(name); } ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) { - nvtxEventAttributes_t eventAttrib = {0}; eventAttrib.version = NVTX_VERSION; eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; @@ -28,16 +36,17 @@ ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payloa eventAttrib.payload.llValue = payload; eventAttrib.payloadType = NVTX_PAYLOAD_TYPE_UNSIGNED_INT64; - nvtxRangePushEx(&eventAttrib); + _rangeId = nvtxRangeStartEx(&eventAttrib); } ProfileRange::~ProfileRange() { - nvtxRangePop(); + nvtxRangeEnd(_rangeId); } #else -ProfileRange::ProfileRange(const char *name) {} -ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {} -ProfileRange::~ProfileRange() {} -#endif + +bool nsightActive() { + return false; +} + #endif // _WIN32 diff --git a/libraries/shared/src/shared/NsightHelpers.h b/libraries/shared/src/shared/NsightHelpers.h index c637c78162..94cb8d5263 100644 --- a/libraries/shared/src/shared/NsightHelpers.h +++ b/libraries/shared/src/shared/NsightHelpers.h @@ -9,7 +9,9 @@ #ifndef hifi_gl_NsightHelpers_h #define hifi_gl_NsightHelpers_h -#ifdef _WIN32 +bool nsightActive(); + +#if defined(_WIN32) && defined(NSIGHT_FOUND) #include class ProfileRange { @@ -17,6 +19,8 @@ public: ProfileRange(const char *name); ProfileRange(const char *name, uint32_t argbColor, uint64_t payload); ~ProfileRange(); +private: + uint64_t _rangeId{ 0 }; }; #define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index 0f06e03672..235d258d21 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -11,9 +11,306 @@ #include "SteamClient.h" +#include + +#include +#include +#include +#include +#include +#include + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverloaded-virtual" +#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif +#endif + #include +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + +static const Ticket INVALID_TICKET = Ticket(); + +class SteamTicketRequests { +public: + SteamTicketRequests(); + ~SteamTicketRequests(); + + HAuthTicket startRequest(TicketRequestCallback callback); + void stopRequest(HAuthTicket authTicket); + void stopAll(); + + STEAM_CALLBACK(SteamTicketRequests, onGetAuthSessionTicketResponse, + GetAuthSessionTicketResponse_t, _getAuthSessionTicketResponse); + +private: + struct PendingTicket { + HAuthTicket authTicket; + Ticket ticket; + TicketRequestCallback callback; + }; + + std::vector _pendingTickets; +}; + +SteamTicketRequests::SteamTicketRequests() : + _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse) +{ +} + +SteamTicketRequests::~SteamTicketRequests() { + stopAll(); +} + +HAuthTicket SteamTicketRequests::startRequest(TicketRequestCallback callback) { + static const uint32 MAX_TICKET_SIZE { 1024 }; + uint32 ticketSize { 0 }; + char ticket[MAX_TICKET_SIZE]; + + auto authTicket = SteamUser()->GetAuthSessionTicket(ticket, MAX_TICKET_SIZE, &ticketSize); + qDebug() << "Got Steam auth session ticket:" << authTicket; + + if (authTicket == k_HAuthTicketInvalid) { + qWarning() << "Auth session ticket is invalid."; + callback(INVALID_TICKET); + } else { + PendingTicket pendingTicket{ authTicket, QByteArray(ticket, ticketSize).toHex(), callback }; + _pendingTickets.push_back(pendingTicket); + } + + return authTicket; +} + +void SteamTicketRequests::stopRequest(HAuthTicket authTicket) { + auto it = std::find_if(_pendingTickets.begin(), _pendingTickets.end(), [&authTicket](const PendingTicket& pendingTicket) { + return pendingTicket.authTicket == authTicket; + }); + + if (it != _pendingTickets.end()) { + SteamUser()->CancelAuthTicket(it->authTicket); + it->callback(INVALID_TICKET); + _pendingTickets.erase(it); + } +} + +void SteamTicketRequests::stopAll() { + auto steamUser = SteamUser(); + if (steamUser) { + for (const auto& pendingTicket : _pendingTickets) { + steamUser->CancelAuthTicket(pendingTicket.authTicket); + pendingTicket.callback(INVALID_TICKET); + } + } + _pendingTickets.clear(); +} + +void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketResponse_t* pCallback) { + auto authTicket = pCallback->m_hAuthTicket; + + auto it = std::find_if(_pendingTickets.begin(), _pendingTickets.end(), [&authTicket](const PendingTicket& pendingTicket) { + return pendingTicket.authTicket == authTicket; + }); + + + if (it != _pendingTickets.end()) { + + if (pCallback->m_eResult == k_EResultOK) { + qDebug() << "Got steam callback, auth session ticket is valid. Send it." << authTicket; + it->callback(it->ticket); + } else { + qWarning() << "Steam auth session ticket callback encountered an error:" << pCallback->m_eResult; + it->callback(INVALID_TICKET); + } + + _pendingTickets.erase(it); + } else { + qWarning() << "Could not find steam auth session ticket in list of pending tickets:" << authTicket; + } +} + + +const QString CONNECT_PREFIX = "--url \""; +const QString CONNECT_SUFFIX = "\""; + +class SteamCallbackManager { +public: + SteamCallbackManager(); + + STEAM_CALLBACK(SteamCallbackManager, onGameRichPresenceJoinRequested, + GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse); + + STEAM_CALLBACK(SteamCallbackManager, onLobbyCreated, + LobbyCreated_t, _lobbyCreatedResponse); + + STEAM_CALLBACK(SteamCallbackManager, onGameLobbyJoinRequested, + GameLobbyJoinRequested_t, _gameLobbyJoinRequestedResponse); + + STEAM_CALLBACK(SteamCallbackManager, onLobbyEnter, + LobbyEnter_t, _lobbyEnterResponse); + + SteamTicketRequests& getTicketRequests() { return _steamTicketRequests; } + +private: + SteamTicketRequests _steamTicketRequests; +}; + +SteamCallbackManager::SteamCallbackManager() : + _gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested), + _lobbyCreatedResponse(this, &SteamCallbackManager::onLobbyCreated), + _gameLobbyJoinRequestedResponse(this, &SteamCallbackManager::onGameLobbyJoinRequested), + _lobbyEnterResponse(this, &SteamCallbackManager::onLobbyEnter) +{ +} + +void parseUrlAndGo(QString url) { + if (url.startsWith(CONNECT_PREFIX) && url.endsWith(CONNECT_SUFFIX)) { + url.remove(0, CONNECT_PREFIX.size()); + url.remove(-CONNECT_SUFFIX.size(), CONNECT_SUFFIX.size()); + } + + qDebug() << "Joining Steam Friend at:" << url; + auto mimeData = new QMimeData(); + mimeData->setUrls(QList() << QUrl(url)); + auto event = new QDropEvent(QPointF(0, 0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier); + + QCoreApplication::postEvent(qApp, event); +} + +void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) { + auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect); + + parseUrlAndGo(url); +} +void SteamCallbackManager::onLobbyCreated(LobbyCreated_t* pCallback) { + if (pCallback->m_eResult == k_EResultOK) { + qDebug() << "Inviting steam friends" << pCallback->m_ulSteamIDLobby; + auto url = SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect"); + SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect", url); + SteamFriends()->ActivateGameOverlayInviteDialog(pCallback->m_ulSteamIDLobby); + } +} + +void SteamCallbackManager::onGameLobbyJoinRequested(GameLobbyJoinRequested_t* pCallback) { + qDebug() << "Joining Steam lobby" << pCallback->m_steamIDLobby.ConvertToUint64(); + SteamMatchmaking()->JoinLobby(pCallback->m_steamIDLobby); +} + +void SteamCallbackManager::onLobbyEnter(LobbyEnter_t* pCallback) { + if (pCallback->m_EChatRoomEnterResponse != k_EChatRoomEnterResponseSuccess) { + qWarning() << "An error occured while joing the Steam lobby:" << pCallback->m_EChatRoomEnterResponse; + return; + } + + qDebug() << "Entered Steam lobby" << pCallback->m_ulSteamIDLobby; + + if (SteamMatchmaking()->GetLobbyOwner(pCallback->m_ulSteamIDLobby) != SteamUser()->GetSteamID()) { + auto url = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect"); + qDebug() << "Jumping to" << url; + parseUrlAndGo(url); + SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby); + } +} + + +static std::atomic_bool initialized { false }; +static SteamCallbackManager steamCallbackManager; + + +bool SteamClient::isRunning() { + return initialized; +} + +bool SteamClient::init() { + if (SteamAPI_IsSteamRunning() && !initialized) { + initialized = SteamAPI_Init(); + } + return initialized; +} + +void SteamClient::shutdown() { + if (initialized) { + SteamAPI_Shutdown(); + } + + steamCallbackManager.getTicketRequests().stopAll(); +} + +void SteamClient::runCallbacks() { + if (!initialized) { + return; + } + + auto steamPipe = SteamAPI_GetHSteamPipe(); + if (!steamPipe) { + qDebug() << "Could not get SteamPipe"; + return; + } + + Steam_RunCallbacks(steamPipe, false); +} + +void SteamClient::requestTicket(TicketRequestCallback callback) { + if (!initialized) { + if (SteamAPI_IsSteamRunning()) { + init(); + } else { + qWarning() << "Steam is not running"; + callback(INVALID_TICKET); + return; + } + } + + if (!initialized) { + qDebug() << "Steam not initialized"; + return; + } + + steamCallbackManager.getTicketRequests().startRequest(callback); +} + +void SteamClient::updateLocation(QString status, QUrl locationUrl) { + if (!initialized) { + return; + } + + auto connectStr = locationUrl.isEmpty() ? "" : CONNECT_PREFIX + locationUrl.toString() + CONNECT_SUFFIX; + + SteamFriends()->SetRichPresence("status", status.toLocal8Bit().data()); + SteamFriends()->SetRichPresence("connect", connectStr.toLocal8Bit().data()); +} + +void SteamClient::openInviteOverlay() { + if (!initialized) { + return; + } + + qDebug() << "Creating Steam lobby"; + static const int MAX_LOBBY_SIZE = 20; + SteamMatchmaking()->CreateLobby(k_ELobbyTypePrivate, MAX_LOBBY_SIZE); +} + + +void SteamClient::joinLobby(QString lobbyIdStr) { + if (!initialized) { + if (SteamAPI_IsSteamRunning()) { + init(); + } else { + qWarning() << "Steam is not running"; + return; + } + } + + qDebug() << "Trying to join Steam lobby:" << lobbyIdStr; + CSteamID lobbyId(lobbyIdStr.toULongLong()); + SteamMatchmaking()->JoinLobby(lobbyId); +} diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h index 369641b0c7..5bf0d4db56 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h @@ -13,8 +13,43 @@ #ifndef hifi_SteamClient_h #define hifi_SteamClient_h -class SteamClient { +#include +#include +#include + +using Ticket = QByteArray; +using TicketRequestCallback = std::function; + +class QUrl; + +class SteamClient { +public: + static bool isRunning(); + + static bool init(); + static void shutdown(); + + static void runCallbacks(); + + static void requestTicket(TicketRequestCallback callback); + static void updateLocation(QString status, QUrl locationUrl); + static void openInviteOverlay(); + static void joinLobby(QString lobbyId); + +}; + +class SteamScriptingInterface : public QObject { + Q_OBJECT + + Q_PROPERTY(bool isRunning READ isRunning) + +public: + SteamScriptingInterface(QObject* parent) : QObject(parent) {} + +public slots: + bool isRunning() const { return SteamClient::isRunning(); } + void openInviteOverlay() const { SteamClient::openInviteOverlay(); } }; diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp index 2581389424..6d54351743 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp @@ -8,12 +8,14 @@ #include "PluginContainer.h" #include +#include #include #include #include #include #include +#include static PluginContainer* INSTANCE{ nullptr }; diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h index 74ac834057..167af100b3 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h @@ -58,8 +58,6 @@ public: virtual void showDisplayPluginsTools(bool show = true) = 0; virtual void requestReset() = 0; virtual bool makeRenderingContextCurrent() = 0; - virtual void releaseSceneTexture(const gpu::TexturePointer& texture) = 0; - virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) = 0; virtual GLWidget* getPrimaryWidget() = 0; virtual MainWindow* getPrimaryWindow() = 0; virtual QOpenGLContext* getPrimaryContext() = 0; diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp index a4c7cf9fb2..f768b5f227 100644 --- a/libraries/ui/src/CursorManager.cpp +++ b/libraries/ui/src/CursorManager.cpp @@ -26,7 +26,7 @@ namespace Cursor { class MouseInstance : public Instance { - Source getType() const { + Source getType() const override { return Source::MOUSE; } }; @@ -79,4 +79,4 @@ namespace Cursor { _scale = scale; } -} \ No newline at end of file +} diff --git a/libraries/ui/src/ErrorDialog.h b/libraries/ui/src/ErrorDialog.h index 38954714a7..4312e82036 100644 --- a/libraries/ui/src/ErrorDialog.h +++ b/libraries/ui/src/ErrorDialog.h @@ -36,7 +36,7 @@ signals: void textChanged(); protected slots: - virtual void accept(); + virtual void accept() override; private: QString _text; diff --git a/libraries/ui/src/MainWindow.h b/libraries/ui/src/MainWindow.h index 3fee62692d..db02bfa1b6 100644 --- a/libraries/ui/src/MainWindow.h +++ b/libraries/ui/src/MainWindow.h @@ -31,14 +31,14 @@ signals: void windowShown(bool shown); protected: - virtual void closeEvent(QCloseEvent* event); - virtual void moveEvent(QMoveEvent* event); - virtual void resizeEvent(QResizeEvent* event); - virtual void showEvent(QShowEvent* event); - virtual void hideEvent(QHideEvent* event); - virtual void changeEvent(QEvent* event); - virtual void dragEnterEvent(QDragEnterEvent *e); - virtual void dropEvent(QDropEvent *e); + virtual void closeEvent(QCloseEvent* event) override; + virtual void moveEvent(QMoveEvent* event) override; + virtual void resizeEvent(QResizeEvent* event) override; + virtual void showEvent(QShowEvent* event) override; + virtual void hideEvent(QHideEvent* event) override; + virtual void changeEvent(QEvent* event) override; + virtual void dragEnterEvent(QDragEnterEvent *e) override; + virtual void dropEvent(QDropEvent *e) override; private: Setting::Handle _windowGeometry; diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index c3ca5f54d9..554ae7d8c2 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -59,9 +59,10 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) { properties = context->argument(0).toVariant().toMap(); } - QString url = properties[SOURCE_PROPERTY].toString(); - if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:")) { - properties[SOURCE_PROPERTY] = QUrl::fromLocalFile(url).toString(); + QUrl url { properties[SOURCE_PROPERTY].toString() }; + if (url.scheme() != "http" && url.scheme() != "https" && url.scheme() != "file" && url.scheme() != "about" && + url.scheme() != "atp") { + properties[SOURCE_PROPERTY] = QUrl::fromLocalFile(url.toString()).toString(); } return properties; diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp index 985d2f3a02..fcce999839 100644 --- a/libraries/ui/src/ui/Menu.cpp +++ b/libraries/ui/src/ui/Menu.cpp @@ -21,6 +21,8 @@ using namespace ui; +static QList groups; + Menu::Menu() {} void Menu::toggleAdvancedMenus() { @@ -40,13 +42,23 @@ void Menu::saveSettings() { } void Menu::loadAction(Settings& settings, QAction& action) { - if (action.isChecked() != settings.value(action.text(), action.isChecked()).toBool()) { + QString prefix; + for (QString group : groups) { + prefix += group; + prefix += "/"; + } + if (action.isChecked() != settings.value(prefix + action.text(), action.isChecked()).toBool()) { action.trigger(); } } void Menu::saveAction(Settings& settings, QAction& action) { - settings.setValue(action.text(), action.isChecked()); + QString prefix; + for (QString group : groups) { + prefix += group; + prefix += "/"; + } + settings.setValue(prefix + action.text(), action.isChecked()); } void Menu::scanMenuBar(settingsAction modifySetting) { @@ -57,7 +69,9 @@ void Menu::scanMenuBar(settingsAction modifySetting) { } void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings) { - settings.beginGroup(menu.title()); + groups.push_back(menu.title()); + +// settings.beginGroup(menu.title()); foreach (QAction* action, menu.actions()) { if (action->menu()) { scanMenu(*action->menu(), modifySetting, settings); @@ -65,7 +79,9 @@ void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& setting modifySetting(settings, *action); } } - settings.endGroup(); +// settings.endGroup(); + + groups.pop_back(); } void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 778be08dcf..a768af932e 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -13,7 +13,9 @@ if (WIN32) set(TARGET_NAME oculus) setup_hifi_plugin(Multimedia) - link_hifi_libraries(shared gl gpu controllers ui plugins ui-plugins display-plugins input-plugins audio-client networking) + link_hifi_libraries(shared gl gpu gpu-gl controllers ui + plugins ui-plugins display-plugins input-plugins + audio-client networking render-utils) include_hifi_library_headers(octree) @@ -23,4 +25,4 @@ if (WIN32) target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) target_link_libraries(${TARGET_NAME} Winmm.lib) -endif() \ No newline at end of file +endif() diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index e26a48b89c..7690736a84 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "OculusHelpers.h" @@ -21,7 +22,7 @@ void OculusBaseDisplayPlugin::resetSensors() { bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { _currentRenderFrameInfo = FrameInfo(); - _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();; + _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); _currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex); auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrTrue); _currentRenderFrameInfo.renderPose = toGlm(trackingState.HeadPose.ThePose); @@ -40,7 +41,7 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { handPoses[hand] = glm::translate(glm::mat4(), correctedPose.translation) * glm::mat4_cast(correctedPose.rotation * HAND_TO_LASER_ROTATION); }); - withRenderThreadLock([&] { + withNonPresentThreadLock([&] { _uiModelTransform = DependencyManager::get()->getModelTransform(); _handPoses = handPoses; _frameInfos[frameIndex] = _currentRenderFrameInfo; @@ -112,3 +113,12 @@ void OculusBaseDisplayPlugin::internalDeactivate() { releaseOculusSession(); _session = nullptr; } + +void OculusBaseDisplayPlugin::updatePresentPose() { + //mat4 sensorResetMat; + //_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); + //_currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, _currentFrame->frameIndex); + //auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse); + //_currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose); + _currentPresentFrameInfo.presentPose = _currentPresentFrameInfo.renderPose; +} diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index 3e2c223908..503d8f0b90 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -28,6 +28,7 @@ protected: void customizeContext() override; bool internalActivate() override; void internalDeactivate() override; + void updatePresentPose() override; protected: ovrSession _session { nullptr }; diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.cpp b/plugins/oculus/src/OculusDebugDisplayPlugin.cpp index 7653fba764..f1d22f3ceb 100644 --- a/plugins/oculus/src/OculusDebugDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDebugDisplayPlugin.cpp @@ -11,7 +11,7 @@ const QString OculusDebugDisplayPlugin::NAME("Oculus Rift (Simulator)"); static const QString DEBUG_FLAG("HIFI_DEBUG_OCULUS"); -static bool enableDebugOculus = QProcessEnvironment::systemEnvironment().contains("HIFI_DEBUG_OCULUS"); +static bool enableDebugOculus = true || QProcessEnvironment::systemEnvironment().contains("HIFI_DEBUG_OCULUS"); bool OculusDebugDisplayPlugin::isSupported() const { if (!enableDebugOculus) { diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index 2b2ec5bdb0..f1cad94281 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -13,6 +13,9 @@ #include #include +#include +#include +#include #include "OculusHelpers.h" @@ -43,67 +46,95 @@ void OculusDisplayPlugin::cycleDebugOutput() { void OculusDisplayPlugin::customizeContext() { Parent::customizeContext(); - _sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_session)); - _sceneFbo->Init(getRecommendedRenderSize()); + _outputFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_SRGBA_32, _renderTargetSize.x, _renderTargetSize.y)); + ovrTextureSwapChainDesc desc = { }; + desc.Type = ovrTexture_2D; + desc.ArraySize = 1; + desc.Width = _renderTargetSize.x; + desc.Height = _renderTargetSize.y; + desc.MipLevels = 1; + desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; + desc.SampleCount = 1; + desc.StaticImage = ovrFalse; + + ovrResult result = ovr_CreateTextureSwapChainGL(_session, &desc, &_textureSwapChain); + if (!OVR_SUCCESS(result)) { + logFatal("Failed to create swap textures"); + } + + int length = 0; + result = ovr_GetTextureSwapChainLength(_session, _textureSwapChain, &length); + if (!OVR_SUCCESS(result) || !length) { + qFatal("Unable to count swap chain textures"); + } + for (int i = 0; i < length; ++i) { + GLuint chainTexId; + ovr_GetTextureSwapChainBufferGL(_session, _textureSwapChain, i, &chainTexId); + glBindTexture(GL_TEXTURE_2D, chainTexId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + glBindTexture(GL_TEXTURE_2D, 0); // We're rendering both eyes to the same texture, so only one of the // pointers is populated - _sceneLayer.ColorTexture[0] = _sceneFbo->color; + _sceneLayer.ColorTexture[0] = _textureSwapChain; // not needed since the structure was zeroed on init, but explicit _sceneLayer.ColorTexture[1] = nullptr; - - enableVsync(false); - // Only enable mirroring if we know vsync is disabled - _enablePreview = !isVsyncEnabled(); } void OculusDisplayPlugin::uncustomizeContext() { - using namespace oglplus; - +#if 0 // Present a final black frame to the HMD _compositeFramebuffer->Bound(FramebufferTarget::Draw, [] { Context::ClearColor(0, 0, 0, 1); Context::Clear().ColorBuffer(); }); - hmdPresent(); - -#if (OVR_MAJOR_VERSION >= 6) - _sceneFbo.reset(); #endif + + ovr_DestroyTextureSwapChain(_session, _textureSwapChain); + _textureSwapChain = nullptr; Parent::uncustomizeContext(); } - -template -void blit(const SrcFbo& srcFbo, const DstFbo& dstFbo) { - using namespace oglplus; - srcFbo->Bound(FramebufferTarget::Read, [&] { - dstFbo->Bound(FramebufferTarget::Draw, [&] { - Context::BlitFramebuffer( - 0, 0, srcFbo->size.x, srcFbo->size.y, - 0, 0, dstFbo->size.x, dstFbo->size.y, - BufferSelectBit::ColorBuffer, BlitFilter::Linear); - }); - }); -} - void OculusDisplayPlugin::hmdPresent() { - PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentPresentFrameIndex) + PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex) - if (!_currentSceneTexture) { - return; - } + int curIndex; + ovr_GetTextureSwapChainCurrentIndex(_session, _textureSwapChain, &curIndex); + GLuint curTexId; + ovr_GetTextureSwapChainBufferGL(_session, _textureSwapChain, curIndex, &curTexId); + + // Manually bind the texture to the FBO + // FIXME we should have a way of wrapping raw GL ids in GPU objects without + // taking ownership of the object + auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer); + glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0); + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setFramebuffer(_outputFramebuffer); + batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize())); + batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize())); + batch.resetViewTransform(); + batch.setProjectionTransform(mat4()); + batch.setPipeline(_presentPipeline); + batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0); - blit(_compositeFramebuffer, _sceneFbo); - _sceneFbo->Commit(); { + auto result = ovr_CommitTextureSwapChain(_session, _textureSwapChain); + Q_ASSERT(OVR_SUCCESS(result)); _sceneLayer.SensorSampleTime = _currentPresentFrameInfo.sensorSampleTime; _sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose); _sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose); ovrLayerHeader* layers = &_sceneLayer.Header; - ovrResult result = ovr_SubmitFrame(_session, _currentPresentFrameIndex, &_viewScaleDesc, &layers, 1); + result = ovr_SubmitFrame(_session, _currentFrame->frameIndex, &_viewScaleDesc, &layers, 1); if (!OVR_SUCCESS(result)) { logWarning("Failed to present"); } @@ -112,11 +143,11 @@ void OculusDisplayPlugin::hmdPresent() { bool OculusDisplayPlugin::isHmdMounted() const { ovrSessionStatus status; - return (OVR_SUCCESS(ovr_GetSessionStatus(_session, &status)) && + return (OVR_SUCCESS(ovr_GetSessionStatus(_session, &status)) && (ovrFalse != status.HmdMounted)); } -QString OculusDisplayPlugin::getPreferredAudioInDevice() const { +QString OculusDisplayPlugin::getPreferredAudioInDevice() const { WCHAR buffer[OVR_AUDIO_MAX_DEVICE_STR_SIZE]; if (!OVR_SUCCESS(ovr_GetAudioDeviceInGuidStr(buffer))) { return QString(); @@ -124,11 +155,10 @@ QString OculusDisplayPlugin::getPreferredAudioInDevice() const { return AudioClient::friendlyNameForAudioDevice(buffer); } -QString OculusDisplayPlugin::getPreferredAudioOutDevice() const { +QString OculusDisplayPlugin::getPreferredAudioOutDevice() const { WCHAR buffer[OVR_AUDIO_MAX_DEVICE_STR_SIZE]; if (!OVR_SUCCESS(ovr_GetAudioDeviceOutGuidStr(buffer))) { return QString(); } return AudioClient::friendlyNameForAudioDevice(buffer); } - diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index ed6e0d13ea..80705319c6 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -9,9 +9,6 @@ #include "OculusBaseDisplayPlugin.h" -struct SwapFramebufferWrapper; -using SwapFboPtr = QSharedPointer; - class OculusDisplayPlugin : public OculusBaseDisplayPlugin { using Parent = OculusBaseDisplayPlugin; public: @@ -32,9 +29,7 @@ protected: private: static const QString NAME; - bool _enablePreview { false }; - bool _monoPreview { true }; - - SwapFboPtr _sceneFbo; + ovrTextureSwapChain _textureSwapChain; + gpu::FramebufferPointer _outputFramebuffer; }; diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 49c14c8d66..80390fd538 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -18,6 +18,7 @@ #include #include +#include using Mutex = std::mutex; using Lock = std::unique_lock; @@ -116,94 +117,6 @@ void releaseOculusSession() { } -// A wrapper for constructing and using a swap texture set, -// where each frame you draw to a texture via the FBO, -// then submit it and increment to the next texture. -// The Oculus SDK manages the creation and destruction of -// the textures - -SwapFramebufferWrapper::SwapFramebufferWrapper(const ovrSession& session) - : _session(session) { - color = nullptr; - depth = nullptr; -} - -SwapFramebufferWrapper::~SwapFramebufferWrapper() { - destroyColor(); -} - -void SwapFramebufferWrapper::Commit() { - auto result = ovr_CommitTextureSwapChain(_session, color); - Q_ASSERT(OVR_SUCCESS(result)); -} - -void SwapFramebufferWrapper::Resize(const uvec2 & size) { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo)); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - this->size = size; - initColor(); - initDone(); -} - -void SwapFramebufferWrapper::destroyColor() { - if (color) { - ovr_DestroyTextureSwapChain(_session, color); - color = nullptr; - } -} - -void SwapFramebufferWrapper::initColor() { - destroyColor(); - - ovrTextureSwapChainDesc desc = {}; - desc.Type = ovrTexture_2D; - desc.ArraySize = 1; - desc.Width = size.x; - desc.Height = size.y; - desc.MipLevels = 1; - desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; - desc.SampleCount = 1; - desc.StaticImage = ovrFalse; - - ovrResult result = ovr_CreateTextureSwapChainGL(_session, &desc, &color); - if (!OVR_SUCCESS(result)) { - logFatal("Failed to create swap textures"); - } - - int length = 0; - result = ovr_GetTextureSwapChainLength(_session, color, &length); - if (!OVR_SUCCESS(result) || !length) { - qFatal("Unable to count swap chain textures"); - } - for (int i = 0; i < length; ++i) { - GLuint chainTexId; - ovr_GetTextureSwapChainBufferGL(_session, color, i, &chainTexId); - glBindTexture(GL_TEXTURE_2D, chainTexId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - glBindTexture(GL_TEXTURE_2D, 0); -} - -void SwapFramebufferWrapper::initDone() { -} - -void SwapFramebufferWrapper::onBind(oglplus::Framebuffer::Target target) { - int curIndex; - ovr_GetTextureSwapChainCurrentIndex(_session, color, &curIndex); - GLuint curTexId; - ovr_GetTextureSwapChainBufferGL(_session, color, curIndex, &curTexId); - glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, curTexId, 0); -} - -void SwapFramebufferWrapper::onUnbind(oglplus::Framebuffer::Target target) { - glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); -} - - controller::Pose ovrControllerPoseToHandPose( ovrHandType hand, const ovrPoseStatef& handPose) { diff --git a/plugins/oculus/src/OculusHelpers.h b/plugins/oculus/src/OculusHelpers.h index 66cdccf15a..ba0547ae0a 100644 --- a/plugins/oculus/src/OculusHelpers.h +++ b/plugins/oculus/src/OculusHelpers.h @@ -12,7 +12,6 @@ #include #include -#include #include void logWarning(const char* what); @@ -106,30 +105,6 @@ inline ovrPosef ovrPoseFromGlm(const glm::mat4 & m) { return result; } - -// A wrapper for constructing and using a swap texture set, -// where each frame you draw to a texture via the FBO, -// then submit it and increment to the next texture. -// The Oculus SDK manages the creation and destruction of -// the textures -struct SwapFramebufferWrapper : public FramebufferWrapper { - SwapFramebufferWrapper(const ovrSession& session); - ~SwapFramebufferWrapper(); - void Commit(); - void Resize(const uvec2 & size); -protected: - void initColor() override final; - void initDepth() override final {} - void initDone() override final; - void onBind(oglplus::Framebuffer::Target target) override final; - void onUnbind(oglplus::Framebuffer::Target target) override final; - - void destroyColor(); - -private: - ovrSession _session; -}; - controller::Pose ovrControllerPoseToHandPose( ovrHandType hand, const ovrPoseStatef& handPose); diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt index c1f2c6249f..f4e1bb76a2 100644 --- a/plugins/oculusLegacy/CMakeLists.txt +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -8,11 +8,12 @@ # Windows doesn't need this, and building it currently make Linux unstable. # if (NOT WIN32) + if (APPLE) set(TARGET_NAME oculusLegacy) setup_hifi_plugin() - link_hifi_libraries(shared gl gpu plugins ui ui-plugins display-plugins input-plugins) + link_hifi_libraries(shared gl gpu gpu-gl plugins ui ui-plugins display-plugins input-plugins) include_hifi_library_headers(octree) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 6da842b7b9..98516011d5 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "OculusHelpers.h" @@ -51,8 +52,8 @@ bool OculusLegacyDisplayPlugin::beginFrameRender(uint32_t frameIndex) { _currentRenderFrameInfo = FrameInfo(); _currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); _trackingState = ovrHmd_GetTrackingState(_hmd, _currentRenderFrameInfo.predictedDisplayTime); - _currentRenderFrameInfo.rawRenderPose = _currentRenderFrameInfo.renderPose = toGlm(_trackingState.HeadPose.ThePose); - withRenderThreadLock([&]{ + _currentRenderFrameInfo.renderPose = toGlm(_trackingState.HeadPose.ThePose); + withNonPresentThreadLock([&]{ _frameInfos[frameIndex] = _currentRenderFrameInfo; }); return Parent::beginFrameRender(frameIndex); @@ -256,13 +257,13 @@ void OculusLegacyDisplayPlugin::hmdPresent() { memset(eyePoses, 0, sizeof(ovrPosef) * 2); eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation; - GLint texture = oglplus::GetName(_compositeFramebuffer->color); + GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false); auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); if (_hmdWindow->makeCurrent()) { glClearColor(0, 0.4, 0.8, 1); glClear(GL_COLOR_BUFFER_BIT); - ovrHmd_BeginFrame(_hmd, _currentPresentFrameIndex); + ovrHmd_BeginFrame(_hmd, _currentFrame->frameIndex); glWaitSync(sync, 0, GL_TIMEOUT_IGNORED); glDeleteSync(sync); ovr_for_each_eye([&](ovrEyeType eye) { diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 8263e87767..79bfd91068 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -13,7 +13,7 @@ if (WIN32) setup_hifi_plugin(OpenGL Script Qml Widgets) link_hifi_libraries(shared gl networking controllers ui plugins display-plugins ui-plugins input-plugins script-engine - render-utils model gpu render model-networking fbx) + render-utils model gpu gpu-gl render model-networking fbx) include_hifi_library_headers(octree) diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 4e84c6d0fa..e751427ce2 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -7,23 +7,23 @@ // #include "OpenVrDisplayPlugin.h" -#include - -#include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include -#include +#include +#include + #include -#include +#include #include +#include +#include +#include +#include +#include + #include "OpenVrHelpers.h" Q_DECLARE_LOGGING_CATEGORY(displayplugins) @@ -31,16 +31,206 @@ Q_DECLARE_LOGGING_CATEGORY(displayplugins) const QString OpenVrDisplayPlugin::NAME("OpenVR (Vive)"); const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; // this probably shouldn't be hardcoded here -static vr::IVRCompositor* _compositor { nullptr }; -vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; +PoseData _nextRenderPoseData; +PoseData _nextSimPoseData; -mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; -vec3 _trackedDeviceLinearVelocities[vr::k_unMaxTrackedDeviceCount]; -vec3 _trackedDeviceAngularVelocities[vr::k_unMaxTrackedDeviceCount]; - -static mat4 _sensorResetMat; static std::array VR_EYES { { vr::Eye_Left, vr::Eye_Right } }; bool _openVrDisplayActive { false }; +// Flip y-axis since GL UV coords are backwards. +static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_LEFT{ 0, 0, 0.5f, 1 }; +static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_RIGHT{ 0.5f, 0, 1, 1 }; + +#if OPENVR_THREADED_SUBMIT + +static QString readFile(const QString& filename) { + QFile file(filename); + file.open(QFile::Text | QFile::ReadOnly); + QString result; + result.append(QTextStream(&file).readAll()); + return result; +} + +class OpenVrSubmitThread : public QThread, public Dependency { +public: + using Mutex = std::mutex; + using Condition = std::condition_variable; + using Lock = std::unique_lock; + friend class OpenVrDisplayPlugin; + OffscreenGLCanvas _canvas; + BasicFramebufferWrapperPtr _framebuffer; + ProgramPtr _program; + ShapeWrapperPtr _plane; + struct ReprojectionUniforms { + int32_t reprojectionMatrix{ -1 }; + int32_t inverseProjectionMatrix{ -1 }; + int32_t projectionMatrix{ -1 }; + } _reprojectionUniforms; + + + OpenVrSubmitThread(OpenVrDisplayPlugin& plugin) : _plugin(plugin) { + _canvas.create(plugin._container->getPrimaryContext()); + _canvas.doneCurrent(); + _canvas.moveToThreadWithContext(this); + } + + void updateReprojectionProgram() { + static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert"; + static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag"; +#if LIVE_SHADER_RELOAD + static qint64 vsBuiltAge = 0; + static qint64 fsBuiltAge = 0; + QFileInfo vsInfo(vsFile); + QFileInfo fsInfo(fsFile); + auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch(); + auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch(); + if (!_reprojectionProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) { + vsBuiltAge = vsAge; + fsBuiltAge = fsAge; +#else + if (!_program) { +#endif + QString vsSource = readFile(vsFile); + QString fsSource = readFile(fsFile); + ProgramPtr program; + try { + compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString()); + if (program) { + using namespace oglplus; + _reprojectionUniforms.reprojectionMatrix = Uniform(*program, "reprojection").Location(); + _reprojectionUniforms.inverseProjectionMatrix = Uniform(*program, "inverseProjections").Location(); + _reprojectionUniforms.projectionMatrix = Uniform(*program, "projections").Location(); + _program = program; + } + } catch (std::runtime_error& error) { + qWarning() << "Error building reprojection shader " << error.what(); + } + } + } + + void updateSource() { + _plugin.withNonPresentThreadLock([&] { + while (!_queue.empty()) { + auto& front = _queue.front(); + auto result = glClientWaitSync(front.fence, 0, 0); + if (GL_TIMEOUT_EXPIRED == result && GL_WAIT_FAILED == result) { + break; + } + + glDeleteSync(front.fence); + front.fence = 0; + _current = front; + _queue.pop(); + } + }); + } + + void run() override { + QThread::currentThread()->setPriority(QThread::Priority::TimeCriticalPriority); + _canvas.makeCurrent(); + glDisable(GL_DEPTH_TEST); + glViewport(0, 0, _plugin._renderTargetSize.x, _plugin._renderTargetSize.y); + _framebuffer = std::make_shared(); + _framebuffer->Init(_plugin._renderTargetSize); + updateReprojectionProgram(); + _plane = loadPlane(_program); + _canvas.doneCurrent(); + while (!_quit) { + _canvas.makeCurrent(); + updateSource(); + if (!_current.texture) { + _canvas.doneCurrent(); + QThread::usleep(1); + continue; + } + + { + auto presentRotation = glm::mat3(_nextRender.poses[0]); + auto renderRotation = glm::mat3(_current.pose); + auto correction = glm::inverse(renderRotation) * presentRotation; + _framebuffer->Bound([&] { + glBindTexture(GL_TEXTURE_2D, _current.textureID); + _program->Use(); + using namespace oglplus; + Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear); + Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear); + Uniform(*_program, _reprojectionUniforms.reprojectionMatrix).Set(correction); + //Uniform(*_reprojectionProgram, PROJECTION_MATRIX_LOCATION).Set(_eyeProjections); + //Uniform(*_reprojectionProgram, INVERSE_PROJECTION_MATRIX_LOCATION).Set(_eyeInverseProjections); + // FIXME what's the right oglplus mechanism to do this? It's not that ^^^ ... better yet, switch to a uniform buffer + glUniformMatrix4fv(_reprojectionUniforms.inverseProjectionMatrix, 2, GL_FALSE, &(_plugin._eyeInverseProjections[0][0][0])); + glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_plugin._eyeProjections[0][0][0])); + _plane->UseInProgram(*_program); + _plane->Draw(); + }); + static const vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; + static const vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 }; + + vr::Texture_t texture{ (void*)oglplus::GetName(_framebuffer->color), vr::API_OpenGL, vr::ColorSpace_Auto }; + vr::VRCompositor()->Submit(vr::Eye_Left, &texture, &leftBounds); + vr::VRCompositor()->Submit(vr::Eye_Right, &texture, &rightBounds); + PoseData nextRender, nextSim; + nextRender.frameIndex = _plugin.presentCount(); + vr::VRCompositor()->WaitGetPoses(nextRender.vrPoses, vr::k_unMaxTrackedDeviceCount, nextSim.vrPoses, vr::k_unMaxTrackedDeviceCount); + + // Copy invalid poses in nextSim from nextRender + for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i) { + if (!nextSim.vrPoses[i].bPoseIsValid) { + nextSim.vrPoses[i] = nextRender.vrPoses[i]; + } + } + + mat4 sensorResetMat; + _plugin.withNonPresentThreadLock([&] { + sensorResetMat = _plugin._sensorResetMat; + }); + + nextRender.update(sensorResetMat); + nextSim.update(sensorResetMat); + + _plugin.withNonPresentThreadLock([&] { + _nextRender = nextRender; + _nextSim = nextSim; + ++_presentCount; + _presented.notify_one(); + }); + } + _canvas.doneCurrent(); + } + + _canvas.makeCurrent(); + _plane.reset(); + _program.reset(); + _framebuffer.reset(); + _canvas.doneCurrent(); + + } + + void update(const CompositeInfo& newCompositeInfo) { + _queue.push(newCompositeInfo); + } + + void waitForPresent() { + auto lastCount = _presentCount.load(); + Lock lock(_plugin._presentMutex); + _presented.wait(lock, [&]()->bool { + return _presentCount.load() > lastCount; + }); + _nextSimPoseData = _nextSim; + _nextRenderPoseData = _nextRender; + } + + CompositeInfo _current; + CompositeInfo::Queue _queue; + + PoseData _nextRender, _nextSim; + bool _quit { false }; + GLuint _currentTexture { 0 }; + std::atomic _presentCount { 0 }; + Condition _presented; + OpenVrDisplayPlugin& _plugin; +}; + +#endif bool OpenVrDisplayPlugin::isSupported() const { return openVrSupported(); @@ -82,7 +272,7 @@ bool OpenVrDisplayPlugin::internalActivate() { // left + right eyes _renderTargetSize.x *= 2; - withRenderThreadLock([&] { + withNonPresentThreadLock([&] { openvr_for_each_eye([&](vr::Hmd_Eye eye) { _eyeOffsets[eye] = toGlm(_system->GetEyeToHeadTransform(eye)); _eyeProjections[eye] = toGlm(_system->GetProjectionMatrix(eye, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, vr::API_OpenGL)); @@ -91,11 +281,8 @@ bool OpenVrDisplayPlugin::internalActivate() { _cullingProjection = _eyeProjections[0]; }); - _compositor = vr::VRCompositor(); - Q_ASSERT(_compositor); - // enable async time warp - // _compositor->ForceInterleavedReprojectionOn(true); + //vr::VRCompositor()->ForceInterleavedReprojectionOn(true); // set up default sensor space such that the UI overlay will align with the front of the room. auto chaperone = vr::VRChaperone(); @@ -114,23 +301,27 @@ bool OpenVrDisplayPlugin::internalActivate() { #endif } +#if OPENVR_THREADED_SUBMIT + withMainThreadContext([&] { + _submitThread = std::make_shared(*this); + }); + _submitThread->setObjectName("OpenVR Submit Thread"); +#endif + return Parent::internalActivate(); } void OpenVrDisplayPlugin::internalDeactivate() { Parent::internalDeactivate(); + _openVrDisplayActive = false; _container->setIsOptionChecked(StandingHMDSensorMode, false); if (_system) { // Invalidate poses. It's fine if someone else sets these shared values, but we're about to stop updating them, and // we don't want ViveControllerManager to consider old values to be valid. - for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { - _trackedDevicePose[i].bPoseIsValid = false; - } releaseOpenVrSystem(); _system = nullptr; } - _compositor = nullptr; } void OpenVrDisplayPlugin::customizeContext() { @@ -143,13 +334,34 @@ void OpenVrDisplayPlugin::customizeContext() { }); Parent::customizeContext(); + +#if OPENVR_THREADED_SUBMIT + _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); + for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) { + if (0 != i) { + _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); + } + _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture, false); + } + _submitThread->start(QThread::HighPriority); +#endif +} + +void OpenVrDisplayPlugin::uncustomizeContext() { + Parent::uncustomizeContext(); + +#if OPENVR_THREADED_SUBMIT + _submitThread->_quit = true; + _submitThread->wait(); +#endif } void OpenVrDisplayPlugin::resetSensors() { - withRenderThreadLock([&] { - glm::mat4 m = toGlm(_trackedDevicePose[0].mDeviceToAbsoluteTracking); - _sensorResetMat = glm::inverse(cancelOutRollAndPitch(m)); + glm::mat4 m; + withNonPresentThreadLock([&] { + m = toGlm(_nextSimPoseData.vrPoses[0].mDeviceToAbsoluteTracking); }); + _sensorResetMat = glm::inverse(cancelOutRollAndPitch(m)); } static bool isBadPose(vr::HmdMatrix34_t* mat) { @@ -160,32 +372,28 @@ static bool isBadPose(vr::HmdMatrix34_t* mat) { } bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { + PROFILE_RANGE_EX(__FUNCTION__, 0xff7fff00, frameIndex) handleOpenVrEvents(); - - double displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float); - double frameDuration = 1.f / displayFrequency; - double vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float); - + if (openVrQuitRequested()) { + QMetaObject::invokeMethod(qApp, "quit"); + return false; + } _currentRenderFrameInfo = FrameInfo(); -#if THREADED_PRESENT - // 3 frames of prediction + vsyncToPhotons = 44ms total - const double NUM_PREDICTION_FRAMES = 3.0f; - _currentRenderFrameInfo.predictedDisplayTime = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons; -#else - _currentRenderFrameInfo.predictedDisplayTime = frameDuration + vsyncToPhotons; -#endif - _system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, _currentRenderFrameInfo.predictedDisplayTime, _trackedDevicePose, vr::k_unMaxTrackedDeviceCount); + PoseData nextSimPoseData; + withNonPresentThreadLock([&] { + nextSimPoseData = _nextSimPoseData; + }); // HACK: when interface is launched and steam vr is NOT running, openvr will return bad HMD poses for a few frames // To workaround this, filter out any hmd poses that are obviously bad, i.e. beneath the floor. - if (isBadPose(&_trackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking)) { + if (isBadPose(&nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking)) { qDebug() << "WARNING: ignoring bad hmd pose from openvr"; // use the last known good HMD pose - _trackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking = _lastGoodHMDPose; + nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking = _lastGoodHMDPose; } else { - _lastGoodHMDPose = _trackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking; + _lastGoodHMDPose = nextSimPoseData.vrPoses[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking; } vr::TrackedDeviceIndex_t handIndices[2] { vr::k_unTrackedDeviceIndexInvalid, vr::k_unTrackedDeviceIndexInvalid }; @@ -194,7 +402,7 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { auto trackedCount = _system->GetSortedTrackedDeviceIndicesOfClass(vr::TrackedDeviceClass_Controller, controllerIndices, 2); // Find the left and right hand controllers, if they exist for (uint32_t i = 0; i < std::min(trackedCount, 2); ++i) { - if (_trackedDevicePose[i].bPoseIsValid) { + if (nextSimPoseData.vrPoses[i].bPoseIsValid) { auto role = _system->GetControllerRoleForTrackedDeviceIndex(controllerIndices[i]); if (vr::TrackedControllerRole_LeftHand == role) { handIndices[0] = controllerIndices[i]; @@ -205,15 +413,7 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { } } - // copy and process predictedTrackedDevicePoses - for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { - _trackedDevicePoseMat4[i] = _sensorResetMat * toGlm(_trackedDevicePose[i].mDeviceToAbsoluteTracking); - _trackedDeviceLinearVelocities[i] = transformVectorFast(_sensorResetMat, toGlm(_trackedDevicePose[i].vVelocity)); - _trackedDeviceAngularVelocities[i] = transformVectorFast(_sensorResetMat, toGlm(_trackedDevicePose[i].vAngularVelocity)); - } - _currentRenderFrameInfo.rawRenderPose = toGlm(_trackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking); - _currentRenderFrameInfo.renderPose = _trackedDevicePoseMat4[vr::k_unTrackedDeviceIndex_Hmd]; - + _currentRenderFrameInfo.renderPose = nextSimPoseData.poses[vr::k_unTrackedDeviceIndex_Hmd]; bool keyboardVisible = isOpenVrKeyboardShown(); std::array handPoses; @@ -223,16 +423,16 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { continue; } auto deviceIndex = handIndices[i]; - const mat4& mat = _trackedDevicePoseMat4[deviceIndex]; - const vec3& linearVelocity = _trackedDeviceLinearVelocities[deviceIndex]; - const vec3& angularVelocity = _trackedDeviceAngularVelocities[deviceIndex]; + const mat4& mat = nextSimPoseData.poses[deviceIndex]; + const vec3& linearVelocity = nextSimPoseData.linearVelocities[deviceIndex]; + const vec3& angularVelocity = nextSimPoseData.angularVelocities[deviceIndex]; auto correctedPose = openVrControllerPoseToHandPose(i == 0, mat, linearVelocity, angularVelocity); static const glm::quat HAND_TO_LASER_ROTATION = glm::rotation(Vectors::UNIT_Z, Vectors::UNIT_NEG_Y); handPoses[i] = glm::translate(glm::mat4(), correctedPose.translation) * glm::mat4_cast(correctedPose.rotation * HAND_TO_LASER_ROTATION); } } - withRenderThreadLock([&] { + withNonPresentThreadLock([&] { _uiModelTransform = DependencyManager::get()->getModelTransform(); // Make controller poses available to the presentation thread _handPoses = handPoses; @@ -241,25 +441,62 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { return Parent::beginFrameRender(frameIndex); } +void OpenVrDisplayPlugin::compositeLayers() { +#if OPENVR_THREADED_SUBMIT + ++_renderingIndex; + _renderingIndex %= COMPOSITING_BUFFER_SIZE; + + auto& newComposite = _compositeInfos[_renderingIndex]; + newComposite.pose = _currentPresentFrameInfo.presentPose; + _compositeFramebuffer->setRenderBuffer(0, newComposite.texture); +#endif + + Parent::compositeLayers(); + +#if OPENVR_THREADED_SUBMIT + newComposite.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + if (!newComposite.textureID) { + newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture, false); + } + withPresentThreadLock([&] { + _submitThread->update(newComposite); + }); +#endif +} + void OpenVrDisplayPlugin::hmdPresent() { - PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentPresentFrameIndex) + PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex) - // Flip y-axis since GL UV coords are backwards. - static vr::VRTextureBounds_t leftBounds { 0, 0, 0.5f, 1 }; - static vr::VRTextureBounds_t rightBounds { 0.5f, 0, 1, 1 }; - - vr::Texture_t texture { (void*)oglplus::GetName(_compositeFramebuffer->color), vr::API_OpenGL, vr::ColorSpace_Auto }; - - _compositor->Submit(vr::Eye_Left, &texture, &leftBounds); - _compositor->Submit(vr::Eye_Right, &texture, &rightBounds); +#if OPENVR_THREADED_SUBMIT + _submitThread->waitForPresent(); +#else + GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false); + vr::Texture_t vrTexture{ (void*)glTexId, vr::API_OpenGL, vr::ColorSpace_Auto }; + vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); + vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); + vr::VRCompositor()->PostPresentHandoff(); +#endif } void OpenVrDisplayPlugin::postPreview() { - PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentPresentFrameIndex) + PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex) + PoseData nextRender, nextSim; + nextRender.frameIndex = presentCount(); +#if !OPENVR_THREADED_SUBMIT + vr::VRCompositor()->WaitGetPoses(nextRender.vrPoses, vr::k_unMaxTrackedDeviceCount, nextSim.vrPoses, vr::k_unMaxTrackedDeviceCount); - vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount]; - _compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0); - _hmdActivityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd); + glm::mat4 resetMat; + withPresentThreadLock([&] { + resetMat = _sensorResetMat; + }); + nextRender.update(resetMat); + nextSim.update(resetMat); + withPresentThreadLock([&] { + _nextSimPoseData = nextSim; + }); + _nextRenderPoseData = nextRender; + _hmdActivityLevel = vr::k_EDeviceActivityLevel_UserInteraction; // _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd); +#endif } bool OpenVrDisplayPlugin::isHmdMounted() const { @@ -267,25 +504,7 @@ bool OpenVrDisplayPlugin::isHmdMounted() const { } void OpenVrDisplayPlugin::updatePresentPose() { - mat4 sensorResetMat; - withPresentThreadLock([&] { - sensorResetMat = _sensorResetMat; - }); - { - float fSecondsSinceLastVsync; - _system->GetTimeSinceLastVsync(&fSecondsSinceLastVsync, nullptr); - float fDisplayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float); - float fFrameDuration = 1.f / fDisplayFrequency; - float fVsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float); - float fPredictedSecondsFromNow = fFrameDuration - fSecondsSinceLastVsync + fVsyncToPhotons; - vr::TrackedDevicePose_t pose; - _system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, fPredictedSecondsFromNow, &pose, 1); - _currentPresentFrameInfo.rawPresentPose = toGlm(pose.mDeviceToAbsoluteTracking); - } - _currentPresentFrameInfo.presentPose = sensorResetMat * _currentPresentFrameInfo.rawPresentPose; - mat3 renderRotation(_currentPresentFrameInfo.rawRenderPose); - mat3 presentRotation(_currentPresentFrameInfo.rawPresentPose); - _currentPresentFrameInfo.presentReprojection = glm::mat3(glm::inverse(renderRotation) * presentRotation); + _currentPresentFrameInfo.presentPose = _nextRenderPoseData.poses[vr::k_unTrackedDeviceIndex_Hmd]; } bool OpenVrDisplayPlugin::suppressKeyboard() { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 4c0c0aebbd..ba51511fe8 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -15,6 +15,23 @@ const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only. +#define OPENVR_THREADED_SUBMIT 1 + +#if OPENVR_THREADED_SUBMIT +class OpenVrSubmitThread; +static const size_t COMPOSITING_BUFFER_SIZE = 3; + +struct CompositeInfo { + using Queue = std::queue; + using Array = std::array; + + gpu::TexturePointer texture; + GLuint textureID { 0 }; + glm::mat4 pose; + GLsync fence{ 0 }; +}; +#endif + class OpenVrDisplayPlugin : public HmdDisplayPlugin { using Parent = HmdDisplayPlugin; public: @@ -26,6 +43,7 @@ public: float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; } void customizeContext() override; + void uncustomizeContext() override; // Stereo specific methods void resetSensors() override; @@ -41,6 +59,7 @@ protected: void internalDeactivate() override; void updatePresentPose() override; + void compositeLayers() override; void hmdPresent() override; bool isHmdMounted() const override; void postPreview() override; @@ -53,4 +72,12 @@ private: static const QString NAME; vr::HmdMatrix34_t _lastGoodHMDPose; + mat4 _sensorResetMat; + +#if OPENVR_THREADED_SUBMIT + CompositeInfo::Array _compositeInfos; + size_t _renderingIndex { 0 }; + std::shared_ptr _submitThread; + friend class OpenVrSubmitThread; +#endif }; diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index c93a2178b5..20a43d98d4 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -34,6 +34,11 @@ using Lock = std::unique_lock; static int refCount { 0 }; static Mutex mutex; static vr::IVRSystem* activeHmd { nullptr }; +static bool _openVrQuitRequested { false }; + +bool openVrQuitRequested() { + return _openVrQuitRequested; +} static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000; @@ -99,6 +104,7 @@ void releaseOpenVrSystem() { qCDebug(displayplugins) << "OpenVR: zero refcount, deallocate VR system"; #endif vr::VR_Shutdown(); + _openVrQuitRequested = false; activeHmd = nullptr; } } @@ -112,7 +118,7 @@ static vr::IVROverlay* _overlay { nullptr }; static QObject* _keyboardFocusObject { nullptr }; static QString _existingText; static Qt::InputMethodHints _currentHints; -extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; +extern PoseData _nextSimPoseData; static bool _keyboardShown { false }; static bool _overlayRevealed { false }; static const uint32_t SHOW_KEYBOARD_DELAY_MS = 400; @@ -154,7 +160,7 @@ void showOpenVrKeyboard(bool show = true) { if (vr::VROverlayError_None == showKeyboardResult) { _keyboardShown = true; // Try to position the keyboard slightly below where the user is looking. - mat4 headPose = cancelOutRollAndPitch(toGlm(_trackedDevicePose[0].mDeviceToAbsoluteTracking)); + mat4 headPose = cancelOutRollAndPitch(toGlm(_nextSimPoseData.vrPoses[0].mDeviceToAbsoluteTracking)); mat4 keyboardTransform = glm::translate(headPose, vec3(0, -0.5, -1)); keyboardTransform = keyboardTransform * glm::rotate(mat4(), 3.14159f / 4.0f, vec3(-1, 0, 0)); auto keyboardTransformVr = toOpenVr(keyboardTransform); @@ -257,8 +263,8 @@ void handleOpenVrEvents() { while (activeHmd->PollNextEvent(&event, sizeof(event))) { switch (event.eventType) { case vr::VREvent_Quit: + _openVrQuitRequested = true; activeHmd->AcknowledgeQuit_Exiting(); - QMetaObject::invokeMethod(qApp, "quit"); break; case vr::VREvent_KeyboardDone: diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h index 19c9cbfff5..4279e6a6ac 100644 --- a/plugins/openvr/src/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -59,4 +59,28 @@ inline vr::HmdMatrix34_t toOpenVr(const mat4& m) { return result; } +struct PoseData { + uint32_t frameIndex{ 0 }; + vr::TrackedDevicePose_t vrPoses[vr::k_unMaxTrackedDeviceCount]; + mat4 poses[vr::k_unMaxTrackedDeviceCount]; + vec3 linearVelocities[vr::k_unMaxTrackedDeviceCount]; + vec3 angularVelocities[vr::k_unMaxTrackedDeviceCount]; + + PoseData() { + memset(vrPoses, 0, sizeof(vr::TrackedDevicePose_t) * vr::k_unMaxTrackedDeviceCount); + } + + void update(const glm::mat4& resetMat) { + for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { + if (!vrPoses[i].bPoseIsValid) { + continue; + } + poses[i] = resetMat * toGlm(vrPoses[i].mDeviceToAbsoluteTracking); + linearVelocities[i] = transformVectorFast(resetMat, toGlm(vrPoses[i].vVelocity)); + angularVelocities[i] = transformVectorFast(resetMat, toGlm(vrPoses[i].vAngularVelocity)); + } + } +}; + + controller::Pose openVrControllerPoseToHandPose(bool isLeftHand, const mat4& mat, const vec3& linearVelocity, const vec3& angularVelocity); diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 85feebda11..bcfc7170dc 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -29,10 +29,7 @@ #include "OpenVrHelpers.h" -extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; -extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; -extern vec3 _trackedDeviceLinearVelocities[vr::k_unMaxTrackedDeviceCount]; -extern vec3 _trackedDeviceAngularVelocities[vr::k_unMaxTrackedDeviceCount]; +extern PoseData _nextSimPoseData; vr::IVRSystem* acquireOpenVrSystem(); void releaseOpenVrSystem(); @@ -214,6 +211,10 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { auto userInputMapper = DependencyManager::get(); handleOpenVrEvents(); + if (openVrQuitRequested()) { + deactivate(); + return; + } // because update mutates the internal state we need to lock userInputMapper->withLock([&, this]() { @@ -275,12 +276,12 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u if (_system->IsTrackedDeviceConnected(deviceIndex) && _system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && - _trackedDevicePose[deviceIndex].bPoseIsValid) { + _nextSimPoseData.vrPoses[deviceIndex].bPoseIsValid) { // process pose - const mat4& mat = _trackedDevicePoseMat4[deviceIndex]; - const vec3 linearVelocity = _trackedDeviceLinearVelocities[deviceIndex]; - const vec3 angularVelocity = _trackedDeviceAngularVelocities[deviceIndex]; + const mat4& mat = _nextSimPoseData.poses[deviceIndex]; + const vec3 linearVelocity = _nextSimPoseData.linearVelocities[deviceIndex]; + const vec3 angularVelocity = _nextSimPoseData.angularVelocities[deviceIndex]; handlePoseEvent(deltaTime, inputCalibrationData, mat, linearVelocity, angularVelocity, isLeftHand); vr::VRControllerState_t controllerState = vr::VRControllerState_t(); @@ -424,7 +425,7 @@ void ViveControllerManager::InputDevice::hapticsHelper(float deltaTime, bool lef if (_system->IsTrackedDeviceConnected(deviceIndex) && _system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && - _trackedDevicePose[deviceIndex].bPoseIsValid) { + _nextSimPoseData.vrPoses[deviceIndex].bPoseIsValid) { float strength = leftHand ? _leftHapticStrength : _rightHapticStrength; float duration = leftHand ? _leftHapticDuration : _rightHapticDuration; diff --git a/script-archive/example/assetsExample.js b/script-archive/example/assetsExample.js index decebbcfa3..76c7fc7e23 100644 --- a/script-archive/example/assetsExample.js +++ b/script-archive/example/assetsExample.js @@ -2,7 +2,7 @@ var data = "this is some data"; var extension = "txt"; var uploadedFile; -Assets.uploadData(data, extension, function (url) { +Assets.uploadData(data, function (url) { print("data uploaded to:" + url); uploadedFile = url; Assets.downloadData(url, function (data) { diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 0efcd0c140..dc252afcf1 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -25,5 +25,6 @@ Script.load("system/controllers/handControllerPointer.js"); Script.load("system/controllers/squeezeHands.js"); Script.load("system/controllers/grab.js"); Script.load("system/controllers/teleport.js"); +Script.load("system/controllers/toggleAdvancedMovementForHandControllers.js") Script.load("system/dialTone.js"); -Script.load("system/firstPersonHMD.js"); +Script.load("system/firstPersonHMD.js"); \ No newline at end of file diff --git a/scripts/system/assets/images/tools/assets-01.svg b/scripts/system/assets/images/tools/assets-01.svg new file mode 100644 index 0000000000..d7bdd1d7f0 --- /dev/null +++ b/scripts/system/assets/images/tools/assets-01.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/assets/images/tools/steam-invite.svg b/scripts/system/assets/images/tools/steam-invite.svg new file mode 100644 index 0000000000..ce225cca68 --- /dev/null +++ b/scripts/system/assets/images/tools/steam-invite.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/assets/models/teleport-cancel.fbx b/scripts/system/assets/models/teleport-cancel.fbx new file mode 100644 index 0000000000..1c12e28159 Binary files /dev/null and b/scripts/system/assets/models/teleport-cancel.fbx differ diff --git a/scripts/system/assets/models/teleport-destination.fbx b/scripts/system/assets/models/teleport-destination.fbx new file mode 100644 index 0000000000..5fdb0d56af Binary files /dev/null and b/scripts/system/assets/models/teleport-destination.fbx differ diff --git a/scripts/system/assets/models/teleport.fbx b/scripts/system/assets/models/teleport.fbx deleted file mode 100644 index 831f152add..0000000000 Binary files a/scripts/system/assets/models/teleport.fbx and /dev/null differ diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index f29fdfb00a..470c227f7b 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -96,7 +96,9 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray // var EQUIP_RADIUS = 0.1; // radius used for palm vs equip-hotspot for equipping. -var EQUIP_HOTSPOT_RENDER_RADIUS = 0.3; // radius used for palm vs equip-hotspot for rendering hot-spots +// if EQUIP_HOTSPOT_RENDER_RADIUS is greater than zero, the hotspot will appear before the hand +// has reached the required position, and then grow larger once the hand is close enough to equip. +var EQUIP_HOTSPOT_RENDER_RADIUS = 0.0; // radius used for palm vs equip-hotspot for rendering hot-spots var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position @@ -110,13 +112,11 @@ var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-g // if an equipped item is "adjusted" to be too far from the hand it's in, it will be unequipped. var CHECK_TOO_FAR_UNEQUIP_TIME = 0.3; // seconds, duration between checks -var AUTO_UNEQUIP_DISTANCE_FACTOR = 1.2; // multiplied by maximum dimension of held item, > this means drop // // other constants // -var HOTSPOT_DRAW_DISTANCE = 10; var RIGHT_HAND = 1; var LEFT_HAND = 0; @@ -1619,7 +1619,7 @@ function MyController(hand) { z: 0 }; - var DROP_ANGLE = Math.PI / 7; + var DROP_ANGLE = Math.PI / 6; var HYSTERESIS_FACTOR = 1.1; var ROTATION_ENTER_THRESHOLD = Math.cos(DROP_ANGLE); var ROTATION_EXIT_THRESHOLD = Math.cos(DROP_ANGLE * HYSTERESIS_FACTOR); @@ -1674,12 +1674,12 @@ function MyController(hand) { var handRotation = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation(); var handPosition = this.getHandPosition(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; + var hasPresetPosition = false; if (this.state == STATE_HOLD && this.grabbedHotspot) { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); // if an object is "equipped" and has a predefined offset, use it. - this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; - var offsets = USE_ATTACH_POINT_SETTINGS && getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); if (offsets) { this.offsetPosition = offsets[0]; @@ -1694,8 +1694,6 @@ function MyController(hand) { } } } else { - this.ignoreIK = false; - var objectRotation = grabbedProperties.rotation; this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); @@ -2204,28 +2202,13 @@ function MyController(hand) { var props = Entities.getEntityProperties(entityID, ["parentID", "velocity", "dynamic", "shapeType"]); var parentID = props.parentID; - var doSetVelocity = false; - if (parentID != NULL_UUID && deactiveProps.parentID == NULL_UUID && propsArePhysical(props)) { - // TODO: EntityScriptingInterface::convertLocationToScriptSemantics should be setting up - // props.velocity to be a world-frame velocity and localVelocity to be vs parent. Until that - // is done, we use a measured velocity here so that things held via a bumper-grab / parenting-grab - // can be thrown. - doSetVelocity = true; - } - if (!noVelocity && - !doSetVelocity && parentID == MyAvatar.sessionUUID && Vec3.length(data["gravity"]) > 0.0 && data["dynamic"] && data["parentID"] == NULL_UUID && !data["collisionless"]) { - deactiveProps["velocity"] = { - x: 0.0, - y: 0.1, - z: 0.0 - }; - doSetVelocity = false; + deactiveProps["velocity"] = this.currentVelocity; } if (noVelocity) { deactiveProps["velocity"] = { @@ -2238,21 +2221,9 @@ function MyController(hand) { y: 0.0, z: 0.0 }; - doSetVelocity = false; } Entities.editEntity(entityID, deactiveProps); - - if (doSetVelocity) { - // this is a continuation of the TODO above -- we shouldn't need to set this here. - // do this after the parent has been reset. setting this at the same time as - // the parent causes it to go off in the wrong direction. This is a bug that should - // be fixed. - Entities.editEntity(entityID, { - velocity: this.currentVelocity - // angularVelocity: this.currentAngularVelocity - }); - } data = null; } else if (this.shouldResetParentOnRelease) { // we parent-grabbed this from another parent grab. try to put it back where we found it. diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 5cd8460172..0aa5820b0f 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -1,34 +1,18 @@ // Created by james b. pollack @imgntn on 7/2/2016 // Copyright 2016 High Fidelity, Inc. // -// Creates a beam and target and then teleports you there. +// Creates a beam and target and then teleports you there. Release when its close to you to cancel. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html var inTeleportMode = false; -// instant -// var NUMBER_OF_STEPS = 0; -// var SMOOTH_ARRIVAL_SPACING = 0; - -// // slow -// var SMOOTH_ARRIVAL_SPACING = 150; -// var NUMBER_OF_STEPS = 2; - -// medium-slow -// var SMOOTH_ARRIVAL_SPACING = 100; -// var NUMBER_OF_STEPS = 4; - -// medium-fast var SMOOTH_ARRIVAL_SPACING = 33; var NUMBER_OF_STEPS = 6; -//fast -// var SMOOTH_ARRIVAL_SPACING = 10; -// var NUMBER_OF_STEPS = 20; - -var TARGET_MODEL_URL = Script.resolvePath("../assets/models/teleport.fbx"); +var TARGET_MODEL_URL = Script.resolvePath("../assets/models/teleport-destination.fbx"); +var TOO_CLOSE_MODEL_URL = Script.resolvePath("../assets/models/teleport-cancel.fbx"); var TARGET_MODEL_DIMENSIONS = { x: 1.15, y: 0.5, @@ -47,7 +31,15 @@ var COLORS_TELEPORT_CANNOT_TELEPORT = { blue: 141 }; -var MAX_AVATAR_SPEED = 0.25; +var COLORS_TELEPORT_TOO_CLOSE = { + red: 255, + green: 184, + blue: 73 +}; + +var TELEPORT_CANCEL_RANGE = 1; +var USE_COOL_IN = true; +var COOL_IN_DURATION = 500; function ThumbPad(hand) { this.hand = hand; @@ -80,35 +72,27 @@ function Trigger(hand) { }; } +var coolInTimeout = null; + function Teleporter() { var _this = this; this.intersection = null; this.rightOverlayLine = null; this.leftOverlayLine = null; this.targetOverlay = null; + this.cancelOverlay = null; this.updateConnected = null; this.smoothArrivalInterval = null; this.teleportHand = null; + this.tooClose = false; + this.inCoolIn = false; + this.initialize = function() { this.createMappings(); this.disableGrab(); }; - this.createTargetOverlay = function() { - - if (_this.targetOverlay !== null) { - return; - } - var targetOverlayProps = { - url: TARGET_MODEL_URL, - dimensions: TARGET_MODEL_DIMENSIONS, - visible: true - }; - - _this.targetOverlay = Overlays.addOverlay("model", targetOverlayProps); - }; - this.createMappings = function() { teleporter.telporterMappingInternalName = 'Hifi-Teleporter-Internal-Dev-' + Math.random(); teleporter.teleportMappingInternal = Controller.newMapping(teleporter.telporterMappingInternalName); @@ -121,6 +105,7 @@ function Teleporter() { }; this.enterTeleportMode = function(hand) { + if (inTeleportMode === true) { return; } @@ -129,6 +114,14 @@ function Teleporter() { } inTeleportMode = true; + this.inCoolIn = true; + if (coolInTimeout !== null) { + Script.clearTimeout(coolInTimeout); + + } + coolInTimeout = Script.setTimeout(function() { + _this.inCoolIn = false; + }, COOL_IN_DURATION) if (this.smoothArrivalInterval !== null) { Script.clearInterval(this.smoothArrivalInterval); @@ -141,13 +134,61 @@ function Teleporter() { this.initialize(); Script.update.connect(this.update); this.updateConnected = true; + + + }; + this.createTargetOverlay = function() { + + if (_this.targetOverlay !== null) { + return; + } + var targetOverlayProps = { + url: TARGET_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + visible: true + }; + + var cancelOverlayProps = { + url: TOO_CLOSE_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + visible: true + }; + + _this.targetOverlay = Overlays.addOverlay("model", targetOverlayProps); + + }; + + this.createCancelOverlay = function() { + + if (_this.cancelOverlay !== null) { + return; + } + + var cancelOverlayProps = { + url: TOO_CLOSE_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + visible: true + }; + + _this.cancelOverlay = Overlays.addOverlay("model", cancelOverlayProps); + }; + + this.deleteCancelOverlay = function() { + if (this.cancelOverlay === null) { + return; + } + + Overlays.deleteOverlay(this.cancelOverlay); + this.cancelOverlay = null; + } this.deleteTargetOverlay = function() { if (this.targetOverlay === null) { return; } + Overlays.deleteOverlay(this.targetOverlay); this.intersection = null; this.targetOverlay = null; @@ -166,20 +207,19 @@ function Teleporter() { if (this.updateConnected === true) { Script.update.disconnect(this.update); } + this.disableMappings(); this.turnOffOverlayBeams(); - this.updateConnected = null; + this.inCoolIn = false; + inTeleportMode = false; Script.setTimeout(function() { - inTeleportMode = false; _this.enableGrab(); - }, 100); + }, 200); }; - - this.update = function() { if (isDisabled === 'both') { return; @@ -191,7 +231,13 @@ function Teleporter() { } teleporter.leftRay(); if ((leftPad.buttonValue === 0) && inTeleportMode === true) { - _this.teleport(); + if (_this.inCoolIn === true) { + _this.exitTeleportMode(); + _this.deleteTargetOverlay(); + _this.deleteCancelOverlay(); + } else { + _this.teleport(); + } return; } @@ -201,7 +247,13 @@ function Teleporter() { } teleporter.rightRay(); if ((rightPad.buttonValue === 0) && inTeleportMode === true) { - _this.teleport(); + if (_this.inCoolIn === true) { + _this.exitTeleportMode(); + _this.deleteTargetOverlay(); + _this.deleteCancelOverlay(); + } else { + _this.teleport(); + } return; } } @@ -209,18 +261,14 @@ function Teleporter() { }; this.rightRay = function() { - - var rightPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.RightHand).translation), MyAvatar.position); - - var rightControllerRotation = Controller.getPoseValue(Controller.Standard.RightHand).rotation; - - var rightRotation = Quat.multiply(MyAvatar.orientation, rightControllerRotation) - - var rightFinal = Quat.multiply(rightRotation, Quat.angleAxis(90, { - x: 1, - y: 0, - z: 0 - })); + var pose = Controller.getPoseValue(Controller.Standard.RightHand); + var rightPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition(); + var rightRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : + Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + })); var rightPickRay = { origin: rightPosition, @@ -235,11 +283,36 @@ function Teleporter() { var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]); if (rightIntersection.intersects) { - this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); - if (this.targetOverlay !== null) { - this.updateTargetOverlay(rightIntersection); + if (this.tooClose === true) { + this.deleteTargetOverlay(); + + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); + if (this.cancelOverlay !== null) { + this.updateCancelOverlay(rightIntersection); + } else { + this.createCancelOverlay(); + } } else { - this.createTargetOverlay(); + if (this.inCoolIn === true) { + this.deleteTargetOverlay(); + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); + if (this.cancelOverlay !== null) { + this.updateCancelOverlay(rightIntersection); + } else { + this.createCancelOverlay(); + } + } else { + this.deleteCancelOverlay(); + + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + if (this.targetOverlay !== null) { + this.updateTargetOverlay(rightIntersection); + } else { + this.createTargetOverlay(); + } + } + + } } else { @@ -251,15 +324,14 @@ function Teleporter() { this.leftRay = function() { - var leftPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.LeftHand).translation), MyAvatar.position); - - var leftRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.LeftHand).rotation) - - var leftFinal = Quat.multiply(leftRotation, Quat.angleAxis(90, { - x: 1, - y: 0, - z: 0 - })); + var pose = Controller.getPoseValue(Controller.Standard.LeftHand); + var leftPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition(); + var leftRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : + Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + })); var leftPickRay = { origin: leftPosition, @@ -275,13 +347,38 @@ function Teleporter() { if (leftIntersection.intersects) { - this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); - if (this.targetOverlay !== null) { - this.updateTargetOverlay(leftIntersection); + if (this.tooClose === true) { + this.deleteTargetOverlay(); + this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); + if (this.cancelOverlay !== null) { + this.updateCancelOverlay(leftIntersection); + } else { + this.createCancelOverlay(); + } } else { - this.createTargetOverlay(); + if (this.inCoolIn === true) { + this.deleteTargetOverlay(); + this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); + if (this.cancelOverlay !== null) { + this.updateCancelOverlay(leftIntersection); + } else { + this.createCancelOverlay(); + } + } else { + this.deleteCancelOverlay(); + this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + + if (this.targetOverlay !== null) { + this.updateTargetOverlay(leftIntersection); + } else { + this.createTargetOverlay(); + } + } + + } + } else { this.deleteTargetOverlay(); @@ -355,20 +452,44 @@ function Teleporter() { this.updateTargetOverlay = function(intersection) { _this.intersection = intersection; - var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP) - var euler = Quat.safeEulerAngles(rotation) + var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP); + var euler = Quat.safeEulerAngles(rotation); var position = { x: intersection.intersection.x, y: intersection.intersection.y + TARGET_MODEL_DIMENSIONS.y / 2, z: intersection.intersection.z - } + }; + + this.tooClose = isTooCloseToTeleport(position); + var towardUs = Quat.fromPitchYawRollDegrees(0, euler.y, 0); + Overlays.editOverlay(this.targetOverlay, { position: position, - rotation: Quat.fromPitchYawRollDegrees(0, euler.y, 0), + rotation: towardUs }); }; + this.updateCancelOverlay = function(intersection) { + _this.intersection = intersection; + + var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP); + var euler = Quat.safeEulerAngles(rotation); + var position = { + x: intersection.intersection.x, + y: intersection.intersection.y + TARGET_MODEL_DIMENSIONS.y / 2, + z: intersection.intersection.z + }; + + this.tooClose = isTooCloseToTeleport(position); + var towardUs = Quat.fromPitchYawRollDegrees(0, euler.y, 0); + + Overlays.editOverlay(this.cancelOverlay, { + position: position, + rotation: towardUs + }); + }; + this.disableGrab = function() { Messages.sendLocalMessage('Hifi-Hand-Disabler', this.teleportHand); }; @@ -383,10 +504,17 @@ function Teleporter() { }; this.teleport = function(value) { + if (value === undefined) { this.exitTeleportMode(); } + if (this.intersection !== null) { + if (this.tooClose === true) { + this.exitTeleportMode(); + this.deleteCancelOverlay(); + return; + } var offset = getAvatarFootOffset(); this.intersection.intersection.y += offset; this.exitTeleportMode(); @@ -394,7 +522,6 @@ function Teleporter() { } }; - this.findMidpoint = function(start, end) { var xy = Vec3.sum(start, end); var midpoint = Vec3.multiply(0.5, xy); @@ -426,6 +553,7 @@ function Teleporter() { _this.smoothArrivalInterval = Script.setInterval(function() { if (_this.arrivalPoints.length === 0) { Script.clearInterval(_this.smoothArrivalInterval); + HMD.centerUI(); return; } var landingPoint = _this.arrivalPoints.shift(); @@ -433,13 +561,15 @@ function Teleporter() { if (_this.arrivalPoints.length === 1 || _this.arrivalPoints.length === 0) { _this.deleteTargetOverlay(); + _this.deleteCancelOverlay(); } }, SMOOTH_ARRIVAL_SPACING); + + } } - //related to repositioning the avatar after you teleport function getAvatarFootOffset() { var data = getJointData(); @@ -505,7 +635,11 @@ function isMoving() { } else { return false; } -} +}; + +function isTooCloseToTeleport(position) { + return Vec3.distance(MyAvatar.position, position) <= TELEPORT_CANCEL_RANGE; +}; function registerMappings() { mappingName = 'Hifi-Teleporter-Dev-' + Math.random(); @@ -559,7 +693,7 @@ function registerMappings() { }, TELEPORT_DELAY) return; }); -} +}; registerMappings(); @@ -573,6 +707,7 @@ function cleanup() { teleportMapping.disable(); teleporter.disableMappings(); teleporter.deleteTargetOverlay(); + teleporter.deleteCancelOverlay(); teleporter.turnOffOverlayBeams(); if (teleporter.updateConnected !== null) { Script.update.disconnect(teleporter.update); diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js new file mode 100644 index 0000000000..3a75482770 --- /dev/null +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -0,0 +1,141 @@ +// Created by james b. pollack @imgntn on 8/18/2016 +// Copyright 2016 High Fidelity, Inc. +// +//advanced movements settings are in individual controller json files +//what we do is check the status of the 'advance movement' checkbox when you enter HMD mode +//if 'advanced movement' is checked...we give you the defaults that are in the json. +//if 'advanced movement' is not checked... we override the advanced controls with basic ones. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +var mappingName, basicMapping, isChecked; + +var TURN_RATE = 1000; +var MENU_ITEM_NAME = "Advanced Movement For Hand Controllers"; +var SETTINGS_KEY = 'advancedMovementForHandControllersIsChecked'; +var previousSetting = Settings.getValue(SETTINGS_KEY); +if (previousSetting === '' || previousSetting === false || previousSetting === 'false') { + previousSetting = false; + isChecked = false; +} + +if (previousSetting === true || previousSetting === 'true') { + previousSetting = true; + isChecked = true; +} + +function addAdvancedMovementItemToSettingsMenu() { + Menu.addMenuItem({ + menuName: "Settings", + menuItemName: MENU_ITEM_NAME, + isCheckable: true, + isChecked: previousSetting + }); + +} + +function rotate180() { + var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.angleAxis(180, { + x: 0, + y: 1, + z: 0 + })) + MyAvatar.orientation = newOrientation +} + +var inFlipTurn = false; + +function registerBasicMapping() { + mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); + basicMapping = Controller.newMapping(mappingName); + basicMapping.from(Controller.Standard.LY).to(function(value) { + var stick = Controller.getValue(Controller.Standard.LS); + if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { + rotate180(); + } else if (Controller.Hardware.Vive !== undefined) { + if (value > 0.75 && inFlipTurn === false) { + inFlipTurn = true; + rotate180(); + Script.setTimeout(function() { + inFlipTurn = false; + }, TURN_RATE) + } + } + return; + }); + basicMapping.from(Controller.Standard.LX).to(Controller.Standard.RX); + basicMapping.from(Controller.Standard.RY).to(function(value) { + var stick = Controller.getValue(Controller.Standard.RS); + if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { + rotate180(); + } else if (Controller.Hardware.Vive !== undefined) { + if (value > 0.75 && inFlipTurn === false) { + inFlipTurn = true; + rotate180(); + Script.setTimeout(function() { + inFlipTurn = false; + }, TURN_RATE) + } + } + return; + }) +} + + +function enableMappings() { + Controller.enableMapping(mappingName); +} + +function disableMappings() { + Controller.disableMapping(mappingName); +} + +function scriptEnding() { + Menu.removeMenuItem("Settings", MENU_ITEM_NAME); + disableMappings(); +} + + +function menuItemEvent(menuItem) { + if (menuItem == MENU_ITEM_NAME) { + isChecked = Menu.isOptionChecked(MENU_ITEM_NAME); + if (isChecked === true) { + Settings.setValue(SETTINGS_KEY, true); + disableMappings(); + } else if (isChecked === false) { + Settings.setValue(SETTINGS_KEY, false); + enableMappings(); + } + } +} + +addAdvancedMovementItemToSettingsMenu(); + +Script.scriptEnding.connect(scriptEnding); + +Menu.menuItemEvent.connect(menuItemEvent); + +registerBasicMapping(); + +Script.setTimeout(function() { + if (previousSetting === true) { + disableMappings(); + } else { + enableMappings(); + } +}, 100) + + +HMD.displayModeChanged.connect(function(isHMDMode) { + if (isHMDMode) { + if (Controller.Hardware.Vive !== undefined || Controller.Hardware.OculusTouch !== undefined) { + if (isChecked === true) { + disableMappings(); + } else if (isChecked === false) { + enableMappings(); + } + + } + } +}); \ No newline at end of file diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 25c25a9a7e..a3e5476bf9 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -249,6 +249,9 @@ var toolBar = (function () { toolBar = Toolbars.getToolbar(EDIT_TOOLBAR); toolBar.writeProperty("shown", false); + addButton("openAssetBrowserButton","assets-01.svg",function(){ + Window.showAssetServer(); + }) addButton("newModelButton", "model-01.svg", function () { var SHAPE_TYPE_NONE = 0; @@ -429,6 +432,7 @@ var toolBar = (function () { }); }); + that.setActive(false); } @@ -1124,37 +1128,40 @@ function handeMenuEvent(menuItem) { } tooltip.show(false); } - -// This function tries to find a reasonable position to place a new entity based on the camera -// position. If a reasonable position within the world bounds can't be found, `null` will -// be returned. The returned position will also take into account grid snapping settings. function getPositionToCreateEntity() { - var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE; - var direction = Quat.getFront(Camera.orientation); - var offset = Vec3.multiply(distance, direction); - var placementPosition = Vec3.sum(Camera.position, offset); - - var cameraPosition = Camera.position; - var HALF_TREE_SCALE = 16384; + var direction = Quat.getFront(MyAvatar.orientation); + var distance = 1; + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, distance)); - var cameraOutOfBounds = Math.abs(cameraPosition.x) > HALF_TREE_SCALE || Math.abs(cameraPosition.y) > HALF_TREE_SCALE || - Math.abs(cameraPosition.z) > HALF_TREE_SCALE; - var placementOutOfBounds = Math.abs(placementPosition.x) > HALF_TREE_SCALE || - Math.abs(placementPosition.y) > HALF_TREE_SCALE || - Math.abs(placementPosition.z) > HALF_TREE_SCALE; - - if (cameraOutOfBounds && placementOutOfBounds) { - return null; + if (Camera.mode === "entity" || Camera.mode === "independent") { + position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), distance)) } - - placementPosition.x = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.x)); - placementPosition.y = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.y)); - placementPosition.z = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.z)); - - return placementPosition; + 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.getFront(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.getFront(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) { print("Import URL requested: " + importURL); if (!Entities.canAdjustLocks()) { @@ -1179,7 +1186,7 @@ function importSVO(importURL) { z: 0 }; if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) { - position = getPositionToCreateEntity(); + position = getPositionToImportEntity(); } if (position !== null && position !== undefined) { var pastedEntityIDs = Clipboard.pasteEntities(position); diff --git a/scripts/system/html/edit-style.css b/scripts/system/html/css/edit-style.css similarity index 94% rename from scripts/system/html/edit-style.css rename to scripts/system/html/css/edit-style.css index 19d1cd95a9..0b58cf22ac 100644 --- a/scripts/system/html/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -10,51 +10,51 @@ @font-face { font-family: Raleway-Regular; - src: url(../../../resources/fonts/Raleway-Regular.ttf), /* Windows production */ - url(../../../fonts/Raleway-Regular.ttf), /* OSX production */ - url(../../../interface/resources/fonts/Raleway-Regular.ttf); /* Development, running script in /HiFi/examples */ + src: url(../../../../resources/fonts/Raleway-Regular.ttf), /* Windows production */ + url(../../../../fonts/Raleway-Regular.ttf), /* OSX production */ + url(../../../../interface/resources/fonts/Raleway-Regular.ttf); /* Development, running script in /HiFi/examples */ } @font-face { font-family: Raleway-Light; - src: url(../../../resources/fonts/Raleway-Light.ttf), - url(../../../fonts/Raleway-Light.ttf), - url(../../../interface/resources/fonts/Raleway-Light.ttf); + src: url(../../../../resources/fonts/Raleway-Light.ttf), + url(../../../../fonts/Raleway-Light.ttf), + url(../../../../interface/resources/fonts/Raleway-Light.ttf); } @font-face { font-family: Raleway-Bold; - src: url(../../../resources/fonts/Raleway-Bold.ttf), - url(../../../fonts/Raleway-Bold.ttf), - url(../../../interface/resources/fonts/Raleway-Bold.ttf); + src: url(../../../../resources/fonts/Raleway-Bold.ttf), + url(../../../../fonts/Raleway-Bold.ttf), + url(../../../../interface/resources/fonts/Raleway-Bold.ttf); } @font-face { font-family: Raleway-SemiBold; - src: url(../../../resources/fonts/Raleway-SemiBold.ttf), - url(../../../fonts/Raleway-SemiBold.ttf), - url(../../../interface/resources/fonts/Raleway-SemiBold.ttf); + src: url(../../../../resources/fonts/Raleway-SemiBold.ttf), + url(../../../../fonts/Raleway-SemiBold.ttf), + url(../../../../interface/resources/fonts/Raleway-SemiBold.ttf); } @font-face { font-family: FiraSans-SemiBold; - src: url(../../../resources/fonts/FiraSans-SemiBold.ttf), - url(../../../fonts/FiraSans-SemiBold.ttf), - url(../../../interface/resources/fonts/FiraSans-SemiBold.ttf); + src: url(../../../../resources/fonts/FiraSans-SemiBold.ttf), + url(../../../../fonts/FiraSans-SemiBold.ttf), + url(../../../../interface/resources/fonts/FiraSans-SemiBold.ttf); } @font-face { font-family: AnonymousPro-Regular; - src: url(../../../resources/fonts/AnonymousPro-Regular.ttf), - url(../../../fonts/AnonymousPro-Regular.ttf), - url(../../../interface/resources/fonts/AnonymousPro-Regular.ttf); + src: url(../../../../resources/fonts/AnonymousPro-Regular.ttf), + url(../../../../fonts/AnonymousPro-Regular.ttf), + url(../../../../interface/resources/fonts/AnonymousPro-Regular.ttf); } @font-face { font-family: HiFi-Glyphs; - src: url(../../../resources/fonts/hifi-glyphs.ttf), - url(../../../fonts/hifi-glyphs.ttf), - url(../../../interface/resources/fonts/hifi-glyphs.ttf); + src: url(../../../../resources/fonts/hifi-glyphs.ttf), + url(../../../../fonts/hifi-glyphs.ttf), + url(../../../../interface/resources/fonts/hifi-glyphs.ttf); } * { @@ -1077,10 +1077,6 @@ input#dimension-rescale-button { input#reset-to-natural-dimensions { margin-right: 0; } -input#preview-camera-button { - margin-left: 1px; - margin-right: 0; -} #animation-fps { margin-top: 48px; diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index dbc224e9fb..2088898613 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -10,314 +10,12 @@ - - + + - - - + + +
@@ -378,4 +76,4 @@
- \ No newline at end of file + diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index f2ade39144..01826b8e10 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -11,1324 +11,15 @@ Properties - + - - + + - - - + + + -
@@ -1363,6 +54,10 @@
+
+ + +
@@ -1379,15 +74,28 @@
- +
M
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
@@ -1396,6 +104,14 @@
+
+ +
+
+
+
+
+
@@ -1404,31 +120,6 @@
- -
-
- -
-
-
-
-
-
- -
-
- -
-
-
-
-
-
- - - -
-
@@ -1439,15 +130,17 @@
-
- -
-
-
-
+
+
+
+
+
+ + +
+
-
diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html index 4b18a8555b..f7f206d702 100644 --- a/scripts/system/html/gridControls.html +++ b/scripts/system/html/gridControls.html @@ -10,144 +10,14 @@ - + - - + + - - - + + +
diff --git a/scripts/system/html/colpick.js b/scripts/system/html/js/colpick.js similarity index 100% rename from scripts/system/html/colpick.js rename to scripts/system/html/js/colpick.js diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js new file mode 100644 index 0000000000..8e5e190068 --- /dev/null +++ b/scripts/system/html/js/entityList.js @@ -0,0 +1,310 @@ +// entityList.js +// +// Created by Ryan Huffman on 19 Nov 2014 +// 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 + +var entities = {}; +var selectedEntities = []; +var currentSortColumn = 'type'; +var currentSortOrder = 'des'; +var entityList = null; +var refreshEntityListTimer = null; +const ASCENDING_STRING = '▾'; +const DESCENDING_STRING = '▴'; +const LOCKED_GLYPH = ""; +const VISIBLE_GLYPH = ""; +const DELETE = 46; // Key code for the delete key. +const MAX_ITEMS = Number.MAX_VALUE; // Used to set the max length of the list of discovered entities. + +debugPrint = function (message) { + console.log(message); +}; + +function loaded() { + openEventBridge(function() { + entityList = new List('entity-list', { valueNames: ['name', 'type', 'url', 'locked', 'visible'], page: MAX_ITEMS}); + entityList.clear(); + elEntityTable = document.getElementById("entity-table"); + elEntityTableBody = document.getElementById("entity-table-body"); + elRefresh = document.getElementById("refresh"); + elToggleLocked = document.getElementById("locked"); + elToggleVisible = document.getElementById("visible"); + elDelete = document.getElementById("delete"); + elTeleport = document.getElementById("teleport"); + elRadius = document.getElementById("radius"); + elFooter = document.getElementById("footer-text"); + elNoEntitiesMessage = document.getElementById("no-entities"); + elNoEntitiesRadius = document.getElementById("no-entities-radius"); + elEntityTableScroll = document.getElementById("entity-table-scroll"); + + document.getElementById("entity-name").onclick = function() { + setSortColumn('name'); + }; + document.getElementById("entity-type").onclick = function() { + setSortColumn('type'); + }; + document.getElementById("entity-url").onclick = function() { + setSortColumn('url'); + }; + document.getElementById("entity-locked").onclick = function () { + setSortColumn('locked'); + }; + document.getElementById("entity-visible").onclick = function () { + setSortColumn('visible'); + }; + + function onRowClicked(clickEvent) { + var id = this.dataset.entityId; + var selection = [this.dataset.entityId]; + if (clickEvent.ctrlKey) { + selection = selection.concat(selectedEntities); + } else if (clickEvent.shiftKey && selectedEntities.length > 0) { + var previousItemFound = -1; + var clickedItemFound = -1; + for (var entity in entityList.visibleItems) { + if (clickedItemFound === -1 && this.dataset.entityId == entityList.visibleItems[entity].values().id) { + clickedItemFound = entity; + } else if(previousItemFound === -1 && selectedEntities[0] == entityList.visibleItems[entity].values().id) { + previousItemFound = entity; + } + } + if (previousItemFound !== -1 && clickedItemFound !== -1) { + var betweenItems = []; + var toItem = Math.max(previousItemFound, clickedItemFound); + // skip first and last item in this loop, we add them to selection after the loop + for (var i = (Math.min(previousItemFound, clickedItemFound) + 1); i < toItem; i++) { + entityList.visibleItems[i].elm.className = 'selected'; + betweenItems.push(entityList.visibleItems[i].values().id); + } + if (previousItemFound > clickedItemFound) { + // always make sure that we add the items in the right order + betweenItems.reverse(); + } + selection = selection.concat(betweenItems, selectedEntities); + } + } + + selectedEntities = selection; + + this.className = 'selected'; + + EventBridge.emitWebEvent(JSON.stringify({ + type: "selectionUpdate", + focus: false, + entityIds: selection, + })); + } + + function onRowDoubleClicked() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "selectionUpdate", + focus: true, + entityIds: [this.dataset.entityId], + })); + } + + function addEntity(id, name, type, url, locked, visible) { + var urlParts = url.split('/'); + var filename = urlParts[urlParts.length - 1]; + + if (entities[id] === undefined) { + entityList.add([{ id: id, name: name, type: type, url: filename, locked: locked, visible: visible }], + function (items) { + var currentElement = items[0].elm; + var id = items[0]._values.id; + entities[id] = { + id: id, + name: name, + el: currentElement, + item: items[0] + }; + currentElement.setAttribute('id', 'entity_' + id); + currentElement.setAttribute('title', url); + currentElement.dataset.entityId = id; + currentElement.onclick = onRowClicked; + currentElement.ondblclick = onRowDoubleClicked; + }); + + if (refreshEntityListTimer) { + clearTimeout(refreshEntityListTimer); + } + refreshEntityListTimer = setTimeout(refreshEntityListObject, 50); + } else { + var item = entities[id].item; + item.values({ name: name, url: filename, locked: locked, visible: visible }); + } + } + + function clearEntities() { + entities = {}; + entityList.clear(); + } + + var elSortOrder = { + name: document.querySelector('#entity-name .sort-order'), + type: document.querySelector('#entity-type .sort-order'), + url: document.querySelector('#entity-url .sort-order'), + locked: document.querySelector('#entity-locked .sort-order'), + visible: document.querySelector('#entity-visible .sort-order') + } + function setSortColumn(column) { + if (currentSortColumn == column) { + currentSortOrder = currentSortOrder == "asc" ? "desc" : "asc"; + } else { + elSortOrder[currentSortColumn].innerHTML = ""; + currentSortColumn = column; + currentSortOrder = "asc"; + } + elSortOrder[column].innerHTML = currentSortOrder == "asc" ? ASCENDING_STRING : DESCENDING_STRING; + entityList.sort(currentSortColumn, { order: currentSortOrder }); + } + setSortColumn('type'); + + function refreshEntities() { + clearEntities(); + EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' })); + } + + function refreshEntityListObject() { + refreshEntityListTimer = null; + entityList.sort(currentSortColumn, { order: currentSortOrder }); + entityList.search(document.getElementById("filter").value); + } + + function updateSelectedEntities(selectedEntities) { + var notFound = false; + for (var id in entities) { + entities[id].el.className = ''; + } + for (var i = 0; i < selectedEntities.length; i++) { + var id = selectedEntities[i]; + if (id in entities) { + var entity = entities[id]; + entity.el.className = 'selected'; + } else { + notFound = true; + } + } + + if (selectedEntities.length > 1) { + elFooter.firstChild.nodeValue = selectedEntities.length + " entities selected"; + } else if (selectedEntities.length === 1) { + elFooter.firstChild.nodeValue = "1 entity selected"; + } else if (entityList.visibleItems.length === 1) { + elFooter.firstChild.nodeValue = "1 entity found"; + } else { + elFooter.firstChild.nodeValue = entityList.visibleItems.length + " entities found"; + } + + // HACK: Fixes the footer and header text sometimes not displaying after adding or deleting entities. + // The problem appears to be a bug in the Qt HTML/CSS rendering (Qt 5.5). + document.getElementById("radius").focus(); + document.getElementById("radius").blur(); + + return notFound; + } + + elRefresh.onclick = function() { + refreshEntities(); + } + elToggleLocked.onclick = function () { + EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleLocked' })); + } + elToggleVisible.onclick = function () { + EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleVisible' })); + } + elTeleport.onclick = function () { + EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' })); + } + elDelete.onclick = function() { + EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); + refreshEntities(); + } + + document.addEventListener("keydown", function (keyDownEvent) { + if (keyDownEvent.target.nodeName === "INPUT") { + return; + } + var keyCode = keyDownEvent.keyCode; + if (keyCode === DELETE) { + EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); + refreshEntities(); + } + }, false); + + elRadius.onchange = function () { + elRadius.value = Math.max(elRadius.value, 0); + EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value })); + refreshEntities(); + elNoEntitiesRadius.firstChild.nodeValue = elRadius.value; + } + + if (window.EventBridge !== undefined) { + EventBridge.scriptEventReceived.connect(function(data) { + data = JSON.parse(data); + + if (data.type === "clearEntityList") { + clearEntities(); + } else if (data.type == "selectionUpdate") { + var notFound = updateSelectedEntities(data.selectedIDs); + if (notFound) { + refreshEntities(); + } + } else if (data.type == "update") { + var newEntities = data.entities; + if (newEntities.length == 0) { + elNoEntitiesMessage.style.display = "block"; + elFooter.firstChild.nodeValue = "0 entities found"; + } else { + elNoEntitiesMessage.style.display = "none"; + for (var i = 0; i < newEntities.length; i++) { + var id = newEntities[i].id; + addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url, + newEntities[i].locked ? LOCKED_GLYPH : null, + newEntities[i].visible ? VISIBLE_GLYPH : null); + } + updateSelectedEntities(data.selectedIDs); + resize(); + } + } + }); + setTimeout(refreshEntities, 1000); + } + + function resize() { + // Take up available window space + elEntityTableScroll.style.height = window.innerHeight - 207; + + var tds = document.querySelectorAll("#entity-table-body tr:first-child td"); + var ths = document.querySelectorAll("#entity-table thead th"); + if (tds.length >= ths.length) { + // Update the widths of the header cells to match the body + for (var i = 0; i < ths.length; i++) { + ths[i].width = tds[i].offsetWidth; + } + } else { + // Reasonable widths if nothing is displayed + var tableWidth = document.getElementById("entity-table").offsetWidth; + ths[0].width = 0.16 * tableWidth; + ths[1].width = 0.34 * tableWidth; + ths[2].width = 0.34 * tableWidth; + ths[3].width = 0.08 * tableWidth; + ths[4].width = 0.08 * tableWidth; + } + }; + + window.onresize = resize; + resize(); + }); + + augmentSpinButtons(); + + // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked + document.addEventListener("contextmenu", function (event) { + event.preventDefault(); + }, false); +} + diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js new file mode 100644 index 0000000000..5898f85e90 --- /dev/null +++ b/scripts/system/html/js/entityProperties.js @@ -0,0 +1,1309 @@ +// entityProperties.js +// +// Created by Ryan Huffman on 13 Nov 2014 +// 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 + +var PI = 3.14159265358979; +var DEGREES_TO_RADIANS = PI / 180.0; +var RADIANS_TO_DEGREES = 180.0 / PI; +var ICON_FOR_TYPE = { + Box: "V", + Sphere: "n", + Shape: "n", + ParticleEffect: "", + Model: "", + Web: "q", + Text: "l", + Light: "p", + Zone: "o", + PolyVox: "", + Multiple: "" +} + +var colorPickers = []; + +debugPrint = function(message) { + EventBridge.emitWebEvent( + JSON.stringify({ + type:"print", + message: message + }) + ); +}; + +function enableChildren(el, selector) { + els = el.querySelectorAll(selector); + for (var i = 0; i < els.length; i++) { + els[i].removeAttribute('disabled'); + } +} +function disableChildren(el, selector) { + els = el.querySelectorAll(selector); + for (var i = 0; i < els.length; i++) { + els[i].setAttribute('disabled', 'disabled'); + } +} + +function enableProperties() { + enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); + enableChildren(document, ".colpick"); +} + +function disableProperties() { + disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); + disableChildren(document, ".colpick"); + for (var i = 0; i < colorPickers.length; i++) { + colorPickers[i].colpickHide(); + } +} + +function showElements(els, show) { + for (var i = 0; i < els.length; i++) { + els[i].style.display = (show) ? 'table' : 'none'; + } +} + +function createEmitCheckedPropertyUpdateFunction(propertyName) { + return function() { + EventBridge.emitWebEvent( + '{ "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}' + ); + }; +} + +function createEmitCheckedToStringPropertyUpdateFunction(checkboxElement, name, propertyName) { + var newString = ""; + if (checkboxElement.checked) { + newString += name + ""; + } else { + + } + +} + +function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) { + return function () { + var properties = {}; + properties[group] = {}; + properties[group][propertyName] = this.checked; + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties + }) + ); + }; +} + +function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { + decimals = decimals == undefined ? 4 : decimals; + return function() { + var value = parseFloat(this.value).toFixed(decimals); + + EventBridge.emitWebEvent( + '{ "type":"update", "properties":{"' + propertyName + '":' + value + '}}' + ); + }; +} +function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { + return function() { + var properties = {}; + properties[group] = {}; + properties[group][propertyName] = this.value; + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); + }; +} + + +function createEmitTextPropertyUpdateFunction(propertyName) { + return function() { + var properties = {}; + properties[propertyName] = this.value; + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); + }; +} + +function createEmitGroupTextPropertyUpdateFunction(group,propertyName) { + return function() { + var properties = {}; + properties[group] = {}; + properties[group][propertyName] = this.value; + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); + }; +} + +function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) { + return function() { + var data = { + type: "update", + properties: { + } + }; + data.properties[property] = { + x: elX.value, + y: elY.value, + z: elZ.value, + }; + EventBridge.emitWebEvent(JSON.stringify(data)); + } +}; + +function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) { + return function() { + var data = { + type: "update", + properties: { + } + }; + data.properties[group] = { }; + data.properties[group][property] = { + x: elX.value, + y: elY.value, + z: elZ ? elZ.value : 0, + }; + EventBridge.emitWebEvent(JSON.stringify(data)); + } +}; + +function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) { + return function() { + var data = { + type: "update", + properties: { + } + }; + data.properties[property] = { + x: elX.value * multiplier, + y: elY.value * multiplier, + z: elZ.value * multiplier, + }; + EventBridge.emitWebEvent(JSON.stringify(data)); + } +}; + +function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) { + return function() { + emitColorPropertyUpdate(property, elRed.value, elGreen.value, elBlue.value); + } +}; + +function emitColorPropertyUpdate(property, red, green, blue, group) { + var data = { + type: "update", + properties: { + } + }; + if (group) { + data.properties[group] = { }; + data.properties[group][property] = { + red: red, + green: green, + blue: blue, + }; + } else { + data.properties[property] = { + red: red, + green: green, + blue: blue, + }; + } + EventBridge.emitWebEvent(JSON.stringify(data)); +}; + + +function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) { + return function() { + var data = { + type: "update", + properties: { + } + }; + data.properties[group] = { }; + + data.properties[group][property] = { + red: elRed.value, + green: elGreen.value, + blue: elBlue.value, + }; + EventBridge.emitWebEvent(JSON.stringify(data)); + } +}; + +function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) { + if (subPropertyElement.checked) { + if (propertyValue.indexOf(subPropertyString)) { + propertyValue += subPropertyString + ','; + } + } else { + // We've unchecked, so remove + propertyValue = propertyValue.replace(subPropertyString + ",", ""); + } + + var _properties ={} + _properties[propertyName] = propertyValue; + + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: _properties + }) + ); + +} + +function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, defaultValue) { + var properties = {}; + var parsedData = {}; + try { + parsedData = JSON.parse(userDataElement.value); + } catch(e) {} + + if (!(groupName in parsedData)) { + parsedData[groupName] = {} + } + delete parsedData[groupName][keyName]; + if (checkBoxElement.checked !== defaultValue) { + parsedData[groupName][keyName] = checkBoxElement.checked; + } + + if (Object.keys(parsedData[groupName]).length == 0) { + delete parsedData[groupName]; + } + if (Object.keys(parsedData).length > 0) { + properties['userData'] = JSON.stringify(parsedData); + } else { + properties['userData'] = ''; + } + + userDataElement.value = properties['userData']; + + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); +}; + +function setTextareaScrolling(element) { + var isScrolling = element.scrollHeight > element.offsetHeight; + element.setAttribute("scrolling", isScrolling ? "true" : "false"); +} + +function loaded() { + openEventBridge(function() { + var allSections = []; + var elID = document.getElementById("property-id"); + var elType = document.getElementById("property-type"); + var elTypeIcon = document.getElementById("type-icon"); + var elName = document.getElementById("property-name"); + var elLocked = document.getElementById("property-locked"); + var elVisible = document.getElementById("property-visible"); + var elPositionX = document.getElementById("property-pos-x"); + var elPositionY = document.getElementById("property-pos-y"); + var elPositionZ = document.getElementById("property-pos-z"); + var elMoveSelectionToGrid = document.getElementById("move-selection-to-grid"); + var elMoveAllToGrid = document.getElementById("move-all-to-grid"); + + var elDimensionsX = document.getElementById("property-dim-x"); + var elDimensionsY = document.getElementById("property-dim-y"); + var elDimensionsZ = document.getElementById("property-dim-z"); + var elResetToNaturalDimensions = document.getElementById("reset-to-natural-dimensions"); + var elRescaleDimensionsPct = document.getElementById("dimension-rescale-pct"); + var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button"); + + var elParentID = document.getElementById("property-parent-id"); + var elParentJointIndex = document.getElementById("property-parent-joint-index"); + + var elRegistrationX = document.getElementById("property-reg-x"); + var elRegistrationY = document.getElementById("property-reg-y"); + var elRegistrationZ = document.getElementById("property-reg-z"); + + var elRotationX = document.getElementById("property-rot-x"); + var elRotationY = document.getElementById("property-rot-y"); + var elRotationZ = document.getElementById("property-rot-z"); + + var elLinearVelocityX = document.getElementById("property-lvel-x"); + var elLinearVelocityY = document.getElementById("property-lvel-y"); + var elLinearVelocityZ = document.getElementById("property-lvel-z"); + var elLinearDamping = document.getElementById("property-ldamping"); + + var elAngularVelocityX = document.getElementById("property-avel-x"); + var elAngularVelocityY = document.getElementById("property-avel-y"); + var elAngularVelocityZ = document.getElementById("property-avel-z"); + var elAngularDamping = document.getElementById("property-adamping"); + + var elRestitution = document.getElementById("property-restitution"); + var elFriction = document.getElementById("property-friction"); + + var elGravityX = document.getElementById("property-grav-x"); + var elGravityY = document.getElementById("property-grav-y"); + var elGravityZ = document.getElementById("property-grav-z"); + + var elAccelerationX = document.getElementById("property-lacc-x"); + var elAccelerationY = document.getElementById("property-lacc-y"); + var elAccelerationZ = document.getElementById("property-lacc-z"); + + var elDensity = document.getElementById("property-density"); + var elCollisionless = document.getElementById("property-collisionless"); + var elDynamic = document.getElementById("property-dynamic" ); + var elCollideStatic = document.getElementById("property-collide-static"); + var elCollideDynamic = document.getElementById("property-collide-dynamic"); + var elCollideKinematic = document.getElementById("property-collide-kinematic"); + var elCollideMyAvatar = document.getElementById("property-collide-myAvatar"); + var elCollideOtherAvatar = document.getElementById("property-collide-otherAvatar"); + var elCollisionSoundURL = document.getElementById("property-collision-sound-url"); + + var elGrabbable = document.getElementById("property-grabbable"); + var elWantsTrigger = document.getElementById("property-wants-trigger"); + var elIgnoreIK = document.getElementById("property-ignore-ik"); + + var elLifetime = document.getElementById("property-lifetime"); + var elScriptURL = document.getElementById("property-script-url"); + /* + FIXME: See FIXME for property-script-url. + var elScriptTimestamp = document.getElementById("property-script-timestamp"); + */ + var elReloadScriptButton = document.getElementById("reload-script-button"); + var elUserData = document.getElementById("property-user-data"); + + var elColorSections = document.querySelectorAll(".color-section"); + var elColor = document.getElementById("property-color"); + var elColorRed = document.getElementById("property-color-red"); + var elColorGreen = document.getElementById("property-color-green"); + var elColorBlue = document.getElementById("property-color-blue"); + + var elShapeSections = document.querySelectorAll(".shape-section"); + allSections.push(elShapeSections); + var elShape = document.getElementById("property-shape"); + + var elLightSections = document.querySelectorAll(".light-section"); + allSections.push(elLightSections); + var elLightSpotLight = document.getElementById("property-light-spot-light"); + var elLightColor = document.getElementById("property-light-color"); + var elLightColorRed = document.getElementById("property-light-color-red"); + var elLightColorGreen = document.getElementById("property-light-color-green"); + var elLightColorBlue = document.getElementById("property-light-color-blue"); + + var elLightIntensity = document.getElementById("property-light-intensity"); + var elLightFalloffRadius = document.getElementById("property-light-falloff-radius"); + var elLightExponent = document.getElementById("property-light-exponent"); + var elLightCutoff = document.getElementById("property-light-cutoff"); + + var elModelSections = document.querySelectorAll(".model-section"); + allSections.push(elModelSections); + var elModelURL = document.getElementById("property-model-url"); + var elShapeType = document.getElementById("property-shape-type"); + var elCompoundShapeURL = document.getElementById("property-compound-shape-url"); + var elModelAnimationURL = document.getElementById("property-model-animation-url"); + var elModelAnimationPlaying = document.getElementById("property-model-animation-playing"); + var elModelAnimationFPS = document.getElementById("property-model-animation-fps"); + var elModelAnimationFrame = document.getElementById("property-model-animation-frame"); + var elModelAnimationFirstFrame = document.getElementById("property-model-animation-first-frame"); + var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame"); + var elModelAnimationLoop = document.getElementById("property-model-animation-loop"); + var elModelAnimationHold = document.getElementById("property-model-animation-hold"); + var elModelTextures = document.getElementById("property-model-textures"); + var elModelOriginalTextures = document.getElementById("property-model-original-textures"); + + var elWebSections = document.querySelectorAll(".web-section"); + allSections.push(elWebSections); + var elWebSourceURL = document.getElementById("property-web-source-url"); + + var elDescription = document.getElementById("property-description"); + + var elHyperlinkHref = document.getElementById("property-hyperlink-href"); + + var elHyperlinkSections = document.querySelectorAll(".hyperlink-section"); + + + var elTextSections = document.querySelectorAll(".text-section"); + allSections.push(elTextSections); + var elTextText = document.getElementById("property-text-text"); + var elTextLineHeight = document.getElementById("property-text-line-height"); + var elTextTextColor = document.getElementById("property-text-text-color"); + var elTextFaceCamera = document.getElementById("property-text-face-camera"); + var elTextTextColorRed = document.getElementById("property-text-text-color-red"); + var elTextTextColorGreen = document.getElementById("property-text-text-color-green"); + var elTextTextColorBlue = document.getElementById("property-text-text-color-blue"); + var elTextBackgroundColor = document.getElementById("property-text-background-color"); + var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red"); + var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green"); + var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue"); + + var elZoneSections = document.querySelectorAll(".zone-section"); + allSections.push(elZoneSections); + var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled"); + + var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color"); + var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red"); + var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green"); + var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue"); + var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity"); + var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity"); + var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x"); + var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); + var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z"); + var elZoneKeyLightAmbientURL = document.getElementById("property-zone-key-ambient-url"); + + var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude"); + var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude"); + var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude"); + var elZoneStageAutomaticHourDay = document.getElementById("property-zone-stage-automatic-hour-day"); + var elZoneStageDay = document.getElementById("property-zone-stage-day"); + var elZoneStageHour = document.getElementById("property-zone-stage-hour"); + + var elZoneBackgroundMode = document.getElementById("property-zone-background-mode"); + + var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color"); + var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red"); + var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green"); + var elZoneSkyboxColorBlue = document.getElementById("property-zone-skybox-color-blue"); + var elZoneSkyboxURL = document.getElementById("property-zone-skybox-url"); + + var elZoneFlyingAllowed = document.getElementById("property-zone-flying-allowed"); + var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed"); + + var elPolyVoxSections = document.querySelectorAll(".poly-vox-section"); + allSections.push(elPolyVoxSections); + var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x"); + var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y"); + var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); + var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style"); + var elXTextureURL = document.getElementById("property-x-texture-url"); + var elYTextureURL = document.getElementById("property-y-texture-url"); + var elZTextureURL = document.getElementById("property-z-texture-url"); + + if (window.EventBridge !== undefined) { + var properties; + EventBridge.scriptEventReceived.connect(function(data) { + data = JSON.parse(data); + if (data.type == "update") { + if (data.selections.length == 0) { + elTypeIcon.style.display = "none"; + elType.innerHTML = "No selection"; + elID.innerHTML = ""; + disableProperties(); + } else if (data.selections.length > 1) { + var selections = data.selections; + + var ids = []; + var types = {}; + var numTypes = 0; + + for (var i = 0; i < selections.length; i++) { + ids.push(selections[i].id); + var type = selections[i].properties.type; + if (types[type] === undefined) { + types[type] = 0; + numTypes += 1; + } + types[type]++; + } + + var type; + if (numTypes === 1) { + type = selections[0].properties.type; + } else { + type = "Multiple"; + } + elType.innerHTML = type + " (" + data.selections.length + ")"; + elTypeIcon.innerHTML = ICON_FOR_TYPE[type]; + elTypeIcon.style.display = "inline-block"; + + elID.innerHTML = ids.join("
"); + + disableProperties(); + } else { + + + properties = data.selections[0].properties; + + elID.innerHTML = properties.id; + + elType.innerHTML = properties.type; + elTypeIcon.innerHTML = ICON_FOR_TYPE[properties.type]; + elTypeIcon.style.display = "inline-block"; + + elLocked.checked = properties.locked; + + if (properties.locked) { + disableProperties(); + elLocked.removeAttribute('disabled'); + } else { + enableProperties(); + } + + elName.value = properties.name; + + elVisible.checked = properties.visible; + + elPositionX.value = properties.position.x.toFixed(4); + elPositionY.value = properties.position.y.toFixed(4); + elPositionZ.value = properties.position.z.toFixed(4); + + elDimensionsX.value = properties.dimensions.x.toFixed(4); + elDimensionsY.value = properties.dimensions.y.toFixed(4); + elDimensionsZ.value = properties.dimensions.z.toFixed(4); + + elParentID.value = properties.parentID; + elParentJointIndex.value = properties.parentJointIndex; + + elRegistrationX.value = properties.registrationPoint.x.toFixed(4); + elRegistrationY.value = properties.registrationPoint.y.toFixed(4); + elRegistrationZ.value = properties.registrationPoint.z.toFixed(4); + + elRotationX.value = properties.rotation.x.toFixed(4); + elRotationY.value = properties.rotation.y.toFixed(4); + elRotationZ.value = properties.rotation.z.toFixed(4); + + elLinearVelocityX.value = properties.velocity.x.toFixed(4); + elLinearVelocityY.value = properties.velocity.y.toFixed(4); + elLinearVelocityZ.value = properties.velocity.z.toFixed(4); + elLinearDamping.value = properties.damping.toFixed(2); + + elAngularVelocityX.value = (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(4); + elAngularVelocityY.value = (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(4); + elAngularVelocityZ.value = (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(4); + elAngularDamping.value = properties.angularDamping.toFixed(4); + + elRestitution.value = properties.restitution.toFixed(4); + elFriction.value = properties.friction.toFixed(4); + + elGravityX.value = properties.gravity.x.toFixed(4); + elGravityY.value = properties.gravity.y.toFixed(4); + elGravityZ.value = properties.gravity.z.toFixed(4); + + elAccelerationX.value = properties.acceleration.x.toFixed(4); + elAccelerationY.value = properties.acceleration.y.toFixed(4); + elAccelerationZ.value = properties.acceleration.z.toFixed(4); + + elDensity.value = properties.density.toFixed(4); + elCollisionless.checked = properties.collisionless; + elDynamic.checked = properties.dynamic; + + elCollideStatic.checked = properties.collidesWith.indexOf("static") > -1; + elCollideKinematic.checked = properties.collidesWith.indexOf("kinematic") > -1; + elCollideDynamic.checked = properties.collidesWith.indexOf("dynamic") > -1; + elCollideMyAvatar.checked = properties.collidesWith.indexOf("myAvatar") > -1; + elCollideOtherAvatar.checked = properties.collidesWith.indexOf("otherAvatar") > -1; + + elGrabbable.checked = properties.dynamic; + elWantsTrigger.checked = false; + elIgnoreIK.checked = false; + var parsedUserData = {} + try { + parsedUserData = JSON.parse(properties.userData); + + if ("grabbableKey" in parsedUserData) { + if ("grabbable" in parsedUserData["grabbableKey"]) { + elGrabbable.checked = parsedUserData["grabbableKey"].grabbable; + } + if ("wantsTrigger" in parsedUserData["grabbableKey"]) { + elWantsTrigger.checked = parsedUserData["grabbableKey"].wantsTrigger; + } + if ("ignoreIK" in parsedUserData["grabbableKey"]) { + elIgnoreIK.checked = parsedUserData["grabbableKey"].ignoreIK; + } + } + } catch(e) {} + + elCollisionSoundURL.value = properties.collisionSoundURL; + elLifetime.value = properties.lifetime; + elScriptURL.value = properties.script; + /* + FIXME: See FIXME for property-script-url. + elScriptTimestamp.value = properties.scriptTimestamp; + */ + elUserData.value = properties.userData; + setTextareaScrolling(elUserData); + + elHyperlinkHref.value = properties.href; + elDescription.value = properties.description; + + for (var i = 0; i < allSections.length; i++) { + for (var j = 0; j < allSections[i].length; j++) { + allSections[i][j].style.display = 'none'; + } + } + + for (var i = 0; i < elHyperlinkSections.length; i++) { + elHyperlinkSections[i].style.display = 'table'; + } + + if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere") { + for (var i = 0; i < elShapeSections.length; i++) { + elShapeSections[i].style.display = 'table'; + } + elShape.value = properties.shape; + setDropdownText(elShape); + + } else { + for (var i = 0; i < elShapeSections.length; i++) { + elShapeSections[i].style.display = 'none'; + } + } + + if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") { + for (var i = 0; i < elColorSections.length; i++) { + elColorSections[i].style.display = 'table'; + } + elColorRed.value = properties.color.red; + elColorGreen.value = properties.color.green; + elColorBlue.value = properties.color.blue; + elColor.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; + } else { + for (var i = 0; i < elColorSections.length; i++) { + elColorSections[i].style.display = 'none'; + } + } + + if (properties.type == "Model") { + for (var i = 0; i < elModelSections.length; i++) { + elModelSections[i].style.display = 'table'; + } + + elModelURL.value = properties.modelURL; + elShapeType.value = properties.shapeType; + setDropdownText(elShapeType); + elCompoundShapeURL.value = properties.compoundShapeURL; + elModelAnimationURL.value = properties.animation.url; + elModelAnimationPlaying.checked = properties.animation.running; + elModelAnimationFPS.value = properties.animation.fps; + elModelAnimationFrame.value = properties.animation.currentFrame; + elModelAnimationFirstFrame.value = properties.animation.firstFrame; + elModelAnimationLastFrame.value = properties.animation.lastFrame; + elModelAnimationLoop.checked = properties.animation.loop; + elModelAnimationHold.checked = properties.animation.hold; + elModelTextures.value = properties.textures; + setTextareaScrolling(elModelTextures); + elModelOriginalTextures.value = properties.originalTextures; + setTextareaScrolling(elModelOriginalTextures); + } else if (properties.type == "Web") { + for (var i = 0; i < elWebSections.length; i++) { + elWebSections[i].style.display = 'table'; + } + for (var i = 0; i < elHyperlinkSections.length; i++) { + elHyperlinkSections[i].style.display = 'none'; + } + + elWebSourceURL.value = properties.sourceUrl; + } else if (properties.type == "Text") { + for (var i = 0; i < elTextSections.length; i++) { + elTextSections[i].style.display = 'table'; + } + + elTextText.value = properties.text; + elTextLineHeight.value = properties.lineHeight.toFixed(4); + elTextFaceCamera = properties.faceCamera; + elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + properties.textColor.green + "," + properties.textColor.blue + ")"; + elTextTextColorRed.value = properties.textColor.red; + elTextTextColorGreen.value = properties.textColor.green; + elTextTextColorBlue.value = properties.textColor.blue; + elTextBackgroundColorRed.value = properties.backgroundColor.red; + elTextBackgroundColorGreen.value = properties.backgroundColor.green; + elTextBackgroundColorBlue.value = properties.backgroundColor.blue; + } else if (properties.type == "Light") { + for (var i = 0; i < elLightSections.length; i++) { + elLightSections[i].style.display = 'table'; + } + + elLightSpotLight.checked = properties.isSpotlight; + + elLightColor.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; + elLightColorRed.value = properties.color.red; + elLightColorGreen.value = properties.color.green; + elLightColorBlue.value = properties.color.blue; + + elLightIntensity.value = properties.intensity.toFixed(1); + elLightFalloffRadius.value = properties.falloffRadius.toFixed(1); + elLightExponent.value = properties.exponent.toFixed(2); + elLightCutoff.value = properties.cutoff.toFixed(2); + } else if (properties.type == "Zone") { + for (var i = 0; i < elZoneSections.length; i++) { + elZoneSections[i].style.display = 'table'; + } + + elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled; + elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; + elZoneKeyLightColorRed.value = properties.keyLight.color.red; + elZoneKeyLightColorGreen.value = properties.keyLight.color.green; + elZoneKeyLightColorBlue.value = properties.keyLight.color.blue; + elZoneKeyLightIntensity.value = properties.keyLight.intensity.toFixed(2); + elZoneKeyLightAmbientIntensity.value = properties.keyLight.ambientIntensity.toFixed(2); + elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2); + elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); + elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL; + + + elZoneStageLatitude.value = properties.stage.latitude.toFixed(2); + elZoneStageLongitude.value = properties.stage.longitude.toFixed(2); + elZoneStageAltitude.value = properties.stage.altitude.toFixed(2); + elZoneStageAutomaticHourDay.checked = properties.stage.automaticHourDay; + elZoneStageDay.value = properties.stage.day; + elZoneStageHour.value = properties.stage.hour; + elShapeType.value = properties.shapeType; + elCompoundShapeURL.value = properties.compoundShapeURL; + + elZoneBackgroundMode.value = properties.backgroundMode; + setDropdownText(elZoneBackgroundMode); + + elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red + "," + properties.skybox.color.green + "," + properties.skybox.color.blue + ")"; + elZoneSkyboxColorRed.value = properties.skybox.color.red; + elZoneSkyboxColorGreen.value = properties.skybox.color.green; + elZoneSkyboxColorBlue.value = properties.skybox.color.blue; + elZoneSkyboxURL.value = properties.skybox.url; + + elZoneFlyingAllowed.checked = properties.flyingAllowed; + elZoneGhostingAllowed.checked = properties.ghostingAllowed; + + showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox'); + } else if (properties.type == "PolyVox") { + for (var i = 0; i < elPolyVoxSections.length; i++) { + elPolyVoxSections[i].style.display = 'table'; + } + + elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); + elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); + elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2); + elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle; + setDropdownText(elVoxelSurfaceStyle); + elXTextureURL.value = properties.xTextureURL; + elYTextureURL.value = properties.yTextureURL; + elZTextureURL.value = properties.zTextureURL; + } + + var activeElement = document.activeElement; + + if(typeof activeElement.select!=="undefined"){ + activeElement.select(); + } + } + } + }); + } + + elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked')); + elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name')); + elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href')); + elDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description')); + elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible')); + + var positionChangeFunction = createEmitVec3PropertyUpdateFunction( + 'position', elPositionX, elPositionY, elPositionZ); + elPositionX.addEventListener('change', positionChangeFunction); + elPositionY.addEventListener('change', positionChangeFunction); + elPositionZ.addEventListener('change', positionChangeFunction); + + var dimensionsChangeFunction = createEmitVec3PropertyUpdateFunction( + 'dimensions', elDimensionsX, elDimensionsY, elDimensionsZ); + elDimensionsX.addEventListener('change', dimensionsChangeFunction); + elDimensionsY.addEventListener('change', dimensionsChangeFunction); + elDimensionsZ.addEventListener('change', dimensionsChangeFunction); + + elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID')); + elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex')); + + var registrationChangeFunction = createEmitVec3PropertyUpdateFunction( + 'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ); + elRegistrationX.addEventListener('change', registrationChangeFunction); + elRegistrationY.addEventListener('change', registrationChangeFunction); + elRegistrationZ.addEventListener('change', registrationChangeFunction); + + var rotationChangeFunction = createEmitVec3PropertyUpdateFunction( + 'rotation', elRotationX, elRotationY, elRotationZ); + elRotationX.addEventListener('change', rotationChangeFunction); + elRotationY.addEventListener('change', rotationChangeFunction); + elRotationZ.addEventListener('change', rotationChangeFunction); + + var velocityChangeFunction = createEmitVec3PropertyUpdateFunction( + 'velocity', elLinearVelocityX, elLinearVelocityY, elLinearVelocityZ); + elLinearVelocityX.addEventListener('change', velocityChangeFunction); + elLinearVelocityY.addEventListener('change', velocityChangeFunction); + elLinearVelocityZ.addEventListener('change', velocityChangeFunction); + elLinearDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('damping')); + + var angularVelocityChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier( + 'angularVelocity', elAngularVelocityX, elAngularVelocityY, elAngularVelocityZ, DEGREES_TO_RADIANS); + elAngularVelocityX.addEventListener('change', angularVelocityChangeFunction); + elAngularVelocityY.addEventListener('change', angularVelocityChangeFunction); + elAngularVelocityZ.addEventListener('change', angularVelocityChangeFunction); + elAngularDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('angularDamping')); + + elRestitution.addEventListener('change', createEmitNumberPropertyUpdateFunction('restitution')); + elFriction.addEventListener('change', createEmitNumberPropertyUpdateFunction('friction')); + + var gravityChangeFunction = createEmitVec3PropertyUpdateFunction( + 'gravity', elGravityX, elGravityY, elGravityZ); + elGravityX.addEventListener('change', gravityChangeFunction); + elGravityY.addEventListener('change', gravityChangeFunction); + elGravityZ.addEventListener('change', gravityChangeFunction); + + var accelerationChangeFunction = createEmitVec3PropertyUpdateFunction( + 'acceleration', elAccelerationX, elAccelerationY, elAccelerationZ); + elAccelerationX.addEventListener('change', accelerationChangeFunction); + elAccelerationY.addEventListener('change', accelerationChangeFunction); + elAccelerationZ.addEventListener('change', accelerationChangeFunction); + + elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density')); + elCollisionless.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionless')); + elDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('dynamic')); + + elCollideDynamic.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideDynamic, 'dynamic'); + }); + + elCollideKinematic.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideKinematic, 'kinematic'); + }); + + elCollideStatic.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideStatic, 'static'); + }); + elCollideMyAvatar.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideMyAvatar, 'myAvatar'); + }); + elCollideOtherAvatar.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideOtherAvatar, 'otherAvatar'); + }); + + elGrabbable.addEventListener('change', function() { + userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, properties.dynamic); + }); + elWantsTrigger.addEventListener('change', function() { + userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false); + }); + elIgnoreIK.addEventListener('change', function() { + userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, false); + }); + + elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL')); + + elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime')); + elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script')); + /* + FIXME: See FIXME for property-script-url. + elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp')); + */ + elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); + + var colorChangeFunction = createEmitColorPropertyUpdateFunction( + 'color', elColorRed, elColorGreen, elColorBlue); + elColorRed.addEventListener('change', colorChangeFunction); + elColorGreen.addEventListener('change', colorChangeFunction); + elColorBlue.addEventListener('change', colorChangeFunction); + colorPickers.push($('#property-color').colpick({ + colorScheme: 'dark', + layout: 'hex', + color: '000000', + onShow: function (colpick) { + $('#property-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); + emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); + } + })); + + elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight')); + + var lightColorChangeFunction = createEmitColorPropertyUpdateFunction( + 'color', elLightColorRed, elLightColorGreen, elLightColorBlue); + elLightColorRed.addEventListener('change', lightColorChangeFunction); + elLightColorGreen.addEventListener('change', lightColorChangeFunction); + elLightColorBlue.addEventListener('change', lightColorChangeFunction); + colorPickers.push($('#property-light-color').colpick({ + colorScheme: 'dark', + layout: 'hex', + color: '000000', + onShow: function (colpick) { + $('#property-light-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-light-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); + emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); + } + })); + + elLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('intensity', 1)); + elLightFalloffRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('falloffRadius', 1)); + elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2)); + elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2)); + + elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); + + elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); + + elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); + elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType')); + elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL')); + + elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url')); + elModelAnimationPlaying.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation','running')); + elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation','fps')); + elModelAnimationFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame')); + elModelAnimationFirstFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame')); + elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame')); + elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop')); + elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold')); + + elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures')); + + elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text')); + elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera')); + elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight')); + var textTextColorChangeFunction = createEmitColorPropertyUpdateFunction( + 'textColor', elTextTextColorRed, elTextTextColorGreen, elTextTextColorBlue); + elTextTextColorRed.addEventListener('change', textTextColorChangeFunction); + elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction); + elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction); + colorPickers.push($('#property-text-text-color').colpick({ + colorScheme:'dark', + layout:'hex', + color: '000000', + onShow: function (colpick) { + $('#property-text-text-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-text-text-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#'+hex); + $(el).colpickHide(); + $(el).attr('active', 'false'); + emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b); + } + })); + + var textBackgroundColorChangeFunction = createEmitColorPropertyUpdateFunction( + 'backgroundColor', elTextBackgroundColorRed, elTextBackgroundColorGreen, elTextBackgroundColorBlue); + elTextBackgroundColorRed.addEventListener('change', textBackgroundColorChangeFunction); + elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction); + elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction); + colorPickers.push($('#property-text-background-color').colpick({ + colorScheme:'dark', + layout:'hex', + color:'000000', + onShow: function (colpick) { + $('#property-text-background-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-text-background-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#'+hex); + $(el).colpickHide(); + emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b); + } + })); + + elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled')); + colorPickers.push($('#property-zone-key-light-color').colpick({ + colorScheme:'dark', + layout:'hex', + color:'000000', + onShow: function (colpick) { + $('#property-zone-key-light-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-zone-key-light-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#'+hex); + $(el).colpickHide(); + emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight'); + } + })); + var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight','color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); + elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction); + elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction); + elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction); + elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity')); + elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity')); + elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL')); + var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY); + elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); + elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); + + elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude')); + elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude')); + elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude')); + elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','automaticHourDay')); + elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','day')); + elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','hour')); + + + elZoneBackgroundMode.addEventListener('change', createEmitTextPropertyUpdateFunction('backgroundMode')); + var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox','color', + elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue); + elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction); + elZoneSkyboxColorGreen.addEventListener('change', zoneSkyboxColorChangeFunction); + elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction); + colorPickers.push($('#property-zone-skybox-color').colpick({ + colorScheme:'dark', + layout:'hex', + color:'000000', + onShow: function (colpick) { + $('#property-zone-skybox-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-zone-skybox-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#'+hex); + $(el).colpickHide(); + emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox'); + } + })); + + elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox','url')); + + elZoneFlyingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('flyingAllowed')); + elZoneGhostingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ghostingAllowed')); + + var voxelVolumeSizeChangeFunction = createEmitVec3PropertyUpdateFunction( + 'voxelVolumeSize', elVoxelVolumeSizeX, elVoxelVolumeSizeY, elVoxelVolumeSizeZ); + elVoxelVolumeSizeX.addEventListener('change', voxelVolumeSizeChangeFunction); + elVoxelVolumeSizeY.addEventListener('change', voxelVolumeSizeChangeFunction); + elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction); + elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle')); + elXTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('xTextureURL')); + elYTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('yTextureURL')); + elZTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('zTextureURL')); + + elMoveSelectionToGrid.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "moveSelectionToGrid", + })); + }); + elMoveAllToGrid.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "moveAllToGrid", + })); + }); + elResetToNaturalDimensions.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "resetToNaturalDimensions", + })); + }); + elRescaleDimensionsButton.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "rescaleDimensions", + percentage: parseInt(elRescaleDimensionsPct.value), + })); + }); + /* + FIXME: See FIXME for property-script-url. + elReloadScriptButton.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "reloadScript" + })); + }); + */ + + window.onblur = function() { + // Fake a change event + var ev = document.createEvent("HTMLEvents"); + ev.initEvent("change", true, true); + document.activeElement.dispatchEvent(ev); + } + + // For input and textarea elements, select all of the text on focus + // WebKit-based browsers, such as is used with QWebView, have a quirk + // where the mouseup event comes after the focus event, causing the + // text to be deselected immediately after selecting all of the text. + // To make this work we block the first mouseup event after the elements + // received focus. If we block all mouseup events the user will not + // be able to click within the selected text. + // We also check to see if the value has changed to make sure we aren't + // blocking a mouse-up event when clicking on an input spinner. + var els = document.querySelectorAll("input, textarea"); + for (var i = 0; i < els.length; i++) { + var clicked = false; + var originalText; + els[i].onfocus = function(e) { + originalText = this.value; + this.select(); + clicked = false; + }; + els[i].onmouseup = function(e) { + if (!clicked && originalText == this.value) { + e.preventDefault(); + } + clicked = true; + }; + } + }); + + // Collapsible sections + var elCollapsible = document.getElementsByClassName("section-header"); + + var toggleCollapsedEvent = function (event) { + var element = event.target; + if (element.nodeName !== "DIV") { + element = element.parentNode; + } + var isCollapsed = element.getAttribute("collapsed") !== "true"; + element.setAttribute("collapsed", isCollapsed ? "true" : "false"); + element.getElementsByTagName("span")[0].textContent = isCollapsed ? "L" : "M"; + }; + + for (var i = 0, length = elCollapsible.length; i < length; i++) { + var element = elCollapsible[i]; + element.addEventListener("click", toggleCollapsedEvent, true); + }; + + + // Textarea scrollbars + var elTextareas = document.getElementsByTagName("TEXTAREA"); + + var textareaOnChangeEvent = function (event) { + setTextareaScrolling(event.target); + } + + for (var i = 0, length = elTextareas.length; i < length; i++) { + var element = elTextareas[i]; + setTextareaScrolling(element); + element.addEventListener("input", textareaOnChangeEvent, false); + element.addEventListener("change", textareaOnChangeEvent, false); + /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize + event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */ + element.addEventListener("mouseup", textareaOnChangeEvent, false); + }; + + // Dropdowns + // For each dropdown the following replacement is created in place of the oriringal dropdown... + // Structure created: + //
+ //
display textcarat
+ //
+ //
    + //
  • 0) { + var el = elDropdowns[0]; + el.parentNode.removeChild(el); + elDropdowns = document.getElementsByTagName("select"); + } + + augmentSpinButtons(); + + // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked + document.addEventListener("contextmenu", function (event) { + event.preventDefault(); + }, false); +} + diff --git a/scripts/system/html/eventBridgeLoader.js b/scripts/system/html/js/eventBridgeLoader.js similarity index 100% rename from scripts/system/html/eventBridgeLoader.js rename to scripts/system/html/js/eventBridgeLoader.js diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js new file mode 100644 index 0000000000..cc268bcbff --- /dev/null +++ b/scripts/system/html/js/gridControls.js @@ -0,0 +1,138 @@ +// gridControls.js +// +// Created by Ryan Huffman on 6 Nov 2014 +// 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 + +function loaded() { + openEventBridge(function() { + elPosY = document.getElementById("horiz-y"); + elMinorSpacing = document.getElementById("minor-spacing"); + elMajorSpacing = document.getElementById("major-spacing"); + elSnapToGrid = document.getElementById("snap-to-grid"); + elHorizontalGridVisible = document.getElementById("horiz-grid-visible"); + elMoveToSelection = document.getElementById("move-to-selection"); + elMoveToAvatar = document.getElementById("move-to-avatar"); + + if (window.EventBridge !== undefined) { + EventBridge.scriptEventReceived.connect(function(data) { + data = JSON.parse(data); + + if (data.origin) { + var origin = data.origin; + elPosY.value = origin.y; + } + + if (data.minorGridEvery !== undefined) { + elMinorSpacing.value = data.minorGridEvery; + } + + if (data.majorGridEvery !== undefined) { + elMajorSpacing.value = data.majorGridEvery; + } + + if (data.gridColor) { + gridColor = data.gridColor; + } + + if (data.snapToGrid !== undefined) { + elSnapToGrid.checked = data.snapToGrid == true; + } + + if (data.visible !== undefined) { + elHorizontalGridVisible.checked = data.visible == true; + } + }); + + function emitUpdate() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "update", + origin: { + y: elPosY.value, + }, + minorGridEvery: elMinorSpacing.value, + majorGridEvery: elMajorSpacing.value, + gridColor: gridColor, + snapToGrid: elSnapToGrid.checked, + visible: elHorizontalGridVisible.checked, + })); + } + + } + + elPosY.addEventListener("change", emitUpdate); + elMinorSpacing.addEventListener("change", emitUpdate); + elMajorSpacing.addEventListener("change", emitUpdate); + elSnapToGrid.addEventListener("change", emitUpdate); + elHorizontalGridVisible.addEventListener("change", emitUpdate); + + elMoveToAvatar.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "moveToAvatar", + })); + }); + elMoveToSelection.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "moveToSelection", + })); + }); + + var gridColor = { red: 255, green: 255, blue: 255 }; + var elColor = document.getElementById("grid-color"); + var elColorRed = document.getElementById("grid-color-red"); + var elColorGreen = document.getElementById("grid-color-green"); + var elColorBlue = document.getElementById("grid-color-blue"); + elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")"; + elColorRed.value = gridColor.red; + elColorGreen.value = gridColor.green; + elColorBlue.value = gridColor.blue; + + var colorChangeFunction = function () { + gridColor = { red: elColorRed.value, green: elColorGreen.value, blue: elColorBlue.value }; + elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")"; + emitUpdate(); + }; + + var colorPickFunction = function (red, green, blue) { + elColorRed.value = red; + elColorGreen.value = green; + elColorBlue.value = blue; + gridColor = { red: red, green: green, blue: blue }; + emitUpdate(); + } + + elColorRed.addEventListener('change', colorChangeFunction); + elColorGreen.addEventListener('change', colorChangeFunction); + elColorBlue.addEventListener('change', colorChangeFunction); + $('#grid-color').colpick({ + colorScheme: 'dark', + layout: 'hex', + color: { r: gridColor.red, g: gridColor.green, b: gridColor.blue }, + onShow: function (colpick) { + $('#grid-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#grid-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); + colorPickFunction(rgb.r, rgb.g, rgb.b); + } + }); + + augmentSpinButtons(); + + EventBridge.emitWebEvent(JSON.stringify({ type: 'init' })); + }); + + // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked + document.addEventListener("contextmenu", function (event) { + event.preventDefault(); + }, false); +} + diff --git a/scripts/system/html/jquery-2.1.4.min.js b/scripts/system/html/js/jquery-2.1.4.min.js similarity index 100% rename from scripts/system/html/jquery-2.1.4.min.js rename to scripts/system/html/js/jquery-2.1.4.min.js diff --git a/scripts/system/html/list.min.js b/scripts/system/html/js/list.min.js similarity index 100% rename from scripts/system/html/list.min.js rename to scripts/system/html/js/list.min.js diff --git a/scripts/system/html/spinButtons.js b/scripts/system/html/js/spinButtons.js similarity index 100% rename from scripts/system/html/spinButtons.js rename to scripts/system/html/js/spinButtons.js diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 2003df3652..461204b7aa 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1109,9 +1109,6 @@ SelectionDisplay = (function() { } - Entities.editEntity(entityID, { - localRenderAlpha: 0.1 - }); Overlays.editOverlay(highlightBox, { visible: false }); diff --git a/scripts/system/mod.js b/scripts/system/mod.js index 035d7726a1..43afb94c9d 100644 --- a/scripts/system/mod.js +++ b/scripts/system/mod.js @@ -22,7 +22,7 @@ var button = toolbar.addButton({ imageURL: buttonImageURL(), visible: true, buttonState: 1, - defaultState: 2, + defaultState: 1, hoverState: 3, alpha: 0.9 }); diff --git a/scripts/tutorials/entity_scripts/pistol.js b/scripts/tutorials/entity_scripts/pistol.js index 8062de4e8e..73a6daab93 100644 --- a/scripts/tutorials/entity_scripts/pistol.js +++ b/scripts/tutorials/entity_scripts/pistol.js @@ -22,7 +22,7 @@ Controller.Standard.LT, Controller.Standard.RT, ]; - var RELOAD_THRESHOLD = 0.95; + var RELOAD_THRESHOLD = 0.90; Pistol = function() { _this = this; @@ -81,11 +81,11 @@ }, toggleWithTriggerPressure: function() { this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]); - + if (this.triggerValue < RELOAD_THRESHOLD) { this.canShoot = true; } - if (this.canShoot === true && this.triggerValue === 1) { + if (this.canShoot === true && this.triggerValue >= RELOAD_THRESHOLD) { this.fire(); this.canShoot = false; } @@ -140,7 +140,7 @@ direction: this.firingDirection }; this.createGunFireEffect(this.barrelPoint) - var intersection = Entities.findRayIntersectionBlocking(pickRay, true); + var intersection = Entities.findRayIntersection(pickRay, true); if (intersection.intersects) { this.createEntityHitEffect(intersection.intersection); if (Math.random() < this.playRichochetSoundChance) { diff --git a/server-console/src/log.html b/server-console/src/log.html index f704f3e35f..3ccf8112e8 100644 --- a/server-console/src/log.html +++ b/server-console/src/log.html @@ -8,6 +8,7 @@
      diff --git a/server-console/src/log.js b/server-console/src/log.js index 3634eaeaa7..455b6828d8 100644 --- a/server-console/src/log.js +++ b/server-console/src/log.js @@ -43,6 +43,7 @@ ready = function() { var domainServer = remote.getGlobal('domainServer'); var acMonitor = remote.getGlobal('acMonitor'); + var openLogDirectory = remote.getGlobal('openLogDirectory'); var pendingLines = { 'ds': new Array(), @@ -218,6 +219,12 @@ ready = function() { appendLogMessages('ac'); } + // Binding a remote function directly does not work, so bind to a function + // that calls the remote function. + $('#view-logs').on('click', function() { + openLogDirectory(); + }); + // handle filtering of table rows on input change $('#search-input').on('input', function() { filter = $(this).val().toLowerCase(); diff --git a/server-console/src/main.js b/server-console/src/main.js index 8f85872d0b..c47308aed6 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -285,6 +285,10 @@ function openFileBrowser(path) { } } +function openLogDirectory() { + openFileBrowser(logPath); +} + // NOTE: this looks like it does nothing, but it's very important. // Without it the default behaviour is to quit the app once all windows closed // which is absolutely not what we want for a taskbar application. @@ -309,6 +313,7 @@ global.homeServer = null; global.domainServer = null; global.acMonitor = null; global.userConfig = userConfig; +global.openLogDirectory = openLogDirectory; var LogWindow = function(ac, ds) { this.ac = ac; diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 1a4f8742e9..2c8f361fac 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -85,8 +85,6 @@ public: virtual void showDisplayPluginsTools(bool show) override {} virtual void requestReset() override {} virtual bool makeRenderingContextCurrent() override { return true; } - virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override {} - virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override {} virtual GLWidget* getPrimaryWidget() override { return nullptr; } virtual MainWindow* getPrimaryWindow() override { return nullptr; } virtual QOpenGLContext* getPrimaryContext() override { return nullptr; } diff --git a/tests/entities/CMakeLists.txt b/tests/entities/CMakeLists.txt index a6ce4cf956..448ea83956 100644 --- a/tests/entities/CMakeLists.txt +++ b/tests/entities/CMakeLists.txt @@ -7,6 +7,6 @@ setup_hifi_project(Network Script) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(entities avatars shared octree gpu model fbx networking animation audio) +link_hifi_libraries(entities avatars shared octree gpu model fbx networking animation audio gl) package_libraries_for_deployment() diff --git a/tests/gpu-test/src/TestFloorTexture.cpp b/tests/gpu-test/src/TestFloorTexture.cpp index 884be5ec30..b7853fdbb4 100644 --- a/tests/gpu-test/src/TestFloorTexture.cpp +++ b/tests/gpu-test/src/TestFloorTexture.cpp @@ -78,7 +78,7 @@ void FloorTextureTest::renderTest(size_t testId, RenderArgs* args) { texture->incremementMinMip(); } - geometryCache->bindSimpleProgram(batch, true, true, true); + geometryCache->bindSimpleProgram(batch, true, false, true, true); batch.setInputBuffer(0, vertexBuffer, 0, sizeof(Vertex)); batch.setInputFormat(vertexFormat); batch.setIndexBuffer(gpu::UINT16, indexBuffer, 0); diff --git a/tests/gpu-test/src/TestWindow.cpp b/tests/gpu-test/src/TestWindow.cpp index f31eb6f7bb..6917cf027f 100644 --- a/tests/gpu-test/src/TestWindow.cpp +++ b/tests/gpu-test/src/TestWindow.cpp @@ -15,7 +15,6 @@ #include #include -#include #include @@ -74,7 +73,7 @@ void TestWindow::initGl() { DependencyManager::set(); resize(QSize(800, 600)); - setupDebugLogger(this); + GLDebug::setupLogger(this); #ifdef DEFERRED_LIGHTING auto deferredLightingEffect = DependencyManager::get(); deferredLightingEffect->init(); @@ -93,7 +92,6 @@ void TestWindow::resizeWindow(const QSize& size) { } void TestWindow::beginFrame() { - _renderArgs->_context->syncCache(); #ifdef DEFERRED_LIGHTING diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index 0f06546327..008f363e53 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -33,7 +33,6 @@ #include #include -#include #include #include diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 22dd96b83b..eec7e994ae 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -30,14 +31,13 @@ #include #include -#include #include #include -#include #include #include #include +#include #include #include @@ -157,7 +157,200 @@ static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits { + using Parent = GenericQueueThread; +public: + QOpenGLContextWrapper* _displayContext{ nullptr }; + QSurface* _displaySurface{ nullptr }; + gpu::PipelinePointer _presentPipeline; + gpu::ContextPointer _gpuContext; // initialized during window creation + std::atomic _presentCount; + QElapsedTimer _elapsed; + std::atomic _fps{ 1 }; + RateCounter<200> _fpsCounter; + std::mutex _mutex; + std::shared_ptr _backend; + std::vector _frameTimes; + size_t _frameIndex; + static const size_t FRAME_TIME_BUFFER_SIZE{ 8192 }; + + + + void initialize(QOpenGLContextWrapper* displayContext, QWindow* surface) { + setObjectName("RenderThread"); + _displayContext = displayContext; + _displaySurface = surface; + _displayContext->makeCurrent(_displaySurface); + // GPU library init + gpu::Context::init(); + _gpuContext = std::make_shared(); + _backend = _gpuContext->getBackend(); + _displayContext->makeCurrent(_displaySurface); + DependencyManager::get()->init(); + _displayContext->doneCurrent(); + Parent::initialize(); + if (isThreaded()) { + _displayContext->moveToThread(thread()); + } + } + + void setup() override { + RENDER_THREAD = QThread::currentThread(); + _displayContext->makeCurrent(_displaySurface); + glewExperimental = true; + glewInit(); + glGetError(); + _frameTimes.resize(FRAME_TIME_BUFFER_SIZE, 0); + + { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_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()); + _presentPipeline = gpu::Pipeline::create(program, state); + } + + //_textOverlay = new TextOverlay(glm::uvec2(800, 600)); + glViewport(0, 0, 800, 600); + _elapsed.start(); + } + + void shutdown() override { + _presentPipeline.reset(); + _gpuContext.reset(); + } + + void renderFrame(gpu::FramePointer& frame) { + ++_presentCount; + _displayContext->makeCurrent(_displaySurface); + _backend->recycle(); + _backend->syncCache(); + if (frame && !frame->batches.empty()) { + _gpuContext->executeFrame(frame); + + { + auto geometryCache = DependencyManager::get(); + gpu::Batch presentBatch; + presentBatch.enableStereo(false); + presentBatch.resetViewTransform(); + presentBatch.setFramebuffer(gpu::FramebufferPointer()); + presentBatch.setResourceTexture(0, frame->framebuffer->getRenderBuffer(0)); + presentBatch.setPipeline(_presentPipeline); + presentBatch.draw(gpu::TRIANGLE_STRIP, 4); + _gpuContext->executeBatch(presentBatch); + } + } + { + //_textOverlay->render(); + } + _displayContext->swapBuffers(_displaySurface); + _fpsCounter.increment(); + static size_t _frameCount{ 0 }; + ++_frameCount; + if (_elapsed.elapsed() >= 500) { + _fps = _fpsCounter.rate(); + _frameCount = 0; + _elapsed.restart(); + } + } + + void report() { + uint64_t total = 0; + for (const auto& t : _frameTimes) { + total += t; + } + auto averageFrameTime = total / FRAME_TIME_BUFFER_SIZE; + qDebug() << "Average frame " << averageFrameTime; + + std::list sortedHighFrames; + for (const auto& t : _frameTimes) { + if (t > averageFrameTime * 6) { + sortedHighFrames.push_back(t); + } + } + + sortedHighFrames.sort(); + for (const auto& t : sortedHighFrames) { + qDebug() << "Long frame " << t; + } + } + + bool processQueueItems(const Queue& items) override { + for (auto frame : items) { + auto start = usecTimestampNow(); + renderFrame(frame); + auto duration = usecTimestampNow() - start; + auto frameBufferIndex = _frameIndex % FRAME_TIME_BUFFER_SIZE; + _frameTimes[frameBufferIndex] = duration; + ++_frameIndex; + if (0 == _frameIndex % FRAME_TIME_BUFFER_SIZE) { + report(); + } + } + return true; + } +}; + + +// Background Render Data & rendering functions +class BackgroundRenderData { +public: + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + static render::ItemID _item; // unique WorldBoxRenderData +}; + +render::ItemID BackgroundRenderData::_item = 0; + +namespace render { + template <> const ItemKey payloadGetKey(const BackgroundRenderData::Pointer& stuff) { + return ItemKey::Builder::background(); + } + + template <> const Item::Bound payloadGetBound(const BackgroundRenderData::Pointer& stuff) { + return Item::Bound(); + } + + template <> void payloadRender(const BackgroundRenderData::Pointer& background, RenderArgs* args) { + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + + // Background rendering decision + auto skyStage = DependencyManager::get()->getSkyStage(); + auto backgroundMode = skyStage->getBackgroundMode(); + + switch (backgroundMode) { + case model::SunSkyStage::SKY_BOX: { + auto skybox = skyStage->getSkybox(); + if (skybox) { + PerformanceTimer perfTimer("skybox"); + skybox->render(batch, args->getViewFrustum()); + break; + } + } + default: + // this line intentionally left blank + break; + } + } +} // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow, public AbstractViewStateInterface { @@ -185,6 +378,7 @@ protected: } void postLambdaEvent(std::function f) override {} + qreal getDevicePixelRatio() override { return 1.0f; } @@ -192,6 +386,7 @@ protected: render::ScenePointer getMain3DScene() override { return _main3DScene; } + render::EnginePointer getRenderEngine() override { return _renderEngine; } @@ -221,13 +416,15 @@ public: } QTestWindow() { + _camera.movementSpeed = 50.0f; + QThreadPool::globalInstance()->setMaxThreadCount(2); QThread::currentThread()->setPriority(QThread::HighestPriority); AbstractViewStateInterface::setInstance(this); _octree = DependencyManager::set(false, this, nullptr); _octree->init(); // Prevent web entities from rendering - REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory) - + REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory); + DependencyManager::set(_octree->getTree()); getEntities()->setViewFrustum(_viewFrustum); auto nodeList = DependencyManager::get(); @@ -241,52 +438,37 @@ public: format.setOption(QSurfaceFormat::DebugContext); setFormat(format); - _context.setFormat(format); - _context.create(); resize(QSize(800, 600)); show(); - makeCurrent(); + + _context.setFormat(format); + _context.create(); + _context.makeCurrent(this); glewExperimental = true; glewInit(); glGetError(); - setupDebugLogger(this); + GLDebug::setupLogger(this); #ifdef Q_OS_WIN wglSwapIntervalEXT(0); #endif - { - makeCurrent(); - _quadProgram = loadDefaultShader(); - _plane = loadPlane(_quadProgram); - _textOverlay = new TextOverlay(glm::uvec2(800, 600)); - glViewport(0, 0, 800, 600); - } + _context.doneCurrent(); - _camera.movementSpeed = 50.0f; - - - // GPU library init - { - _offscreenContext = new OffscreenGLCanvas(); - _offscreenContext->create(_context.getContext()); - _offscreenContext->makeCurrent(); - gpu::Context::init(); - _gpuContext = std::make_shared(); - } + _initContext.create(_context.getContext()); + _renderThread.initialize(&_context, this); + // FIXME use a wait condition + QThread::msleep(1000); + _renderThread.queueItem(gpu::FramePointer()); + _initContext.makeCurrent(); + // Render engine init + _renderEngine->addJob("RenderShadowTask", _cullFunctor); + _renderEngine->addJob("RenderDeferredTask", _cullFunctor); + _renderEngine->load(); + _renderEngine->registerScene(_main3DScene); // Render engine library init - { - _offscreenContext->makeCurrent(); - DependencyManager::get()->init(); - _renderEngine->addJob("RenderShadowTask", _cullFunctor); - _renderEngine->addJob("RenderDeferredTask", _cullFunctor); - _renderEngine->load(); - _renderEngine->registerScene(_main3DScene); - } - reloadScene(); restorePosition(); - _elapsed.start(); QTimer* timer = new QTimer(this); timer->setInterval(0); connect(timer, &QTimer::timeout, this, [this] { @@ -297,9 +479,21 @@ public: } virtual ~QTestWindow() { + _renderThread.terminate(); + getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts + _renderEngine.reset(); + _main3DScene.reset(); + EntityTreePointer tree = getEntities()->getTree(); + tree->setSimulation(nullptr); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); ResourceManager::cleanup(); - try { _quadProgram.reset(); } catch (std::runtime_error&) {} - try { _plane.reset(); } catch (std::runtime_error&) {} + // remove the NodeList from the DependencyManager + DependencyManager::destroy(); } protected: @@ -363,6 +557,7 @@ private: return (renderAccuracy > 0.0f); } + uint16_t _fps; void draw() { if (!_ready) { return; @@ -370,17 +565,21 @@ private: if (!isVisible()) { return; } + if (_renderCount.load() != 0 && _renderCount.load() >= _renderThread._presentCount.load()) { + return; + } + _renderCount = _renderThread._presentCount.load(); update(); - _offscreenContext->makeCurrent(); - - RenderArgs renderArgs(_gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE, + RenderArgs renderArgs(_renderThread._gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE, 0, RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); auto framebufferCache = DependencyManager::get(); QSize windowSize = size(); framebufferCache->setFrameBufferSize(windowSize); + + renderArgs._blitFramebuffer = framebufferCache->getFramebuffer(); // Viewport is assigned to the size of the framebuffer renderArgs._viewport = ivec4(0, 0, windowSize.width(), windowSize.height()); @@ -398,49 +597,11 @@ private: } // Final framebuffer that will be handled to the display-plugin - { - auto finalFramebuffer = framebufferCache->getFramebuffer(); - renderArgs._blitFramebuffer = finalFramebuffer; - } - - _gpuContext->beginFrame(renderArgs._blitFramebuffer); - gpu::doInBatch(renderArgs._context, [&](gpu::Batch& batch) { - batch.resetStages(); - }); render(&renderArgs); - _gpuContext->endFrame(); - GLuint glTex; - { - auto gpuTex = renderArgs._blitFramebuffer->getRenderBuffer(0); - glTex = gpu::Backend::getGPUObject(*gpuTex)->_id; - } - makeCurrent(); - { - glBindTexture(GL_TEXTURE_2D, glTex); - _quadProgram->Use(); - _plane->Use(); - _plane->Draw(); - glBindVertexArray(0); - } - - { - //_textOverlay->render(); - } - - _context.swapBuffers(this); - - _offscreenContext->makeCurrent(); - framebufferCache->releaseFramebuffer(renderArgs._blitFramebuffer); - renderArgs._blitFramebuffer.reset(); - _fpsCounter.increment(); - static size_t _frameCount { 0 }; - ++_frameCount; - if (_elapsed.elapsed() >= 500) { - _fps = _fpsCounter.rate(); + if (_fps != _renderThread._fps) { + _fps = _renderThread._fps; updateText(); - _frameCount = 0; - _elapsed.restart(); } } @@ -463,11 +624,13 @@ private: const qint64& now; }; - - - void updateText() { - //qDebug() << "FPS " << fps.rate(); + setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4") + .arg(_fps).arg(_cullingEnabled) + .arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2)) + .arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2))); + +#if 0 { _textBlocks.erase(TextBlock::Info); auto& infoTextBlock = _textBlocks[TextBlock::Info]; @@ -475,13 +638,10 @@ private: infoTextBlock.push_back({ vec2(100, 10), std::to_string((uint32_t)_fps), TextOverlay::alignLeft }); infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight }); infoTextBlock.push_back({ vec2(100, 30), _cullingEnabled ? "Enabled" : "Disabled", TextOverlay::alignLeft }); - - setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4") - .arg(_fps).arg(_cullingEnabled) - .arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2)) - .arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2))); } +#endif +#if 0 _textOverlay->beginTextUpdate(); for (const auto& e : _textBlocks) { for (const auto& b : e.second) { @@ -489,6 +649,7 @@ private: } } _textOverlay->endTextUpdate(); +#endif } void update() { @@ -522,9 +683,41 @@ private: } last = now; + + getEntities()->update(); + + // The pending changes collecting the changes here + render::PendingChanges pendingChanges; + + // FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities + // Background rendering decision + if (!render::Item::isValidID(BackgroundRenderData::_item)) { + auto backgroundRenderData = std::make_shared(); + auto backgroundRenderPayload = std::make_shared(backgroundRenderData); + BackgroundRenderData::_item = _main3DScene->allocateID(); + pendingChanges.resetItem(BackgroundRenderData::_item, backgroundRenderPayload); + } + // Setup the current Zone Entity lighting + { + auto stage = DependencyManager::get()->getSkyStage(); + DependencyManager::get()->setGlobalLight(stage->getSunLight()); + } + + { + PerformanceTimer perfTimer("SceneProcessPendingChanges"); + _main3DScene->enqueuePendingChanges(pendingChanges); + + _main3DScene->processPendingChangesQueue(); + } + } void render(RenderArgs* renderArgs) { + auto& gpuContext = renderArgs->_context; + gpuContext->beginFrame(); + gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) { + batch.resetStages(); + }); PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("draw"); // The pending changes collecting the changes here @@ -544,6 +737,17 @@ private: // Before the deferred pass, let's try to use the render engine _renderEngine->run(); } + auto frame = gpuContext->endFrame(); + frame->framebuffer = renderArgs->_blitFramebuffer; + frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){ + DependencyManager::get()->releaseFramebuffer(framebuffer); + }; + _renderThread.queueItem(frame); + if (!_renderThread.isThreaded()) { + _renderThread.process(); + } + + } bool makeCurrent() { @@ -558,9 +762,8 @@ private: if (!_ready) { return; } - _textOverlay->resize(toGlm(_size)); - makeCurrent(); - glViewport(0, 0, size.width(), size.height()); + //_textOverlay->resize(toGlm(_size)); + //glViewport(0, 0, size.width(), size.height()); } void parsePath(const QString& viewpointString) { @@ -704,32 +907,29 @@ private: std::map> _textBlocks; - gpu::ContextPointer _gpuContext; // initialized during window creation render::EnginePointer _renderEngine { new render::Engine() }; render::ScenePointer _main3DScene { new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) }; - OffscreenGLCanvas* _offscreenContext { nullptr }; QOpenGLContextWrapper _context; QSize _size; - RateCounter<200> _fpsCounter; QSettings _settings; - ProgramPtr _quadProgram; - ShapeWrapperPtr _plane; - + std::atomic _renderCount; + OffscreenGLCanvas _initContext; + RenderThread _renderThread; QWindowCamera _camera; ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _shadowViewFrustum; // current state of view frustum, perspective, orientation, etc. model::SunSkyStage _sunSkyStage; model::LightPointer _globalLight { std::make_shared() }; - QElapsedTimer _elapsed; bool _ready { false }; - float _fps { 0 }; - TextOverlay* _textOverlay; - bool _cullingEnabled { true }; + //TextOverlay* _textOverlay; + static bool _cullingEnabled; bool _stereoEnabled { false }; QSharedPointer _octree; }; +bool QTestWindow::_cullingEnabled = true; + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { if (!message.isEmpty()) { #ifdef Q_OS_WIN diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index ccb91590c3..4b7354f35d 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -114,7 +113,7 @@ public: gpu::Context::init(); - setupDebugLogger(this); + GLDebug::setupLogger(this); qDebug() << (const char*)glGetString(GL_VERSION); //_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false); diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index 2ef7bbdd02..5355f29f19 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -20,7 +20,6 @@ #include -#include #include #include @@ -75,10 +74,6 @@ #include #include -#include -#include -#include - #include #include #include @@ -115,7 +110,7 @@ public: makeCurrent(); gpu::Context::init(); - setupDebugLogger(this); + GLDebug::setupLogger(this); makeCurrent(); resize(QSize(800, 600)); } @@ -168,9 +163,6 @@ void QTestWindow::draw() { testShaderBuild(deferred_light_limited_vert, spot_light_frag); testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag); testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag); - - testShaderBuild(standardTransformPNTC_vert, starsGrid_frag); - testShaderBuild(stars_vert, stars_frag); testShaderBuild(model_vert, model_frag); testShaderBuild(model_normal_map_vert, model_normal_map_frag); diff --git a/tools/srgb_gen.py b/tools/srgb_gen.py new file mode 100644 index 0000000000..6db2c1da03 --- /dev/null +++ b/tools/srgb_gen.py @@ -0,0 +1,34 @@ +# +# srgb_gen.py +# tools/ +# +# Created by Ryan Huffman on 8/8/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 + +# Generates a lookup table for SRGB to Linear color transformations + +NUM_VALUES = 256 +srgb_to_linear = [] + +# Calculate srgb to linear +for i in range(NUM_VALUES): + s = float(i) / 255 + if s < 0.04045: + l = s / 12.92 + else: + l = ((s + 0.055) / 1.055) ** 2.4 + srgb_to_linear.append(l) + +# Format and print +data = "{\n " +for i, v in enumerate(srgb_to_linear): + data += str(v) + "f" + if i < NUM_VALUES - 1: + data += ", " + if i > 0 and i % 6 == 0: + data += "\n " +data += "\n}" +print(data) diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt index c94b2ad083..810d13ffd7 100644 --- a/tools/vhacd-util/CMakeLists.txt +++ b/tools/vhacd-util/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME vhacd-util) setup_hifi_project(Core Widgets) -link_hifi_libraries(shared fbx model gpu) +link_hifi_libraries(shared fbx model gpu gl) add_dependency_external_projects(vhacd) diff --git a/tools/vhacd-util/src/VHACDUtil.h b/tools/vhacd-util/src/VHACDUtil.h index 8f82c4e4e4..35ec3ef56b 100644 --- a/tools/vhacd-util/src/VHACDUtil.h +++ b/tools/vhacd-util/src/VHACDUtil.h @@ -51,7 +51,7 @@ namespace vhacd { // Couldn't follow coding guideline here due to virtual function declared in IUserCallback void Update(const double overallProgress, const double stageProgress, const double operationProgress, - const char * const stage, const char * const operation); + const char * const stage, const char * const operation) override; }; } diff --git a/unpublishedScripts/steam.js b/unpublishedScripts/steam.js new file mode 100644 index 0000000000..a80f0072ac --- /dev/null +++ b/unpublishedScripts/steam.js @@ -0,0 +1,31 @@ +// +// steam.js +// scripts/system/ +// +// Created by Clement on 7/28/16 +// 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 +// + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + + +var steamInviteButton = toolBar.addButton({ + objectName: "steamInvite", + imageURL: Script.resolvePath("assets/images/tools/steam-invite.svg"), + visible: Steam.isRunning, + buttonState: 1, + defaultState: 1, + hoverState: 3, + alpha: 0.9 +}); + +steamInviteButton.clicked.connect(function(){ + Steam.openInviteOverlay(); +}); + +Script.scriptEnding.connect(function () { + toolBar.removeButton("steamInvite"); +}); \ No newline at end of file