diff --git a/CMakeLists.txt b/CMakeLists.txt index a9bcc9dd8b..5cee3357a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12.2) +cmake_minimum_required(VERSION 3.2) if (USE_ANDROID_TOOLCHAIN) set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/android/android.toolchain.cmake") @@ -37,11 +37,11 @@ if (WIN32) set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ") elseif (MSVC12) if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(WINDOW_SDK_FOLDER "x64") + set(WINDOW_SDK_FOLDER "x64") else() - set(WINDOW_SDK_FOLDER "x86") + set(WINDOW_SDK_FOLDER "x86") endif() - set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}") + set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}") endif () message (WINDOW_SDK_PATH= ${WINDOW_SDK_PATH}) set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH}) @@ -67,9 +67,9 @@ if ((NOT MSVC12) AND (NOT MSVC14)) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) if (COMPILER_SUPPORTS_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") elseif(COMPILER_SUPPORTS_CXX0X) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") else() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif() @@ -197,6 +197,10 @@ if (WIN32) add_paths_to_fixup_libs("${QT_DIR}/bin") endif () +if (NOT DEFINED SERVER_ONLY) + set(SERVER_ONLY 0) +endif() + # add subdirectories for all targets if (NOT ANDROID) add_subdirectory(assignment-client) @@ -205,13 +209,15 @@ if (NOT ANDROID) set_target_properties(domain-server PROPERTIES FOLDER "Apps") add_subdirectory(ice-server) set_target_properties(ice-server PROPERTIES FOLDER "Apps") - add_subdirectory(interface) - set_target_properties(interface PROPERTIES FOLDER "Apps") add_subdirectory(console) - add_subdirectory(tests) - add_subdirectory(plugins) + if (NOT SERVER_ONLY) + add_subdirectory(interface) + set_target_properties(interface PROPERTIES FOLDER "Apps") + add_subdirectory(tests) + add_subdirectory(plugins) + endif() add_subdirectory(tools) -endif () +endif() if (ANDROID OR DESKTOP_GVR) add_subdirectory(gvr-interface) @@ -227,34 +233,4 @@ if (HIFI_MEMORY_DEBUGGING) endif () include_application_version() - -if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE) - message(STATUS "+++++ Package for deployment will be generated on this build +++++") - - file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment) - - set(ICONPATH_INTERFACE "$INSTDIR/${PATH_INSTALL_DATA}/interface.ico") - set(ICONPATH_STACK_MANAGER "$INSTDIR/${PATH_INSTALL_DATA}/stack-manager.ico") - string(REPLACE "/" "\\\\" ICONPATH_INTERFACE ${ICONPATH_INTERFACE}) - string(REPLACE "/" "\\\\" ICONPATH_STACK_MANAGER ${ICONPATH_STACK_MANAGER}) - - set(CPACK_PACKAGE_NAME "High Fidelity") - set(CPACK_PACKAGE_VENDOR "High Fidelity, Inc") - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High Fidelity Interface and Stack") - set(CPACK_PACKAGE_VERSION "${BUILD_SEQ}") - set(CPACK_PACKAGE_VERSION_MAJOR "${BUILD_SEQ}") - set(CPACK_PACKAGE_VERSION_MINOR "0") - set(CPACK_PACKAGE_VERSION_PATCH "0") - set(CPACK_PACKAGE_INSTALL_DIRECTORY "High Fidelity-${BUILD_SEQ}") - set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") - set(CPACK_PACKAGE_EXECUTABLES - stack-manager "Stack Manager" - interface "Interface" - ) - - if (WIN32) - install(DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment/ DESTINATION "./") - endif (WIN32) - - include(CPack) -endif () +generate_installers() diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index fc0cfe1abb..bbee597797 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -35,6 +35,7 @@ #include "AssignmentActionFactory.h" #include "AssignmentClient.h" +#include "AssignmentClientLogging.h" #include "avatars/ScriptableAvatar.h" const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client"; @@ -84,7 +85,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri // check for a wallet UUID on the command line or in the config // this would represent where the user running AC wants funds sent to if (!walletUUID.isNull()) { - qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); + qCDebug(assigmnentclient) << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } @@ -98,13 +99,13 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri _assignmentServerSocket.setObjectName("AssigmentServer"); nodeList->setAssignmentServerSocket(_assignmentServerSocket); - qDebug() << "Assignment server socket is" << _assignmentServerSocket; + qCDebug(assigmnentclient) << "Assignment server socket is" << _assignmentServerSocket; // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required - qDebug() << "Waiting for assignment -" << _requestAssignment; + qCDebug(assigmnentclient) << "Waiting for assignment -" << _requestAssignment; if (_assignmentServerHostname != "localhost") { - qDebug () << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort(); + qCDebug(assigmnentclient) << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort(); } connect(&_requestTimer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); @@ -122,7 +123,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri _assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort); _assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor"); - qDebug() << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket; + qCDebug(assigmnentclient) << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket; // Hook up a timer to send this child's status to the Monitor once per second setUpStatusToMonitor(); @@ -133,7 +134,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri } void AssignmentClient::stopAssignmentClient() { - qDebug() << "Forced stop of assignment-client."; + qCDebug(assigmnentclient) << "Forced stop of assignment-client."; _requestTimer.stop(); _statsTimerACM.stop(); @@ -209,14 +210,14 @@ void AssignmentClient::sendAssignmentRequest() { quint16 localAssignmentServerPort; if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, localAssignmentServerPort)) { if (localAssignmentServerPort != _assignmentServerSocket.getPort()) { - qDebug() << "Port for local assignment server read from shared memory is" + qCDebug(assigmnentclient) << "Port for local assignment server read from shared memory is" << localAssignmentServerPort; _assignmentServerSocket.setPort(localAssignmentServerPort); nodeList->setAssignmentServerSocket(_assignmentServerSocket); } } else { - qDebug() << "Failed to read local assignment server port from shared memory" + qCWarning(assigmnentclient) << "Failed to read local assignment server port from shared memory" << "- will send assignment request to previous assignment server socket."; } } @@ -226,13 +227,13 @@ void AssignmentClient::sendAssignmentRequest() { } void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer message) { - qDebug() << "Received a PacketType::CreateAssignment - attempting to unpack."; + qCDebug(assigmnentclient) << "Received a PacketType::CreateAssignment - attempting to unpack."; // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(*message); if (_currentAssignment && !_isAssigned) { - qDebug() << "Received an assignment -" << *_currentAssignment; + qDebug(assigmnentclient) << "Received an assignment -" << *_currentAssignment; _isAssigned = true; auto nodeList = DependencyManager::get(); @@ -242,7 +243,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointergetDomainHandler().setSockAddr(message->getSenderSockAddr(), _assignmentServerHostname); nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID()); - qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString(); + qCDebug(assigmnentclient) << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString(); // start the deployed assignment QThread* workerThread = new QThread; @@ -270,7 +271,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointerstarted() workerThread->start(); } else { - qDebug() << "Received an assignment that could not be unpacked. Re-requesting."; + qCWarning(assigmnentclient) << "Received an assignment that could not be unpacked. Re-requesting."; } } @@ -278,12 +279,12 @@ void AssignmentClient::handleStopNodePacket(QSharedPointer mess const HifiSockAddr& senderSockAddr = message->getSenderSockAddr(); if (senderSockAddr.getAddress() == QHostAddress::LocalHost || - senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) { - qDebug() << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode."; - + senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) { + + qCDebug(assigmnentclient) << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode."; QCoreApplication::quit(); } else { - qDebug() << "Got a stop packet from other than localhost."; + qCWarning(assigmnentclient) << "Got a stop packet from other than localhost."; } } @@ -303,7 +304,7 @@ void AssignmentClient::handleAuthenticationRequest() { // ask the account manager to log us in from the env variables accountManager.requestAccessToken(username, password); } else { - qDebug() << "Authentication was requested against" << qPrintable(accountManager.getAuthURL().toString()) + qCWarning(assigmnentclient) << "Authentication was requested against" << qPrintable(accountManager.getAuthURL().toString()) << "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV) << "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate."; @@ -321,7 +322,7 @@ void AssignmentClient::assignmentCompleted() { // reset the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - qDebug() << "Assignment finished or never started - waiting for new assignment."; + qCDebug(assigmnentclient) << "Assignment finished or never started - waiting for new assignment."; auto nodeList = DependencyManager::get(); diff --git a/assignment-client/src/AssignmentClientLogging.cpp b/assignment-client/src/AssignmentClientLogging.cpp new file mode 100644 index 0000000000..890187ecaa --- /dev/null +++ b/assignment-client/src/AssignmentClientLogging.cpp @@ -0,0 +1,14 @@ +// +// AssignmentClientLogging.cpp +// assignment-client/src +// +// Created by Clement on 12/14/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 "AssignmentClientLogging.h" + +Q_LOGGING_CATEGORY(assigmnentclient, "hifi.assignment-client") \ No newline at end of file diff --git a/assignment-client/src/AssignmentClientLogging.h b/assignment-client/src/AssignmentClientLogging.h new file mode 100644 index 0000000000..d6b5ee90e0 --- /dev/null +++ b/assignment-client/src/AssignmentClientLogging.h @@ -0,0 +1,19 @@ +// +// AssignmentClientLogging.h +// assignment-client/src +// +// Created by Clement on 12/14/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_AssignmentClientLogging_h +#define hifi_AssignmentClientLogging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(assigmnentclient) + +#endif // hifi_AssignmentClientLogging_h \ No newline at end of file diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index ffe2e24ca0..78a049edd6 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -20,80 +20,8 @@ #include "OctreeSendThread.h" -OctreeQueryNode::OctreeQueryNode() : - _viewSent(false), - _octreePacket(), - _octreePacketWaiting(false), - _lastOctreePayload(new char[udt::MAX_PACKET_SIZE]), - _lastOctreePacketLength(0), - _duplicatePacketCount(0), - _firstSuppressedPacket(usecTimestampNow()), - _maxSearchLevel(1), - _maxLevelReachedInLastSearch(1), - _lastTimeBagEmpty(0), - _viewFrustumChanging(false), - _viewFrustumJustStoppedChanging(true), - _octreeSendThread(NULL), - _lastClientBoundaryLevelAdjust(0), - _lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE), - _lodChanged(false), - _lodInitialized(false), - _sequenceNumber(0), - _lastRootTimestamp(0), - _myPacketType(PacketType::Unknown), - _isShuttingDown(false), - _sentPacketHistory() -{ -} - -OctreeQueryNode::~OctreeQueryNode() { - _isShuttingDown = true; - if (_octreeSendThread) { - forceNodeShutdown(); - } - - delete[] _lastOctreePayload; -} - void OctreeQueryNode::nodeKilled() { _isShuttingDown = true; - if (_octreeSendThread) { - // just tell our thread we want to shutdown, this is asynchronous, and fast, we don't need or want it to block - // while the thread actually shuts down - _octreeSendThread->setIsShuttingDown(); - } -} - -void OctreeQueryNode::forceNodeShutdown() { - _isShuttingDown = true; - if (_octreeSendThread) { - // we really need to force our thread to shutdown, this is synchronous, we will block while the thread actually - // shuts down because we really need it to shutdown, and it's ok if we wait for it to complete - OctreeSendThread* sendThread = _octreeSendThread; - _octreeSendThread = NULL; - sendThread->setIsShuttingDown(); - sendThread->terminate(); - delete sendThread; - } -} - -void OctreeQueryNode::sendThreadFinished() { - // We've been notified by our thread that it is shutting down. So we can clean up our reference to it, and - // delete the actual thread object. Cleaning up our thread will correctly unroll all refereces to shared - // pointers to our node as well as the octree server assignment - if (_octreeSendThread) { - OctreeSendThread* sendThread = _octreeSendThread; - _octreeSendThread = NULL; - delete sendThread; - } -} - -void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) { - _octreeSendThread = new OctreeSendThread(myServer, node); - - // we want to be notified when the thread finishes - connect(_octreeSendThread, &GenericThread::finished, this, &OctreeQueryNode::sendThreadFinished); - _octreeSendThread->initialize(true); } bool OctreeQueryNode::packetIsDuplicate() const { @@ -105,7 +33,7 @@ bool OctreeQueryNode::packetIsDuplicate() const { // of the entire packet, we need to compare only the packet content... if (_lastOctreePacketLength == _octreePacket->getPayloadSize()) { - if (memcmp(_lastOctreePayload + OCTREE_PACKET_EXTRA_HEADERS_SIZE, + if (memcmp(_lastOctreePayload.data() + OCTREE_PACKET_EXTRA_HEADERS_SIZE, _octreePacket->getPayload() + OCTREE_PACKET_EXTRA_HEADERS_SIZE, _octreePacket->getPayloadSize() - OCTREE_PACKET_EXTRA_HEADERS_SIZE) == 0) { return true; @@ -173,7 +101,7 @@ void OctreeQueryNode::resetOctreePacket() { // scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing // packet send rate. _lastOctreePacketLength = _octreePacket->getPayloadSize(); - memcpy(_lastOctreePayload, _octreePacket->getPayload(), _lastOctreePacketLength); + memcpy(_lastOctreePayload.data(), _octreePacket->getPayload(), _lastOctreePacketLength); // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // the clients requested color state. diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h index 89583492e0..0ec876e674 100644 --- a/assignment-client/src/octree/OctreeQueryNode.h +++ b/assignment-client/src/octree/OctreeQueryNode.h @@ -29,8 +29,8 @@ class OctreeServer; class OctreeQueryNode : public OctreeQuery { Q_OBJECT public: - OctreeQueryNode(); - virtual ~OctreeQueryNode(); + OctreeQueryNode() = default; + virtual ~OctreeQueryNode() = default; void init(); // called after creation to set up some virtual items virtual PacketType getMyPacketType() const = 0; @@ -79,9 +79,6 @@ public: OctreeSceneStats stats; - void initializeOctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node); - bool isOctreeSendThreadInitalized() { return _octreeSendThread; } - void dumpOutOfView(); quint64 getLastRootTimestamp() const { return _lastRootTimestamp; } @@ -92,7 +89,6 @@ public: void sceneStart(quint64 sceneSendStartTime) { _sceneSendStartTime = sceneSendStartTime; } void nodeKilled(); - void forceNodeShutdown(); bool isShuttingDown() const { return _isShuttingDown; } void octreePacketSent() { packetSent(*_octreePacket); } @@ -104,49 +100,47 @@ public: bool hasNextNackedPacket() const; const NLPacket* getNextNackedPacket(); -private slots: - void sendThreadFinished(); - private: OctreeQueryNode(const OctreeQueryNode &); OctreeQueryNode& operator= (const OctreeQueryNode&); - bool _viewSent; + bool _viewSent { false }; std::unique_ptr _octreePacket; bool _octreePacketWaiting; - char* _lastOctreePayload = nullptr; - unsigned int _lastOctreePacketLength; - int _duplicatePacketCount; - quint64 _firstSuppressedPacket; + unsigned int _lastOctreePacketLength { 0 }; + int _duplicatePacketCount { 0 }; + quint64 _firstSuppressedPacket { usecTimestampNow() }; - int _maxSearchLevel; - int _maxLevelReachedInLastSearch; + int _maxSearchLevel { 1 }; + int _maxLevelReachedInLastSearch { 1 }; ViewFrustum _currentViewFrustum; ViewFrustum _lastKnownViewFrustum; - quint64 _lastTimeBagEmpty; - bool _viewFrustumChanging; - bool _viewFrustumJustStoppedChanging; + quint64 _lastTimeBagEmpty { 0 }; + bool _viewFrustumChanging { false }; + bool _viewFrustumJustStoppedChanging { true }; - OctreeSendThread* _octreeSendThread; + OctreeSendThread* _octreeSendThread { nullptr }; // watch for LOD changes - int _lastClientBoundaryLevelAdjust; - float _lastClientOctreeSizeScale; - bool _lodChanged; - bool _lodInitialized; + int _lastClientBoundaryLevelAdjust { 0 }; + float _lastClientOctreeSizeScale { DEFAULT_OCTREE_SIZE_SCALE }; + bool _lodChanged { false }; + bool _lodInitialized { false }; - OCTREE_PACKET_SEQUENCE _sequenceNumber; + OCTREE_PACKET_SEQUENCE _sequenceNumber { 0 }; - quint64 _lastRootTimestamp; + quint64 _lastRootTimestamp { 0 }; - PacketType _myPacketType; - bool _isShuttingDown; + PacketType _myPacketType { PacketType::Unknown }; + bool _isShuttingDown { false }; SentPacketHistory _sentPacketHistory; QQueue _nackedSequenceNumbers; quint64 _sceneSendStartTime = 0; + + std::array _lastOctreePayload; }; #endif // hifi_OctreeQueryNode_h diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index b4eb75ede9..2ff06c7439 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -14,6 +14,7 @@ #include #include +#include "OctreeQueryNode.h" #include "OctreeSendThread.h" #include "OctreeServer.h" #include "OctreeServerConsts.h" @@ -25,15 +26,12 @@ quint64 endSceneSleepTime = 0; OctreeSendThread::OctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) : _myServer(myServer), _node(node), - _nodeUUID(node->getUUID()), - _packetData(), - _nodeMissingCount(0), - _isShuttingDown(false) + _nodeUuid(node->getUUID()) { QString safeServerName("Octree"); // set our QThread object name so we can identify this thread while debugging - setObjectName(QString("Octree Send Thread (%1)").arg(uuidStringWithoutCurlyBraces(node->getUUID()))); + setObjectName(QString("Octree Send Thread (%1)").arg(uuidStringWithoutCurlyBraces(_nodeUuid))); if (_myServer) { safeServerName = _myServer->getMyServerName(); @@ -46,6 +44,8 @@ OctreeSendThread::OctreeSendThread(OctreeServer* myServer, const SharedNodePoint } OctreeSendThread::~OctreeSendThread() { + setIsShuttingDown(); + QString safeServerName("Octree"); if (_myServer) { safeServerName = _myServer->getMyServerName(); @@ -56,8 +56,6 @@ OctreeSendThread::~OctreeSendThread() { OctreeServer::clientDisconnected(); OctreeServer::stopTrackingThread(this); - - _node.clear(); } void OctreeSendThread::setIsShuttingDown() { @@ -79,15 +77,17 @@ bool OctreeSendThread::process() { // don't do any send processing until the initial load of the octree is complete... if (_myServer->isInitialLoadComplete()) { - if (_node) { + if (auto node = _node.lock()) { _nodeMissingCount = 0; - OctreeQueryNode* nodeData = static_cast(_node->getLinkedData()); + OctreeQueryNode* nodeData = static_cast(node->getLinkedData()); // Sometimes the node data has not yet been linked, in which case we can't really do anything if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - packetDistributor(nodeData, viewFrustumChanged); + packetDistributor(node, nodeData, viewFrustumChanged); } + } else { + return false; // exit early if we're shutting down } } @@ -123,7 +123,8 @@ AtomicUIntStat OctreeSendThread::_totalSpecialBytes { 0 }; AtomicUIntStat OctreeSendThread::_totalSpecialPackets { 0 }; -int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { +int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, + int& truePacketsSent) { OctreeServer::didHandlePacketSend(this); // if we're shutting down, then exit early @@ -183,12 +184,12 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes // actually send it OctreeServer::didCallWriteDatagram(this); - DependencyManager::get()->sendUnreliablePacket(statsPacket, *_node); + DependencyManager::get()->sendUnreliablePacket(statsPacket, *node); packetSent = true; } else { // not enough room in the packet, send two packets OctreeServer::didCallWriteDatagram(this); - DependencyManager::get()->sendUnreliablePacket(statsPacket, *_node); + DependencyManager::get()->sendUnreliablePacket(statsPacket, *node); // since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since // there was nothing else to send. @@ -219,7 +220,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes packetsSent++; OctreeServer::didCallWriteDatagram(this); - DependencyManager::get()->sendUnreliablePacket(nodeData->getPacket(), *_node); + DependencyManager::get()->sendUnreliablePacket(nodeData->getPacket(), *node); packetSent = true; int packetSizeWithHeader = nodeData->getPacket().getDataSize(); @@ -251,7 +252,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) { // just send the octree packet OctreeServer::didCallWriteDatagram(this); - DependencyManager::get()->sendUnreliablePacket(nodeData->getPacket(), *_node); + DependencyManager::get()->sendUnreliablePacket(nodeData->getPacket(), *node); packetSent = true; int packetSizeWithHeader = nodeData->getPacket().getDataSize(); @@ -293,7 +294,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes } /// Version of octree element distributor that sends the deepest LOD level at once -int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged) { +int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { OctreeServer::didPacketDistributor(this); @@ -322,7 +323,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color // then let's just send that waiting packet. if (nodeData->isPacketWaiting()) { - packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } else { nodeData->resetOctreePacket(); } @@ -355,7 +356,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus //unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); //unsigned long elapsedTime = nodeData->stats.getElapsedTime(); - int packetsJustSent = handlePacketSend(nodeData, trueBytesSent, truePacketsSent); + int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); packetsSentThisInterval += packetsJustSent; // If we're starting a full scene, then definitely we want to empty the elementBag @@ -431,8 +432,8 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // Our trackSend() function is implemented by the server subclass, and will be called back // during the encodeTreeBitstream() as new entities/data elements are sent - params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) { - _myServer->trackSend(dataID, dataEdited, _nodeUUID); + params.trackSend = [this, node](const QUuid& dataID, quint64 dataEdited) { + _myServer->trackSend(dataID, dataEdited, node->getUUID()); }; // TODO: should this include the lock time or not? This stat is sent down to the client, @@ -481,7 +482,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus unsigned int writtenSize = _packetData.getFinalizedSize() + sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); if (writtenSize > nodeData->getAvailable()) { - packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); @@ -501,7 +502,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; if (sendNow) { quint64 packetSendingStart = usecTimestampNow(); - packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); quint64 packetSendingEnd = usecTimestampNow(); packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart); @@ -538,9 +539,9 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // Here's where we can/should allow the server to send other data... // send the environment packet // TODO: should we turn this into a while loop to better handle sending multiple special packets - if (_myServer->hasSpecialPacketsToSend(_node) && !nodeData->isShuttingDown()) { + if (_myServer->hasSpecialPacketsToSend(node) && !nodeData->isShuttingDown()) { int specialPacketsSent = 0; - trueBytesSent += _myServer->sendSpecialPackets(_node, nodeData, specialPacketsSent); + trueBytesSent += _myServer->sendSpecialPackets(node, nodeData, specialPacketsSent); nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed truePacketsSent += specialPacketsSent; packetsSentThisInterval += specialPacketsSent; @@ -556,7 +557,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus while (nodeData->hasNextNackedPacket() && packetsSentThisInterval < maxPacketsPerInterval) { const NLPacket* packet = nodeData->getNextNackedPacket(); if (packet) { - DependencyManager::get()->sendUnreliablePacket(*packet, *_node); + DependencyManager::get()->sendUnreliablePacket(*packet, *node); truePacketsSent++; packetsSentThisInterval++; diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 6775e56820..38f5de722f 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -18,8 +18,7 @@ #include -#include "OctreeQueryNode.h" - +class OctreeQueryNode; class OctreeServer; using AtomicUIntStat = std::atomic; @@ -32,6 +31,9 @@ public: virtual ~OctreeSendThread(); void setIsShuttingDown(); + bool isShuttingDown() { return _isShuttingDown; } + + QUuid getNodeUuid() const { return _nodeUuid; } static AtomicUIntStat _totalBytes; static AtomicUIntStat _totalWastedBytes; @@ -48,17 +50,18 @@ protected: virtual bool process(); private: - OctreeServer* _myServer; - SharedNodePointer _node; - QUuid _nodeUUID; - - int handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); - int packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged); + int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); + int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged); + + + OctreeServer* _myServer { nullptr }; + QWeakPointer _node; + QUuid _nodeUuid; OctreePacketData _packetData; - int _nodeMissingCount; - bool _isShuttingDown; + int _nodeMissingCount { 0 }; + bool _isShuttingDown { false }; }; #endif // hifi_OctreeSendThread_h diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index ec34ad4410..410aa922dc 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -25,9 +25,9 @@ #include "../AssignmentClient.h" +#include "OctreeQueryNode.h" #include "OctreeServerConsts.h" -OctreeServer* OctreeServer::_instance = NULL; int OctreeServer::_clientCount = 0; const int MOVING_AVERAGE_SAMPLE_COUNTS = 1000000; @@ -231,13 +231,6 @@ OctreeServer::OctreeServer(ReceivedMessage& message) : _started(time(0)), _startedUSecs(usecTimestampNow()) { - if (_instance) { - qDebug() << "Octree Server starting... while old instance still running _instance=["<<_instance<<"] this=[" << this << "]"; - } - - qDebug() << "Octree Server starting... setting _instance to=[" << this << "]"; - _instance = this; - _averageLoopTime.updateAverage(0); qDebug() << "Octree server starting... [" << this << "]"; @@ -281,9 +274,6 @@ OctreeServer::~OctreeServer() { _tree.reset(); qDebug() << qPrintable(_safeServerName) << "server DONE cleaning up octree... [" << this << "]"; - if (_instance == this) { - _instance = NULL; // we are gone - } qDebug() << qPrintable(_safeServerName) << "server DONE shutting down... [" << this << "]"; } @@ -878,16 +868,38 @@ void OctreeServer::parsePayload() { } } +OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) { + auto sendThread = std::unique_ptr(new OctreeSendThread(this, node)); + + // we want to be notified when the thread finishes + connect(sendThread.get(), &GenericThread::finished, this, &OctreeServer::removeSendThread); + sendThread->initialize(true); + + return sendThread; +} + +void OctreeServer::removeSendThread() { + // If the object has been deleted since the event was queued, sender() will return nullptr + if (auto sendThread = qobject_cast(sender())) { + // This deletes the unique_ptr, so sendThread is destructed after that line + _sendThreads.erase(sendThread->getNodeUuid()); + } +} + void OctreeServer::handleOctreeQueryPacket(QSharedPointer message, SharedNodePointer senderNode) { - if (!_isFinished) { + if (!_isFinished && !_isShuttingDown) { // If we got a query packet, then we're talking to an agent, and we // need to make sure we have it in our nodeList. auto nodeList = DependencyManager::get(); nodeList->updateNodeWithDataFromPacket(message, senderNode); - OctreeQueryNode* nodeData = dynamic_cast(senderNode->getLinkedData()); - if (nodeData && !nodeData->isOctreeSendThreadInitalized()) { - nodeData->initializeOctreeSendThread(this, senderNode); + auto it = _sendThreads.find(senderNode->getUUID()); + if (it == _sendThreads.end()) { + _sendThreads.emplace(senderNode->getUUID(), createSendThread(senderNode)); + } else if (it->second->isShuttingDown()) { + _sendThreads.erase(it); // Remove right away and wait on thread to be + + _sendThreads.emplace(senderNode->getUUID(), createSendThread(senderNode)); } } } @@ -1117,8 +1129,8 @@ void OctreeServer::domainSettingsRequestComplete() { setvbuf(stdout, NULL, _IOLBF, 0); #endif - nodeList->linkedDataCreateCallback = [] (Node* node) { - auto queryNodeData = _instance->createOctreeQueryNode(); + nodeList->linkedDataCreateCallback = [this](Node* node) { + auto queryNodeData = createOctreeQueryNode(); queryNodeData->init(); node->setLinkedData(std::move(queryNodeData)); }; @@ -1167,6 +1179,13 @@ void OctreeServer::nodeAdded(SharedNodePointer node) { void OctreeServer::nodeKilled(SharedNodePointer node) { quint64 start = usecTimestampNow(); + + // Shutdown send thread + auto it = _sendThreads.find(node->getUUID()); + if (it != _sendThreads.end()) { + auto& sendThread = *it->second; + sendThread.setIsShuttingDown(); + } // calling this here since nodeKilled slot in ReceivedPacketProcessor can't be triggered by signals yet!! _octreeInboundPacketProcessor->nodeKilled(node); @@ -1188,24 +1207,6 @@ void OctreeServer::nodeKilled(SharedNodePointer node) { trackViewerGone(node->getUUID()); } -void OctreeServer::forceNodeShutdown(SharedNodePointer node) { - quint64 start = usecTimestampNow(); - - qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node; - OctreeQueryNode* nodeData = dynamic_cast(node->getLinkedData()); - if (nodeData) { - nodeData->forceNodeShutdown(); // tell our node data and sending threads that we'd like to shut down - } else { - qDebug() << qPrintable(_safeServerName) << "server node missing linked data node:" << *node; - } - - quint64 end = usecTimestampNow(); - quint64 usecsElapsed = (end - start); - qDebug() << qPrintable(_safeServerName) << "server forceNodeShutdown() took: " - << usecsElapsed << " usecs for node:" << *node; -} - - void OctreeServer::aboutToFinish() { qDebug() << qPrintable(_safeServerName) << "server STARTING about to finish..."; @@ -1214,9 +1215,8 @@ void OctreeServer::aboutToFinish() { qDebug() << qPrintable(_safeServerName) << "inform Octree Inbound Packet Processor that we are shutting down..."; // we're going down - set the NodeList linkedDataCallback to NULL so we do not create any more OctreeQueryNode objects. - // This ensures that when we forceNodeShutdown below for each node we don't get any more newly connecting nodes - auto nodeList = DependencyManager::get(); - nodeList->linkedDataCreateCallback = NULL; + // This ensures that we don't get any more newly connecting nodes + DependencyManager::get()->linkedDataCreateCallback = nullptr; if (_octreeInboundPacketProcessor) { _octreeInboundPacketProcessor->terminating(); @@ -1226,21 +1226,15 @@ void OctreeServer::aboutToFinish() { _jurisdictionSender->terminating(); } - QSet nodesToShutdown; - - // Force a shutdown of all of our OctreeSendThreads. - // At this point it has to be impossible for a linkedDataCreateCallback to be called for a new node - nodeList->eachNode([&nodesToShutdown](const SharedNodePointer& node) { - nodesToShutdown << node; - }); - - // What follows is a hack to force OctreeSendThreads to cleanup before the OctreeServer is gone. - // I would prefer to allow the SharedNodePointer ref count drop to zero to do this automatically - // but that isn't possible as long as the OctreeSendThread has an OctreeServer* that it uses. - for (auto& node : nodesToShutdown) { - qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node; - forceNodeShutdown(node); + // Shut down all the send threads + for (auto& it : _sendThreads) { + auto& sendThread = *it.second; + sendThread.setIsShuttingDown(); } + + // Clear will destruct all the unique_ptr to OctreeSendThreads which will call the GenericThread's dtor + // which waits on the thread to be done before returning + _sendThreads.clear(); // Cleans up all the send threads. if (_persistThread) { _persistThread->aboutToFinish(); @@ -1459,15 +1453,22 @@ void OctreeServer::didCallWriteDatagram(OctreeSendThread* thread) { void OctreeServer::stopTrackingThread(OctreeSendThread* thread) { - QMutexLocker lockerA(&_threadsDidProcessMutex); - QMutexLocker lockerB(&_threadsDidPacketDistributorMutex); - QMutexLocker lockerC(&_threadsDidHandlePacketSendMutex); - QMutexLocker lockerD(&_threadsDidCallWriteDatagramMutex); - - _threadsDidProcess.remove(thread); - _threadsDidPacketDistributor.remove(thread); - _threadsDidHandlePacketSend.remove(thread); - _threadsDidCallWriteDatagram.remove(thread); + { + QMutexLocker locker(&_threadsDidProcessMutex); + _threadsDidProcess.remove(thread); + } + { + QMutexLocker locker(&_threadsDidPacketDistributorMutex); + _threadsDidPacketDistributor.remove(thread); + } + { + QMutexLocker locker(&_threadsDidHandlePacketSendMutex); + _threadsDidHandlePacketSend.remove(thread); + } + { + QMutexLocker locker(&_threadsDidCallWriteDatagramMutex); + _threadsDidCallWriteDatagram.remove(thread); + } } int howManyThreadsDidSomething(QMutex& mutex, QMap& something, quint64 since) { diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 80668f841c..2aabb97428 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -124,7 +124,6 @@ public: bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler); virtual void aboutToFinish(); - void forceNodeShutdown(SharedNodePointer node); public slots: /// runs the octree server assignment @@ -138,8 +137,12 @@ private slots: void handleOctreeQueryPacket(QSharedPointer message, SharedNodePointer senderNode); void handleOctreeDataNackPacket(QSharedPointer message, SharedNodePointer senderNode); void handleJurisdictionRequestPacket(QSharedPointer message, SharedNodePointer senderNode); + void removeSendThread(); protected: + using UniqueSendThread = std::unique_ptr; + using SendThreads = std::unordered_map; + virtual OctreePointer createTree() = 0; bool readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result); bool readOptionInt(const QString& optionName, const QJsonObject& settingsSectionObject, int& result); @@ -153,6 +156,8 @@ protected: QString getFileLoadTime(); QString getConfiguration(); QString getStatusLink(); + + UniqueSendThread createSendThread(const SharedNodePointer& node); int _argc; const char** _argv; @@ -187,11 +192,11 @@ protected: int _backupInterval; int _maxBackupVersions; - static OctreeServer* _instance; - time_t _started; quint64 _startedUSecs; QString _safeServerName; + + SendThreads _sendThreads; static int _clientCount; static SimpleMovingAverage _averageLoopTime; diff --git a/cmake/externals/qxmpp/CMakeLists.txt b/cmake/externals/qxmpp/CMakeLists.txt deleted file mode 100644 index 600aa7b2ff..0000000000 --- a/cmake/externals/qxmpp/CMakeLists.txt +++ /dev/null @@ -1,77 +0,0 @@ -set(EXTERNAL_NAME qxmpp) - -# we need to find qmake inside QT_DIR -find_program(QMAKE_COMMAND NAME qmake PATHS ${QT_DIR}/bin $ENV{QTTOOLDIR} NO_DEFAULT_PATH) - -if (NOT QMAKE_COMMAND) - message(FATAL_ERROR "Could not find qmake. Qxmpp cannot be compiled without qmake.") -endif () - -if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") -endif () - -if (WIN32) - find_program(PLATFORM_BUILD_COMMAND nmake PATHS "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin") - - if (NOT PLATFORM_BUILD_COMMAND) - message(FATAL_ERROR "You asked CMake to grap QXmpp and build it, but nmake was not found. Please make sure the folder containing nmake.exe is in your PATH.") - endif () -else () - find_program(PLATFORM_BUILD_COMMAND make) -endif () - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://qxmpp.googlecode.com/files/qxmpp-0.7.6.tar.gz - URL_MD5 ee45a97313306ded2ff0f6618a3ed1e1 - BUILD_IN_SOURCE 1 - PATCH_COMMAND patch -p2 -t -N --verbose < ${CMAKE_CURRENT_SOURCE_DIR}/qxmpp.patch - CONFIGURE_COMMAND ${QMAKE_COMMAND} PREFIX= - BUILD_COMMAND ${PLATFORM_BUILD_COMMAND} - INSTALL_COMMAND ${PLATFORM_BUILD_COMMAND} install - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -if (CMAKE_GENERATOR STREQUAL Xcode) - find_program(DITTO_COMMAND ditto) - - ExternalProject_Add_Step( - ${EXTERNAL_NAME} - copy-from-xcode-install - COMMENT "Copying from /tmp/hifi.dst${INSTALL_DIR} to move install to proper location" - COMMAND ${DITTO_COMMAND} /tmp/hifi.dst${INSTALL_DIR} ${INSTALL_DIR} - DEPENDEES install - LOG 1 - ) -endif () - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Qxmpp include directory") - -set(_LIB_DIR ${INSTALL_DIR}/lib) - -if (WIN32) - set(_LIB_EXT "0.lib") - - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${_LIB_DIR} CACHE PATH "Location of QXmpp DLL") -else () - if (APPLE) - set(_LIB_EXT ".dylib") - else () - set(_LIB_EXT ".so") - endif () - - set(_LIB_PREFIX "lib") -endif () - -set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${_LIB_DIR}/${_LIB_PREFIX}qxmpp${_LIB_EXT} CACHE FILEPATH "Path to QXmpp release library") -set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to QXmpp debug library") diff --git a/cmake/macros/ConsolidateStackComponents.cmake b/cmake/macros/ConsolidateStackComponents.cmake index 4bcc777751..ca272f6485 100644 --- a/cmake/macros/ConsolidateStackComponents.cmake +++ b/cmake/macros/ConsolidateStackComponents.cmake @@ -1,26 +1,27 @@ macro(CONSOLIDATE_STACK_COMPONENTS) - if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32) - - # Copy all the output for this target into the common deployment location - add_custom_command( - TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_directory $ ${CMAKE_BINARY_DIR}/full-stack-deployment - ) - - # Copy icon files for interface and stack manager - if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager") - if (TARGET_NAME STREQUAL "interface") - set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/interface.ico") - set (ICON_DESTINATION_NAME "interface.ico") - elseif (TARGET_NAME STREQUAL "stack-manager") - set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/icon.ico") - set (ICON_DESTINATION_NAME "stack-manager.ico") - endif () + if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE) + if (WIN32) + # Copy all the output for this target into the common deployment location add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/full-stack-deployment/${ICON_DESTINATION_NAME} + COMMAND "${CMAKE_COMMAND}" -E copy_directory $ ${CMAKE_BINARY_DIR}/full-stack-deployment ) + + # Copy icon files for interface and stack manager + if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager") + if (TARGET_NAME STREQUAL "interface") + set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/interface.ico") + set (ICON_DESTINATION_NAME "interface.ico") + elseif (TARGET_NAME STREQUAL "stack-manager") + set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/icon.ico") + set (ICON_DESTINATION_NAME "stack-manager.ico") + endif () + add_custom_command( + TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/full-stack-deployment/${ICON_DESTINATION_NAME} + ) + endif () endif () endif () diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake new file mode 100644 index 0000000000..570e24332b --- /dev/null +++ b/cmake/macros/GenerateInstallers.cmake @@ -0,0 +1,30 @@ +# +# GenerateInstallers.cmake +# cmake/macros +# +# Created by Leonardo Murillo on 12/16/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 +# + +macro(GENERATE_INSTALLERS) + if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32) + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/full-stack-deployment") + find_program(MAKENSIS_COMMAND makensis PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS]) + if (NOT MAKENSIS_COMMAND) + message(FATAL_ERROR "The Nullsoft Scriptable Install Systems is required for generating packaged installers on Windows (http://nsis.sourceforge.net/)") + endif () + add_custom_target( + build-package ALL + DEPENDS interface assignment-client domain-server stack-manager + COMMAND set INSTALLER_SOURCE_DIR=${CMAKE_BINARY_DIR}/full-stack-deployment + COMMAND set INSTALLER_NAME=${CMAKE_BINARY_DIR}/${INSTALLER_NAME} + COMMAND set INSTALLER_SCRIPTS_DIR=${CMAKE_SOURCE_DIR}/examples + COMMAND set INSTALLER_COMPANY=${INSTALLER_COMPANY} + COMMAND set INSTALLER_DIRECTORY=${INSTALLER_DIRECTORY} + COMMAND CMD /C "\"${MAKENSIS_COMMAND}\" ${CMAKE_SOURCE_DIR}/tools/nsis/release.nsi" + ) + endif () +endmacro() \ No newline at end of file diff --git a/cmake/macros/IncludeApplicationVersion.cmake b/cmake/macros/IncludeApplicationVersion.cmake index a91aad6acc..753a12a01c 100644 --- a/cmake/macros/IncludeApplicationVersion.cmake +++ b/cmake/macros/IncludeApplicationVersion.cmake @@ -14,13 +14,22 @@ macro(INCLUDE_APPLICATION_VERSION) # We are relying on Jenkins defined environment variables to determine the origin of this build # and will only package if this is a PR or Release build if (DEFINED ENV{JOB_ID}) - set (DEPLOY_PACKAGE 1) - set (BUILD_SEQ $ENV{JOB_ID}) + set(DEPLOY_PACKAGE 1) + set(BUILD_SEQ $ENV{JOB_ID}) + set(INSTALLER_COMPANY "High Fidelity") + set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}") + set(INSTALLER_NAME "interface-win64-${BUILD_SEQ}.exe") elseif (DEFINED ENV{ghprbPullId}) - set (DEPLOY_PACKAGE 1) - set (BUILD_SEQ "PR-$ENV{ghprbPullId}") + set(DEPLOY_PACKAGE 1) + set(BUILD_SEQ "PR-$ENV{ghprbPullId}") + set(INSTALLER_COMPANY "High Fidelity - PR") + set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}\\${BUILD_SEQ}") + set(INSTALLER_NAME "pr-interface-win64-${BUILD_SEQ}.exe") else () set(BUILD_SEQ "dev") + set(INSTALLER_COMPANY "High Fidelity - Dev") + set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}") + set(INSTALLER_NAME "dev-interface-win64.exe") endif () configure_file("${MACRO_DIR}/ApplicationVersion.h.in" "${PROJECT_BINARY_DIR}/includes/ApplicationVersion.h") include_directories("${PROJECT_BINARY_DIR}/includes") diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index bb0b268dd4..17b5d5f49d 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -40,7 +40,7 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} $<$,$,$>:--release> $" + COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> $" ) elseif (DEFINED BUILD_BUNDLE AND BUILD_BUNDLE AND APPLE) find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH) diff --git a/examples/acScripts/triggeredRecordingOnAC.js b/examples/acScripts/triggeredRecordingOnAC.js new file mode 100644 index 0000000000..9de5173615 --- /dev/null +++ b/examples/acScripts/triggeredRecordingOnAC.js @@ -0,0 +1,72 @@ +// +// triggeredRecordingOnAC.js +// examples/acScripts +// +// Created by Thijs Wenker on 12/21/15. +// Copyright 2015 High Fidelity, Inc. +// +// This is the triggered rocording script used in the winterSmashUp target practice game. +// Change the CLIP_URL to your asset, +// the RECORDING_CHANNEL and RECORDING_CHANNEL_MESSAGE are used to trigger it i.e.: +// Messages.sendMessage("PlayBackOnAssignment", "BowShootingGameWelcome"); +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +const CLIP_URL = "atp:3fbe82f2153c443f12f9a2b14ce2d7fa2fff81977263746d9e0885ea5aabed62.hfr"; + +const RECORDING_CHANNEL = 'PlayBackOnAssignment'; +const RECORDING_CHANNEL_MESSAGE = 'BowShootingGameWelcome'; // For each different assignment add a different message here. +const PLAY_FROM_CURRENT_LOCATION = true; +const USE_DISPLAY_NAME = true; +const USE_ATTACHMENTS = true; +const USE_AVATAR_MODEL = true; +const AUDIO_OFFSET = 0.0; +const STARTING_TIME = 0.0; +const COOLDOWN_PERIOD = 0; // The period in ms that no animations can be played after one has been played already + +var isPlaying = false; +var isPlayable = true; + +var playRecording = function() { + if (!isPlayable || isPlaying) { + return; + } + Agent.isAvatar = true; + Recording.setPlayFromCurrentLocation(PLAY_FROM_CURRENT_LOCATION); + Recording.setPlayerUseDisplayName(USE_DISPLAY_NAME); + Recording.setPlayerUseAttachments(USE_ATTACHMENTS); + Recording.setPlayerUseHeadModel(false); + Recording.setPlayerUseSkeletonModel(USE_AVATAR_MODEL); + Recording.setPlayerLoop(false); + Recording.setPlayerTime(STARTING_TIME); + Recording.setPlayerAudioOffset(AUDIO_OFFSET); + Recording.loadRecording(CLIP_URL); + Recording.startPlaying(); + isPlaying = true; + isPlayable = false; // Set this true again after the cooldown period +}; + +Script.update.connect(function(deltaTime) { + if (isPlaying && !Recording.isPlaying()) { + print('Reached the end of the recording. Resetting.'); + isPlaying = false; + Agent.isAvatar = false; + if (COOLDOWN_PERIOD === 0) { + isPlayable = true; + return; + } + Script.setTimeout(function () { + isPlayable; + }, COOLDOWN_PERIOD); + } +}); + +Messages.subscribe(RECORDING_CHANNEL); + +Messages.messageReceived.connect(function (channel, message, senderID) { + if (channel === RECORDING_CHANNEL && message === RECORDING_CHANNEL_MESSAGE) { + playRecording(); + } +}); diff --git a/examples/controllers/getHUDLookAtPositionTest.js b/examples/controllers/getHUDLookAtPositionTest.js new file mode 100644 index 0000000000..348b6757b8 --- /dev/null +++ b/examples/controllers/getHUDLookAtPositionTest.js @@ -0,0 +1,54 @@ +// +// getHUDLookAtPositionTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/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 +// + + +// This script demonstrates the testing of the HMD.getHUDLookAtPosition--() functions. +// If these functions are working correctly, we'd expect to see a 3D cube and a 2D square +// follow around the center of the HMD view. + +var cubePosition = { x: 0, y: 0, z: 0 }; +var cubeSize = 0.03; +var cube = Overlays.addOverlay("cube", { + position: cubePosition, + size: cubeSize, + color: { red: 255, green: 0, blue: 0}, + alpha: 1, + solid: false + }); + +var square = Overlays.addOverlay("text", { + x: 0, + y: 0, + width: 20, + height: 20, + color: { red: 255, green: 255, blue: 0}, + backgroundColor: { red: 255, green: 255, blue: 0}, + alpha: 1 + }); + + +Script.update.connect(function(deltaTime) { + if (!HMD.active) { + return; + } + var lookAt3D = HMD.getHUDLookAtPosition3D(); + Overlays.editOverlay(cube, { position: lookAt3D }); + + var lookAt2D = HMD.getHUDLookAtPosition2D(); + Overlays.editOverlay(square, { x: lookAt2D.x, y: lookAt2D.y }); +}); + +Script.scriptEnding.connect(function(){ + Overlays.deleteOverlay(cube); + Overlays.deleteOverlay(square); +}); + + diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 07894b46d1..9652132c08 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1,5 +1,4 @@ // handControllerGrab.js -// examples // // Created by Eric Levin on 9/2/15 // Additions by James B. Pollack @imgntn on 9/24/2015 @@ -7,6 +6,7 @@ // Copyright 2015 High Fidelity, Inc. // // Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. +// Also supports touch and equipping objects. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -14,7 +14,6 @@ Script.include("../libraries/utils.js"); - // // add lines where the hand ray picking is happening // @@ -54,6 +53,7 @@ var LINE_ENTITY_DIMENSIONS = { y: 1000, z: 1000 }; + var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray @@ -117,6 +117,22 @@ var DEFAULT_GRABBABLE_DATA = { }; +// sometimes we want to exclude objects from being picked +var USE_BLACKLIST = true; +var blacklist = []; + +//we've created various ways of visualizing looking for and moving distant objects +var USE_ENTITY_LINES_FOR_SEARCHING = false; +var USE_OVERLAY_LINES_FOR_SEARCHING = false; +var USE_PARTICLE_BEAM_FOR_SEARCHING = true; + +var USE_ENTITY_LINES_FOR_MOVING = false; +var USE_OVERLAY_LINES_FOR_MOVING = false; +var USE_PARTICLE_BEAM_FOR_MOVING = true; + +var USE_SPOTLIGHT = false; +var USE_POINTLIGHT = false; + // states for the state machine var STATE_OFF = 0; var STATE_SEARCHING = 1; @@ -137,6 +153,7 @@ var STATE_WAITING_FOR_BUMPER_RELEASE = 15; var STATE_EQUIP_SPRING = 16; + function stateToName(state) { switch (state) { case STATE_OFF: @@ -214,10 +231,14 @@ function getSpatialOffsetPosition(hand, spatialKey) { position = spatialKey.relativePosition; } + // add the relative hand center offset + var handSizeRatio = calculateHandSizeRatio(); + position = Vec3.multiply(position, handSizeRatio); return position; } var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); + function getSpatialOffsetRotation(hand, spatialKey) { var rotation = Quat.IDENTITY; @@ -261,9 +282,15 @@ function MyController(hand) { this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; this.rawBumperValue = 0; - + //for visualizations this.overlayLine = null; - + this.particleBeam = null; + + //for lights + this.spotlight = null; + this.pointlight = null; + this.overlayLine = null; + this.ignoreIK = false; this.offsetPosition = Vec3.ZERO; this.offsetRotation = Quat.IDENTITY; @@ -329,7 +356,7 @@ function MyController(hand) { print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); } this.state = newState; - } + }; this.debugLine = function(closePoint, farPoint, color) { Entities.addEntity({ @@ -349,34 +376,7 @@ function MyController(hand) { } }) }); - } - - this.overlayLineOn = function(closePoint, farPoint, color) { - if (this.overlayLine === null) { - var lineProperties = { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - ignoreRayIntersection: true, // always ignore this - visible: true, - alpha: 1 - }; - - this.overlayLine = Overlays.addOverlay("line3d", lineProperties); - - } else { - var success = Overlays.editOverlay(this.overlayLine, { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - visible: true, - ignoreRayIntersection: true, // always ignore this - alpha: 1 - }); - } - } + }; this.lineOn = function(closePoint, farPoint, color) { // draw a line @@ -409,6 +409,237 @@ function MyController(hand) { } }; + this.overlayLineOn = function(closePoint, farPoint, color) { + if (this.overlayLine === null) { + var lineProperties = { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + ignoreRayIntersection: true, // always ignore this + visible: true, + alpha: 1 + }; + + this.overlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.overlayLine, { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + visible: true, + ignoreRayIntersection: true, // always ignore this + alpha: 1 + }); + } + }; + + this.handleParticleBeam = function(position, orientation, color) { + + var rotation = Quat.angleAxis(0, { + x: 1, + y: 0, + z: 0 + }); + + var finalRotation = Quat.multiply(orientation, rotation); + var lifespan = LINE_LENGTH / 10; + var speed = 5; + var spread = 2; + if (this.particleBeam === null) { + this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); + } else { + this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); + } + }; + + this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { + + var handToObject = Vec3.subtract(objectPosition, handPosition); + var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); + + var distance = Vec3.distance(handPosition, objectPosition); + var speed = 5; + var spread = 0; + + var lifespan = distance / speed; + + + if (this.particleBeam === null) { + this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); + } else { + this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); + } + }; + + this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + + var particleBeamProperties = { + type: "ParticleEffect", + isEmitting: true, + position: position, + visible: false, + "name": "Particle Beam", + "color": color, + "maxParticles": 2000, + "lifespan": lifespan, + "emitRate": 50, + "emitSpeed": speed, + "speedSpread": spread, + "emitOrientation": { + "x": -1, + "y": 0, + "z": 0, + "w": 1 + }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "emitRadiusStart": 0.5, + "polarStart": 0, + "polarFinish": 0, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 3.1415927410125732, + "emitAcceleration": { + x: 0, + y: 0, + z: 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.015, + "radiusSpread": 0.005, + // "radiusStart": 0.01, + // "radiusFinish": 0.01, + // "colorSpread": { + // "red": 0, + // "green": 0, + // "blue": 0 + // }, + // "colorStart": color, + // "colorFinish": color, + "alpha": 1, + "alphaSpread": 0, + "alphaStart": 1, + "alphaFinish": 1, + "additiveBlending": 0, + "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" + } + + this.particleBeam = Entities.addEntity(particleBeamProperties); + }; + + this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + Entities.editEntity(this.particleBeam, { + rotation: orientation, + position: position, + visible: true, + color: color, + emitSpeed: speed, + speedSpread: spread, + lifespan: lifespan + }) + + }; + + this.evalLightWorldTransform = function(modelPos, modelRot) { + + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + }; + + this.handleSpotlight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + var lightProperties = { + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.spotlight === null) { + this.spotlight = Entities.addEntity(lightProperties); + } else { + Entities.editEntity(this.spotlight, { + //without this, this light would maintain rotation with its parent + rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), + }) + } + }; + + this.handlePointLight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + + var lightProperties = { + type: "Light", + isSpotlight: false, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.pointlight === null) { + this.pointlight = Entities.addEntity(lightProperties); + } else { + + } + }; + this.lineOff = function() { if (this.pointer !== null) { Entities.deleteEntity(this.pointer); @@ -423,6 +654,41 @@ function MyController(hand) { this.overlayLine = null; }; + this.particleBeamOff = function() { + if (this.particleBeam !== null) { + Entities.editEntity(this.particleBeam, { + visible: false + }) + } + } + + this.turnLightsOff = function() { + if (this.spotlight !== null) { + Entities.deleteEntity(this.spotlight); + this.spotlight = null; + } + + if (this.pointlight !== null) { + Entities.deleteEntity(this.pointlight); + this.pointlight = null; + } + }; + + + this.turnOffVisualizations = function() { + if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOff(); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOff(); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.particleBeamOff(); + } + }; + this.triggerPress = function(value) { _this.rawTriggerValue = value; }; @@ -431,7 +697,6 @@ function MyController(hand) { _this.rawBumperValue = value; }; - this.updateSmoothedTrigger = function() { var triggerValue = this.rawTriggerValue; // smooth out trigger value @@ -454,12 +719,11 @@ function MyController(hand) { this.bumperSqueezed = function() { return _this.rawBumperValue > BUMPER_ON_VALUE; - } + }; this.bumperReleased = function() { return _this.rawBumperValue < BUMPER_ON_VALUE; - } - + }; this.off = function() { if (this.triggerSmoothedSqueezed()) { @@ -472,7 +736,7 @@ function MyController(hand) { this.setState(STATE_EQUIP_SEARCHING); return; } - } + }; this.search = function() { this.grabbedEntity = null; @@ -515,13 +779,28 @@ function MyController(hand) { }) } - var intersection = Entities.findRayIntersection(pickRayBacked, true); + Messages.sendMessage('Hifi-Light-Overlay-Ray-Check', JSON.stringify(pickRayBacked)); + + var intersection; + + if (USE_BLACKLIST === true && blacklist.length !== 0) { + intersection = Entities.findRayIntersection(pickRayBacked, true, [], blacklist); + } else { + intersection = Entities.findRayIntersection(pickRayBacked, true); + } + if (intersection.intersects) { + // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); + var defaultDisableNearGrabData = { + disableNearGrab: false + }; + //sometimes we want things to stay right where they are when we let go. + var disableNearGrabData = getEntityCustomData('handControllerKey', intersection.entityID, defaultDisableNearGrabData); if (intersection.properties.name == "Grab Debug Entity") { continue; @@ -543,7 +822,11 @@ function MyController(hand) { } else if (!intersection.properties.locked) { this.grabbedEntity = intersection.entityID; if (this.state == STATE_SEARCHING) { - this.setState(STATE_NEAR_GRABBING); + if (disableNearGrabData.disableNearGrab !== true) { + this.setState(STATE_NEAR_GRABBING); + } else { + //disable near grab on this thing + } } else { // equipping if (typeof grabbableData.spatialKey !== 'undefined') { // TODO @@ -666,13 +949,35 @@ function MyController(hand) { this.setState(STATE_NEAR_TRIGGER); return; } else if (!props.locked && props.collisionsWillMove) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + var defaultDisableNearGrabData = { + disableNearGrab: false + }; + //sometimes we want things to stay right where they are when we let go. + var disableNearGrabData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultDisableNearGrabData); + if (disableNearGrabData.disableNearGrab === true) { + //do nothing because near grab is disabled for this object + } else { + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + + } + return; } } - //this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + //search line visualizations + if (USE_ENTITY_LINES_FOR_SEARCHING === true) { + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { + this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { + this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); + } + }; this.distanceHolding = function() { @@ -725,7 +1030,7 @@ function MyController(hand) { this.currentAvatarPosition = MyAvatar.position; this.currentAvatarOrientation = MyAvatar.orientation; - this.overlayLineOff(); + this.turnOffVisualizations(); }; this.continueDistanceHolding = function() { @@ -751,7 +1056,6 @@ function MyController(hand) { return; } - this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); // the action was set up on a previous call. update the targets. var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * @@ -816,110 +1120,199 @@ function MyController(hand) { Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); - // mix in head motion - if (MOVE_WITH_HEAD) { - var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { x: 0.0, y: 0.0, z: objDistance }); - var after = Vec3.multiplyQbyV(Camera.orientation, { x: 0.0, y: 0.0, z: objDistance }); - var change = Vec3.subtract(before, after); - this.currentCameraOrientation = Camera.orientation; - this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + var defaultMoveWithHeadData = { + disableMoveWithHead: false + }; + + var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); + + if (handControllerData.disableMoveWithHead !== true) { + // mix in head motion + if (MOVE_WITH_HEAD) { + var objDistance = Vec3.length(objectToAvatar); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var change = Vec3.subtract(before, after); + this.currentCameraOrientation = Camera.orientation; + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + } + } else { + // print('should not head move!'); + } + + + var defaultConstraintData = { + axisStart: false, + axisEnd: false, + } + + var constraintData = getEntityCustomData('lightModifierKey', this.grabbedEntity, defaultConstraintData); + var clampedVector; + var targetPosition; + if (constraintData.axisStart !== false) { + clampedVector = this.projectVectorAlongAxis(this.currentObjectPosition, constraintData.axisStart, constraintData.axisEnd); + targetPosition = clampedVector; + } else { + targetPosition = { + x: this.currentObjectPosition.x, + y: this.currentObjectPosition.y, + z: this.currentObjectPosition.z + } + + } + + + //visualizations + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + } + if (USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); + } + if (USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.handleDistantParticleBeam(handPosition, grabbedProperties.position, INTERSECT_COLOR) + // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) + } + if (USE_POINTLIGHT === true) { + this.handlePointLight(this.grabbedEntity); + } + if (USE_SPOTLIGHT === true) { + this.handleSpotlight(this.grabbedEntity); } Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, + targetPosition: targetPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, ttl: ACTION_TTL }); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + }; - this.nearGrabbing = function() { - var now = Date.now(); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + this.projectVectorAlongAxis = function(position, axisStart, axisEnd) { - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } + var aPrime = Vec3.subtract(position, axisStart); - this.lineOff(); - this.overlayLineOff(); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { - Entities.editEntity(this.grabbedEntity, { - collisionsWillMove: false + var bPrime = Vec3.subtract(axisEnd, axisStart); + + + var bPrimeMagnitude = Vec3.length(bPrime); + + var dotProduct = Vec3.dot(aPrime, bPrime); + + + var scalar = dotProduct / bPrimeMagnitude; + + if (scalar < 0) { + scalar = 0; + } + + if (scalar > 1) { + scalar = 1; + } + + var projection = Vec3.sum(axisStart, Vec3.multiply(scalar, Vec3.normalize(bPrime))); + + return projection + + }, + + this.nearGrabbing = function() { + var now = Date.now(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + this.lineOff(); + this.overlayLineOff(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { + Entities.editEntity(this.grabbedEntity, { + collisionsWillMove: false + }); + } + + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { + // if an object is "equipped" and has a spatialKey, use it. + this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + } else { + this.ignoreIK = false; + + var objectRotation = grabbedProperties.rotation; + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("hold", this.grabbedEntity, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK }); - } - - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { - // if an object is "equipped" and has a spatialKey, use it. - this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - } else { - this.ignoreIK = false; - - var objectRotation = grabbedProperties.rotation; - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("hold", this.grabbedEntity, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } else { - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - if (this.state == STATE_NEAR_GRABBING) { - this.setState(STATE_CONTINUE_NEAR_GRABBING); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; } else { - // equipping - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - this.startHandGrasp(); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + if (this.state == STATE_NEAR_GRABBING) { + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else { + // equipping + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + this.startHandGrasp(); + + this.setState(STATE_CONTINUE_EQUIP_BD); + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - this.setState(STATE_CONTINUE_EQUIP_BD); } - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } + this.currentHandControllerTipPosition = + (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - - } - - this.currentHandControllerTipPosition = - (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - - this.currentObjectTime = Date.now(); - }; + this.currentObjectTime = Date.now(); + }; this.continueNearGrabbing = function() { if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { @@ -984,13 +1377,12 @@ function MyController(hand) { Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); Entities.callEntityMethod(this.grabbedEntity, "unequip"); this.endHandGrasp(); - } }; this.pullTowardEquipPosition = function() { - this.lineOff(); - this.overlayLineOff(); + + this.turnOffVisualizations(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); @@ -1108,11 +1500,15 @@ function MyController(hand) { } } - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); }; _this.allTouchedIDs = {}; + this.touchTest = function() { var maxDistance = 0.05; var leftHandPosition = MyAvatar.getLeftPalmPosition(); @@ -1181,11 +1577,39 @@ function MyController(hand) { this.release = function() { - this.lineOff(); - this.overlayLineOff(); + this.turnLightsOff(); + this.turnOffVisualizations(); + if (this.grabbedEntity !== null) { if (this.actionID !== null) { - Entities.deleteAction(this.grabbedEntity, this.actionID); + //add velocity whatnot + var defaultReleaseVelocityData = { + disableReleaseVelocity: false + }; + //sometimes we want things to stay right where they are when we let go. + var releaseVelocityData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultReleaseVelocityData); + if (releaseVelocityData.disableReleaseVelocity === true) { + Entities.deleteAction(this.grabbedEntity, this.actionID); + + Entities.editEntity(this.grabbedEntity, { + velocity: { + x: 0, + y: 0, + z: 0 + }, + angularVelocity: { + x: 0, + y: 0, + z: 0 + } + }) + Entities.deleteAction(this.grabbedEntity, this.actionID); + + } else { + //don't make adjustments + Entities.deleteAction(this.grabbedEntity, this.actionID); + + } } } @@ -1199,6 +1623,9 @@ function MyController(hand) { this.cleanup = function() { this.release(); this.endHandGrasp(); + Entities.deleteEntity(this.particleBeam); + Entities.deleteEntity(this.spotLight); + Entities.deleteEntity(this.pointLight); }; this.activateEntity = function(entityID, grabbedProperties) { @@ -1269,27 +1696,34 @@ function MyController(hand) { } //return an object with our updated settings return result; - } + }; this.graspHandler = null + this.startHandGrasp = function() { if (this.hand === RIGHT_HAND) { this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); } else if (this.hand === LEFT_HAND) { this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); } - } + }; this.endHandGrasp = function() { // Tell the animation system we don't need any more callbacks. MyAvatar.removeAnimationStateHandler(this.graspHandler); - } + }; -} +}; var rightController = new MyController(RIGHT_HAND); var leftController = new MyController(LEFT_HAND); +//preload the particle beams so that they are full length when you start searching +if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + rightController.createParticleBeam(); + leftController.createParticleBeam(); +} + var MAPPING_NAME = "com.highfidelity.handControllerGrab"; var mapping = Controller.newMapping(MAPPING_NAME); @@ -1301,39 +1735,66 @@ mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); Controller.enableMapping(MAPPING_NAME); +//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items var handToDisable = 'none'; function update() { - if (handToDisable !== LEFT_HAND && handToDisable!=='both') { + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { leftController.update(); } - if (handToDisable !== RIGHT_HAND && handToDisable!=='both') { + if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { rightController.update(); } } Messages.subscribe('Hifi-Hand-Disabler'); +Messages.subscribe('Hifi-Hand-Grab'); +Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); -handleHandDisablerMessages = function(channel, message, sender) { - +handleHandMessages = function(channel, message, sender) { if (sender === MyAvatar.sessionUUID) { - if (message === 'left') { - handToDisable = LEFT_HAND; - } - if (message === 'right') { - handToDisable = RIGHT_HAND; - } - if(message==='both'){ - handToDisable='both'; - } - if(message==='none'){ - handToDisable='none'; + if (channel === 'Hifi-Hand-Disabler') { + if (message === 'left') { + handToDisable = LEFT_HAND; + } + if (message === 'right') { + handToDisable = RIGHT_HAND; + } + if (message === 'both' || message === 'none') { + handToDisable = message; + } + } else if (channel === 'Hifi-Hand-Grab') { + try { + var data = JSON.parse(message); + var selectedController = (data.hand === 'left') ? leftController : rightController; + selectedController.release(); + selectedController.setState(STATE_EQUIP); + selectedController.grabbedEntity = data.entityID; + + } catch (e) {} + + } else if (channel === 'Hifi-Hand-RayPick-Blacklist') { + try { + var data = JSON.parse(message); + var action = data.action; + var id = data.id; + var index = blacklist.indexOf(id); + + if (action === 'add' && index === -1) { + blacklist.push(id); + } + if (action === 'remove') { + if (index > -1) { + blacklist.splice(index, 1); + } + } + + } catch (e) {} } } - } -Messages.messageReceived.connect(handleHandDisablerMessages); +Messages.messageReceived.connect(handleHandMessages); function cleanup() { rightController.cleanup(); @@ -1342,4 +1803,4 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -Script.update.connect(update); +Script.update.connect(update); \ No newline at end of file diff --git a/examples/controllers/philipsVersion.js b/examples/controllers/philipsVersion.js new file mode 100644 index 0000000000..4ae617cf0b --- /dev/null +++ b/examples/controllers/philipsVersion.js @@ -0,0 +1,87 @@ +// +// reticleTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/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 +// + +function length(posA, posB) { + var dx = posA.x - posB.x; + var dy = posA.y - posB.y; + var length = Math.sqrt((dx*dx) + (dy*dy)) + return length; +} + +var PITCH_DEADZONE = 1.0; +var PITCH_MAX = 20.0; +var YAW_DEADZONE = 1.0; +var YAW_MAX = 20.0; +var PITCH_SCALING = 10.0; +var YAW_SCALING = 10.0; + +var EXPECTED_CHANGE = 50; +var lastPos = Controller.getReticlePosition(); +function moveReticle(dY, dX) { + var globalPos = Controller.getReticlePosition(); + + // some debugging to see if position is jumping around on us... + var distanceSinceLastMove = length(lastPos, globalPos); + if (distanceSinceLastMove > EXPECTED_CHANGE) { + print("distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); + } + + if (Math.abs(dX) > EXPECTED_CHANGE) { + print("UNEXPECTED dX:" + dX + "----------------------------"); + dX = 0; + } + if (Math.abs(dY) > EXPECTED_CHANGE) { + print("UNEXPECTED dY:" + dY + "----------------------------"); + dY = 0; + } + + globalPos.x += dX; + globalPos.y += dY; + Controller.setReticlePosition(globalPos); + lastPos = globalPos; +} + + +var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; +var mapping = Controller.newMapping(MAPPING_NAME); + +var lastHandPitch = 0; +var lastHandYaw = 0; + +mapping.from(Controller.Standard.LeftHand).peek().to(function(pose) { + var handEulers = Quat.safeEulerAngles(pose.rotation); + //Vec3.print("handEulers:", handEulers); + + var handPitch = handEulers.y; + var handYaw = handEulers.x; + var changePitch = (handPitch - lastHandPitch) * PITCH_SCALING; + var changeYaw = (handYaw - lastHandYaw) * YAW_SCALING; + if (Math.abs(changePitch) > PITCH_MAX) { + print("Pitch: " + changePitch); + changePitch = 0; + } + if (Math.abs(changeYaw) > YAW_MAX) { + print("Yaw: " + changeYaw); + changeYaw = 0; + } + changePitch = Math.abs(changePitch) < PITCH_DEADZONE ? 0 : changePitch; + changeYaw = Math.abs(changeYaw) < YAW_DEADZONE ? 0 : changeYaw; + moveReticle(changePitch, changeYaw); + lastHandPitch = handPitch; + lastHandYaw = handYaw; + +}); +mapping.enable(); + + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); diff --git a/examples/controllers/proceduralHandPoseExample.js b/examples/controllers/proceduralHandPoseExample.js new file mode 100644 index 0000000000..8b735f579f --- /dev/null +++ b/examples/controllers/proceduralHandPoseExample.js @@ -0,0 +1,76 @@ +// +// proceduralHandPoseExample.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/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 +// + + +var MAPPING_NAME = "com.highfidelity.examples.proceduralHandPose"; +var mapping = Controller.newMapping(MAPPING_NAME); +var translation = { x: 0, y: 0.1, z: 0 }; +var translationDx = 0.01; +var translationDy = 0.01; +var translationDz = -0.01; +var TRANSLATION_LIMIT = 0.5; + +var pitch = 45; +var yaw = 0; +var roll = 45; +var pitchDelta = 1; +var yawDelta = -1; +var rollDelta = 1; +var ROTATION_MIN = -90; +var ROTATION_MAX = 90; + +mapping.from(function() { + + // adjust the hand translation in a periodic back and forth motion for each of the 3 axes + translation.x = translation.x + translationDx; + translation.y = translation.y + translationDy; + translation.z = translation.z + translationDz; + if ((translation.x > TRANSLATION_LIMIT) || (translation.x < (-1 * TRANSLATION_LIMIT))) { + translationDx = translationDx * -1; + } + if ((translation.y > TRANSLATION_LIMIT) || (translation.y < (-1 * TRANSLATION_LIMIT))) { + translationDy = translationDy * -1; + } + if ((translation.z > TRANSLATION_LIMIT) || (translation.z < (-1 * TRANSLATION_LIMIT))) { + translationDz = translationDz * -1; + } + + // adjust the hand rotation in a periodic back and forth motion for each of pitch/yaw/roll + pitch = pitch + pitchDelta; + yaw = yaw + yawDelta; + roll = roll + rollDelta; + if ((pitch > ROTATION_MAX) || (pitch < ROTATION_MIN)) { + pitchDelta = pitchDelta * -1; + } + if ((yaw > ROTATION_MAX) || (yaw < ROTATION_MIN)) { + yawDelta = yawDelta * -1; + } + if ((roll > ROTATION_MAX) || (roll < ROTATION_MIN)) { + rollDelta = rollDelta * -1; + } + + var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll); + + var pose = { + translation: translation, + rotation: rotation, + velocity: { x: 0, y: 0, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0 } + }; + return pose; +}).debug(true).to(Controller.Standard.LeftHand); + +Controller.enableMapping(MAPPING_NAME); + + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); diff --git a/examples/controllers/reticleHandAngularVelocityTest.js b/examples/controllers/reticleHandAngularVelocityTest.js new file mode 100644 index 0000000000..94288b7bfb --- /dev/null +++ b/examples/controllers/reticleHandAngularVelocityTest.js @@ -0,0 +1,121 @@ +// +// reticleHandAngularVelocityTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/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 +// + +// If you set this to true, you will get the raw instantaneous angular velocity. +// note: there is a LOT of noise in the hydra rotation, you will probably be very +// frustrated with the level of jitter. +var USE_INSTANTANEOUS_ANGULAR_VELOCITY = false; +var whichHand = Controller.Standard.RightHand; +var whichTrigger = Controller.Standard.RT; + + + +function msecTimestampNow() { + var d = new Date(); + return d.getTime(); +} + +function length(posA, posB) { + var dx = posA.x - posB.x; + var dy = posA.y - posB.y; + var length = Math.sqrt((dx*dx) + (dy*dy)) + return length; +} + +var EXPECTED_CHANGE = 50; +var lastPos = Controller.getReticlePosition(); +function moveReticle(dX, dY) { + var globalPos = Controller.getReticlePosition(); + + // some debugging to see if position is jumping around on us... + var distanceSinceLastMove = length(lastPos, globalPos); + if (distanceSinceLastMove > EXPECTED_CHANGE) { + print("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); + } + + if (Math.abs(dX) > EXPECTED_CHANGE) { + print("surpressing unexpectedly large change dX:" + dX + "----------------------------"); + dX = 0; + } + if (Math.abs(dY) > EXPECTED_CHANGE) { + print("surpressing unexpectedly large change dY:" + dY + "----------------------------"); + dY = 0; + } + + globalPos.x += dX; + globalPos.y += dY; + Controller.setReticlePosition(globalPos); + lastPos = globalPos; +} + +var firstTime = true; +var lastTime = msecTimestampNow(); +var previousRotation; + +var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from(whichTrigger).peek().constrainToInteger().to(Controller.Actions.ReticleClick); +mapping.from(whichHand).peek().to(function(pose) { + + var MSECS_PER_SECOND = 1000; + var now = msecTimestampNow(); + var deltaMsecs = (now - lastTime); + var deltaTime = deltaMsecs / MSECS_PER_SECOND; + + if (firstTime) { + previousRotation = pose.rotation; + lastTime = msecTimestampNow(); + firstTime = false; + } + + // pose.angularVelocity - is the angularVelocity in a "physics" sense, that + // means the direction of the vector is the axis of symetry of rotation + // and the scale of the vector is the speed in radians/second of rotation + // around that axis. + // + // we want to deconstruct that in the portion of the rotation on the Y axis + // and make that portion move our reticle in the horizontal/X direction + // and the portion of the rotation on the X axis and make that portion + // move our reticle in the veritcle/Y direction + var xPart = -pose.angularVelocity.y; + var yPart = -pose.angularVelocity.x; + + // pose.angularVelocity is "smoothed", we can calculate our own instantaneous + // angular velocity as such: + if (USE_INSTANTANEOUS_ANGULAR_VELOCITY) { + var previousConjugate = Quat.conjugate(previousRotation); + var deltaRotation = Quat.multiply(pose.rotation, previousConjugate); + var normalizedDeltaRotation = Quat.normalize(deltaRotation); + var axis = Quat.axis(normalizedDeltaRotation); + var speed = Quat.angle(normalizedDeltaRotation) / deltaTime; + var instantaneousAngularVelocity = Vec3.multiply(speed, axis); + + xPart = -instantaneousAngularVelocity.y; + yPart = -instantaneousAngularVelocity.x; + + previousRotation = pose.rotation; + } + + var MOVE_SCALE = 1; + lastTime = now; + + var dX = (xPart * MOVE_SCALE) / deltaTime; + var dY = (yPart * MOVE_SCALE) / deltaTime; + + moveReticle(dX, dY); +}); +mapping.enable(); + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); + + diff --git a/examples/controllers/reticleHandRotationTest.js b/examples/controllers/reticleHandRotationTest.js new file mode 100644 index 0000000000..ece9283deb --- /dev/null +++ b/examples/controllers/reticleHandRotationTest.js @@ -0,0 +1,254 @@ +// +// reticleHandRotationTest.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2015/12/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 +// + +var DEBUGGING = false; + +Math.clamp=function(a,b,c) { + return Math.max(b,Math.min(c,a)); +} + +function length(posA, posB) { + var dx = posA.x - posB.x; + var dy = posA.y - posB.y; + var length = Math.sqrt((dx*dx) + (dy*dy)) + return length; +} + +var EXPECTED_CHANGE = 50; +var lastPos = Controller.getReticlePosition(); +function moveReticleAbsolute(x, y) { + var globalPos = Controller.getReticlePosition(); + var dX = x - globalPos.x; + var dY = y - globalPos.y; + + // some debugging to see if position is jumping around on us... + var distanceSinceLastMove = length(lastPos, globalPos); + if (distanceSinceLastMove > EXPECTED_CHANGE) { + debugPrint("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); + } + + if (Math.abs(dX) > EXPECTED_CHANGE) { + debugPrint("surpressing unexpectedly large change dX:" + dX + "----------------------------"); + } + if (Math.abs(dY) > EXPECTED_CHANGE) { + debugPrint("surpressing unexpectedly large change dY:" + dY + "----------------------------"); + } + + globalPos.x = x; + globalPos.y = y; + Controller.setReticlePosition(globalPos); + lastPos = globalPos; +} + + +var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from(Controller.Standard.LT).peek().constrainToInteger().to(Controller.Actions.ReticleClick); +mapping.from(Controller.Standard.RT).peek().constrainToInteger().to(Controller.Actions.ReticleClick); +mapping.enable(); + + +var lastRotatedLeft = Vec3.UNIT_NEG_Y; +var lastRotatedRight = Vec3.UNIT_NEG_Y; + +function debugPrint(message) { + if (DEBUGGING) { + print(message); + } +} + +var MAX_WAKE_UP_DISTANCE = 0.005; +var MIN_WAKE_UP_DISTANCE = 0.001; +var INITIAL_WAKE_UP_DISTANCE = MIN_WAKE_UP_DISTANCE; +var INCREMENTAL_WAKE_UP_DISTANCE = 0.001; + +var MAX_SLEEP_DISTANCE = 0.0004; +var MIN_SLEEP_DISTANCE = 0.00001; //0.00002; +var INITIAL_SLEEP_DISTANCE = MIN_SLEEP_DISTANCE; +var INCREMENTAL_SLEEP_DISTANCE = 0.000002; // 0.00002; + +var leftAsleep = true; +var rightAsleep = true; + +var leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; +var rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + +var leftSleepDistance = INITIAL_SLEEP_DISTANCE; +var rightSleepDistance = INITIAL_SLEEP_DISTANCE; + +Script.update.connect(function(deltaTime) { + + var poseRight = Controller.getPoseValue(Controller.Standard.RightHand); + var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand); + + // NOTE: hack for now + var screenSizeX = 1920; + var screenSizeY = 1080; + + var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y); + var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y); + + var suppressRight = false; + var suppressLeft = false; + + // What I really want to do is to slowly increase the epsilon you have to move it + // to wake up, the longer you go without moving it + var leftDistance = Vec3.distance(rotatedLeft, lastRotatedLeft); + var rightDistance = Vec3.distance(rotatedRight, lastRotatedRight); + + // check to see if hand should wakeup or sleep + if (leftAsleep) { + if (leftDistance > leftWakeUpDistance) { + leftAsleep = false; + leftSleepDistance = INITIAL_SLEEP_DISTANCE; + leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + } else { + // grow the wake up distance to make it harder to wake up + leftWakeUpDistance = Math.min(leftWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE); + } + } else { + // we are awake, determine if we should fall asleep, if we haven't moved + // at least as much as our sleep distance then we sleep + if (leftDistance < leftSleepDistance) { + leftAsleep = true; + leftSleepDistance = INITIAL_SLEEP_DISTANCE; + leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + } else { + // if we moved more than the sleep amount, but we moved less than the max sleep + // amount, then increase our liklihood of sleep. + if (leftDistance < MAX_SLEEP_DISTANCE) { + print("growing sleep...."); + leftSleepDistance = Math.max(leftSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE); + } else { + // otherwise reset it to initial + leftSleepDistance = INITIAL_SLEEP_DISTANCE; + } + } + } + if (leftAsleep) { + suppressLeft = true; + debugPrint("suppressing left not moving enough"); + } + + // check to see if hand should wakeup or sleep + if (rightAsleep) { + if (rightDistance > rightWakeUpDistance) { + rightAsleep = false; + rightSleepDistance = INITIAL_SLEEP_DISTANCE; + rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + } else { + // grow the wake up distance to make it harder to wake up + rightWakeUpDistance = Math.min(rightWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE); + } + } else { + // we are awake, determine if we should fall asleep, if we haven't moved + // at least as much as our sleep distance then we sleep + if (rightDistance < rightSleepDistance) { + rightAsleep = true; + rightSleepDistance = INITIAL_SLEEP_DISTANCE; + rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + } else { + // if we moved more than the sleep amount, but we moved less than the max sleep + // amount, then increase our liklihood of sleep. + if (rightDistance < MAX_SLEEP_DISTANCE) { + print("growing sleep...."); + rightSleepDistance = Math.max(rightSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE); + } else { + // otherwise reset it to initial + rightSleepDistance = INITIAL_SLEEP_DISTANCE; + } + } + } + if (rightAsleep) { + suppressRight = true; + debugPrint("suppressing right not moving enough"); + } + + // check to see if hand is on base station + if (Vec3.equal(rotatedLeft, Vec3.UNIT_NEG_Y)) { + suppressLeft = true; + debugPrint("suppressing left on base station"); + } + if (Vec3.equal(rotatedRight, Vec3.UNIT_NEG_Y)) { + suppressRight = true; + debugPrint("suppressing right on base station"); + } + + // Keep track of last rotations, to detect resting (but not on base station hands) in the future + lastRotatedLeft = rotatedLeft; + lastRotatedRight = rotatedRight; + + if (suppressLeft && suppressRight) { + debugPrint("both hands suppressed bail out early"); + return; + } + + if (suppressLeft) { + debugPrint("right only"); + rotatedLeft = rotatedRight; + } + if (suppressRight) { + debugPrint("left only"); + rotatedRight = rotatedLeft; + } + + // Average the two hand positions, if either hand is on base station, the + // other hand becomes the only used hand and the average is the hand in use + var rotated = Vec3.multiply(Vec3.sum(rotatedRight,rotatedLeft), 0.5); + + if (DEBUGGING) { + Vec3.print("rotatedRight:", rotatedRight); + Vec3.print("rotatedLeft:", rotatedLeft); + Vec3.print("rotated:", rotated); + } + + var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again... + var absoluteYaw = -rotated.x; // from -1 left to 1 right + + if (DEBUGGING) { + print("absolutePitch:" + absolutePitch); + print("absoluteYaw:" + absoluteYaw); + Vec3.print("rotated:", rotated); + } + + var ROTATION_BOUND = 0.6; + var clampYaw = Math.clamp(absoluteYaw, -ROTATION_BOUND, ROTATION_BOUND); + var clampPitch = Math.clamp(absolutePitch, -ROTATION_BOUND, ROTATION_BOUND); + if (DEBUGGING) { + print("clampYaw:" + clampYaw); + print("clampPitch:" + clampPitch); + } + + // using only from -ROTATION_BOUND to ROTATION_BOUND + var xRatio = (clampYaw + ROTATION_BOUND) / (2 * ROTATION_BOUND); + var yRatio = (clampPitch + ROTATION_BOUND) / (2 * ROTATION_BOUND); + + if (DEBUGGING) { + print("xRatio:" + xRatio); + print("yRatio:" + yRatio); + } + + var x = screenSizeX * xRatio; + var y = screenSizeY * yRatio; + + if (DEBUGGING) { + print("position x:" + x + " y:" + y); + } + if (!(xRatio == 0.5 && yRatio == 0)) { + moveReticleAbsolute(x, y); + } +}); + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); + + diff --git a/examples/controllers/reticleTests.js b/examples/controllers/reticleTests.js index 4c805c8b1c..56b2ba413a 100644 --- a/examples/controllers/reticleTests.js +++ b/examples/controllers/reticleTests.js @@ -33,7 +33,6 @@ var mappingJSON = { mapping = Controller.parseMapping(JSON.stringify(mappingJSON)); mapping.enable(); - Script.scriptEnding.connect(function(){ mapping.disable(); }); diff --git a/examples/directory.js b/examples/directory.js index b1fac19e8b..d2a3768051 100644 --- a/examples/directory.js +++ b/examples/directory.js @@ -62,7 +62,7 @@ var directory = (function () { function setUp() { viewport = Controller.getViewportDimensions(); - directoryWindow = new WebWindow('Directory', DIRECTORY_URL, 900, 700, false); + directoryWindow = new OverlayWebWindow('Directory', DIRECTORY_URL, 900, 700, false); directoryWindow.setVisible(false); directoryButton = Overlays.addOverlay("image", { diff --git a/examples/edit.js b/examples/edit.js index 59b6ae3e7d..074b43c8c1 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -140,9 +140,33 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", { }); var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false); +var marketplaceWindow = new OverlayWebWindow('Marketplace', "about:blank", 900, 700, false); marketplaceWindow.setVisible(false); +function showMarketplace(marketplaceID) { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + print("setting marketplace URL to " + url); + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); + marketplaceWindow.raise(); +} + +function hideMarketplace() { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); +} + +function toggleMarketplace() { + if (marketplaceWindow.visible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + var toolBar = (function() { var that = {}, toolBar, @@ -413,12 +437,9 @@ var toolBar = (function() { newModelButtonDown = true; return true; } + if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { - if (marketplaceWindow.url != MARKETPLACE_URL) { - marketplaceWindow.setURL(MARKETPLACE_URL); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + toggleMarketplace(); return true; } @@ -1336,6 +1357,7 @@ function getPositionToCreateEntity() { } function importSVO(importURL) { + print("Import URL requested: " + importURL) if (!Entities.canAdjustLocks()) { Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; @@ -1574,11 +1596,7 @@ PropertiesTool = function(opts) { pushCommandForSelections(); selectionManager._update(); } else if (data.type == "showMarketplace") { - if (marketplaceWindow.url != data.url) { - marketplaceWindow.setURL(data.url); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + showMarketplace(); } else if (data.type == "action") { if (data.action == "moveSelectionToGrid") { if (selectionManager.hasSelection()) { @@ -1859,12 +1877,7 @@ var propertyMenu = PopupMenu(); propertyMenu.onSelectMenuItem = function(name) { if (propertyMenu.marketplaceID) { - var url = MARKETPLACE_URL + "/items/" + propertyMenu.marketplaceID; - if (marketplaceWindow.url != url) { - marketplaceWindow.setURL(url); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + showMarketplace(propertyMenu.marketplaceID); } }; diff --git a/examples/example/entities/particlesTest.js b/examples/example/entities/particlesTest.js index 5f95c348b2..e7f88ce468 100644 --- a/examples/example/entities/particlesTest.js +++ b/examples/example/entities/particlesTest.js @@ -60,7 +60,7 @@ }); break; case 3: - print("Radius spread"); + print("Radius spread - temporarily not working"); Entities.editEntity(particles, { accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 }, radiusSpread: 0.035 @@ -71,6 +71,7 @@ Entities.editEntity(particles, { radiusSpread: 0.0, radiusStart: 0.0, + particleRadius: 2 * PARTICLE_RADIUS, // Bezier interpolation used means that middle value isn't intersected radiusFinish: 0.0 }); break; @@ -78,12 +79,13 @@ print("Alpha 0.5"); Entities.editEntity(particles, { radiusStart: PARTICLE_RADIUS, + particleRadius: PARTICLE_RADIUS, radiusFinish: PARTICLE_RADIUS, alpha: 0.5 }); break; case 6: - print("Alpha spread"); + print("Alpha spread - temporarily not working"); Entities.editEntity(particles, { alpha: 0.5, alphaSpread: 0.5 @@ -99,7 +101,7 @@ }); break; case 8: - print("Color spread"); + print("Color spread - temporarily not working"); Entities.editEntity(particles, { alpha: 1.0, alphaStart: 1.0, @@ -255,7 +257,6 @@ textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", color: { red: 255, green: 255, blue: 255 }, lifespan: 5.0, - visible: false, locked: false, isEmitting: false, lifetime: 3600 // 1 hour; just in case diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js new file mode 100644 index 0000000000..12ef2df48a --- /dev/null +++ b/examples/flowArts/arcBall/arcBall.js @@ -0,0 +1,145 @@ +// +// arcBall.js +// examples/arcBall +// +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creats a particle light ball which makes particle trails as you move it. +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/utils.js"); + + +var scriptURL = Script.resolvePath("arcBallEntityScript.js?v1" + Math.random()); +ArcBall = function(spawnPosition) { + + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; + + + var containerBall = Entities.addEntity({ + type: "Sphere", + name: "Arc Ball", + script: scriptURL, + position: Vec3.sum(spawnPosition, { + x: 0, + y: .7, + z: 0 + }), + dimensions: { + x: .05, + y: .05, + z: .05 + }, + color: { + red: 100, + green: 10, + blue: 150 + }, + ignoreForCollisions: true, + damping: 0.8, + collisionsWillMove: true, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + // relativePosition: { + // x: 0, + // y: -0.5, + // z: 0.0 + // }, + }, + // invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + name: "ballLight", + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var arcBall = Entities.addEntity({ + type: "ParticleEffect", + parentID: containerBall, + isEmitting: true, + name: "Arc Ball Particle Effect", + colorStart: { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: { + red: 25, + green: 20, + blue: 255 + }, + maxParticles: 100000, + lifespan: 2, + emitRate: 400, + emitSpeed: .1, + lifetime: -1, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 + }, + polarStart: 0, + polarFinish: Math.PI, + azimuthStart: -Math.PI, + azimuthFinish: Math.PI, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + particleRadius: 0.02, + radiusSpread: 0, + radiusStart: 0.03, + radiusFinish: 0.0003, + alpha: 0, + alphaSpread: .5, + alphaStart: 0, + alphaFinish: 0.5, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + emitterShouldTrail: true + }) + + + + function cleanup() { + Entities.deleteEntity(arcBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; +} \ No newline at end of file diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js new file mode 100644 index 0000000000..987ddc7a31 --- /dev/null +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -0,0 +1,155 @@ +// arcBallEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This entity script handles the logic for the arcBall rave toy +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../../libraries/utils.js"); + var _this; + var ArcBall = function() { + _this = this; + this.colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }, { + red: 200, + green: 10, + blue: 10 + }]; + + this.searchRadius = 10; + }; + + ArcBall.prototype = { + isGrabbed: false, + startDistantGrab: function() { + this.searchForNearbyArcBalls(); + }, + + startNearGrab: function() { + this.searchForNearbyArcBalls(); + }, + + searchForNearbyArcBalls: function() { + //Search for nearby balls and create an arc to it if one is found + var position = Entities.getEntityProperties(this.entityID, "position").position + var entities = Entities.findEntities(position, this.searchRadius); + entities.forEach(function(entity) { + var props = Entities.getEntityProperties(entity, ["position", "name"]); + if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { + _this.target = entity; + _this.createBeam(position, props.position); + + } + }); + }, + + createBeam: function(startPosition, endPosition) { + + // Creates particle arc from start position to end position + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; + var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); + + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; + var props = { + type: "ParticleEffect", + name: "Particle Arc", + parentID: this.entityID, + parentJointIndex: -1, + // position: startPosition, + isEmitting: true, + colorStart: color, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 1, + emitRate: 1000, + emitOrientation: emitOrientation, + emitSpeed: 1, + speedSpread: 0.02, + emitDimensions: { + x: .01, + y: .01, + z: .01 + }, + polarStart: 0, + polarFinish: 0, + azimuthStart: 0.02, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: 0, + y: 0, + z: 0 + }, + radiusStart: 0.01, + radiusFinish: 0.005, + radiusSpread: 0.005, + alpha: 0.5, + alphaSpread: 0.1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: true + } + this.particleArc = Entities.addEntity(props); + }, + + updateBeam: function() { + if(!this.target) { + return; + } + var startPosition = Entities.getEntityProperties(this.entityID, "position").position; + var targetPosition = Entities.getEntityProperties(this.target, "position").position; + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; + var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + Entities.editEntity(this.particleArc, { + emitOrientation: emitOrientation + }); + }, + + continueNearGrab: function() { + this.updateBeam(); + }, + + continueDistantGrab: function() { + this.updateBeam(); + }, + + releaseGrab: function() { + Entities.editEntity(this.particleArc, { + isEmitting: false + }); + this.target = null; + }, + + unload: function() { + if (this.particleArc) { + Entities.deleteEntity(this.particleArc); + } + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + }; + return new ArcBall(); +}); \ No newline at end of file diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js new file mode 100644 index 0000000000..faa07c186d --- /dev/null +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -0,0 +1,91 @@ +// +// flowArtsHutSpawner.js +// examples/flowArts +// +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creates a special flow arts hut with a bunch of flow art toys people can go in and play with +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +Script.include("../../libraries/utils.js"); +Script.include("lightBall/lightBall.js"); +Script.include("raveStick/raveStick.js"); +Script.include("lightSaber/lightSaber.js"); +Script.include("arcBall/arcBall.js"); + + + +var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); +basePosition.y = MyAvatar.position.y + 1; + +// RAVE ITEMS +// var lightBall = new LightBall(basePosition); + +var arcBall = new ArcBall(basePosition); +var arcBall2 = new ArcBall(Vec3.sum(basePosition, {x: -1, y: 0, z: 0})); +var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); +var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); + + +var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/RaveRoom.fbx"; + +var roomDimensions = {x: 30.58, y: 15.29, z: 30.58}; + +var raveRoom = Entities.addEntity({ + type: "Model", + name: "Rave Hut Room", + modelURL: modelURL, + position: basePosition, + dimensions:roomDimensions, + visible: true +}); + +var floor = Entities.addEntity({ + type: "Box", + name: "Rave Floor", + position: Vec3.sum(basePosition, {x: 0, y: -1.2, z: 0}), + dimensions: {x: roomDimensions.x, y: 0.6, z: roomDimensions.z}, + color: {red: 50, green: 10, blue: 100}, + shapeType: 'box' +}); + + + +var lightZone = Entities.addEntity({ + type: "Zone", + name: "Rave Hut Zone", + shapeType: 'box', + keyLightIntensity: 0.4, + keyLightColor: { + red: 50, + green: 0, + blue: 50 + }, + keyLightAmbientIntensity: .2, + position: MyAvatar.position, + dimensions: { + x: 100, + y: 100, + z: 100 + } +}); + +function cleanup() { + + Entities.deleteEntity(raveRoom); + Entities.deleteEntity(lightZone); + Entities.deleteEntity(floor); + // lightBall.cleanup(); + arcBall.cleanup(); + arcBall2.cleanup(); + raveStick.cleanup(); + lightSaber.cleanup(); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js new file mode 100644 index 0000000000..b2ed00f326 --- /dev/null +++ b/examples/flowArts/lightBall/lightBall.js @@ -0,0 +1,145 @@ +// +// LightBall.js +// examples/lightBall +// +// Created by Eric Levin on 12/17/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script creats a particle light ball which makes particle trails as you move it. +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/utils.js"); + +LightBall = function(spawnPosition) { + + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; + + + var containerBall = Entities.addEntity({ + type: "Sphere", + name: "containerBall", + position: Vec3.sum(spawnPosition, { + x: 0, + y: 0.5, + z: 0 + }), + dimensions: { + x: 0.1, + y: 0.1, + z: 0.1 + }, + color: { + red: 15, + green: 10, + blue: 150 + }, + collisionsWillMove: true, + // gravity: { + // x: 0, + // y: -0.5, + // z: 0 + // }, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0.1, + z: 0 + } + }, + invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + name: "ballLight", + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var lightBall = Entities.addEntity({ + type: "ParticleEffect", + parentID: containerBall, + isEmitting: true, + name: "particleBall", + colorStart: { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: { + red: 25, + green: 20, + blue: 255 + }, + maxParticles: 100000, + lifespan: 2, + emitRate: 10000, + emitSpeed: 0.1, + lifetime: -1, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 + }, + polarStart: 0, + polarFinish: Math.PI, + azimuthStart: -Math.PI, + azimuthFinish: Math.PI, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: 0.00, + y: 0.00, + z: 0.00 + }, + particleRadius: 0.02, + radiusSpread: 0, + radiusStart: 0.03, + radiusFinish: 0.0003, + alpha: 0, + alphaSpread: 0.5, + alphaStart: 0, + alphaFinish: 0.5, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + emitterShouldTrail: true + }) + + + + function cleanup() { + Entities.deleteEntity(lightBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; +} diff --git a/examples/flowArts/lightSaber/lightSaber.js b/examples/flowArts/lightSaber/lightSaber.js new file mode 100644 index 0000000000..c4ab49e8e6 --- /dev/null +++ b/examples/flowArts/lightSaber/lightSaber.js @@ -0,0 +1,67 @@ +// +// LightSaber.js +// examples +// +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creates a lightsaber which activates on grab +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/utils.js"); +var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/lightSaber.fbx"; +var scriptURL = Script.resolvePath("lightSaberEntityScript.js"); +LightSaber = function(spawnPosition) { + + var saberHandle = Entities.addEntity({ + type: "Model", + name: "LightSaber Handle", + modelURL: modelURL, + position: spawnPosition, + shapeType: 'box', + collisionsWillMove: true, + script: scriptURL, + dimensions: { + x: 0.06, + y: 0.06, + z: 0.31 + }, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0, + z: -0.1 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(180, 90, 0) + }, + invertSolidWhileHeld: true + } + }) + }); + + var light = Entities.addEntity({ + type: 'Light', + name: "raveLight", + parentID: saberHandle, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: {red: 200, green: 10, blue: 200}, + intensity: 5 + }); + + + function cleanup() { + Entities.deleteEntity(saberHandle); + } + + this.cleanup = cleanup; +} diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js new file mode 100644 index 0000000000..a86f471449 --- /dev/null +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -0,0 +1,116 @@ +// lightSaberEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This entity script creates the logic for displaying the lightsaber beam. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../../libraries/utils.js"); + var _this; + // this is the "constructor" for the entity as a JS object we don't do much here + var LightSaber = function() { + _this = this; + this.colorPalette = [{ + red: 0, + green: 200, + blue: 40 + }, { + red: 200, + green: 10, + blue: 40 + }]; + }; + + LightSaber.prototype = { + isGrabbed: false, + + startNearGrab: function() { + Entities.editEntity(this.beam, { + isEmitting: true, + visible: true + }); + }, + + releaseGrab: function() { + Entities.editEntity(this.beam, { + visible: false, + isEmitting: false + }); + }, + + preload: function(entityID) { + this.entityID = entityID; + this.createBeam(); + }, + + unload: function() { + Entities.deleteEntity(this.beam); + }, + + createBeam: function() { + + this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec); + var position = this.props.position; + + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; + var props = { + type: "ParticleEffect", + name: "LightSaber Beam", + position: position, + parentID: this.entityID, + isEmitting: false, + colorStart: color, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 2, + emitRate: 1000, + emitOrientation: forwardQuat, + emitSpeed: 0.7, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 + }, + polarStart: 0, + polarFinish: 0, + azimuthStart: 0.1, + azimuthFinish: 0.01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + radiusStart: 0.03, + adiusFinish: 0.025, + alpha: 0.7, + alphaSpread: 0.1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: false + } + this.beam = Entities.addEntity(props); + + } + }; + // entity scripts always need to return a newly constructed object of our type + return new LightSaber(); +}); diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js new file mode 100644 index 0000000000..bb936d55c2 --- /dev/null +++ b/examples/flowArts/lightTrails.js @@ -0,0 +1,191 @@ +// +// lightTrails.js +// examples +// +// Created by Eric Levin on 5/14/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creates light trails as you move your hydra hands +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../libraries/utils.js"); + +var eraseTrail = true; +var ugLSD = 25; +// var eraseTrail = false; +var LEFT = 0; +var RIGHT = 1; +var MAX_POINTS_PER_LINE = 50; + +var LIFETIME = 6000; +var DRAWING_DEPTH = 0.8; +var LINE_DIMENSIONS = 100; + + +var MIN_POINT_DISTANCE = 0.02; + + +var colorPalette = [{ + red: 250, + green: 137, + blue: 162 +}, { + red: 204, + green: 244, + blue: 249 +}, { + red: 146, + green: 206, + blue: 116 +}, { + red: 240, + green: 87, + blue: 129 +}]; + +var STROKE_WIDTH = 0.04; + +function controller(side, triggerAction) { + this.triggerHeld = false; + this.triggerThreshold = 0.9; + this.side = side; + this.triggerAction = triggerAction; + var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png"; + + this.light = Entities.addEntity({ + type: 'Light', + position: MyAvatar.position, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + this.trail = Entities.addEntity({ + type: "PolyLine", + dimensions: { + x: LINE_DIMENSIONS, + y: LINE_DIMENSIONS, + z: LINE_DIMENSIONS + }, + color: {red: 255, green: 255, blue: 255}, + textures: texture, + lifetime: LIFETIME + }); + this.points = []; + this.normals = []; + this.strokeWidths = []; + var self = this; + + this.trailEraseInterval = Script.setInterval(function() { + if (self.points.length > 0 && eraseTrail) { + self.points.shift(); + self.normals.shift(); + self.strokeWidths.shift(); + Entities.editEntity(self.trail, { + linePoints: self.points, + strokeWidths: self.strokeWidths, + normals: self.normals + }); + } + }, ugLSD); + + + this.setTrailPosition = function(position) { + this.trailPosition = position; + Entities.editEntity(this.trail, { + position: this.trailPosition + }); + } + + + this.update = function(deltaTime) { + this.updateControllerState(); + var newTrailPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH); + var newTrailPos = Vec3.sum(this.palmPosition, newTrailPosOffset); + Entities.editEntity(this.light, { + position: newTrailPos + }); + + + if (!this.drawing) { + this.setTrailPosition(newTrailPos); + this.drawing = true; + } + + if (this.drawing) { + var localPoint = Vec3.subtract(newTrailPos, this.trailPosition); + if (Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { + //Need a minimum distance to avoid binormal NANs + return; + } + + if (this.points.length === MAX_POINTS_PER_LINE) { + this.points.shift(); + this.normals.shift(); + this.strokeWidths.shift(); + } + + this.points.push(localPoint); + var normal = computeNormal(newTrailPos, Camera.getPosition()); + + this.normals.push(normal); + this.strokeWidths.push(STROKE_WIDTH + Math.random() * 0.01); + Entities.editEntity(this.trail, { + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths, + }); + + } + } + + + this.updateControllerState = function() { + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; + this.triggerValue = Controller.getActionValue(this.triggerAction); + + } + + this.cleanup = function() { + Entities.deleteEntity(this.trail); + Entities.deleteEntity(this.light); + Script.clearInterval(this.trailEraseInterval); + } +} + +function computeNormal(p1, p2) { + return Vec3.normalize(Vec3.subtract(p2, p1)); +} + +function update(deltaTime) { + leftController.update(deltaTime); + rightController.update(deltaTime); +} + +function scriptEnding() { + leftController.cleanup(); + rightController.cleanup(); +} + +function vectorIsZero(v) { + return v.x === 0 && v.y === 0 && v.z === 0; +} + + +var rightController = new controller(RIGHT, Controller.findAction("RIGHT_HAND_CLICK")); +var leftController = new controller(LEFT, Controller.findAction("LEFT_HAND_CLICK")); +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + +function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} \ No newline at end of file diff --git a/examples/flowArts/raveStick/raveStick.js b/examples/flowArts/raveStick/raveStick.js new file mode 100644 index 0000000000..5fb019bf97 --- /dev/null +++ b/examples/flowArts/raveStick/raveStick.js @@ -0,0 +1,95 @@ +// +// RaveStick.js +// examples/flowArats/raveStick +// +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script creates a rave stick which makes pretty light trails as you paint +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/utils.js"); +var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/raveStick.fbx"; +var scriptURL = Script.resolvePath("raveStickEntityScript.js"); +RaveStick = function(spawnPosition) { + var colorPalette = [{ + red: 0, + green: 200, + blue: 40 + }, { + red: 200, + green: 10, + blue: 40 + }]; + var stick = Entities.addEntity({ + type: "Model", + name: "raveStick", + modelURL: modelURL, + position: spawnPosition, + shapeType: 'box', + collisionsWillMove: true, + script: scriptURL, + dimensions: { + x: 0.06, + y: 0.06, + z: 0.31 + }, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + rightRelativePosition: { + x: 0.02, + y: 0, + z: 0 + }, + leftRelativePosition: { + x: -0.02, + y: 0, + z: 0 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0) + }, + invertSolidWhileHeld: true + } + }) + }); + + var light = Entities.addEntity({ + type: 'Light', + name: "raveLight", + parentID: stick, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0) + var forwardVec = Quat.getFront(Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + var position = Vec3.sum(spawnPosition, Vec3.multiply(Quat.getFront(rotation), 0.1)); + position.z += 0.1; + position.x += -0.035; + var color = { + red: 0, + green: 200, + blue: 40 + }; + + + + + function cleanup() { + Entities.deleteEntity(stick); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; +} \ No newline at end of file diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js new file mode 100644 index 0000000000..3f571817d2 --- /dev/null +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -0,0 +1,141 @@ +// raveStickEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 12/16/15. +// Copyright 2015 High Fidelity, Inc. +// +// This entity script create light trails on a given object as it moves. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../../libraries/utils.js"); + var _this; + var LIFETIME = 6000; + var DRAWING_DEPTH = 0.8; + var LINE_DIMENSIONS = 100; + var MAX_POINTS_PER_LINE = 50; + var MIN_POINT_DISTANCE = 0.02; + var STROKE_WIDTH = 0.05 + var ugLSD = 35; + var RaveStick = function() { + _this = this; + this.colorPalette = [{ + red: 0, + green: 200, + blue: 40 + }, { + red: 200, + green: 10, + blue: 40 + }]; + var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png"; + this.trail = Entities.addEntity({ + type: "PolyLine", + dimensions: { + x: LINE_DIMENSIONS, + y: LINE_DIMENSIONS, + z: LINE_DIMENSIONS + }, + color: { + red: 255, + green: 255, + blue: 255 + }, + textures: texture, + lifetime: LIFETIME + }); + + this.points = []; + this.normals = []; + this.strokeWidths = []; + }; + + RaveStick.prototype = { + isGrabbed: false, + + startNearGrab: function() { + this.trailBasePosition = Entities.getEntityProperties(this.entityID, "position").position; + Entities.editEntity(this.trail, { + position: this.trailBasePosition + }); + this.points = []; + this.normals = []; + this.strokeWidths = []; + this.setupEraseInterval(); + }, + + continueNearGrab: function() { + var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.04)); + var localPoint = Vec3.subtract(position, this.trailBasePosition); + if (this.points.length >= 1 && Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { + //Need a minimum distance to avoid binormal NANs + return; + } + if (this.points.length === MAX_POINTS_PER_LINE) { + this.points.shift(); + this.normals.shift(); + this.strokeWidths.shift(); + } + + this.points.push(localPoint); + var normal = Quat.getUp(props.rotation); + this.normals.push(normal); + this.strokeWidths.push(STROKE_WIDTH); + Entities.editEntity(this.trail, { + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths + }); + + + }, + + setupEraseInterval: function() { + this.trailEraseInterval = Script.setInterval(function() { + if (_this.points.length > 0) { + _this.points.shift(); + _this.normals.shift(); + _this.strokeWidths.shift(); + Entities.editEntity(_this.trail, { + linePoints: _this.points, + strokeWidths: _this.strokeWidths, + normals: _this.normals + }); + } + }, ugLSD); + }, + + releaseGrab: function() { + if(!this.trailEraseInterval) { + return; + } + Script.setTimeout(function() { + Script.clearInterval(_this.trailEraseInterval); + _this.trailEraseInterval = null; + }, 3000); + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + + unload: function() { + Entities.deleteEntity(this.beam); + Entities.deleteEntity(this.trail); + if (this.trailEraseInterval) { + Script.clearInterval(this.trailEraseInterval); + } + } + }; + return new RaveStick(); + + function computeNormal(p1, p2) { + return Vec3.normalize(Vec3.subtract(p2, p1)); + } +}); \ No newline at end of file diff --git a/examples/html/eventBridgeLoader.js b/examples/html/eventBridgeLoader.js new file mode 100644 index 0000000000..b62e7d9384 --- /dev/null +++ b/examples/html/eventBridgeLoader.js @@ -0,0 +1,59 @@ + +//public slots: +// void emitWebEvent(const QString& data); +// void emitScriptEvent(const QString& data); +// +//signals: +// void webEventReceived(const QString& data); +// void scriptEventReceived(const QString& data); +// + +EventBridgeConnectionProxy = function(parent) { + this.parent = parent; + this.realSignal = this.parent.realBridge.scriptEventReceived + this.webWindowId = this.parent.webWindow.windowId; +} + +EventBridgeConnectionProxy.prototype.connect = function(callback) { + var that = this; + this.realSignal.connect(function(id, message) { + if (id === that.webWindowId) { callback(message); } + }); +} + +EventBridgeProxy = function(webWindow) { + this.webWindow = webWindow; + this.realBridge = this.webWindow.eventBridge; + this.scriptEventReceived = new EventBridgeConnectionProxy(this); +} + +EventBridgeProxy.prototype.emitWebEvent = function(data) { + this.realBridge.emitWebEvent(data); +} + +openEventBridge = function(callback) { + EVENT_BRIDGE_URI = "ws://localhost:51016"; + socket = new WebSocket(this.EVENT_BRIDGE_URI); + + socket.onclose = function() { + console.error("web channel closed"); + }; + + socket.onerror = function(error) { + console.error("web channel error: " + error); + }; + + socket.onopen = function() { + channel = new QWebChannel(socket, function(channel) { + console.log("Document url is " + document.URL); + for(var key in channel.objects){ + console.log("registered object: " + key); + } + var webWindow = channel.objects[document.URL.toLowerCase()]; + console.log("WebWindow is " + webWindow) + eventBridgeProxy = new EventBridgeProxy(webWindow); + if (callback) { callback(eventBridgeProxy); } + }); + } +} + diff --git a/examples/html/qmlWebTest.html b/examples/html/qmlWebTest.html new file mode 100644 index 0000000000..e59535701d --- /dev/null +++ b/examples/html/qmlWebTest.html @@ -0,0 +1,31 @@ + + +Properties + + + + + + + + + + + + + diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 6edbe6844b..94ffb48a71 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -16,23 +16,72 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; SPACE_LOCAL = "local"; SPACE_WORLD = "world"; + SelectionManager = (function() { var that = {}; + function subscribeToUpdateMessages() { + Messages.subscribe('entityToolUpdates'); + Messages.messageReceived.connect(handleEntitySelectionToolUpdates); + } + + function handleEntitySelectionToolUpdates(channel, message, sender) { + if (channel !== 'entityToolUpdates') { + return; + } + if (sender !== MyAvatar.sessionUUID) { + return; + } + + if (message === 'callUpdate') { + that._update(); + } + } + + subscribeToUpdateMessages(); + that.savedProperties = {}; that.selections = []; var listeners = []; that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - that.localPosition = { x: 0, y: 0, z: 0 }; - that.localDimensions = { x: 0, y: 0, z: 0 }; - that.localRegistrationPoint = { x: 0.5, y: 0.5, z: 0.5 }; + that.localPosition = { + x: 0, + y: 0, + z: 0 + }; + that.localDimensions = { + x: 0, + y: 0, + z: 0 + }; + that.localRegistrationPoint = { + x: 0.5, + y: 0.5, + z: 0.5 + }; that.worldRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - that.worldPosition = { x: 0, y: 0, z: 0 }; - that.worldDimensions = { x: 0, y: 0, z: 0 }; - that.worldRegistrationPoint = { x: 0.5, y: 0.5, z: 0.5 }; - that.centerPosition = { x: 0, y: 0, z: 0 }; + that.worldPosition = { + x: 0, + y: 0, + z: 0 + }; + that.worldDimensions = { + x: 0, + y: 0, + z: 0 + }; + that.worldRegistrationPoint = { + x: 0.5, + y: 0.5, + z: 0.5 + }; + that.centerPosition = { + x: 0, + y: 0, + z: 0 + }; that.saveProperties = function() { that.savedProperties = {}; @@ -177,9 +226,9 @@ function getRelativeCenterPosition(dimensions, registrationPoint) { } } -SelectionDisplay = (function () { +SelectionDisplay = (function() { var that = {}; - + var MINIMUM_DIMENSION = 0.001; var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.0075; @@ -194,14 +243,18 @@ SelectionDisplay = (function () { var ROTATE_ARROW_WEST_SOUTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-south.svg"; var showExtendedStretchHandles = false; - + var spaceMode = SPACE_LOCAL; var mode = "UNKNOWN"; var overlayNames = new Array(); var lastCameraPosition = Camera.getPosition(); var lastCameraOrientation = Camera.getOrientation(); - var handleHoverColor = { red: 224, green: 67, blue: 36 }; + var handleHoverColor = { + red: 224, + green: 67, + blue: 36 + }; var handleHoverAlpha = 1.0; var rotateOverlayTargetSize = 10000; // really big target @@ -221,19 +274,27 @@ SelectionDisplay = (function () { var pitchNormal; var rollNormal; var rotationNormal; - + var originalRotation; var originalPitch; var originalYaw; var originalRoll; - - var handleColor = { red: 255, green: 255, blue: 255 }; + + var handleColor = { + red: 255, + green: 255, + blue: 255 + }; var handleAlpha = 0.7; - var highlightedHandleColor = { red: 183, green: 64, blue: 44 }; + var highlightedHandleColor = { + red: 183, + green: 64, + blue: 44 + }; var highlightedHandleAlpha = 0.9; - + var previousHandle = false; var previousHandleColor; var previousHandleAlpha; @@ -242,115 +303,182 @@ SelectionDisplay = (function () { var grabberSizeEdge = 0.015; var grabberSizeFace = 0.025; var grabberAlpha = 1; - var grabberColorCorner = { red: 120, green: 120, blue: 120 }; - var grabberColorEdge = { red: 0, green: 0, blue: 0 }; - var grabberColorFace = { red: 120, green: 120, blue: 120 }; + var grabberColorCorner = { + red: 120, + green: 120, + blue: 120 + }; + var grabberColorEdge = { + red: 0, + green: 0, + blue: 0 + }; + var grabberColorFace = { + red: 120, + green: 120, + blue: 120 + }; var grabberLineWidth = 0.5; var grabberSolid = true; - var grabberMoveUpPosition = { x: 0, y: 0, z: 0 }; + var grabberMoveUpPosition = { + x: 0, + y: 0, + z: 0 + }; - var lightOverlayColor = { red: 255, green: 153, blue: 0 }; + var lightOverlayColor = { + red: 255, + green: 153, + blue: 0 + }; var grabberPropertiesCorner = { - position: { x:0, y: 0, z: 0}, - size: grabberSizeCorner, - color: grabberColorCorner, - alpha: 1, - solid: grabberSolid, - visible: false, - dashed: false, - lineWidth: grabberLineWidth, - drawInFront: true, - borderSize: 1.4, - }; + position: { + x: 0, + y: 0, + z: 0 + }, + size: grabberSizeCorner, + color: grabberColorCorner, + alpha: 1, + solid: grabberSolid, + visible: false, + dashed: false, + lineWidth: grabberLineWidth, + drawInFront: true, + borderSize: 1.4, + }; var grabberPropertiesEdge = { - position: { x:0, y: 0, z: 0}, - size: grabberSizeEdge, - color: grabberColorEdge, - alpha: 1, - solid: grabberSolid, - visible: false, - dashed: false, - lineWidth: grabberLineWidth, - drawInFront: true, - borderSize: 1.4, - }; + position: { + x: 0, + y: 0, + z: 0 + }, + size: grabberSizeEdge, + color: grabberColorEdge, + alpha: 1, + solid: grabberSolid, + visible: false, + dashed: false, + lineWidth: grabberLineWidth, + drawInFront: true, + borderSize: 1.4, + }; var grabberPropertiesFace = { - position: { x:0, y: 0, z: 0}, - size: grabberSizeFace, - color: grabberColorFace, - alpha: 1, - solid: grabberSolid, - visible: false, - dashed: false, - lineWidth: grabberLineWidth, - drawInFront: true, - borderSize: 1.4, - }; - + position: { + x: 0, + y: 0, + z: 0 + }, + size: grabberSizeFace, + color: grabberColorFace, + alpha: 1, + solid: grabberSolid, + visible: false, + dashed: false, + lineWidth: grabberLineWidth, + drawInFront: true, + borderSize: 1.4, + }; + var spotLightLineProperties = { color: lightOverlayColor, lineWidth: 1.5, }; - + var highlightBox = Overlays.addOverlay("cube", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 90, green: 90, blue: 90}, - alpha: 1, - solid: false, - visible: false, - dashed: true, - lineWidth: 2.0, - ignoreRayIntersection: true, // this never ray intersects - drawInFront: true - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 90, + green: 90, + blue: 90 + }, + alpha: 1, + solid: false, + visible: false, + dashed: true, + lineWidth: 2.0, + ignoreRayIntersection: true, // this never ray intersects + drawInFront: true + }); var selectionBox = Overlays.addOverlay("cube", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 255, green: 0, blue: 0}, - alpha: 1, - solid: false, - visible: false, - dashed: false, - lineWidth: 1.0, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 255, + green: 0, + blue: 0 + }, + alpha: 1, + solid: false, + visible: false, + dashed: false, + lineWidth: 1.0, + }); var selectionBoxes = []; var rotationDegreesDisplay = Overlays.addOverlay("text3d", { - position: { x:0, y: 0, z: 0}, - text: "", - color: { red: 0, green: 0, blue: 0}, - backgroundColor: { red: 255, green: 255, blue: 255 }, - alpha: 0.7, - backgroundAlpha: 0.7, - visible: false, - isFacingAvatar: true, - drawInFront: true, - ignoreRayIntersection: true, - dimensions: { x: 0, y: 0 }, - lineHeight: 0.0, - topMargin: 0, - rightMargin: 0, - bottomMargin: 0, - leftMargin: 0, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + text: "", + color: { + red: 0, + green: 0, + blue: 0 + }, + backgroundColor: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 0.7, + backgroundAlpha: 0.7, + visible: false, + isFacingAvatar: true, + drawInFront: true, + ignoreRayIntersection: true, + dimensions: { + x: 0, + y: 0 + }, + lineHeight: 0.0, + topMargin: 0, + rightMargin: 0, + bottomMargin: 0, + leftMargin: 0, + }); var grabberMoveUp = Overlays.addOverlay("image3d", { - url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg", - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: true, - drawInFront: true, - }); + url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg", + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: true, + drawInFront: true, + }); // var normalLine = Overlays.addOverlay("line3d", { // visible: true, @@ -360,7 +488,7 @@ SelectionDisplay = (function () { // color: { red: 255, green: 255, blue: 0 }, // ignoreRayIntersection: true, // }); - + var grabberLBN = Overlays.addOverlay("cube", grabberPropertiesCorner); var grabberRBN = Overlays.addOverlay("cube", grabberPropertiesCorner); var grabberLBF = Overlays.addOverlay("cube", grabberPropertiesCorner); @@ -481,171 +609,324 @@ SelectionDisplay = (function () { ]; - var baseOverlayAngles = { x: 0, y: 0, z: 0 }; + var baseOverlayAngles = { + x: 0, + y: 0, + z: 0 + }; var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles); var baseOfEntityProjectionOverlay = Overlays.addOverlay("rectangle3d", { - position: { x: 1, y: 0, z: 0}, - color: { red: 51, green: 152, blue: 203 }, - alpha: 0.5, - solid: true, - visible: false, - width: 300, height: 200, - rotation: baseOverlayRotation, - ignoreRayIntersection: true, // always ignore this - }); + position: { + x: 1, + y: 0, + z: 0 + }, + color: { + red: 51, + green: 152, + blue: 203 + }, + alpha: 0.5, + solid: true, + visible: false, + width: 300, + height: 200, + rotation: baseOverlayRotation, + ignoreRayIntersection: true, // always ignore this + }); - var yawOverlayAngles = { x: 90, y: 0, z: 0 }; + var yawOverlayAngles = { + x: 90, + y: 0, + z: 0 + }; var yawOverlayRotation = Quat.fromVec3Degrees(yawOverlayAngles); - var pitchOverlayAngles = { x: 0, y: 90, z: 0 }; + var pitchOverlayAngles = { + x: 0, + y: 90, + z: 0 + }; var pitchOverlayRotation = Quat.fromVec3Degrees(pitchOverlayAngles); - var rollOverlayAngles = { x: 0, y: 180, z: 0 }; + var rollOverlayAngles = { + x: 0, + y: 180, + z: 0 + }; var rollOverlayRotation = Quat.fromVec3Degrees(rollOverlayAngles); var xRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 1.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - visible: false, - }); + visible: false, + lineWidth: 1.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 255, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); var yRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 1.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 0, green: 255, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - visible: false, - }); + visible: false, + lineWidth: 1.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 0, + green: 255, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); var zRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 1.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 0, green: 0, blue: 255 }, - ignoreRayIntersection: true, // always ignore this - visible: false, - }); + visible: false, + lineWidth: 1.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 0, + green: 0, + blue: 255 + }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); var rotateZeroOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 2.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); + visible: false, + lineWidth: 2.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 255, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateCurrentOverlay = Overlays.addOverlay("line3d", { - visible: false, - lineWidth: 2.0, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 0, green: 0, blue: 255 }, - ignoreRayIntersection: true, // always ignore this - }); + visible: false, + lineWidth: 2.0, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 0, + green: 0, + blue: 255 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateOverlayTarget = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: rotateOverlayTargetSize, - color: { red: 0, green: 0, blue: 0 }, - alpha: 0.0, - solid: true, - visible: false, - rotation: yawOverlayRotation, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: rotateOverlayTargetSize, + color: { + red: 0, + green: 0, + blue: 0 + }, + alpha: 0.0, + solid: true, + visible: false, + rotation: yawOverlayRotation, + }); var rotateOverlayInner = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 51, green: 152, blue: 203 }, - alpha: 0.2, - solid: true, - visible: false, - rotation: yawOverlayRotation, - hasTickMarks: true, - majorTickMarksAngle: innerSnapAngle, - minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, - minorTickMarksLength: 0, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 51, + green: 152, + blue: 203 + }, + alpha: 0.2, + solid: true, + visible: false, + rotation: yawOverlayRotation, + hasTickMarks: true, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + majorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + minorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateOverlayOuter = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 51, green: 152, blue: 203 }, - alpha: 0.2, - solid: true, - visible: false, - rotation: yawOverlayRotation, + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 51, + green: 152, + blue: 203 + }, + alpha: 0.2, + solid: true, + visible: false, + rotation: yawOverlayRotation, - hasTickMarks: true, - majorTickMarksAngle: 45.0, - minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, - minorTickMarksLength: 0.1, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); + hasTickMarks: true, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + majorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + minorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + ignoreRayIntersection: true, // always ignore this + }); var rotateOverlayCurrent = Overlays.addOverlay("circle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, - color: { red: 224, green: 67, blue: 36}, - alpha: 0.8, - solid: true, - visible: false, - rotation: yawOverlayRotation, - ignoreRayIntersection: true, // always ignore this - hasTickMarks: true, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - }); + position: { + x: 0, + y: 0, + z: 0 + }, + size: 1, + color: { + red: 224, + green: 67, + blue: 36 + }, + alpha: 0.8, + solid: true, + visible: false, + rotation: yawOverlayRotation, + ignoreRayIntersection: true, // always ignore this + hasTickMarks: true, + majorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + minorTickMarksColor: { + red: 0, + green: 0, + blue: 0 + }, + }); var yawHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true, - }); + url: ROTATE_ARROW_WEST_NORTH_URL, + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: false, + drawInFront: true, + }); var pitchHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true, - }); + url: ROTATE_ARROW_WEST_NORTH_URL, + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: false, + drawInFront: true, + }); var rollHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: { x:0, y: 0, z: 0}, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true, - }); + url: ROTATE_ARROW_WEST_NORTH_URL, + position: { + x: 0, + y: 0, + z: 0 + }, + color: handleColor, + alpha: handleAlpha, + visible: false, + size: 0.1, + scale: 0.1, + isFacingAvatar: false, + drawInFront: true, + }); var allOverlays = [ highlightBox, @@ -703,7 +984,7 @@ SelectionDisplay = (function () { overlayNames[grabberEdgeNL] = "grabberEdgeNL"; overlayNames[grabberEdgeFR] = "grabberEdgeFR"; overlayNames[grabberEdgeFL] = "grabberEdgeFL"; - + overlayNames[yawHandle] = "yawHandle"; overlayNames[pitchHandle] = "pitchHandle"; overlayNames[rollHandle] = "rollHandle"; @@ -718,6 +999,7 @@ SelectionDisplay = (function () { var activeTool = null; var grabberTools = {}; + function addGrabberTool(overlay, tool) { grabberTools[overlay] = { mode: tool.mode, @@ -727,8 +1009,8 @@ SelectionDisplay = (function () { } } - - that.cleanup = function () { + + that.cleanup = function() { for (var i = 0; i < allOverlays.length; i++) { Overlays.deleteOverlay(allOverlays[i]); } @@ -741,19 +1023,21 @@ SelectionDisplay = (function () { var properties = Entities.getEntityProperties(entityID); Overlays.editOverlay(highlightBox, { visible: true, - position: properties.boundingBox.center, + position: properties.boundingBox.center, dimensions: properties.dimensions, rotation: properties.rotation }); }; that.unhighlightSelectable = function(entityID) { - Overlays.editOverlay(highlightBox,{ visible: false}); + Overlays.editOverlay(highlightBox, { + visible: false + }); }; that.select = function(entityID, event) { var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - + lastCameraPosition = Camera.getPosition(); lastCameraOrientation = Camera.getOrientation(); @@ -766,12 +1050,16 @@ SelectionDisplay = (function () { print(" event.y:" + event.y); Vec3.print(" current position:", properties.position); } - - + + } - Entities.editEntity(entityID, { localRenderAlpha: 0.1 }); - Overlays.editOverlay(highlightBox, { visible: false }); + Entities.editEntity(entityID, { + localRenderAlpha: 0.1 + }); + Overlays.editOverlay(highlightBox, { + visible: false + }); that.updateHandles(); } @@ -789,7 +1077,7 @@ SelectionDisplay = (function () { } var rotateHandleOffset = 0.05; - + var top, far, left, bottom, near, right, boundsCenter, objectCenter, BLN, BRN, BLF, TLN, TRN, TLF, TRF; var dimensions, rotation; @@ -828,136 +1116,320 @@ SelectionDisplay = (function () { * ------------------------------*/ - + var cameraPosition = Camera.getPosition(); if (cameraPosition.x > objectCenter.x) { // must be BRF or BRN if (cameraPosition.z < objectCenter.z) { - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 90, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }); + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 90, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 90, + z: 0 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 0 + }); - yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; - yawCorner = { x: left + rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: near - rotateHandleOffset }; + yawCorner = { + x: left + rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: near - rotateHandleOffset + }; - pitchCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset}; + pitchCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - rollCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset }; + rollCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - pitchCenter = { x: right, y: boundsCenter.y, z: boundsCenter.z}; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: far }; - + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + pitchCenter = { + x: right, + y: boundsCenter.y, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: far + }; + + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_SOUTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_SOUTH_URL + }); - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_SOUTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_SOUTH_URL }); - } else { - - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 0, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 90 }); - yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 0, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 180, + y: 270, + z: 0 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 90 + }); + + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; - yawCorner = { x: left + rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: far + rotateHandleOffset }; + yawCorner = { + x: left + rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: far + rotateHandleOffset + }; - pitchCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset }; + pitchCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - rollCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset}; + rollCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - pitchCenter = { x: right, y: boundsCenter.y, z: boundsCenter.z }; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: near}; + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + pitchCenter = { + x: right, + y: boundsCenter.y, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: near + }; - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); } } else { - + // must be BLF or BLN if (cameraPosition.z < objectCenter.z) { - - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 180, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 90 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 180, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 90, + y: 0, + z: 90 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 180 + }); - yawCorner = { x: right - rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: near - rotateHandleOffset }; + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; - pitchCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset }; + yawCorner = { + x: right - rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: near - rotateHandleOffset + }; - rollCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset}; + pitchCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - pitchCenter = { x: left, y: boundsCenter.y, z: boundsCenter.z }; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: far}; + rollCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + pitchCenter = { + x: left, + y: boundsCenter.y, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: far + }; + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); } else { - - yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 270, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - yawNormal = { x: 0, y: 1, z: 0 }; - rollNormal = { x: 0, y: 0, z: 1 }; - pitchNormal = { x: 1, y: 0, z: 0 }; + yawHandleRotation = Quat.fromVec3Degrees({ + x: 270, + y: 270, + z: 0 + }); + pitchHandleRotation = Quat.fromVec3Degrees({ + x: 180, + y: 270, + z: 0 + }); + rollHandleRotation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 180 + }); - yawCorner = { x: right - rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: far + rotateHandleOffset }; + yawNormal = { + x: 0, + y: 1, + z: 0 + }; + rollNormal = { + x: 0, + y: 0, + z: 1 + }; + pitchNormal = { + x: 1, + y: 0, + z: 0 + }; - rollCorner = { x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset }; + yawCorner = { + x: right - rotateHandleOffset, + y: bottom - rotateHandleOffset, + z: far + rotateHandleOffset + }; - pitchCorner = { x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset}; + rollCorner = { + x: right - rotateHandleOffset, + y: top + rotateHandleOffset, + z: near - rotateHandleOffset + }; - yawCenter = { x: boundsCenter.x, y: bottom, z: boundsCenter.z }; - rollCenter = { x: boundsCenter.x, y: boundsCenter.y, z: near }; - pitchCenter = { x: left, y: boundsCenter.y, z: boundsCenter.z}; + pitchCorner = { + x: left + rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset + }; - Overlays.editOverlay(pitchHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); - Overlays.editOverlay(rollHandle, { url: ROTATE_ARROW_WEST_NORTH_URL }); + yawCenter = { + x: boundsCenter.x, + y: bottom, + z: boundsCenter.z + }; + rollCenter = { + x: boundsCenter.x, + y: boundsCenter.y, + z: near + }; + pitchCenter = { + x: left, + y: boundsCenter.y, + z: boundsCenter.z + }; + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); } } - + var rotateHandlesVisible = true; var rotationOverlaysVisible = false; var translateHandlesVisible = true; @@ -984,20 +1456,38 @@ SelectionDisplay = (function () { rotateHandlesVisible = false; translateHandlesVisible = false; } - + var rotation = selectionManager.worldRotation; var dimensions = selectionManager.worldDimensions; var position = selectionManager.worldPosition; - - Overlays.editOverlay(rotateOverlayTarget, { visible: rotationOverlaysVisible }); - Overlays.editOverlay(rotateZeroOverlay, { visible: rotationOverlaysVisible }); - Overlays.editOverlay(rotateCurrentOverlay, { visible: rotationOverlaysVisible }); + + Overlays.editOverlay(rotateOverlayTarget, { + visible: rotationOverlaysVisible + }); + Overlays.editOverlay(rotateZeroOverlay, { + visible: rotationOverlaysVisible + }); + Overlays.editOverlay(rotateCurrentOverlay, { + visible: rotationOverlaysVisible + }); // TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden - Overlays.editOverlay(yawHandle, { visible: rotateHandlesVisible, position: yawCorner, rotation: yawHandleRotation}); - Overlays.editOverlay(pitchHandle, { visible: rotateHandlesVisible, position: pitchCorner, rotation: pitchHandleRotation}); - Overlays.editOverlay(rollHandle, { visible: rotateHandlesVisible, position: rollCorner, rotation: rollHandleRotation}); + Overlays.editOverlay(yawHandle, { + visible: rotateHandlesVisible, + position: yawCorner, + rotation: yawHandleRotation + }); + Overlays.editOverlay(pitchHandle, { + visible: rotateHandlesVisible, + position: pitchCorner, + rotation: pitchHandleRotation + }); + Overlays.editOverlay(rollHandle, { + visible: rotateHandlesVisible, + position: rollCorner, + rotation: rollHandleRotation + }); }; that.setSpaceMode = function(newSpaceMode) { @@ -1016,8 +1506,7 @@ SelectionDisplay = (function () { that.updateHandles(); }; - that.unselectAll = function () { - }; + that.unselectAll = function() {}; that.updateHandles = function() { if (SelectionManager.selections.length == 0) { @@ -1061,34 +1550,138 @@ SelectionDisplay = (function () { var worldTop = SelectionManager.worldDimensions.y / 2; - var LBN = { x: left, y: bottom, z: near }; - var RBN = { x: right, y: bottom, z: near }; - var LBF = { x: left, y: bottom, z: far }; - var RBF = { x: right, y: bottom, z: far }; - var LTN = { x: left, y: top, z: near }; - var RTN = { x: right, y: top, z: near }; - var LTF = { x: left, y: top, z: far }; - var RTF = { x: right, y: top, z: far }; + var LBN = { + x: left, + y: bottom, + z: near + }; + var RBN = { + x: right, + y: bottom, + z: near + }; + var LBF = { + x: left, + y: bottom, + z: far + }; + var RBF = { + x: right, + y: bottom, + z: far + }; + var LTN = { + x: left, + y: top, + z: near + }; + var RTN = { + x: right, + y: top, + z: near + }; + var LTF = { + x: left, + y: top, + z: far + }; + var RTF = { + x: right, + y: top, + z: far + }; - var TOP = { x: center.x, y: top, z: center.z }; - var BOTTOM = { x: center.x, y: bottom, z: center.z }; - var LEFT = { x: left, y: center.y, z: center.z }; - var RIGHT = { x: right, y: center.y, z: center.z }; - var NEAR = { x: center.x, y: center.y, z: near }; - var FAR = { x: center.x, y: center.y, z: far }; + var TOP = { + x: center.x, + y: top, + z: center.z + }; + var BOTTOM = { + x: center.x, + y: bottom, + z: center.z + }; + var LEFT = { + x: left, + y: center.y, + z: center.z + }; + var RIGHT = { + x: right, + y: center.y, + z: center.z + }; + var NEAR = { + x: center.x, + y: center.y, + z: near + }; + var FAR = { + x: center.x, + y: center.y, + z: far + }; - var EdgeTR = { x: right, y: top, z: center.z }; - var EdgeTL = { x: left, y: top, z: center.z }; - var EdgeTF = { x: center.x, y: top, z: front }; - var EdgeTN = { x: center.x, y: top, z: near }; - var EdgeBR = { x: right, y: bottom, z: center.z }; - var EdgeBL = { x: left, y: bottom, z: center.z }; - var EdgeBF = { x: center.x, y: bottom, z: front }; - var EdgeBN = { x: center.x, y: bottom, z: near }; - var EdgeNR = { x: right, y: center.y, z: near }; - var EdgeNL = { x: left, y: center.y, z: near }; - var EdgeFR = { x: right, y: center.y, z: front }; - var EdgeFL = { x: left, y: center.y, z: front }; + var EdgeTR = { + x: right, + y: top, + z: center.z + }; + var EdgeTL = { + x: left, + y: top, + z: center.z + }; + var EdgeTF = { + x: center.x, + y: top, + z: front + }; + var EdgeTN = { + x: center.x, + y: top, + z: near + }; + var EdgeBR = { + x: right, + y: bottom, + z: center.z + }; + var EdgeBL = { + x: left, + y: bottom, + z: center.z + }; + var EdgeBF = { + x: center.x, + y: bottom, + z: front + }; + var EdgeBN = { + x: center.x, + y: bottom, + z: near + }; + var EdgeNR = { + x: right, + y: center.y, + z: near + }; + var EdgeNL = { + x: left, + y: center.y, + z: near + }; + var EdgeFR = { + x: right, + y: center.y, + z: front + }; + var EdgeFL = { + x: left, + y: center.y, + z: front + }; LBN = Vec3.multiplyQbyV(rotation, LBN); RBN = Vec3.multiplyQbyV(rotation, RBN); @@ -1151,7 +1744,7 @@ SelectionDisplay = (function () { var stretchHandlesVisible = spaceMode == SPACE_LOCAL; var extendedStretchHandlesVisible = stretchHandlesVisible && showExtendedStretchHandles; - if (selectionManager.selections.length == 1 ) { + if (selectionManager.selections.length == 1) { var properties = Entities.getEntityProperties(selectionManager.selections[0]); if (properties.type == "Light" && properties.isSpotlight == true) { var stretchHandlesVisible = false; @@ -1190,7 +1783,11 @@ SelectionDisplay = (function () { }); Overlays.editOverlay(grabberSpotLightCircle, { position: NEAR, - dimensions: { x: distance, y: distance, z: 1 }, + dimensions: { + x: distance, + y: distance, + z: 1 + }, lineWidth: 1.5, rotation: rotation, visible: true, @@ -1217,15 +1814,33 @@ SelectionDisplay = (function () { visible: true, }); - Overlays.editOverlay(grabberPointLightCircleX, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleY, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleZ, { visible: false }); - Overlays.editOverlay(grabberPointLightT, { visible: false }); - Overlays.editOverlay(grabberPointLightB, { visible: false }); - Overlays.editOverlay(grabberPointLightL, { visible: false }); - Overlays.editOverlay(grabberPointLightR, { visible: false }); - Overlays.editOverlay(grabberPointLightF, { visible: false }); - Overlays.editOverlay(grabberPointLightN, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleX, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleY, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleZ, { + visible: false + }); + Overlays.editOverlay(grabberPointLightT, { + visible: false + }); + Overlays.editOverlay(grabberPointLightB, { + visible: false + }); + Overlays.editOverlay(grabberPointLightL, { + visible: false + }); + Overlays.editOverlay(grabberPointLightR, { + visible: false + }); + Overlays.editOverlay(grabberPointLightF, { + visible: false + }); + Overlays.editOverlay(grabberPointLightN, { + visible: false + }); } else if (properties.type == "Light" && properties.isSpotlight == false) { var stretchHandlesVisible = false; var extendedStretchHandlesVisible = false; @@ -1262,75 +1877,203 @@ SelectionDisplay = (function () { Overlays.editOverlay(grabberPointLightCircleX, { position: position, rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)), - dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, + dimensions: { + x: properties.dimensions.z / 2.0, + y: properties.dimensions.z / 2.0, + z: 1 + }, visible: true, }); Overlays.editOverlay(grabberPointLightCircleY, { position: position, rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)), - dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, + dimensions: { + x: properties.dimensions.z / 2.0, + y: properties.dimensions.z / 2.0, + z: 1 + }, visible: true, }); Overlays.editOverlay(grabberPointLightCircleZ, { position: position, rotation: rotation, - dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, + dimensions: { + x: properties.dimensions.z / 2.0, + y: properties.dimensions.z / 2.0, + z: 1 + }, visible: true, }); - Overlays.editOverlay(grabberSpotLightRadius, { visible: false }); - Overlays.editOverlay(grabberSpotLightL, { visible: false }); - Overlays.editOverlay(grabberSpotLightR, { visible: false }); - Overlays.editOverlay(grabberSpotLightT, { visible: false }); - Overlays.editOverlay(grabberSpotLightB, { visible: false }); - Overlays.editOverlay(grabberSpotLightCircle, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineL, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineR, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineT, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineB, { visible: false }); + Overlays.editOverlay(grabberSpotLightRadius, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightB, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightCircle, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineB, { + visible: false + }); } else { - Overlays.editOverlay(grabberSpotLightCenter, { visible: false }); - Overlays.editOverlay(grabberSpotLightRadius, { visible: false }); - Overlays.editOverlay(grabberSpotLightL, { visible: false }); - Overlays.editOverlay(grabberSpotLightR, { visible: false }); - Overlays.editOverlay(grabberSpotLightT, { visible: false }); - Overlays.editOverlay(grabberSpotLightB, { visible: false }); - Overlays.editOverlay(grabberSpotLightCircle, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineL, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineR, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineT, { visible: false }); - Overlays.editOverlay(grabberSpotLightLineB, { visible: false }); + Overlays.editOverlay(grabberSpotLightCenter, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightRadius, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightB, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightCircle, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineL, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineR, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineT, { + visible: false + }); + Overlays.editOverlay(grabberSpotLightLineB, { + visible: false + }); - Overlays.editOverlay(grabberPointLightCircleX, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleY, { visible: false }); - Overlays.editOverlay(grabberPointLightCircleZ, { visible: false }); - Overlays.editOverlay(grabberPointLightT, { visible: false }); - Overlays.editOverlay(grabberPointLightB, { visible: false }); - Overlays.editOverlay(grabberPointLightL, { visible: false }); - Overlays.editOverlay(grabberPointLightR, { visible: false }); - Overlays.editOverlay(grabberPointLightF, { visible: false }); - Overlays.editOverlay(grabberPointLightN, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleX, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleY, { + visible: false + }); + Overlays.editOverlay(grabberPointLightCircleZ, { + visible: false + }); + Overlays.editOverlay(grabberPointLightT, { + visible: false + }); + Overlays.editOverlay(grabberPointLightB, { + visible: false + }); + Overlays.editOverlay(grabberPointLightL, { + visible: false + }); + Overlays.editOverlay(grabberPointLightR, { + visible: false + }); + Overlays.editOverlay(grabberPointLightF, { + visible: false + }); + Overlays.editOverlay(grabberPointLightN, { + visible: false + }); } } - Overlays.editOverlay(grabberLBN, { visible: stretchHandlesVisible, rotation: rotation, position: LBN }); - Overlays.editOverlay(grabberRBN, { visible: stretchHandlesVisible, rotation: rotation, position: RBN }); - Overlays.editOverlay(grabberLBF, { visible: stretchHandlesVisible, rotation: rotation, position: LBF }); - Overlays.editOverlay(grabberRBF, { visible: stretchHandlesVisible, rotation: rotation, position: RBF }); + Overlays.editOverlay(grabberLBN, { + visible: stretchHandlesVisible, + rotation: rotation, + position: LBN + }); + Overlays.editOverlay(grabberRBN, { + visible: stretchHandlesVisible, + rotation: rotation, + position: RBN + }); + Overlays.editOverlay(grabberLBF, { + visible: stretchHandlesVisible, + rotation: rotation, + position: LBF + }); + Overlays.editOverlay(grabberRBF, { + visible: stretchHandlesVisible, + rotation: rotation, + position: RBF + }); - Overlays.editOverlay(grabberLTN, { visible: extendedStretchHandlesVisible, rotation: rotation, position: LTN }); - Overlays.editOverlay(grabberRTN, { visible: extendedStretchHandlesVisible, rotation: rotation, position: RTN }); - Overlays.editOverlay(grabberLTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: LTF }); - Overlays.editOverlay(grabberRTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: RTF }); + Overlays.editOverlay(grabberLTN, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: LTN + }); + Overlays.editOverlay(grabberRTN, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: RTN + }); + Overlays.editOverlay(grabberLTF, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: LTF + }); + Overlays.editOverlay(grabberRTF, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: RTF + }); - Overlays.editOverlay(grabberTOP, { visible: stretchHandlesVisible, rotation: rotation, position: TOP }); - Overlays.editOverlay(grabberBOTTOM, { visible: stretchHandlesVisible, rotation: rotation, position: BOTTOM }); - Overlays.editOverlay(grabberLEFT, { visible: extendedStretchHandlesVisible, rotation: rotation, position: LEFT }); - Overlays.editOverlay(grabberRIGHT, { visible: extendedStretchHandlesVisible, rotation: rotation, position: RIGHT }); - Overlays.editOverlay(grabberNEAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: NEAR }); - Overlays.editOverlay(grabberFAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: FAR }); + Overlays.editOverlay(grabberTOP, { + visible: stretchHandlesVisible, + rotation: rotation, + position: TOP + }); + Overlays.editOverlay(grabberBOTTOM, { + visible: stretchHandlesVisible, + rotation: rotation, + position: BOTTOM + }); + Overlays.editOverlay(grabberLEFT, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: LEFT + }); + Overlays.editOverlay(grabberRIGHT, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: RIGHT + }); + Overlays.editOverlay(grabberNEAR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: NEAR + }); + Overlays.editOverlay(grabberFAR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: FAR + }); var boxPosition = Vec3.multiplyQbyV(rotation, center); boxPosition = Vec3.sum(position, boxPosition); @@ -1346,9 +2089,17 @@ SelectionDisplay = (function () { for (var i = 0; i < overlaysNeeded; i++) { selectionBoxes.push( Overlays.addOverlay("cube", { - position: { x: 0, y: 0, z: 0 }, + position: { + x: 0, + y: 0, + z: 0 + }, size: 1, - color: { red: 255, green: 153, blue: 0 }, + color: { + red: 255, + green: 153, + blue: 0 + }, alpha: 1, solid: false, visible: false, @@ -1366,7 +2117,11 @@ SelectionDisplay = (function () { // Adjust overlay position to take registrationPoint into account // centeredRP = registrationPoint with range [-0.5, 0.5] - var centeredRP = Vec3.subtract(properties.registrationPoint, { x: 0.5, y: 0.5, z: 0.5 }); + var centeredRP = Vec3.subtract(properties.registrationPoint, { + x: 0.5, + y: 0.5, + z: 0.5 + }); var offset = vec3Mult(properties.dimensions, centeredRP); offset = Vec3.multiply(-1, offset); offset = Vec3.multiplyQbyV(properties.rotation, offset); @@ -1382,25 +2137,81 @@ SelectionDisplay = (function () { } // Hide any remaining selection boxes for (; i < selectionBoxes.length; i++) { - Overlays.editOverlay(selectionBoxes[i], { visible: false }); + Overlays.editOverlay(selectionBoxes[i], { + visible: false + }); } - Overlays.editOverlay(grabberEdgeTR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTR }); - Overlays.editOverlay(grabberEdgeTL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTL }); - Overlays.editOverlay(grabberEdgeTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTF }); - Overlays.editOverlay(grabberEdgeTN, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTN }); - Overlays.editOverlay(grabberEdgeBR, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBR }); - Overlays.editOverlay(grabberEdgeBL, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBL }); - Overlays.editOverlay(grabberEdgeBF, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBF }); - Overlays.editOverlay(grabberEdgeBN, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBN }); - Overlays.editOverlay(grabberEdgeNR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeNR }); - Overlays.editOverlay(grabberEdgeNL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeNL }); - Overlays.editOverlay(grabberEdgeFR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeFR }); - Overlays.editOverlay(grabberEdgeFL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeFL }); + Overlays.editOverlay(grabberEdgeTR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTR + }); + Overlays.editOverlay(grabberEdgeTL, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTL + }); + Overlays.editOverlay(grabberEdgeTF, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTF + }); + Overlays.editOverlay(grabberEdgeTN, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeTN + }); + Overlays.editOverlay(grabberEdgeBR, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBR + }); + Overlays.editOverlay(grabberEdgeBL, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBL + }); + Overlays.editOverlay(grabberEdgeBF, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBF + }); + Overlays.editOverlay(grabberEdgeBN, { + visible: stretchHandlesVisible, + rotation: rotation, + position: EdgeBN + }); + Overlays.editOverlay(grabberEdgeNR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeNR + }); + Overlays.editOverlay(grabberEdgeNL, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeNL + }); + Overlays.editOverlay(grabberEdgeFR, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeFR + }); + Overlays.editOverlay(grabberEdgeFL, { + visible: extendedStretchHandlesVisible, + rotation: rotation, + position: EdgeFL + }); var grabberMoveUpOffset = 0.1; - grabberMoveUpPosition = { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z } - Overlays.editOverlay(grabberMoveUp, { visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" }); + grabberMoveUpPosition = { + x: position.x, + y: position.y + worldTop + grabberMoveUpOffset, + z: position.z + } + Overlays.editOverlay(grabberMoveUp, { + visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" + }); Overlays.editOverlay(baseOfEntityProjectionOverlay, { visible: mode != "ROTATE_YAW" && mode != "ROTATE_PITCH" && mode != "ROTATE_ROLL", @@ -1423,16 +2234,19 @@ SelectionDisplay = (function () { that.setOverlaysVisible = function(isVisible) { var length = allOverlays.length; for (var i = 0; i < length; i++) { - Overlays.editOverlay(allOverlays[i], { visible: isVisible }); + Overlays.editOverlay(allOverlays[i], { + visible: isVisible + }); } length = selectionBoxes.length; for (var i = 0; i < length; i++) { - Overlays.editOverlay(selectionBoxes[i], { visible: isVisible }); + Overlays.editOverlay(selectionBoxes[i], { + visible: isVisible + }); } }; - that.unselect = function (entityID) { - }; + that.unselect = function(entityID) {}; var initialXZPick = null; var isConstrained = false; @@ -1447,7 +2261,11 @@ SelectionDisplay = (function () { var dimensions = SelectionManager.worldDimensions; var pickRay = Camera.computePickRay(event.x, event.y); - initialXZPick = rayPlaneIntersection(pickRay, startPosition, { x: 0, y: 1, z: 0 }); + initialXZPick = rayPlaneIntersection(pickRay, startPosition, { + x: 0, + y: 1, + z: 0 + }); // Duplicate entities if alt is pressed. This will make a // copy of the selected entities and move the _original_ entities, not @@ -1473,13 +2291,21 @@ SelectionDisplay = (function () { onEnd: function(event, reason) { pushCommandForSelections(duplicatedEntityIDs); - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); }, onMove: function(event) { pickRay = Camera.computePickRay(event.x, event.y); - var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { x: 0, y: 1, z: 0 }); + var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { + x: 0, + y: 1, + z: 0 + }); var vector = Vec3.subtract(pick, initialXZPick); // If shifted, constrain to one axis @@ -1490,19 +2316,49 @@ SelectionDisplay = (function () { vector.x = 0; } if (!isConstrained) { - Overlays.editOverlay(xRailOverlay, { visible: true }); - var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 }); - var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 }); - var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 }); - var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 }); - Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true }); - Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true }); + Overlays.editOverlay(xRailOverlay, { + visible: true + }); + var xStart = Vec3.sum(startPosition, { + x: -10000, + y: 0, + z: 0 + }); + var xEnd = Vec3.sum(startPosition, { + x: 10000, + y: 0, + z: 0 + }); + var zStart = Vec3.sum(startPosition, { + x: 0, + y: 0, + z: -10000 + }); + var zEnd = Vec3.sum(startPosition, { + x: 0, + y: 0, + z: 10000 + }); + Overlays.editOverlay(xRailOverlay, { + start: xStart, + end: xEnd, + visible: true + }); + Overlays.editOverlay(zRailOverlay, { + start: zStart, + end: zEnd, + visible: true + }); isConstrained = true; } } else { if (isConstrained) { - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); isConstrained = false; } } @@ -1510,14 +2366,18 @@ SelectionDisplay = (function () { constrainMajorOnly = event.isControl; var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(-0.5, selectionManager.worldDimensions)); vector = Vec3.subtract( - grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), - cornerPosition); + grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), + cornerPosition); var wantDebug = false; for (var i = 0; i < SelectionManager.selections.length; i++) { var properties = SelectionManager.savedProperties[SelectionManager.selections[i]]; - var newPosition = Vec3.sum(properties.position, { x: vector.x, y: 0, z: vector.z }); + var newPosition = Vec3.sum(properties.position, { + x: vector.x, + y: 0, + z: vector.z + }); Entities.editEntity(SelectionManager.selections[i], { position: newPosition, }); @@ -1533,7 +2393,7 @@ SelectionDisplay = (function () { SelectionManager._update(); } }; - + var lastXYPick = null var upDownPickNormal = null; addGrabberTool(grabberMoveUp, { @@ -1579,11 +2439,11 @@ SelectionDisplay = (function () { var vector = Vec3.subtract(newIntersection, lastXYPick); vector = grid.snapToGrid(vector); - + // we only care about the Y axis vector.x = 0; vector.z = 0; - + var wantDebug = false; if (wantDebug) { print("translateUpDown... "); @@ -1609,12 +2469,16 @@ SelectionDisplay = (function () { }); var vec3Mult = function(v1, v2) { - return { x: v1.x * v2.x, y: v1.y * v2.y, z: v1.z * v2.z }; - } - // stretchMode - name of mode - // direction - direction to stretch in - // pivot - point to use as a pivot - // offset - the position of the overlay tool relative to the selections center position + return { + x: v1.x * v2.x, + y: v1.y * v2.y, + z: v1.z * v2.z + }; + } + // stretchMode - name of mode + // direction - direction to stretch in + // pivot - point to use as a pivot + // offset - the position of the overlay tool relative to the selections center position var makeStretchTool = function(stretchMode, direction, pivot, offset, customOnMove) { var signs = { x: direction.x < 0 ? -1 : (direction.x > 0 ? 1 : 0), @@ -1659,7 +2523,11 @@ SelectionDisplay = (function () { } // Modify range of registrationPoint to be [-0.5, 0.5] - var centeredRP = Vec3.subtract(registrationPoint, { x: 0.5, y: 0.5, z: 0.5 }); + var centeredRP = Vec3.subtract(registrationPoint, { + x: 0.5, + y: 0.5, + z: 0.5 + }); // Scale pivot to be in the same range as registrationPoint var scaledPivot = Vec3.multiply(0.5, pivot) @@ -1675,9 +2543,17 @@ SelectionDisplay = (function () { pickRayPosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld)); if (numDimensions == 1 && mask.x) { - var start = Vec3.multiplyQbyV(rotation, { x: -10000, y: 0, z: 0 }); + var start = Vec3.multiplyQbyV(rotation, { + x: -10000, + y: 0, + z: 0 + }); start = Vec3.sum(start, properties.position); - var end = Vec3.multiplyQbyV(rotation, { x: 10000, y: 0, z: 0 }); + var end = Vec3.multiplyQbyV(rotation, { + x: 10000, + y: 0, + z: 0 + }); end = Vec3.sum(end, properties.position); Overlays.editOverlay(xRailOverlay, { start: start, @@ -1686,9 +2562,17 @@ SelectionDisplay = (function () { }); } if (numDimensions == 1 && mask.y) { - var start = Vec3.multiplyQbyV(rotation, { x: 0, y: -10000, z: 0 }); + var start = Vec3.multiplyQbyV(rotation, { + x: 0, + y: -10000, + z: 0 + }); start = Vec3.sum(start, properties.position); - var end = Vec3.multiplyQbyV(rotation, { x: 0, y: 10000, z: 0 }); + var end = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 10000, + z: 0 + }); end = Vec3.sum(end, properties.position); Overlays.editOverlay(yRailOverlay, { start: start, @@ -1697,9 +2581,17 @@ SelectionDisplay = (function () { }); } if (numDimensions == 1 && mask.z) { - var start = Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: -10000 }); + var start = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 0, + z: -10000 + }); start = Vec3.sum(start, properties.position); - var end = Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: 10000 }); + var end = Vec3.multiplyQbyV(rotation, { + x: 0, + y: 0, + z: 10000 + }); end = Vec3.sum(end, properties.position); Overlays.editOverlay(zRailOverlay, { start: start, @@ -1709,26 +2601,50 @@ SelectionDisplay = (function () { } if (numDimensions == 1) { if (mask.x == 1) { - planeNormal = { x: 0, y: 1, z: 0 }; + planeNormal = { + x: 0, + y: 1, + z: 0 + }; } else if (mask.y == 1) { - planeNormal = { x: 1, y: 0, z: 0 }; + planeNormal = { + x: 1, + y: 0, + z: 0 + }; } else { - planeNormal = { x: 0, y: 1, z: 0 }; + planeNormal = { + x: 0, + y: 1, + z: 0 + }; } } else if (numDimensions == 2) { if (mask.x == 0) { - planeNormal = { x: 1, y: 0, z: 0 }; + planeNormal = { + x: 1, + y: 0, + z: 0 + }; } else if (mask.y == 0) { - planeNormal = { x: 0, y: 1, z: 0 }; + planeNormal = { + x: 0, + y: 1, + z: 0 + }; } else { - planeNormal = { x: 0, y: 0, z: z }; + planeNormal = { + x: 0, + y: 0, + z: z + }; } } planeNormal = Vec3.multiplyQbyV(rotation, planeNormal); var pickRay = Camera.computePickRay(event.x, event.y); lastPick = rayPlaneIntersection(pickRay, - pickRayPosition, - planeNormal); + pickRayPosition, + planeNormal); // Overlays.editOverlay(normalLine, { // start: initialPosition, @@ -1739,9 +2655,15 @@ SelectionDisplay = (function () { }; var onEnd = function(event, reason) { - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(yRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + Overlays.editOverlay(xRailOverlay, { + visible: false + }); + Overlays.editOverlay(yRailOverlay, { + visible: false + }); + Overlays.editOverlay(zRailOverlay, { + visible: false + }); pushCommandForSelections(); }; @@ -1762,8 +2684,8 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); newPick = rayPlaneIntersection(pickRay, - pickRayPosition, - planeNormal); + pickRayPosition, + planeNormal); var vector = Vec3.subtract(newPick, lastPick); vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); @@ -1798,14 +2720,14 @@ SelectionDisplay = (function () { } else { newDimensions = Vec3.sum(initialDimensions, changeInDimensions); } - + newDimensions.x = Math.max(newDimensions.x, MINIMUM_DIMENSION); newDimensions.y = Math.max(newDimensions.y, MINIMUM_DIMENSION); newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION); - + var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(deltaPivot, changeInDimensions)); var newPosition = Vec3.sum(initialPosition, changeInPosition); - + for (var i = 0; i < SelectionManager.selections.length; i++) { Entities.editEntity(SelectionManager.selections[i], { position: newPosition, @@ -1882,7 +2804,11 @@ SelectionDisplay = (function () { size = props.dimensions.z + change.z; } - var newDimensions = { x: size, y: size, z: size }; + var newDimensions = { + x: size, + y: size, + z: size + }; Entities.editEntity(selectionManager.selections[0], { dimensions: newDimensions, @@ -1891,47 +2817,411 @@ SelectionDisplay = (function () { SelectionManager._update(); } - addStretchTool(grabberNEAR, "STRETCH_NEAR", { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); - addStretchTool(grabberFAR, "STRETCH_FAR", { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }); - addStretchTool(grabberTOP, "STRETCH_TOP", { x: 0, y: -1, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }); - addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { x: 0, y: 1, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }); - addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { x: -1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }); - addStretchTool(grabberLEFT, "STRETCH_LEFT", { x: 1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }); + addStretchTool(grabberNEAR, "STRETCH_NEAR", { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: -1 + }); + addStretchTool(grabberFAR, "STRETCH_FAR", { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: 0, + z: 1 + }); + addStretchTool(grabberTOP, "STRETCH_TOP", { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }); + addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }); + addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { + x: -1, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }); + addStretchTool(grabberLEFT, "STRETCH_LEFT", { + x: 1, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }); - addStretchTool(grabberSpotLightRadius, "STRETCH_RADIUS", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); - addStretchTool(grabberSpotLightT, "STRETCH_CUTOFF_T", { x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }, cutoffStretchFunc); - addStretchTool(grabberSpotLightB, "STRETCH_CUTOFF_B", { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }, cutoffStretchFunc); - addStretchTool(grabberSpotLightL, "STRETCH_CUTOFF_L", { x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, cutoffStretchFunc); - addStretchTool(grabberSpotLightR, "STRETCH_CUTOFF_R", { x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, cutoffStretchFunc); + addStretchTool(grabberSpotLightRadius, "STRETCH_RADIUS", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: -1 + }); + addStretchTool(grabberSpotLightT, "STRETCH_CUTOFF_T", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, cutoffStretchFunc); + addStretchTool(grabberSpotLightB, "STRETCH_CUTOFF_B", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, cutoffStretchFunc); + addStretchTool(grabberSpotLightL, "STRETCH_CUTOFF_L", { + x: 0, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, cutoffStretchFunc); + addStretchTool(grabberSpotLightR, "STRETCH_CUTOFF_R", { + x: 0, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, cutoffStretchFunc); - addStretchTool(grabberPointLightT, "STRETCH_RADIUS_T", { x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightB, "STRETCH_RADIUS_B", { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightL, "STRETCH_RADIUS_L", { x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightR, "STRETCH_RADIUS_R", { x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightF, "STRETCH_RADIUS_F", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); - addStretchTool(grabberPointLightN, "STRETCH_RADIUS_N", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }, radiusStretchFunc); + addStretchTool(grabberPointLightT, "STRETCH_RADIUS_T", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: -1, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightB, "STRETCH_RADIUS_B", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 1, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightL, "STRETCH_RADIUS_L", { + x: 0, + y: 0, + z: 0 + }, { + x: 1, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightR, "STRETCH_RADIUS_R", { + x: 0, + y: 0, + z: 0 + }, { + x: -1, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightF, "STRETCH_RADIUS_F", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: 0, + z: 1 + }, radiusStretchFunc); + addStretchTool(grabberPointLightN, "STRETCH_RADIUS_N", { + x: 0, + y: 0, + z: 0 + }, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: 0, + z: -1 + }, radiusStretchFunc); - addStretchTool(grabberLBN, "STRETCH_LBN", null, {x: 1, y: 0, z: 1}, { x: -1, y: -1, z: -1 }); - addStretchTool(grabberRBN, "STRETCH_RBN", null, {x: -1, y: 0, z: 1}, { x: 1, y: -1, z: -1 }); - addStretchTool(grabberLBF, "STRETCH_LBF", null, {x: 1, y: 0, z: -1}, { x: -1, y: -1, z: 1 }); - addStretchTool(grabberRBF, "STRETCH_RBF", null, {x: -1, y: 0, z: -1}, { x: 1, y: -1, z: 1 }); - addStretchTool(grabberLTN, "STRETCH_LTN", null, {x: 1, y: 0, z: 1}, { x: -1, y: 1, z: -1 }); - addStretchTool(grabberRTN, "STRETCH_RTN", null, {x: -1, y: 0, z: 1}, { x: 1, y: 1, z: -1 }); - addStretchTool(grabberLTF, "STRETCH_LTF", null, {x: 1, y: 0, z: -1}, { x: -1, y: 1, z: 1 }); - addStretchTool(grabberRTF, "STRETCH_RTF", null, {x: -1, y: 0, z: -1}, { x: 1, y: 1, z: 1 }); + addStretchTool(grabberLBN, "STRETCH_LBN", null, { + x: 1, + y: 0, + z: 1 + }, { + x: -1, + y: -1, + z: -1 + }); + addStretchTool(grabberRBN, "STRETCH_RBN", null, { + x: -1, + y: 0, + z: 1 + }, { + x: 1, + y: -1, + z: -1 + }); + addStretchTool(grabberLBF, "STRETCH_LBF", null, { + x: 1, + y: 0, + z: -1 + }, { + x: -1, + y: -1, + z: 1 + }); + addStretchTool(grabberRBF, "STRETCH_RBF", null, { + x: -1, + y: 0, + z: -1 + }, { + x: 1, + y: -1, + z: 1 + }); + addStretchTool(grabberLTN, "STRETCH_LTN", null, { + x: 1, + y: 0, + z: 1 + }, { + x: -1, + y: 1, + z: -1 + }); + addStretchTool(grabberRTN, "STRETCH_RTN", null, { + x: -1, + y: 0, + z: 1 + }, { + x: 1, + y: 1, + z: -1 + }); + addStretchTool(grabberLTF, "STRETCH_LTF", null, { + x: 1, + y: 0, + z: -1 + }, { + x: -1, + y: 1, + z: 1 + }); + addStretchTool(grabberRTF, "STRETCH_RTF", null, { + x: -1, + y: 0, + z: -1 + }, { + x: 1, + y: 1, + z: 1 + }); - addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, {x: 1, y: 1, z: 0}, { x: 1, y: 1, z: 0 }); - addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, {x: -1, y: 1, z: 0}, { x: -1, y: 1, z: 0 }); - addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, {x: 0, y: 1, z: -1}, { x: 0, y: 1, z: -1 }); - addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, {x: 0, y: 1, z: 1}, { x: 0, y: 1, z: 1 }); - addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, {x: -1, y: 0, z: 0}, { x: 1, y: -1, z: 0 }); - addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, {x: 1, y: 0, z: 0}, { x: -1, y: -1, z: 0 }); - addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, {x: 0, y: 0, z: -1}, { x: 0, y: -1, z: -1 }); - addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, {x: 0, y: 0, z: 1}, { x: 0, y: -1, z: 1 }); - addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, {x: -1, y: 0, z: 1}, { x: 1, y: 0, z: -1 }); - addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, {x: 1, y: 0, z: 1}, { x: -1, y: 0, z: -1 }); - addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, {x: -1, y: 0, z: -1}, { x: 1, y: 0, z: 1 }); - addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, {x: 1, y: 0, z: -1}, { x: -1, y: 0, z: 1 }); + addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, { + x: 1, + y: 1, + z: 0 + }, { + x: 1, + y: 1, + z: 0 + }); + addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, { + x: -1, + y: 1, + z: 0 + }, { + x: -1, + y: 1, + z: 0 + }); + addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, { + x: 0, + y: 1, + z: -1 + }, { + x: 0, + y: 1, + z: -1 + }); + addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, { + x: 0, + y: 1, + z: 1 + }, { + x: 0, + y: 1, + z: 1 + }); + addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, { + x: -1, + y: 0, + z: 0 + }, { + x: 1, + y: -1, + z: 0 + }); + addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, { + x: 1, + y: 0, + z: 0 + }, { + x: -1, + y: -1, + z: 0 + }); + addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, { + x: 0, + y: 0, + z: -1 + }, { + x: 0, + y: -1, + z: -1 + }); + addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, { + x: 0, + y: 0, + z: 1 + }, { + x: 0, + y: -1, + z: 1 + }); + addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, { + x: -1, + y: 0, + z: 1 + }, { + x: 1, + y: 0, + z: -1 + }); + addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, { + x: 1, + y: 0, + z: 1 + }, { + x: -1, + y: 0, + z: -1 + }); + addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, { + x: -1, + y: 0, + z: -1 + }, { + x: 1, + y: 0, + z: 1 + }); + addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, { + x: 1, + y: 0, + z: -1 + }, { + x: -1, + y: 0, + z: 1 + }); function updateRotationDegreesOverlay(angleFromZero, handleRotation, centerPosition) { var angle = angleFromZero * (Math.PI / 180); @@ -1967,34 +3257,31 @@ SelectionDisplay = (function () { outerRadius = diagonal * 1.15; var innerAlpha = 0.2; var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + size: innerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: innerAlpha + }); - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); Overlays.editOverlay(rotationDegreesDisplay, { visible: true, @@ -2003,19 +3290,35 @@ SelectionDisplay = (function () { updateRotationDegreesOverlay(0, yawHandleRotation, yawCenter); }, onEnd: function(event, reason) { - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); + Overlays.editOverlay(rotationDegreesDisplay, { + visible: false + }); pushCommandForSelections(); }, onMove: function(event) { var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); - + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(rotateOverlayTarget, { + ignoreRayIntersection: false + }); + var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2028,7 +3331,11 @@ SelectionDisplay = (function () { var snapToInner = distanceFromCenter < innerRadius; var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); + var yawChange = Quat.fromVec3Degrees({ + x: 0, + y: angleFromZero, + z: 0 + }); // Entities should only reposition if we are rotating multiple selections around // the selections center point. Otherwise, the rotation will be around the entities @@ -2066,19 +3373,43 @@ SelectionDisplay = (function () { endAtRemainder = startAtCurrent; } if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: innerRadius, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + }); } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: outerRadius, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + }); } - + } } }); @@ -2096,34 +3427,31 @@ SelectionDisplay = (function () { outerRadius = diagonal * 1.15; var innerAlpha = 0.2; var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + size: innerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: innerAlpha + }); - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); Overlays.editOverlay(rotationDegreesDisplay, { visible: true, @@ -2132,18 +3460,34 @@ SelectionDisplay = (function () { updateRotationDegreesOverlay(0, pitchHandleRotation, pitchCenter); }, onEnd: function(event, reason) { - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); + Overlays.editOverlay(rotationDegreesDisplay, { + visible: false + }); pushCommandForSelections(); }, onMove: function(event) { var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(rotateOverlayTarget, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2158,8 +3502,12 @@ SelectionDisplay = (function () { var snapToInner = distanceFromCenter < innerRadius; var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - - var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); + + var pitchChange = Quat.fromVec3Degrees({ + x: angleFromZero, + y: 0, + z: 0 + }); for (var i = 0; i < SelectionManager.selections.length; i++) { var entityID = SelectionManager.selections[i]; @@ -2188,17 +3536,41 @@ SelectionDisplay = (function () { endAtRemainder = startAtCurrent; } if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: innerRadius, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + }); } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: outerRadius, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + }); } } } @@ -2217,34 +3589,31 @@ SelectionDisplay = (function () { outerRadius = diagonal * 1.15; var innerAlpha = 0.2; var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + size: innerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: innerAlpha + }); - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); Overlays.editOverlay(rotationDegreesDisplay, { visible: true, @@ -2253,18 +3622,34 @@ SelectionDisplay = (function () { updateRotationDegreesOverlay(0, rollHandleRotation, rollCenter); }, onEnd: function(event, reason) { - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); + Overlays.editOverlay(rotationDegreesDisplay, { + visible: false + }); pushCommandForSelections(); }, onMove: function(event) { var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + ignoreRayIntersection: true, + visible: false + }); + Overlays.editOverlay(rotateOverlayTarget, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2280,7 +3665,11 @@ SelectionDisplay = (function () { var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); + var rollChange = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: angleFromZero + }); for (var i = 0; i < SelectionManager.selections.length; i++) { var entityID = SelectionManager.selections[i]; var properties = Entities.getEntityProperties(entityID); @@ -2308,17 +3697,41 @@ SelectionDisplay = (function () { endAtRemainder = startAtCurrent; } if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: innerRadius, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + }); } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + Overlays.editOverlay(rotateOverlayInner, { + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayOuter, { + startAt: startAtRemainder, + endAt: endAtRemainder + }); + Overlays.editOverlay(rotateOverlayCurrent, { + startAt: startAtCurrent, + endAt: endAtCurrent, + size: outerRadius, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + }); } } } @@ -2329,10 +3742,10 @@ SelectionDisplay = (function () { // FIXME - this cause problems with editing in the entity properties window //SelectionManager._update(); - + if (!Vec3.equal(Camera.getPosition(), lastCameraPosition) || !Quat.equal(Camera.getOrientation(), lastCameraOrientation)) { - + that.updateRotationHandles(); } } @@ -2347,12 +3760,20 @@ SelectionDisplay = (function () { var somethingClicked = false; var pickRay = Camera.computePickRay(event.x, event.y); - + // before we do a ray test for grabbers, disable the ray intersection for our selection box - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); - Overlays.editOverlay(yawHandle, { ignoreRayIntersection: true }); - Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: true }); - Overlays.editOverlay(rollHandle, { ignoreRayIntersection: true }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: true + }); + Overlays.editOverlay(yawHandle, { + ignoreRayIntersection: true + }); + Overlays.editOverlay(pitchHandle, { + ignoreRayIntersection: true + }); + Overlays.editOverlay(rollHandle, { + ignoreRayIntersection: true + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -2377,14 +3798,16 @@ SelectionDisplay = (function () { activeTool.onBegin(event); } } else { - switch(result.overlayID) { + switch (result.overlayID) { case grabberMoveUp: mode = "TRANSLATE_UP_DOWN"; somethingClicked = true; // in translate mode, we hide our stretch handles... for (var i = 0; i < stretchHandles.length; i++) { - Overlays.editOverlay(stretchHandles[i], { visible: false }); + Overlays.editOverlay(stretchHandles[i], { + visible: false + }); } break; @@ -2397,8 +3820,8 @@ SelectionDisplay = (function () { break; case grabberFAR: - case grabberEdgeTF: // TODO: maybe this should be TOP+FAR stretching? - case grabberEdgeBF: // TODO: maybe this should be BOTTOM+FAR stretching? + case grabberEdgeTF: // TODO: maybe this should be TOP+FAR stretching? + case grabberEdgeBF: // TODO: maybe this should be BOTTOM+FAR stretching? mode = "STRETCH_FAR"; somethingClicked = true; break; @@ -2411,14 +3834,14 @@ SelectionDisplay = (function () { somethingClicked = true; break; case grabberRIGHT: - case grabberEdgeTR: // TODO: maybe this should be TOP+RIGHT stretching? - case grabberEdgeBR: // TODO: maybe this should be BOTTOM+RIGHT stretching? + case grabberEdgeTR: // TODO: maybe this should be TOP+RIGHT stretching? + case grabberEdgeBR: // TODO: maybe this should be BOTTOM+RIGHT stretching? mode = "STRETCH_RIGHT"; somethingClicked = true; break; case grabberLEFT: - case grabberEdgeTL: // TODO: maybe this should be TOP+LEFT stretching? - case grabberEdgeBL: // TODO: maybe this should be BOTTOM+LEFT stretching? + case grabberEdgeTL: // TODO: maybe this should be TOP+LEFT stretching? + case grabberEdgeBL: // TODO: maybe this should be BOTTOM+LEFT stretching? mode = "STRETCH_LEFT"; somethingClicked = true; break; @@ -2429,29 +3852,43 @@ SelectionDisplay = (function () { } } } - + // if one of the items above was clicked, then we know we are in translate or stretch mode, and we // should hide our rotate handles... if (somethingClicked) { - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); - + Overlays.editOverlay(yawHandle, { + visible: false + }); + Overlays.editOverlay(pitchHandle, { + visible: false + }); + Overlays.editOverlay(rollHandle, { + visible: false + }); + if (mode != "TRANSLATE_UP_DOWN") { - Overlays.editOverlay(grabberMoveUp, { visible: false }); + Overlays.editOverlay(grabberMoveUp, { + visible: false + }); } } - + if (!somethingClicked) { - + print("rotate handle case..."); - + // After testing our stretch handles, then check out rotate handles - Overlays.editOverlay(yawHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(rollHandle, { ignoreRayIntersection: false }); + Overlays.editOverlay(yawHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(pitchHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(rollHandle, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); - + var overlayOrientation; var overlayCenter; @@ -2460,12 +3897,12 @@ SelectionDisplay = (function () { var pitch = angles.x; var yaw = angles.y; var roll = angles.z; - + originalRotation = properties.rotation; originalPitch = pitch; originalYaw = yaw; originalRoll = roll; - + if (result.intersects) { var tool = grabberTools[result.overlayID]; if (tool) { @@ -2476,7 +3913,7 @@ SelectionDisplay = (function () { activeTool.onBegin(event); } } - switch(result.overlayID) { + switch (result.overlayID) { case yawHandle: mode = "ROTATE_YAW"; somethingClicked = true; @@ -2514,59 +3951,147 @@ SelectionDisplay = (function () { print(" somethingClicked:" + somethingClicked); print(" mode:" + mode); - + if (somethingClicked) { - - Overlays.editOverlay(rotateOverlayTarget, { visible: true, rotation: overlayOrientation, position: overlayCenter }); - Overlays.editOverlay(rotateOverlayInner, { visible: true, rotation: overlayOrientation, position: overlayCenter }); - Overlays.editOverlay(rotateOverlayOuter, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 0 }); - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); + + Overlays.editOverlay(rotateOverlayTarget, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + Overlays.editOverlay(rotateOverlayInner, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter, + startAt: 0, + endAt: 360 + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: true, + rotation: overlayOrientation, + position: overlayCenter, + startAt: 0, + endAt: 0 + }); + Overlays.editOverlay(yawHandle, { + visible: false + }); + Overlays.editOverlay(pitchHandle, { + visible: false + }); + Overlays.editOverlay(rollHandle, { + visible: false + }); - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); - Overlays.editOverlay(grabberMoveUp, { visible: false }); - Overlays.editOverlay(grabberLBN, { visible: false }); - Overlays.editOverlay(grabberLBF, { visible: false }); - Overlays.editOverlay(grabberRBN, { visible: false }); - Overlays.editOverlay(grabberRBF, { visible: false }); - Overlays.editOverlay(grabberLTN, { visible: false }); - Overlays.editOverlay(grabberLTF, { visible: false }); - Overlays.editOverlay(grabberRTN, { visible: false }); - Overlays.editOverlay(grabberRTF, { visible: false }); + Overlays.editOverlay(yawHandle, { + visible: false + }); + Overlays.editOverlay(pitchHandle, { + visible: false + }); + Overlays.editOverlay(rollHandle, { + visible: false + }); + Overlays.editOverlay(grabberMoveUp, { + visible: false + }); + Overlays.editOverlay(grabberLBN, { + visible: false + }); + Overlays.editOverlay(grabberLBF, { + visible: false + }); + Overlays.editOverlay(grabberRBN, { + visible: false + }); + Overlays.editOverlay(grabberRBF, { + visible: false + }); + Overlays.editOverlay(grabberLTN, { + visible: false + }); + Overlays.editOverlay(grabberLTF, { + visible: false + }); + Overlays.editOverlay(grabberRTN, { + visible: false + }); + Overlays.editOverlay(grabberRTF, { + visible: false + }); - Overlays.editOverlay(grabberTOP, { visible: false }); - Overlays.editOverlay(grabberBOTTOM, { visible: false }); - Overlays.editOverlay(grabberLEFT, { visible: false }); - Overlays.editOverlay(grabberRIGHT, { visible: false }); - Overlays.editOverlay(grabberNEAR, { visible: false }); - Overlays.editOverlay(grabberFAR, { visible: false }); + Overlays.editOverlay(grabberTOP, { + visible: false + }); + Overlays.editOverlay(grabberBOTTOM, { + visible: false + }); + Overlays.editOverlay(grabberLEFT, { + visible: false + }); + Overlays.editOverlay(grabberRIGHT, { + visible: false + }); + Overlays.editOverlay(grabberNEAR, { + visible: false + }); + Overlays.editOverlay(grabberFAR, { + visible: false + }); - Overlays.editOverlay(grabberEdgeTR, { visible: false }); - Overlays.editOverlay(grabberEdgeTL, { visible: false }); - Overlays.editOverlay(grabberEdgeTF, { visible: false }); - Overlays.editOverlay(grabberEdgeTN, { visible: false }); - Overlays.editOverlay(grabberEdgeBR, { visible: false }); - Overlays.editOverlay(grabberEdgeBL, { visible: false }); - Overlays.editOverlay(grabberEdgeBF, { visible: false }); - Overlays.editOverlay(grabberEdgeBN, { visible: false }); - Overlays.editOverlay(grabberEdgeNR, { visible: false }); - Overlays.editOverlay(grabberEdgeNL, { visible: false }); - Overlays.editOverlay(grabberEdgeFR, { visible: false }); - Overlays.editOverlay(grabberEdgeFL, { visible: false }); + Overlays.editOverlay(grabberEdgeTR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeTL, { + visible: false + }); + Overlays.editOverlay(grabberEdgeTF, { + visible: false + }); + Overlays.editOverlay(grabberEdgeTN, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBL, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBF, { + visible: false + }); + Overlays.editOverlay(grabberEdgeBN, { + visible: false + }); + Overlays.editOverlay(grabberEdgeNR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeNL, { + visible: false + }); + Overlays.editOverlay(grabberEdgeFR, { + visible: false + }); + Overlays.editOverlay(grabberEdgeFL, { + visible: false + }); } } if (!somethingClicked) { - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: false }); + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: false + }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { - switch(result.overlayID) { + switch (result.overlayID) { case selectionBox: activeTool = translateXZTool; mode = translateXZTool.mode; @@ -2584,17 +4109,25 @@ SelectionDisplay = (function () { if (somethingClicked) { pickRay = Camera.computePickRay(event.x, event.y); if (wantDebug) { - print("mousePressEvent()...... " + overlayNames[result.overlayID]); + print("mousePressEvent()...... " + overlayNames[result.overlayID]); } } // reset everything as intersectable... // TODO: we could optimize this since some of these were already flipped back - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: false }); - Overlays.editOverlay(yawHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: false }); - Overlays.editOverlay(rollHandle, { ignoreRayIntersection: false }); - + Overlays.editOverlay(selectionBox, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(yawHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(pitchHandle, { + ignoreRayIntersection: false + }); + Overlays.editOverlay(rollHandle, { + ignoreRayIntersection: false + }); + return somethingClicked; }; @@ -2613,7 +4146,7 @@ SelectionDisplay = (function () { var highlightNeeded = false; if (result.intersects) { - switch(result.overlayID) { + switch (result.overlayID) { case yawHandle: case pitchHandle: case rollHandle: @@ -2621,7 +4154,7 @@ SelectionDisplay = (function () { pickedAlpha = handleAlpha; highlightNeeded = true; break; - + case grabberMoveUp: pickedColor = handleColor; pickedAlpha = handleAlpha; @@ -2682,30 +4215,42 @@ SelectionDisplay = (function () { default: if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + Overlays.editOverlay(previousHandle, { + color: previousHandleColor, + alpha: previousHandleAlpha + }); previousHandle = false; } break; } - + if (highlightNeeded) { if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + Overlays.editOverlay(previousHandle, { + color: previousHandleColor, + alpha: previousHandleAlpha + }); previousHandle = false; } - Overlays.editOverlay(result.overlayID, { color: highlightedHandleColor, alpha: highlightedHandleAlpha }); + Overlays.editOverlay(result.overlayID, { + color: highlightedHandleColor, + alpha: highlightedHandleAlpha + }); previousHandle = result.overlayID; previousHandleColor = pickedColor; previousHandleAlpha = pickedAlpha; } - + } else { if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + Overlays.editOverlay(previousHandle, { + color: previousHandleColor, + alpha: previousHandleAlpha + }); previousHandle = false; } } - + return false; }; @@ -2728,7 +4273,11 @@ SelectionDisplay = (function () { Overlays.editOverlay(rollHandle, { scale: handleSize, }); - var pos = Vec3.sum(grabberMoveUpPosition, { x: 0, y: Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 3, z: 0 }); + var pos = Vec3.sum(grabberMoveUpPosition, { + x: 0, + y: Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 3, + z: 0 + }); Overlays.editOverlay(grabberMoveUp, { position: pos, scale: handleSize / 2, @@ -2745,34 +4294,43 @@ SelectionDisplay = (function () { activeTool = null; // hide our rotation overlays..., and show our handles if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL") { - Overlays.editOverlay(rotateOverlayTarget, { visible: false }); - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + Overlays.editOverlay(rotateOverlayTarget, { + visible: false + }); + Overlays.editOverlay(rotateOverlayInner, { + visible: false + }); + Overlays.editOverlay(rotateOverlayOuter, { + visible: false + }); + Overlays.editOverlay(rotateOverlayCurrent, { + visible: false + }); showHandles = true; } if (mode != "UNKNOWN") { showHandles = true; } - + mode = "UNKNOWN"; - + // if something is selected, then reset the "original" properties for any potential next click+move operation if (SelectionManager.hasSelection()) { if (showHandles) { that.select(SelectionManager.selections[0], event); } } - + }; // NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these: // Controller.mousePressEvent.connect(that.mousePressEvent); // Controller.mouseMoveEvent.connect(that.mouseMoveEvent); Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); - + + + return that; -}()); - +}()); \ No newline at end of file diff --git a/examples/libraries/lightOverlayManager.js b/examples/libraries/lightOverlayManager.js index 0942fae723..2d3618096b 100644 --- a/examples/libraries/lightOverlayManager.js +++ b/examples/libraries/lightOverlayManager.js @@ -53,7 +53,9 @@ LightOverlayManager = function() { if (visible != isVisible) { visible = isVisible; for (var id in entityOverlays) { - Overlays.editOverlay(entityOverlays[id], { visible: visible }); + Overlays.editOverlay(entityOverlays[id], { + visible: visible + }); } } }; @@ -61,8 +63,7 @@ LightOverlayManager = function() { // Allocate or get an unused overlay function getOverlay() { if (unusedOverlays.length == 0) { - var overlay = Overlays.addOverlay("image3d", { - }); + var overlay = Overlays.addOverlay("image3d", {}); allOverlays.push(overlay); } else { var overlay = unusedOverlays.pop(); @@ -72,7 +73,9 @@ LightOverlayManager = function() { function releaseOverlay(overlay) { unusedOverlays.push(overlay); - Overlays.editOverlay(overlay, { visible: false }); + Overlays.editOverlay(overlay, { + visible: false + }); } function addEntity(entityID) { @@ -88,7 +91,11 @@ LightOverlayManager = function() { visible: visible, alpha: 0.9, scale: 0.5, - color: { red: 255, green: 255, blue: 255 } + color: { + red: 255, + green: 255, + blue: 255 + } }); } } @@ -123,4 +130,4 @@ LightOverlayManager = function() { Overlays.deleteOverlay(allOverlays[i]); } }); -}; +}; \ No newline at end of file diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 5d14bfb7dd..115c1bcb65 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -271,3 +271,29 @@ hexToRgb = function(hex) { } : null; } +calculateHandSizeRatio = function() { + // Get the ratio of the current avatar's hand to Owen's hand + + var standardCenterHandPoint = 0.11288; + var jointNames = MyAvatar.getJointNames(); + //get distance from handJoint up to leftHandIndex3 as a proxy for center of hand + var wristToFingertipDistance = 0;; + for (var i = 0; i < jointNames.length; i++) { + var jointName = jointNames[i]; + print(jointName) + if (jointName.indexOf("LeftHandIndex") !== -1) { + // translations are relative to parent joint, so simply add them together + // joints face down the y-axis + var translation = MyAvatar.getDefaultJointTranslation(i).y; + wristToFingertipDistance += translation; + } + } + // Right now units are in cm, so convert to meters + wristToFingertipDistance /= 100; + + var centerHandPoint = wristToFingertipDistance/2; + + // Compare against standard hand (Owen) + var handSizeRatio = centerHandPoint/standardCenterHandPoint; + return handSizeRatio; +} diff --git a/examples/light_modifier/README.md b/examples/light_modifier/README.md new file mode 100644 index 0000000000..f23f22148a --- /dev/null +++ b/examples/light_modifier/README.md @@ -0,0 +1,29 @@ +This PR demonstrates one way in-world editing of objects might work. + +Running this script will show light overlay icons in-world. Enter edit mode by running your distance beam through a light overlay. Exit using the red X. + +When you distant grab the sliders, you can move them along their axis to change their values. You may also rotate / move the block to which the spotlight is attached. + +To test: https://rawgit.com/imgntn/hifi/light_mod/examples/lights/lightLoader.js +To reset, I recommend stopping all scripts then re-loading lightLoader.js + +When you run the lightLoader.js script, several scripts will be loaded: +- handControllerGrab.js (will not impart velocity when you move the parent or a slider, will not move sliders with head movement,will constrain movement for a slider to a given axis start and end, will support blacklisting of entities for raypicking during search for objects) +- lightModifier.js (listens for message to create sliders for a given light. will start with slider set to the light's initial properties) +- lightModifierTestScene.js (creates a light) +- slider.js (attached to each slider entity) +- lightParent.js (attached to a 3d model of a light, to which a light is parented, so you can move it around. or keep the current parent if a light already has a parent) +- visiblePanel.js (the transparent panel) +- closeButton.js (for closing the ui) +- ../libraries/lightOverlayManager.js (shows 2d overlays for lights in the world) +- ../libraries/entitySelectionTool.js (visualizes volume of the lights) + +Current sliders are (top to bottom): +red +green +blue +intensity +cutoff +exponent + +![capture](https://cloud.githubusercontent.com/assets/843228/11910139/afaaf1ae-a5a5-11e5-8b66-0eb3fc6976df.PNG) diff --git a/examples/light_modifier/closeButton.js b/examples/light_modifier/closeButton.js new file mode 100644 index 0000000000..72fcfbc382 --- /dev/null +++ b/examples/light_modifier/closeButton.js @@ -0,0 +1,36 @@ +// +// closeButton.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that closes sliders when interacted with. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + function CloseButton() { + return this; + } + + CloseButton.prototype = { + preload: function(entityID) { + this.entityID = entityID; + var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + this.initialProperties = entityProperties + this.userData = JSON.parse(entityProperties.userData); + }, + startNearGrab: function() { + + }, + startFarTrigger: function() { + Messages.sendMessage('Hifi-Light-Modifier-Cleanup', 'callCleanup') + } + + }; + + return new CloseButton(); +}); \ No newline at end of file diff --git a/examples/light_modifier/lightLoader.js b/examples/light_modifier/lightLoader.js new file mode 100644 index 0000000000..83618f85c2 --- /dev/null +++ b/examples/light_modifier/lightLoader.js @@ -0,0 +1,20 @@ +// +// lightLoader.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Loads a test scene showing sliders that you can grab and move to change entity properties. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var grabScript = Script.resolvePath('../controllers/handControllerGrab.js?' + Math.random(0 - 100)); +Script.load(grabScript); +var lightModifier = Script.resolvePath('lightModifier.js?' + Math.random(0 - 100)); +Script.load(lightModifier); +Script.setTimeout(function() { + var lightModifierTestScene = Script.resolvePath('lightModifierTestScene.js?' + Math.random(0 - 100)); + Script.load(lightModifierTestScene); +}, 750) \ No newline at end of file diff --git a/examples/light_modifier/lightModifier.js b/examples/light_modifier/lightModifier.js new file mode 100644 index 0000000000..b50bbe9478 --- /dev/null +++ b/examples/light_modifier/lightModifier.js @@ -0,0 +1,876 @@ +// +// lightModifier.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Given a selected light, instantiate some entities that represent various values you can dynamically adjust by grabbing and moving. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +//some experimental options +var ONLY_I_CAN_EDIT = false; +var SLIDERS_SHOULD_STAY_WITH_AVATAR = false; +var VERTICAL_SLIDERS = false; +var SHOW_OVERLAYS = true; +var SHOW_LIGHT_VOLUME = true; +var USE_PARENTED_PANEL = true; +var VISIBLE_PANEL = true; +var USE_LABELS = true; +var LEFT_LABELS = false; +var RIGHT_LABELS = true; +var ROTATE_CLOSE_BUTTON = false; + +//variables for managing overlays +var selectionDisplay; +var selectionManager; +var lightOverlayManager; + +//for when we make a 3d model of a light a parent for the light +var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100)); + +if (SHOW_OVERLAYS === true) { + + Script.include('../libraries/gridTool.js'); + Script.include('../libraries/entitySelectionTool.js?' + Math.random(0 - 100)); + Script.include('../libraries/lightOverlayManager.js'); + + var grid = Grid(); + gridTool = GridTool({ + horizontalGrid: grid + }); + gridTool.setVisible(false); + + selectionDisplay = SelectionDisplay; + selectionManager = SelectionManager; + lightOverlayManager = new LightOverlayManager(); + selectionManager.addEventListener(function() { + selectionDisplay.updateHandles(); + lightOverlayManager.updatePositions(); + }); + lightOverlayManager.setVisible(true); +} + +var DEFAULT_PARENT_ID = '{00000000-0000-0000-0000-000000000000}' + +var AXIS_SCALE = 1; +var COLOR_MAX = 255; +var INTENSITY_MAX = 0.05; +var CUTOFF_MAX = 360; +var EXPONENT_MAX = 1; + +var SLIDER_SCRIPT_URL = Script.resolvePath('slider.js?' + Math.random(0, 100)); +var LIGHT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/source4_very_good.fbx'; +var CLOSE_BUTTON_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/red_x.fbx'; +var CLOSE_BUTTON_SCRIPT_URL = Script.resolvePath('closeButton.js?' + Math.random(0, 100)); +var TRANSPARENT_PANEL_URL = 'http://hifi-content.s3.amazonaws.com/james/light_modifier/transparent_box_alpha_15.fbx'; +var VISIBLE_PANEL_SCRIPT_URL = Script.resolvePath('visiblePanel.js?' + Math.random(0, 100)); + +var RED = { + red: 255, + green: 0, + blue: 0 +}; + +var GREEN = { + red: 0, + green: 255, + blue: 0 +}; + +var BLUE = { + red: 0, + green: 0, + blue: 255 +}; + +var PURPLE = { + red: 255, + green: 0, + blue: 255 +}; + +var WHITE = { + red: 255, + green: 255, + blue: 255 +}; + +var ORANGE = { + red: 255, + green: 165, + blue: 0 +} + +var SLIDER_DIMENSIONS = { + x: 0.075, + y: 0.075, + z: 0.075 +}; + +var CLOSE_BUTTON_DIMENSIONS = { + x: 0.1, + y: 0.025, + z: 0.1 +} + +var LIGHT_MODEL_DIMENSIONS = { + x: 0.58, + y: 1.21, + z: 0.57 +} + +var PER_ROW_OFFSET = { + x: 0, + y: -0.2, + z: 0 +}; +var sliders = []; +var slidersRef = { + 'color_red': null, + 'color_green': null, + 'color_blue': null, + intensity: null, + cutoff: null, + exponent: null +}; +var light = null; + +var basePosition; +var avatarRotation; + +function entitySlider(light, color, sliderType, displayText, row) { + this.light = light; + this.lightID = light.id.replace(/[{}]/g, ""); + this.initialProperties = light.initialProperties; + this.color = color; + this.sliderType = sliderType; + this.displayText = displayText; + this.verticalOffset = Vec3.multiply(row, PER_ROW_OFFSET); + this.avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + this.basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(this.avatarRot))); + this.basePosition.y += 1; + basePosition = this.basePosition; + avatarRot = this.avatarRot; + + var message = { + lightID: this.lightID, + sliderType: this.sliderType, + sliderValue: null + }; + + if (this.sliderType === 'color_red') { + message.sliderValue = this.initialProperties.color.red + this.setValueFromMessage(message); + } + if (this.sliderType === 'color_green') { + message.sliderValue = this.initialProperties.color.green + this.setValueFromMessage(message); + } + if (this.sliderType === 'color_blue') { + message.sliderValue = this.initialProperties.color.blue + this.setValueFromMessage(message); + } + + if (this.sliderType === 'intensity') { + message.sliderValue = this.initialProperties.intensity + this.setValueFromMessage(message); + } + + if (this.sliderType === 'exponent') { + message.sliderValue = this.initialProperties.exponent + this.setValueFromMessage(message); + } + + if (this.sliderType === 'cutoff') { + message.sliderValue = this.initialProperties.cutoff + this.setValueFromMessage(message); + } + + this.setInitialSliderPositions(); + this.createAxis(); + this.createSliderIndicator(); + if (USE_LABELS === true) { + this.createLabel() + } + return this; +} + +//what's the ux for adjusting values? start with simple entities, try image overlays etc +entitySlider.prototype = { + createAxis: function() { + //start of line + var position; + var extension; + + if (VERTICAL_SLIDERS == true) { + position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot))))); + //line starts on bottom and goes up + var upVector = Quat.getUp(this.avatarRot); + extension = Vec3.multiply(AXIS_SCALE, upVector); + } else { + position = Vec3.sum(this.basePosition, this.verticalOffset); + //line starts on left and goes to right + //set the end of the line to the right + var rightVector = Quat.getRight(this.avatarRot); + extension = Vec3.multiply(AXIS_SCALE, rightVector); + } + + + this.axisStart = position; + this.endOfAxis = Vec3.sum(position, extension); + this.createEndOfAxisEntity(); + + var properties = { + type: 'Line', + name: 'Hifi-Slider-Axis::' + this.sliderType, + color: this.color, + collisionsWillMove: false, + ignoreForCollisions: true, + dimensions: { + x: 3, + y: 3, + z: 3 + }, + position: position, + linePoints: [{ + x: 0, + y: 0, + z: 0 + }, extension], + lineWidth: 5, + }; + + this.axis = Entities.addEntity(properties); + }, + createEndOfAxisEntity: function() { + //we use this to track the end of the axis while parented to a panel + var properties = { + name: 'Hifi-End-Of-Axis', + type: 'Box', + collisionsWillMove: false, + ignoreForCollisions: true, + dimensions: { + x: 0.01, + y: 0.01, + z: 0.01 + }, + color: { + red: 255, + green: 255, + blue: 255 + }, + position: this.endOfAxis, + parentID: this.axis, + visible: false + } + + this.endOfAxisEntity = Entities.addEntity(this.endOfAxis); + }, + createLabel: function() { + + var LABEL_WIDTH = 0.25 + var PER_LETTER_SPACING = 0.1; + var textWidth = this.displayText.length * PER_LETTER_SPACING; + + var position; + if (LEFT_LABELS === true) { + var leftVector = Vec3.multiply(-1, Quat.getRight(this.avatarRot)); + + var extension = Vec3.multiply(textWidth, leftVector); + + position = Vec3.sum(this.axisStart, extension); + } + + if (RIGHT_LABELS === true) { + var rightVector = Quat.getRight(this.avatarRot); + + var extension = Vec3.multiply(textWidth / 1.75, rightVector); + + position = Vec3.sum(this.endOfAxis, extension); + } + + + var labelProperties = { + name: 'Hifi-Slider-Label-' + this.sliderType, + type: 'Text', + dimensions: { + x: textWidth, + y: 0.2, + z: 0.1 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, + text: this.displayText, + lineHeight: 0.14, + backgroundColor: { + red: 0, + green: 0, + blue: 0 + }, + position: position, + rotation: this.avatarRot, + } + print('BEFORE CREATE LABEL' + JSON.stringify(labelProperties)) + this.label = Entities.addEntity(labelProperties); + print('AFTER CREATE LABEL') + }, + createSliderIndicator: function() { + var extensionVector; + var position; + if (VERTICAL_SLIDERS == true) { + position = Vec3.sum(this.basePosition, Vec3.multiply(row, (Vec3.multiply(0.2, Quat.getRight(this.avatarRot))))); + extensionVector = Quat.getUp(this.avatarRot); + + } else { + position = Vec3.sum(this.basePosition, this.verticalOffset); + extensionVector = Quat.getRight(this.avatarRot); + + } + + var initialDistance; + if (this.sliderType === 'color_red') { + initialDistance = this.distanceRed; + } + if (this.sliderType === 'color_green') { + initialDistance = this.distanceGreen; + } + if (this.sliderType === 'color_blue') { + initialDistance = this.distanceBlue; + } + if (this.sliderType === 'intensity') { + initialDistance = this.distanceIntensity; + } + if (this.sliderType === 'cutoff') { + initialDistance = this.distanceCutoff; + } + if (this.sliderType === 'exponent') { + initialDistance = this.distanceExponent; + } + + var extension = Vec3.multiply(initialDistance, extensionVector); + var sliderPosition = Vec3.sum(position, extension); + + var properties = { + type: 'Sphere', + name: 'Hifi-Slider-' + this.sliderType, + dimensions: SLIDER_DIMENSIONS, + collisionsWillMove: true, + color: this.color, + position: sliderPosition, + script: SLIDER_SCRIPT_URL, + ignoreForCollisions: true, + userData: JSON.stringify({ + lightModifierKey: { + lightID: this.lightID, + sliderType: this.sliderType, + axisStart: position, + axisEnd: this.endOfAxis, + }, + handControllerKey: { + disableReleaseVelocity: true, + disableMoveWithHead: true, + disableNearGrab:true + } + }), + }; + + this.sliderIndicator = Entities.addEntity(properties); + }, + setValueFromMessage: function(message) { + + //message is not for our light + if (message.lightID !== this.lightID) { + // print('not our light') + return; + } + + //message is not our type + if (message.sliderType !== this.sliderType) { + // print('not our slider type') + return + } + + var lightProperties = Entities.getEntityProperties(this.lightID); + + if (this.sliderType === 'color_red') { + Entities.editEntity(this.lightID, { + color: { + red: message.sliderValue, + green: lightProperties.color.green, + blue: lightProperties.color.blue + } + }); + } + + if (this.sliderType === 'color_green') { + Entities.editEntity(this.lightID, { + color: { + red: lightProperties.color.red, + green: message.sliderValue, + blue: lightProperties.color.blue + } + }); + } + + if (this.sliderType === 'color_blue') { + Entities.editEntity(this.lightID, { + color: { + red: lightProperties.color.red, + green: lightProperties.color.green, + blue: message.sliderValue, + } + }); + } + + if (this.sliderType === 'intensity') { + Entities.editEntity(this.lightID, { + intensity: message.sliderValue + }); + } + + if (this.sliderType === 'cutoff') { + Entities.editEntity(this.lightID, { + cutoff: message.sliderValue + }); + } + + if (this.sliderType === 'exponent') { + Entities.editEntity(this.lightID, { + exponent: message.sliderValue + }); + } + }, + setInitialSliderPositions: function() { + this.distanceRed = (this.initialProperties.color.red / COLOR_MAX) * AXIS_SCALE; + this.distanceGreen = (this.initialProperties.color.green / COLOR_MAX) * AXIS_SCALE; + this.distanceBlue = (this.initialProperties.color.blue / COLOR_MAX) * AXIS_SCALE; + this.distanceIntensity = (this.initialProperties.intensity / INTENSITY_MAX) * AXIS_SCALE; + this.distanceCutoff = (this.initialProperties.cutoff / CUTOFF_MAX) * AXIS_SCALE; + this.distanceExponent = (this.initialProperties.exponent / EXPONENT_MAX) * AXIS_SCALE; + } + +}; + + +var panel; +var visiblePanel; + +function makeSliders(light) { + + if (USE_PARENTED_PANEL === true) { + panel = createPanelEntity(MyAvatar.position); + } + + if (light.type === 'spotlight') { + var USE_COLOR_SLIDER = true; + var USE_INTENSITY_SLIDER = true; + var USE_CUTOFF_SLIDER = true; + var USE_EXPONENT_SLIDER = true; + } + if (light.type === 'pointlight') { + var USE_COLOR_SLIDER = true; + var USE_INTENSITY_SLIDER = true; + var USE_CUTOFF_SLIDER = false; + var USE_EXPONENT_SLIDER = false; + } + if (USE_COLOR_SLIDER === true) { + slidersRef.color_red = new entitySlider(light, RED, 'color_red', 'Red', 1); + slidersRef.color_green = new entitySlider(light, GREEN, 'color_green', 'Green', 2); + slidersRef.color_blue = new entitySlider(light, BLUE, 'color_blue', 'Blue', 3); + + sliders.push(slidersRef.color_red); + sliders.push(slidersRef.color_green); + sliders.push(slidersRef.color_blue); + + } + if (USE_INTENSITY_SLIDER === true) { + slidersRef.intensity = new entitySlider(light, WHITE, 'intensity', 'Intensity', 4); + sliders.push(slidersRef.intensity); + } + if (USE_CUTOFF_SLIDER === true) { + slidersRef.cutoff = new entitySlider(light, PURPLE, 'cutoff', 'Cutoff', 5); + sliders.push(slidersRef.cutoff); + } + if (USE_EXPONENT_SLIDER === true) { + slidersRef.exponent = new entitySlider(light, ORANGE, 'exponent', 'Exponent', 6); + sliders.push(slidersRef.exponent); + } + + createCloseButton(slidersRef.color_red.axisStart); + + subscribeToSliderMessages(); + + if (USE_PARENTED_PANEL === true) { + parentEntitiesToPanel(panel); + } + + if (SLIDERS_SHOULD_STAY_WITH_AVATAR === true) { + parentPanelToAvatar(panel); + } + + if (VISIBLE_PANEL === true) { + visiblePanel = createVisiblePanel(); + } +}; + +function parentPanelToAvatar(panel) { + //this is going to need some more work re: the sliders actually being grabbable. probably something to do with updating axis movement + Entities.editEntity(panel, { + parentID: MyAvatar.sessionUUID, + //actually figure out which one to parent it to -- probably a spine or something. + parentJointIndex: 1, + }) +} + + +function parentEntitiesToPanel(panel) { + + sliders.forEach(function(slider) { + Entities.editEntity(slider.axis, { + parentID: panel + }) + Entities.editEntity(slider.sliderIndicator, { + parentID: panel + }) + }) + + closeButtons.forEach(function(button) { + Entities.editEntity(button, { + parentID: panel + }) + }) +} + +function createPanelEntity(position) { + print('CREATING PANEL at ' + JSON.stringify(position)); + var panelProperties = { + name: 'Hifi-Slider-Panel', + type: 'Box', + dimensions: { + x: 0.1, + y: 0.1, + z: 0.1 + }, + visible: false, + collisionsWillMove: false, + ignoreForCollisions: true + } + + var panel = Entities.addEntity(panelProperties); + return panel +} + +function createVisiblePanel() { + var totalOffset = -PER_ROW_OFFSET.y * sliders.length; + + var moveRight = Vec3.sum(basePosition, Vec3.multiply(AXIS_SCALE / 2, Quat.getRight(avatarRot))); + + var moveDown = Vec3.sum(moveRight, Vec3.multiply((sliders.length + 1) / 2, PER_ROW_OFFSET)) + var panelProperties = { + name: 'Hifi-Visible-Transparent-Panel', + type: 'Model', + modelURL: TRANSPARENT_PANEL_URL, + dimensions: { + x: AXIS_SCALE + 0.1, + y: totalOffset, + z: SLIDER_DIMENSIONS.z / 4 + }, + visible: true, + collisionsWillMove: false, + ignoreForCollisions: true, + position: moveDown, + rotation: avatarRot, + script: VISIBLE_PANEL_SCRIPT_URL + } + + var panel = Entities.addEntity(panelProperties); + + return panel +} + + +function createLightModel(position, rotation) { + var blockProperties = { + name: 'Hifi-Spotlight-Model', + type: 'Model', + shapeType: 'box', + modelURL: LIGHT_MODEL_URL, + dimensions: LIGHT_MODEL_DIMENSIONS, + collisionsWillMove: true, + position: position, + rotation: rotation, + script: PARENT_SCRIPT_URL, + userData: JSON.stringify({ + handControllerKey: { + disableReleaseVelocity: true + } + }) + }; + + var block = Entities.addEntity(blockProperties); + + return block +} + +var closeButtons = []; + +function createCloseButton(axisStart) { + var MARGIN = 0.10; + var VERTICAL_OFFFSET = { + x: 0, + y: 0.15, + z: 0 + }; + var leftVector = Vec3.multiply(-1, Quat.getRight(avatarRot)); + var extension = Vec3.multiply(MARGIN, leftVector); + var position = Vec3.sum(axisStart, extension); + + var buttonProperties = { + name: 'Hifi-Close-Button', + type: 'Model', + modelURL: CLOSE_BUTTON_MODEL_URL, + dimensions: CLOSE_BUTTON_DIMENSIONS, + position: Vec3.sum(position, VERTICAL_OFFFSET), + rotation: Quat.multiply(avatarRot, Quat.fromPitchYawRollDegrees(90, 0, 45)), + //rotation: Quat.fromPitchYawRollDegrees(0, 0, 90), + collisionsWillMove: false, + ignoreForCollisions: true, + script: CLOSE_BUTTON_SCRIPT_URL, + userData: JSON.stringify({ + grabbableKey: { + wantsTrigger: true + } + }) + } + + var button = Entities.addEntity(buttonProperties); + + closeButtons.push(button); + + if (ROTATE_CLOSE_BUTTON === true) { + Script.update.connect(rotateCloseButtons); + } +} + +function rotateCloseButtons() { + closeButtons.forEach(function(button) { + Entities.editEntity(button, { + angularVelocity: { + x: 0, + y: 0.5, + z: 0 + } + }) + + }) +} + +function subScribeToNewLights() { + Messages.subscribe('Hifi-Light-Mod-Receiver'); + Messages.messageReceived.connect(handleLightModMessages); +} + +function subscribeToSliderMessages() { + Messages.subscribe('Hifi-Slider-Value-Reciever'); + Messages.messageReceived.connect(handleValueMessages); +} + +function subscribeToLightOverlayRayCheckMessages() { + Messages.subscribe('Hifi-Light-Overlay-Ray-Check'); + Messages.messageReceived.connect(handleLightOverlayRayCheckMessages); +} + +function subscribeToCleanupMessages() { + Messages.subscribe('Hifi-Light-Modifier-Cleanup'); + Messages.messageReceived.connect(handleCleanupMessages); +} + + +function handleLightModMessages(channel, message, sender) { + if (channel !== 'Hifi-Light-Mod-Receiver') { + return; + } + if (sender !== MyAvatar.sessionUUID) { + return; + } + var parsedMessage = JSON.parse(message); + + makeSliders(parsedMessage.light); + light = parsedMessage.light.id + if (SHOW_LIGHT_VOLUME === true) { + selectionManager.setSelections([parsedMessage.light.id]); + } +} + +function handleValueMessages(channel, message, sender) { + + if (channel !== 'Hifi-Slider-Value-Reciever') { + return; + } + if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) { + return; + } + var parsedMessage = JSON.parse(message); + + slidersRef[parsedMessage.sliderType].setValueFromMessage(parsedMessage); +} + +var currentLight; +var block; +var oldParent = null; +var hasParent = false; + +function handleLightOverlayRayCheckMessages(channel, message, sender) { + if (channel !== 'Hifi-Light-Overlay-Ray-Check') { + return; + } + if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) { + return; + } + + var pickRay = JSON.parse(message); + + var doesIntersect = lightOverlayManager.findRayIntersection(pickRay); + // print('DOES INTERSECT A LIGHT WE HAVE???' + doesIntersect.intersects); + if (doesIntersect.intersects === true) { + // print('FULL MESSAGE:::' + JSON.stringify(doesIntersect)) + + var lightID = doesIntersect.entityID; + if (currentLight === lightID) { + // print('ALREADY HAVE A BLOCK, EXIT') + return; + } + + currentLight = lightID; + var lightProperties = Entities.getEntityProperties(lightID); + if (lightProperties.parentID !== DEFAULT_PARENT_ID) { + //this light has a parent already. so lets call our block the parent and then make sure not to delete it at the end; + oldParent = lightProperties.parentID; + hasParent = true; + block = lightProperties.parentID; + if (lightProperties.parentJointIndex !== -1) { + //should make sure to retain the parent too. but i don't actually know what the + } + } else { + block = createLightModel(lightProperties.position, lightProperties.rotation); + } + + var light = { + id: lightID, + type: 'spotlight', + initialProperties: lightProperties + } + + makeSliders(light); + + if (SHOW_LIGHT_VOLUME === true) { + selectionManager.setSelections([lightID]); + } + + Entities.editEntity(lightID, { + parentID: block, + parentJointIndex: -1 + }); + + } +} + +function handleCleanupMessages(channel, message, sender) { + + if (channel !== 'Hifi-Light-Modifier-Cleanup') { + return; + } + if (ONLY_I_CAN_EDIT === true && sender !== MyAvatar.sessionUUID) { + return; + } + if (message === 'callCleanup') { + cleanup(true); + } +} + +function updateSliderAxis() { + sliders.forEach(function(slider) { + + }) +} + +function cleanup(fromMessage) { + var i; + for (i = 0; i < sliders.length; i++) { + Entities.deleteEntity(sliders[i].axis); + Entities.deleteEntity(sliders[i].sliderIndicator); + Entities.deleteEntity(sliders[i].label); + } + + while (closeButtons.length > 0) { + Entities.deleteEntity(closeButtons.pop()); + } + + //if the light was already parented to something we will want to restore that. or come up with groups or something clever. + if (oldParent !== null) { + Entities.editEntity(currentLight, { + parentID: oldParent, + }); + } else { + Entities.editEntity(currentLight, { + parentID: null, + }); + } + + + if (fromMessage !== true) { + Messages.messageReceived.disconnect(handleLightModMessages); + Messages.messageReceived.disconnect(handleValueMessages); + Messages.messageReceived.disconnect(handleLightOverlayRayCheckMessages); + lightOverlayManager.setVisible(false); + } + + + Entities.deleteEntity(panel); + Entities.deleteEntity(visiblePanel); + + selectionManager.clearSelections(); + + if (ROTATE_CLOSE_BUTTON === true) { + Script.update.disconnect(rotateCloseButtons); + } + + if (hasParent === false) { + Entities.deleteEntity(block); + } + + oldParent = null; + hasParent = false; + currentLight = null; + sliders = []; + +} + +Script.scriptEnding.connect(cleanup); + +Script.scriptEnding.connect(function() { + lightOverlayManager.setVisible(false); +}) + + +subscribeToLightOverlayRayCheckMessages(); +subScribeToNewLights(); +subscribeToCleanupMessages(); + + + +//other light properties +// diffuseColor: { red: 255, green: 255, blue: 255 }, +// ambientColor: { red: 255, green: 255, blue: 255 }, +// specularColor: { red: 255, green: 255, blue: 255 }, +// constantAttenuation: 1, +// linearAttenuation: 0, +// quadraticAttenuation: 0, +// exponent: 0, +// cutoff: 180, // in degrees \ No newline at end of file diff --git a/examples/light_modifier/lightModifierTestScene.js b/examples/light_modifier/lightModifierTestScene.js new file mode 100644 index 0000000000..58956850f2 --- /dev/null +++ b/examples/light_modifier/lightModifierTestScene.js @@ -0,0 +1,73 @@ +// +// lightModifierTestScene.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Given a selected light, instantiate some entities that represent various values you can dynamically adjust. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var PARENT_SCRIPT_URL = Script.resolvePath('lightParent.js?' + Math.random(0 - 100)); +var basePosition, avatarRot; +avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); +basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(0, Quat.getUp(avatarRot))); + +var light; + +function createLight() { + var position = basePosition; + position.y += 2; + var lightTransform = evalLightWorldTransform(position, avatarRot); + var lightProperties = { + name: 'Hifi-Spotlight', + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 8 + }, + color: { + red: 255, + green: 0, + blue: 255 + }, + intensity: 0.035, + exponent: 1, + cutoff: 30, + lifetime: -1, + position: lightTransform.p, + rotation: lightTransform.q + }; + + light = Entities.addEntity(lightProperties); + +} + +function evalLightWorldTransform(modelPos, modelRot) { + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; +} + +function cleanup() { + Entities.deleteEntity(light); +} + +Script.scriptEnding.connect(cleanup); + +createLight(); \ No newline at end of file diff --git a/examples/light_modifier/lightParent.js b/examples/light_modifier/lightParent.js new file mode 100644 index 0000000000..2b53c05d0a --- /dev/null +++ b/examples/light_modifier/lightParent.js @@ -0,0 +1,40 @@ +// +// lightParent.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that tells the light parent to update the selection tool when we move it. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + (function() { + + function LightParent() { + return this; + } + + LightParent.prototype = { + preload: function(entityID) { + this.entityID = entityID; + var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + this.initialProperties = entityProperties + this.userData = JSON.parse(entityProperties.userData); + }, + startNearGrab: function() {}, + startDistantGrab: function() { + + }, + continueNearGrab: function() { + this.continueDistantGrab(); + }, + continueDistantGrab: function() { + Messages.sendMessage('entityToolUpdates', 'callUpdate'); + }, + + }; + + return new LightParent(); + }); \ No newline at end of file diff --git a/examples/light_modifier/slider.js b/examples/light_modifier/slider.js new file mode 100644 index 0000000000..e1dfea4e87 --- /dev/null +++ b/examples/light_modifier/slider.js @@ -0,0 +1,105 @@ +// +// slider.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that sends a scaled value to a light based on its distance from the start of its constraint axis. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + var AXIS_SCALE = 1; + var COLOR_MAX = 255; + var INTENSITY_MAX = 0.05; + var CUTOFF_MAX = 360; + var EXPONENT_MAX = 1; + + function Slider() { + return this; + } + + Slider.prototype = { + preload: function(entityID) { + this.entityID = entityID; + var entityProperties = Entities.getEntityProperties(this.entityID, "userData"); + var parsedUserData = JSON.parse(entityProperties.userData); + this.userData = parsedUserData.lightModifierKey; + }, + startNearGrab: function() { + this.setInitialProperties(); + }, + startDistantGrab: function() { + this.setInitialProperties(); + }, + setInitialProperties: function() { + this.initialProperties = Entities.getEntityProperties(this.entityID); + }, + continueNearGrab: function() { + // this.continueDistantGrab(); + }, + continueDistantGrab: function() { + this.setSliderValueBasedOnDistance(); + }, + setSliderValueBasedOnDistance: function() { + var currentPosition = Entities.getEntityProperties(this.entityID, "position").position; + + var distance = Vec3.distance(this.userData.axisStart, currentPosition); + + if (this.userData.sliderType === 'color_red' || this.userData.sliderType === 'color_green' || this.userData.sliderType === 'color_blue') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, COLOR_MAX); + } + if (this.userData.sliderType === 'intensity') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, INTENSITY_MAX); + } + if (this.userData.sliderType === 'cutoff') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, CUTOFF_MAX); + } + if (this.userData.sliderType === 'exponent') { + this.sliderValue = this.scaleValueBasedOnDistanceFromStart(distance, 0, EXPONENT_MAX); + }; + + this.sendValueToSlider(); + }, + releaseGrab: function() { + Entities.editEntity(this.entityID, { + velocity: { + x: 0, + y: 0, + z: 0 + }, + angularVelocity: { + x: 0, + y: 0, + z: 0 + } + }) + + this.sendValueToSlider(); + }, + scaleValueBasedOnDistanceFromStart: function(value, min2, max2) { + var min1 = 0; + var max1 = AXIS_SCALE; + var min2 = min2; + var max2 = max2; + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); + }, + sendValueToSlider: function() { + var _t = this; + var message = { + lightID: _t.userData.lightID, + sliderType: _t.userData.sliderType, + sliderValue: _t.sliderValue + } + Messages.sendMessage('Hifi-Slider-Value-Reciever', JSON.stringify(message)); + if (_t.userData.sliderType === 'cutoff') { + Messages.sendMessage('entityToolUpdates', 'callUpdate'); + } + } + }; + + return new Slider(); +}); \ No newline at end of file diff --git a/examples/light_modifier/visiblePanel.js b/examples/light_modifier/visiblePanel.js new file mode 100644 index 0000000000..cf6875fc59 --- /dev/null +++ b/examples/light_modifier/visiblePanel.js @@ -0,0 +1,40 @@ +// +// visiblePanel.js +// +// Created by James Pollack @imgntn on 12/15/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Entity script that disables picking on this panel. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + function VisiblePanel() { + return this; + } + + VisiblePanel.prototype = { + preload: function(entityID) { + this.entityID = entityID; + + var data = { + action: 'add', + id: this.entityID + }; + Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)) + }, + unload: function() { + var data = { + action: 'remove', + id: this.entityID + }; + Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)) + } + + }; + + return new VisiblePanel(); +}); \ No newline at end of file diff --git a/examples/painting/closePaint.js b/examples/painting/closePaint.js index 60b4ac2e25..bea4d9c9aa 100644 --- a/examples/painting/closePaint.js +++ b/examples/painting/closePaint.js @@ -43,6 +43,8 @@ var MAX_STROKE_WIDTH = 0.04; var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(Camera.getOrientation()))); +var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png"; + function MyController(hand, triggerAction) { @@ -148,7 +150,8 @@ function MyController(hand, triggerAction) { y: 50, z: 50 }, - lifetime: 200 + lifetime: 200, + textures: textureURL }); this.strokePoints = []; this.strokeNormals = []; diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 61d7291e11..374ec8b873 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -29,6 +29,8 @@ var MIN_STROKE_WIDTH = 0.0005; var MAX_STROKE_WIDTH = 0.03; + var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png"; + var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT, @@ -168,6 +170,7 @@ type: "PolyLine", name: "paintStroke", color: this.strokeColor, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png", dimensions: { x: 50, y: 50, diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index f3005495ec..f6c7aa77ca 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -247,4 +247,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -//Script.scriptEnding.connect(cleanup); +// Script.scriptEnding.connect(cleanup); diff --git a/examples/rayPickingFilterExample.js b/examples/rayPickingFilterExample.js new file mode 100644 index 0000000000..73d151847b --- /dev/null +++ b/examples/rayPickingFilterExample.js @@ -0,0 +1,73 @@ + // + // rayPickingFilterExample.js + // examples + // + // Created by Eric Levin on 12/24/2015 + // Copyright 2015 High Fidelity, Inc. + // + // This is an example script that demonstrates the use of filtering entities for ray picking + // + // Distributed under the Apache License, Version 2.0. + // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + // + + + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); + + var whiteListBox = Entities.addEntity({ + type: "Box", + color: { + red: 10, + green: 200, + blue: 10 + }, + dimensions: { + x: 0.2, + y: 0.2, + z: 0.2 + }, + position: center + }); + + var blackListBox = Entities.addEntity({ + type: "Box", + color: { + red: 100, + green: 10, + blue: 10 + }, + dimensions: { + x: 0.2, + y: 0.2, + z: 0.2 + }, + position: Vec3.sum(center, { + x: 0, + y: 0.3, + z: 0 + }) + }); + + + function castRay(event) { + var pickRay = Camera.computePickRay(event.x, event.y); + // In this example every entity will be pickable except the entities in the blacklist array + // the third argument is the whitelist array,and the fourth and final is the blacklist array + var pickResults = Entities.findRayIntersection(pickRay, true, [], [blackListBox]); + + // With below example, only entities added to whitelist will be pickable + // var pickResults = Entities.findRayIntersection(pickRay, true, [whiteListBox], []); + + if (pickResults.intersects) { + print("INTERSECTION!"); + } + + } + + function cleanup() { + Entities.deleteEntity(whiteListBox); + Entities.deleteEntity(blackListBox); + } + + Script.scriptEnding.connect(cleanup); + Controller.mousePressEvent.connect(castRay); \ No newline at end of file diff --git a/examples/tests/qmlWebTest.js b/examples/tests/qmlWebTest.js new file mode 100644 index 0000000000..f905e494dc --- /dev/null +++ b/examples/tests/qmlWebTest.js @@ -0,0 +1,32 @@ +print("Launching web window"); + +webWindow = new OverlayWebWindow('Test Event Bridge', "file:///C:/Users/bdavis/Git/hifi/examples/html/qmlWebTest.html", 320, 240, false); +print("JS Side window: " + webWindow); +print("JS Side bridge: " + webWindow.eventBridge); +webWindow.eventBridge.webEventReceived.connect(function(data) { + print("JS Side event received: " + data); +}); + +var titles = ["A", "B", "C"]; +var titleIndex = 0; + +Script.setInterval(function() { + webWindow.eventBridge.emitScriptEvent("JS Event sent"); + var size = webWindow.size; + var position = webWindow.position; + print("Window url: " + webWindow.url) + print("Window visible: " + webWindow.visible) + print("Window size: " + size.x + "x" + size.y) + print("Window pos: " + position.x + "x" + position.y) + webWindow.setVisible(!webWindow.visible); + webWindow.setTitle(titles[titleIndex]); + webWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100); + titleIndex += 1; + titleIndex %= titles.length; +}, 2 * 1000); + +Script.setTimeout(function() { + print("Closing script"); + webWindow.close(); + Script.stop(); +}, 15 * 1000) diff --git a/examples/toybox/bow/bow.js b/examples/toybox/bow/bow.js index f0128acc77..44fe33bb31 100644 --- a/examples/toybox/bow/bow.js +++ b/examples/toybox/bow/bow.js @@ -241,9 +241,9 @@ userData: JSON.stringify({ grabbableKey: { grabbable: false - } + }, + creatorSessionUUID: MyAvatar.sessionUUID }) - }); var makeArrowStick = function(entityA, entityB, collision) { diff --git a/examples/toybox/ping_pong_gun/createPingPongGun.js b/examples/toybox/ping_pong_gun/createPingPongGun.js index 4b93842896..7835dbf7f9 100644 --- a/examples/toybox/ping_pong_gun/createPingPongGun.js +++ b/examples/toybox/ping_pong_gun/createPingPongGun.js @@ -38,6 +38,14 @@ var pingPongGun = Entities.addEntity({ collisionSoundURL: COLLISION_SOUND_URL, userData: JSON.stringify({ grabbableKey: { + spatialKey: { + relativePosition: { + x: -0.05, + y: 0, + z: 0.0 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, -90) + }, invertSolidWhileHeld: true } }) diff --git a/examples/utilities/tools/cookies.js b/examples/utilities/tools/cookies.js index d9fa999a13..0e08f80d0e 100644 --- a/examples/utilities/tools/cookies.js +++ b/examples/utilities/tools/cookies.js @@ -384,10 +384,10 @@ var CHECK_MARK_COLOR = { y: newY }); Overlays.editOverlay(this.checkMark, { - y: newY + y: newY + (0.25 * this.thumbSize) }); Overlays.editOverlay(this.unCheckMark, { - y: newY + y: newY + (0.25 * this.thumbSize) }); }; @@ -399,10 +399,10 @@ var CHECK_MARK_COLOR = { y: this.y }); Overlays.editOverlay(this.checkMark, { - y: this.y + y: this.y + (0.25 * this.thumbSize) }); Overlays.editOverlay(this.unCheckMark, { - y: this.y + y: this.y+ (0.25 * this.thumbSize) }); }; @@ -814,12 +814,14 @@ var CHECK_MARK_COLOR = { }); }; - CollapsablePanelItem.prototype.destroy = function() { Overlays.deleteOverlay(this.title); Overlays.deleteOverlay(this.thumb); }; + CollapsablePanelItem.prototype.editTitle = function(opts) { + Overlays.editOverlay(this.title, opts); + }; CollapsablePanelItem.prototype.hide = function() { Overlays.editOverlay(this.title, { @@ -1531,4 +1533,4 @@ Controller.captureKeyEvents({ }); Controller.captureKeyEvents({ text: "right" -}); \ No newline at end of file +}); diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index cca97b7184..3e619dbd5a 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -1,6 +1,7 @@ // -// SunLightExample.js -// examples +// renderEngineDebug.js +// examples/utilities/tools +// // Sam Gateau // Copyright 2015 High Fidelity, Inc. // @@ -10,78 +11,93 @@ Script.include("cookies.js"); +var MENU = "Developer>Render>Debug Deferred Buffer"; +var ACTIONS = ["Off", "Diffuse", "Alpha", "Specular", "Roughness", "Normal", "Depth", "Lighting", "Custom"]; +var SETTINGS_KEY = "EngineDebugScript.DebugMode"; + +Number.prototype.clamp = function(min, max) { + return Math.min(Math.max(this, min), max); +}; + var panel = new Panel(10, 100); -function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) { - this.subPanel = panel.newSubPanel(name); +function CounterWidget(parentPanel, name, counter) { + var subPanel = parentPanel.newSubPanel(name); + var widget = parentPanel.items[name]; + widget.editTitle({ width: 270 }); - this.subPanel.newSlider("Num Feed", 0, 1, - function(value) { }, - feedGetter, - function(value) { return (value); }); - this.subPanel.newSlider("Num Drawn", 0, 1, - function(value) { }, - drawGetter, - function(value) { return (value); }); - this.subPanel.newSlider("Max Drawn", -1, 1, - capSetter, - capGetter, - function(value) { return (value); }); + subPanel.newSlider('Max Drawn', -1, 1, + function(value) { counter.maxDrawn = value; }, // setter + function() { return counter.maxDrawn; }, // getter + function(value) { return value; }); - this.update = function () { - var numFeed = this.subPanel.get("Num Feed"); - this.subPanel.set("Num Feed", numFeed); - this.subPanel.set("Num Drawn", this.subPanel.get("Num Drawn")); + var slider = subPanel.getWidget('Max Drawn'); - var numMax = Math.max(numFeed, 1); - this.subPanel.getWidget("Num Feed").setMaxValue(numMax); - this.subPanel.getWidget("Num Drawn").setMaxValue(numMax); - this.subPanel.getWidget("Max Drawn").setMaxValue(numMax); + this.update = function () { + var numDrawn = counter.numDrawn; // avoid double polling + var numMax = Math.max(numDrawn, 1); + var title = [ + ' ' + name, + numDrawn + ' / ' + counter.numFeed + ].join('\t'); + + widget.editTitle({ text: title }); + slider.setMaxValue(numMax); }; }; -var opaquesCounter = new CounterWidget(panel, "Opaques", - function () { return Scene.getEngineNumFeedOpaqueItems(); }, - function () { return Scene.getEngineNumDrawnOpaqueItems(); }, - function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); }, - function () { return Scene.getEngineMaxDrawnOpaqueItems(); } -); +var opaquesCounter = new CounterWidget(panel, "Opaques", Render.opaque); +var transparentsCounter = new CounterWidget(panel, "Transparents", Render.transparent); +var overlaysCounter = new CounterWidget(panel, "Overlays", Render.overlay3D); -var transparentsCounter = new CounterWidget(panel, "Transparents", - function () { return Scene.getEngineNumFeedTransparentItems(); }, - function () { return Scene.getEngineNumDrawnTransparentItems(); }, - function(value) { Scene.setEngineMaxDrawnTransparentItems(value); }, - function () { return Scene.getEngineMaxDrawnTransparentItems(); } -); +var resizing = false; +var previousMode = Settings.getValue(SETTINGS_KEY, -1); +Menu.addActionGroup(MENU, ACTIONS, ACTIONS[previousMode + 1]); +Render.deferredDebugMode = previousMode; +Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 }; // Reset to default size -var overlaysCounter = new CounterWidget(panel, "Overlays", - function () { return Scene.getEngineNumFeedOverlay3DItems(); }, - function () { return Scene.getEngineNumDrawnOverlay3DItems(); }, - function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); }, - function () { return Scene.getEngineMaxDrawnOverlay3DItems(); } -); +function setEngineDeferredDebugSize(eventX) { + var scaledX = (2.0 * (eventX / Window.innerWidth) - 1.0).clamp(-1.0, 1.0); + Render.deferredDebugSize = { x: scaledX, y: -1.0, z: 1.0, w: 1.0 }; +} +function shouldStartResizing(eventX) { + var x = Math.abs(eventX - Window.innerWidth * (1.0 + Render.deferredDebugSize.x) / 2.0); + var mode = Render.deferredDebugMode; + return mode !== -1 && x < 20; +} +function menuItemEvent(menuItem) { + var index = ACTIONS.indexOf(menuItem); + if (index >= 0) { + Render.deferredDebugMode = (index - 1); + } +} // see libraries/render/src/render/Engine.h var showDisplayStatusFlag = 1; var showNetworkStatusFlag = 2; panel.newCheckbox("Display status", - function(value) { Scene.setEngineDisplayItemStatus(value ? - Scene.doEngineDisplayItemStatus() | showDisplayStatusFlag : - Scene.doEngineDisplayItemStatus() & ~showDisplayStatusFlag); }, - function() { return (Scene.doEngineDisplayItemStatus() & showDisplayStatusFlag) > 0; }, + function(value) { Render.displayItemStatus = (value ? + Render.displayItemStatus | showDisplayStatusFlag : + Render.displayItemStatus & ~showDisplayStatusFlag); }, + function() { return (Render.displayItemStatus & showDisplayStatusFlag) > 0; }, function(value) { return (value & showDisplayStatusFlag) > 0; } ); panel.newCheckbox("Network/Physics status", - function(value) { Scene.setEngineDisplayItemStatus(value ? - Scene.doEngineDisplayItemStatus() | showNetworkStatusFlag : - Scene.doEngineDisplayItemStatus() & ~showNetworkStatusFlag); }, - function() { return (Scene.doEngineDisplayItemStatus() & showNetworkStatusFlag) > 0; }, + function(value) { Render.displayItemStatus = (value ? + Render.displayItemStatus | showNetworkStatusFlag : + Render.displayItemStatus & ~showNetworkStatusFlag); }, + function() { return (Render.displayItemStatus & showNetworkStatusFlag) > 0; }, function(value) { return (value & showNetworkStatusFlag) > 0; } ); +panel.newSlider("Tone Mapping Exposure", -10, 10, + function (value) { Render.tone.exposure = value; }, + function() { return Render.tone.exposure; }, + function (value) { return (value); }); + var tickTackPeriod = 500; function updateCounters() { @@ -91,11 +107,51 @@ function updateCounters() { } Script.setInterval(updateCounters, tickTackPeriod); -Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); }); -Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); }); -Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); }); +function mouseMoveEvent(event) { + if (resizing) { + setEngineDeferredDebugSize(event.x); + } else { + panel.mouseMoveEvent(event); + } +} + +function mousePressEvent(event) { + if (shouldStartResizing(event.x)) { + resizing = true; + } else { + panel.mousePressEvent(event); + } +} + +function mouseReleaseEvent(event) { + if (resizing) { + resizing = false; + } else { + panel.mouseReleaseEvent(event); + } +} + +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + +Menu.menuItemEvent.connect(menuItemEvent); function scriptEnding() { panel.destroy(); + Menu.removeActionGroup(MENU); + // Reset + Settings.setValue(SETTINGS_KEY, Render.deferredDebugMode); + Render.deferredDebugMode = -1; + Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 }; + Render.opaque.maxDrawn = -1; + Render.transparent.maxDrawn = -1; + Render.overlay3D.maxDrawn = -1; } Script.scriptEnding.connect(scriptEnding); + + +// Collapse items +panel.mousePressEvent({ x: panel.x, y: panel.items["Overlays"].y}); +panel.mousePressEvent({ x: panel.x, y: panel.items["Transparents"].y}); +panel.mousePressEvent({ x: panel.x, y: panel.items["Opaques"].y}); diff --git a/examples/winterSmashUp/targetPractice/shooterPlatform.js b/examples/winterSmashUp/targetPractice/shooterPlatform.js new file mode 100644 index 0000000000..4fae47a415 --- /dev/null +++ b/examples/winterSmashUp/targetPractice/shooterPlatform.js @@ -0,0 +1,73 @@ +// +// shooterPlatform.js +// examples/winterSmashUp/targetPractice +// +// Created by Thijs Wenker on 12/21/15. +// Copyright 2015 High Fidelity, Inc. +// +// The Winter Smash Up Target Practice Game using a bow. +// This is the platform that spawns the bow and attaches it to the avatars hand, +// then de-rez the bow on leaving to prevent walking up to the targets with the bow. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var _this = this; + + const GAME_CHANNEL = 'winterSmashUpGame'; + const SCRIPT_URL = Script.resolvePath('../../toybox/bow/bow.js'); + const MODEL_URL = "https://hifi-public.s3.amazonaws.com/models/bow/new/bow-deadly.fbx"; + const COLLISION_HULL_URL = "https://hifi-public.s3.amazonaws.com/models/bow/new/bow_collision_hull.obj"; + const RIGHT_HAND = 1; + const LEFT_HAND = 0; + + var bowEntity = undefined; + + _this.enterEntity = function(entityID) { + print('entered bow game entity'); + + // Triggers a recording on an assignment client: + Messages.sendMessage('PlayBackOnAssignment', 'BowShootingGameExplaination'); + + bowEntity = Entities.addEntity({ + name: 'Hifi-Bow-For-Game', + type: 'Model', + modelURL: MODEL_URL, + position: MyAvatar.position, + dimensions: {x: 0.04, y: 1.3, z: 0.21}, + collisionsWillMove: true, + gravity: {x: 0, y: 0, z: 0}, + shapeType: 'compound', + compoundShapeURL: COLLISION_HULL_URL, + script: SCRIPT_URL, + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true, + spatialKey: { + leftRelativePosition: { + x: 0.05, + y: 0.06, + z: -0.05 + }, + rightRelativePosition: { + x: -0.05, + y: 0.06, + z: -0.05 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90) + } + } + }) + }); + Messages.sendMessage('Hifi-Hand-Grab', JSON.stringify({hand: 'left', entityID: bowEntity})); + }; + + _this.leaveEntity = function(entityID) { + if (bowEntity !== undefined) { + Entities.deleteEntity(bowEntity); + bowEntity = undefined; + } + }; +}); diff --git a/examples/winterSmashUp/targetPractice/startTargetPractice.js b/examples/winterSmashUp/targetPractice/startTargetPractice.js new file mode 100644 index 0000000000..2e65d09fb1 --- /dev/null +++ b/examples/winterSmashUp/targetPractice/startTargetPractice.js @@ -0,0 +1,93 @@ +// +// startTargetPractice.js +// examples/winterSmashUp/targetPractice +// +// Created by Thijs Wenker on 12/21/15. +// Copyright 2015 High Fidelity, Inc. +// +// The Winter Smash Up Target Practice Game using a bow. +// This script starts the game, when the entity that contains the script gets shot. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var _this = this; + var waitForScriptLoad = false; + + const MAX_GAME_TIME = 60; //seconds + const SCRIPT_URL = 'http://s3.amazonaws.com/hifi-public/scripts/winterSmashUp/targetPractice/targetPracticeGame.js'; + const GAME_CHANNEL = 'winterSmashUpGame'; + + var isScriptRunning = function(script) { + script = script.toLowerCase().trim(); + var runningScripts = ScriptDiscoveryService.getRunning(); + for (i in runningScripts) { + if (runningScripts[i].url.toLowerCase().trim() == script) { + return true; + } + } + return false; + }; + + var sendStartSignal = function() { + Messages.sendMessage(GAME_CHANNEL, JSON.stringify({ + action: 'start', + gameEntityID: _this.entityID, + playerSessionUUID: MyAvatar.sessionUUID + })); + } + + var startGame = function() { + //TODO: check here if someone is already playing this game instance by verifying the userData for playerSessionID + // and startTime with a maximum timeout of X seconds (30?) + + + if (!isScriptRunning(SCRIPT_URL)) { + // Loads the script for the player if this isn't yet loaded + Script.load(SCRIPT_URL); + waitForScriptLoad = true; + return; + } + sendStartSignal(); + }; + + Messages.messageReceived.connect(function (channel, message, senderID) { + if (channel == GAME_CHANNEL) { + var data = JSON.parse(message); + switch (data.action) { + case 'scriptLoaded': + if (waitForPing) { + sendStartSignal(); + waitForPing = false; + } + break; + } + } + }); + + _this.preload = function(entityID) { + _this.entityID = entityID; + }; + + _this.collisionWithEntity = function(entityA, entityB, collisionInfo) { + if (entityA == _this.entityID) { + try { + var data = JSON.parse(Entities.getEntityProperties(entityB).userData); + if (data.creatorSessionUUID === MyAvatar.sessionUUID) { + print('attempting to startGame by collisionWithEntity.'); + startGame(); + } + } catch(e) { + } + } + }; + + _this.onShot = function(forceDirection) { + print('attempting to startGame by onShot.'); + startGame(); + }; + + Messages.subscribe(GAME_CHANNEL); +}); diff --git a/examples/winterSmashUp/targetPractice/targetPracticeGame.js b/examples/winterSmashUp/targetPractice/targetPracticeGame.js new file mode 100644 index 0000000000..d58258af5a --- /dev/null +++ b/examples/winterSmashUp/targetPractice/targetPracticeGame.js @@ -0,0 +1,227 @@ +// +// targetPracticeGame.js +// examples/winterSmashUp/targetPractice +// +// Created by Thijs Wenker on 12/21/15. +// Copyright 2015 High Fidelity, Inc. +// +// The Winter Smash Up Target Practice Game using a bow. +// This script runs on the client side (it is loaded through the platform trigger entity) +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +const GAME_CHANNEL = 'winterSmashUpGame'; +const SCORE_POST_URL = 'https://script.google.com/macros/s/AKfycbwZAMx6cMBx6-8NGEhR8ELUA-dldtpa_4P55z38Q4vYHW6kneg/exec'; +const MODEL_URL = 'http://cdn.highfidelity.com/chris/production/winter/game/'; +const MAX_GAME_TIME = 120; //seconds +const TARGET_CLOSE_OFFSET = 0.5; +const MILLISECS_IN_SEC = 1000; +const HIT_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Clay_Pigeon_02.L.wav'; +const GRAVITY = -9.8; +const MESSAGE_WIDTH = 100; +const MESSAGE_HEIGHT = 50; + +const TARGETS = [ + {pitch: -1, yaw: -20, maxDistance: 17}, + {pitch: -1, yaw: -15, maxDistance: 17}, + {pitch: -1, yaw: -10, maxDistance: 17}, + {pitch: -2, yaw: -5, maxDistance: 17}, + {pitch: -1, yaw: 0, maxDistance: 17}, + {pitch: 3, yaw: 10, maxDistance: 17}, + {pitch: -1, yaw: 15, maxDistance: 17}, + {pitch: -1, yaw: 20, maxDistance: 17}, + {pitch: 2, yaw: 25, maxDistance: 17}, + {pitch: 0, yaw: 30, maxDistance: 17} +]; + +var gameRunning = false; +var gameStartTime; +var gameEntityID; +var gameTimeoutTimer; +var targetEntities = []; +var hitSound = SoundCache.getSound(HIT_SOUND_URL); +var messageOverlay = undefined; +var messageExpire = 0; + +var clearMessage = function() { + if (messageOverlay !== undefined) { + Overlays.deleteOverlay(messageOverlay); + messageOverlay = undefined; + } +}; + +var displayMessage = function(message, timeout) { + clearMessage(); + messageExpire = Date.now() + timeout; + messageOverlay = Overlays.addOverlay('text', { + text: message, + x: (Window.innerWidth / 2) - (MESSAGE_WIDTH / 2), + y: (Window.innerHeight / 2) - (MESSAGE_HEIGHT / 2), + width: MESSAGE_WIDTH, + height: MESSAGE_HEIGHT, + alpha: 1, + backgroundAlpha: 0.0, + font: {size: 20} + }); +}; + +var getStatsText = function() { + var timeLeft = MAX_GAME_TIME - ((Date.now() - gameStartTime) / MILLISECS_IN_SEC); + return 'Time remaining: ' + timeLeft.toFixed(1) + 's\nTargets hit: ' + (TARGETS.length - targetEntities.length) + '/' + TARGETS.length; +}; + +const timerOverlayWidth = 50; +var timerOverlay = Overlays.addOverlay('text', { + text: '', + x: Window.innerWidth / 2 - (timerOverlayWidth / 2), + y: 100, + width: timerOverlayWidth, + alpha: 1, + backgroundAlpha: 0.0, + visible: false, + font: {size: 20} +}); + +var cleanRemainingTargets = function() { + while (targetEntities.length > 0) { + Entities.deleteEntity(targetEntities.pop()); + } +}; + +var createTarget = function(position, rotation, scale) { + var initialDimensions = {x: 1.8437, y: 0.1614, z: 1.8438}; + var dimensions = Vec3.multiply(initialDimensions, scale); + return Entities.addEntity({ + type: 'Model', + rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)), + lifetime: MAX_GAME_TIME, + modelURL: MODEL_URL + 'target.fbx', + shapeType: 'compound', + compoundShapeURL: MODEL_URL + 'targetCollision.obj', + dimensions: dimensions, + position: position + }); +}; + +var createTargetInDirection = function(startPosition, startRotation, pitch, yaw, maxDistance, scale) { + var directionQuat = Quat.multiply(startRotation, Quat.fromPitchYawRollDegrees(pitch, yaw, 0.0)); + var directionVec = Vec3.multiplyQbyV(directionQuat, Vec3.FRONT); + var intersection = Entities.findRayIntersection({direction: directionVec, origin: startPosition}, true); + var distance = maxDistance; + if (intersection.intersects && intersection.distance <= maxDistance) { + distance = intersection.distance - TARGET_CLOSE_OFFSET; + } + return createTarget(Vec3.sum(startPosition, Vec3.multiplyQbyV(directionQuat, Vec3.multiply(Vec3.FRONT, distance))), startRotation, scale); +}; + +var killTimer = function() { + if (gameTimeoutTimer !== undefined) { + Script.clearTimeout(gameTimeoutTimer); + gameTimeoutTimer = undefined; + } +}; + +var submitScore = function() { + gameRunning = false; + killTimer(); + Overlays.editOverlay(timerOverlay, {visible: false}); + if (!GlobalServices.username) { + displayMessage('Could not submit score, you are not logged in.', 5000); + return; + } + var timeItTook = Date.now() - gameStartTime; + var req = new XMLHttpRequest(); + req.open('POST', SCORE_POST_URL, false); + req.send(JSON.stringify({ + username: GlobalServices.username, + time: timeItTook / MILLISECS_IN_SEC + })); + displayMessage('Your score has been submitted!', 5000); +}; + +var onTargetHit = function(targetEntity, projectileEntity, collision) { + var targetIndex = targetEntities.indexOf(targetEntity); + if (targetIndex !== -1) { + try { + var data = JSON.parse(Entities.getEntityProperties(projectileEntity).userData); + if (data.creatorSessionUUID === MyAvatar.sessionUUID) { + this.audioInjector = Audio.playSound(hitSound, { + position: collision.contactPoint, + volume: 0.5 + }); + // Attach arrow to target for the nice effect + Entities.editEntity(projectileEntity, { + ignoreForCollisions: true, + parentID: targetEntity + }); + Entities.editEntity(targetEntity, { + collisionsWillMove: true, + gravity: {x: 0, y: GRAVITY, z: 0}, + velocity: {x: 0, y: -0.01, z: 0} + }); + targetEntities.splice(targetIndex, 1); + if (targetEntities.length === 0) { + submitScore(); + } + } + } catch(e) { + } + } +}; + +var startGame = function(entityID) { + cleanRemainingTargets(); + killTimer(); + gameEntityID = entityID; + targetEntities = []; + var parentEntity = Entities.getEntityProperties(gameEntityID); + for (var i in TARGETS) { + var target = TARGETS[i]; + var targetEntity = createTargetInDirection(parentEntity.position, parentEntity.rotation, target.pitch, target.yaw, target.maxDistance, 0.67); + Script.addEventHandler(targetEntity, 'collisionWithEntity', onTargetHit); + targetEntities.push(targetEntity); + } + gameStartTime = Date.now(); + gameTimeoutTimer = Script.setTimeout(function() { + cleanRemainingTargets(); + Overlays.editOverlay(timerOverlay, {visible: false}); + gameRunning = false; + displayMessage('Game timed out.', 5000); + }, MAX_GAME_TIME * MILLISECS_IN_SEC); + gameRunning = true; + Overlays.editOverlay(timerOverlay, {visible: true, text: getStatsText()}); + displayMessage('Game started! GO GO GO!', 3000); +}; + +Messages.messageReceived.connect(function (channel, message, senderID) { + if (channel == GAME_CHANNEL) { + var data = JSON.parse(message); + switch (data.action) { + case 'start': + if (data.playerSessionUUID === MyAvatar.sessionUUID) { + startGame(data.gameEntityID); + } + break; + } + } +}); + +Messages.subscribe(GAME_CHANNEL); +Messages.sendMessage(GAME_CHANNEL, JSON.stringify({action: 'scriptLoaded'})); + +Script.update.connect(function(deltaTime) { + if (gameRunning) { + Overlays.editOverlay(timerOverlay, {text: getStatsText()}); + } + if (messageOverlay !== undefined && Date.now() > messageExpire) { + clearMessage(); + } +}); + +Script.scriptEnding.connect(function() { + Overlays.deleteOverlay(timerOverlay); + cleanRemainingTargets(); + clearMessage(); +}); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 87cf1a384c..1d9557a835 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -45,7 +45,9 @@ else () list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP}) endif () -find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets WebSockets) +find_package(Qt5 COMPONENTS + Gui Multimedia Network OpenGL Qml Quick Script Svg + WebChannel WebEngine WebEngineWidgets WebKitWidgets WebSockets) # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) @@ -175,9 +177,17 @@ include_directories("${PROJECT_SOURCE_DIR}/src") target_link_libraries( ${TARGET_NAME} - Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets + Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL + Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg + Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets ) +# Issue causes build failure unless we add this directory. +# See https://bugreports.qt.io/browse/QTBUG-43351 +if (WIN32) + add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine) +endif() + # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) @@ -209,5 +219,9 @@ else (APPLE) endif() endif (APPLE) +if (WIN32) + set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/resources/qml") +endif() + package_libraries_for_deployment() consolidate_stack_components() diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index ae470509be..d63942c510 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -415,10 +415,25 @@ "states": [ { "id": "idle", - "interpTarget": 15, - "interpDuration": 15, + "interpTarget": 10, + "interpDuration": 10, "transitions": [ - { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingForward", "state": "idleToWalkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } + ] + }, + { + "id": "idleToWalkFwd", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "idleToWalkFwdOnDone", "state": "walkFwd" }, + { "var": "isNotMoving", "state": "idle" }, { "var": "isMovingBackward", "state": "walkBwd" }, { "var": "isMovingRight", "state": "strafeRight" }, { "var": "isMovingLeft", "state": "strafeLeft" }, @@ -429,7 +444,7 @@ }, { "id": "walkFwd", - "interpTarget": 6, + "interpTarget": 15, "interpDuration": 6, "transitions": [ { "var": "isNotMoving", "state": "idle" }, @@ -638,6 +653,18 @@ } ] }, + { + "id": "idleToWalkFwd", + "type": "clip", + "data": { + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims/idle_to_walk.fbx", + "startFrame": 1.0, + "endFrame": 19.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, { "id": "walkBwd", "type": "blendLinearMove", diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 947bf739fc..8f3cd0e1e7 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -1,6 +1,6 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtWebKit 3.0 +import QtWebEngine 1.1 import "controls" import "styles" @@ -39,9 +39,10 @@ VrDialog { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - anchors.bottom: scrollView.top + anchors.bottom: webview.top color: "white" } + Row { id: buttons spacing: 4 @@ -112,26 +113,22 @@ VrDialog { } } - ScrollView { - id: scrollView + WebEngineView { + id: webview + url: "http://highfidelity.com" anchors.top: buttons.bottom anchors.topMargin: 8 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right - WebView { - id: webview - url: "http://highfidelity.com" - anchors.fill: parent - onLoadingChanged: { - if (loadRequest.status == WebView.LoadSucceededStarted) { - addressBar.text = loadRequest.url - } - } - onIconChanged: { - barIcon.source = icon + onLoadingChanged: { + if (loadRequest.status == WebEngineView.LoadSucceededStatus) { + addressBar.text = loadRequest.url } } + onIconChanged: { + console.log("New icon: " + icon) + } } } // item @@ -146,5 +143,4 @@ VrDialog { break; } } - } // dialog diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml index 012f04f1fd..75b82342ca 100644 --- a/interface/resources/qml/InfoView.qml +++ b/interface/resources/qml/InfoView.qml @@ -2,7 +2,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.3 -import QtWebKit 3.0 +import QtWebEngine 1.1 import "controls" VrDialog { @@ -18,15 +18,11 @@ VrDialog { anchors.margins: parent.margins anchors.topMargin: parent.topMargin - ScrollView { + WebEngineView { + id: webview + objectName: "WebView" anchors.fill: parent - WebView { - objectName: "WebView" - id: webview - url: infoView.url - anchors.fill: parent - } - } - + url: infoView.url + } } } diff --git a/interface/resources/qml/MarketplaceDialog.qml b/interface/resources/qml/MarketplaceDialog.qml index 946f32e84a..3a66c5340f 100644 --- a/interface/resources/qml/MarketplaceDialog.qml +++ b/interface/resources/qml/MarketplaceDialog.qml @@ -2,7 +2,7 @@ import Hifi 1.0 import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.3 -import QtWebKit 3.0 +import QtWebEngine 1.1 import "controls" VrDialog { @@ -24,27 +24,22 @@ VrDialog { anchors.margins: parent.margins anchors.topMargin: parent.topMargin - - ScrollView { + WebEngineView { + objectName: "WebView" + id: webview + url: "https://metaverse.highfidelity.com/marketplace" anchors.fill: parent - WebView { - objectName: "WebView" - id: webview - url: "https://metaverse.highfidelity.com/marketplace" - anchors.fill: parent - onNavigationRequested: { - console.log(request.url) - if (!marketplaceDialog.navigationRequested(request.url)) { - console.log("Application absorbed the request") - request.action = WebView.IgnoreRequest; - return; - } - console.log("Application passed on the request") - request.action = WebView.AcceptRequest; + onNavigationRequested: { + console.log(request.url) + if (!marketplaceDialog.navigationRequested(request.url)) { + console.log("Application absorbed the request") + request.action = WebView.IgnoreRequest; return; - } + } + console.log("Application passed on the request") + request.action = WebView.AcceptRequest; + return; } - } - - } + } + } } diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml new file mode 100644 index 0000000000..6e9502edb2 --- /dev/null +++ b/interface/resources/qml/QmlWebWindow.qml @@ -0,0 +1,73 @@ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtWebEngine 1.1 +import QtWebChannel 1.0 +import QtWebSockets 1.0 + +import "controls" +import "styles" + +VrDialog { + id: root + HifiConstants { id: hifi } + title: "WebWindow" + resizable: true + // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer + destroyOnCloseButton: false + contentImplicitWidth: clientArea.implicitWidth + contentImplicitHeight: clientArea.implicitHeight + backgroundColor: "#7f000000" + property url source: "about:blank" + + signal navigating(string url) + function stop() { + webview.stop(); + } + + + Component.onCompleted: { + enabled = true + console.log("Web Window Created " + root); + webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { + console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); + }); + webview.loadingChanged.connect(handleWebviewLoading) + } + + + function handleWebviewLoading(loadRequest) { + if (WebEngineView.LoadStartedStatus == loadRequest.status) { + var newUrl = loadRequest.url.toString(); + root.navigating(newUrl) + } + } + + Item { + id: clientArea + implicitHeight: 600 + implicitWidth: 800 + x: root.clientX + y: root.clientY + width: root.clientWidth + height: root.clientHeight + + WebEngineView { + id: webview + url: root.source + anchors.fill: parent + onUrlChanged: { + var currentUrl = url.toString(); + var newUrl = urlFixer.fixupUrl(currentUrl); + if (newUrl != currentUrl) { + url = newUrl; + } + } + profile: WebEngineProfile { + id: webviewProfile + httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)" + storageName: "qmlWebEngine" + } + } + } // item +} // dialog diff --git a/interface/resources/qml/TestMenu.qml b/interface/resources/qml/TestMenu.qml index 4d109e6298..fe8a26e234 100644 --- a/interface/resources/qml/TestMenu.qml +++ b/interface/resources/qml/TestMenu.qml @@ -4,116 +4,7 @@ import Hifi 1.0 // Currently for testing a pure QML replacement menu Item { - Item { - objectName: "AllActions" - Action { - id: aboutApp - objectName: "HifiAction_" + MenuConstants.AboutApp - text: qsTr("About Interface") - } - - // - // File Menu - // - Action { - id: login - objectName: "HifiAction_" + MenuConstants.Login - text: qsTr("Login") - } - Action { - id: quit - objectName: "HifiAction_" + MenuConstants.Quit - text: qsTr("Quit") - //shortcut: StandardKey.Quit - shortcut: "Ctrl+Q" - } - - - // - // Edit menu - // - Action { - id: undo - text: "Undo" - shortcut: StandardKey.Undo - } - - Action { - id: redo - text: "Redo" - shortcut: StandardKey.Redo - } - - Action { - id: animations - objectName: "HifiAction_" + MenuConstants.Animations - text: qsTr("Animations...") - } - Action { - id: attachments - text: qsTr("Attachments...") - } - Action { - id: explode - text: qsTr("Explode on quit") - checkable: true - checked: true - } - Action { - id: freeze - text: qsTr("Freeze on quit") - checkable: true - checked: false - } - ExclusiveGroup { - Action { - id: visibleToEveryone - objectName: "HifiAction_" + MenuConstants.VisibleToEveryone - text: qsTr("Everyone") - checkable: true - checked: true - } - Action { - id: visibleToFriends - objectName: "HifiAction_" + MenuConstants.VisibleToFriends - text: qsTr("Friends") - checkable: true - } - Action { - id: visibleToNoOne - objectName: "HifiAction_" + MenuConstants.VisibleToNoOne - text: qsTr("No one") - checkable: true - } - } - } - Menu { objectName: "rootMenu"; - Menu { - title: "File" - MenuItem { action: login } - MenuItem { action: explode } - MenuItem { action: freeze } - MenuItem { action: quit } - } - Menu { - title: "Tools" - Menu { - title: "I Am Visible To" - MenuItem { action: visibleToEveryone } - MenuItem { action: visibleToFriends } - MenuItem { action: visibleToNoOne } - } - MenuItem { action: animations } - } - Menu { - title: "Long menu name top menu" - MenuItem { action: aboutApp } - } - Menu { - title: "Help" - MenuItem { action: aboutApp } - } } } diff --git a/interface/resources/qml/WebEntity.qml b/interface/resources/qml/WebEntity.qml index 0eb943cac7..ae94105672 100644 --- a/interface/resources/qml/WebEntity.qml +++ b/interface/resources/qml/WebEntity.qml @@ -1,10 +1,10 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtWebKit 3.0 +import QtWebEngine 1.1 -WebView { +WebEngineView { id: root - objectName: "webview" anchors.fill: parent + objectName: "webview" url: "about:blank" } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index db251c6966..724e72b7a5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -92,6 +92,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,7 @@ #include #include #include +#include #include "AnimDebugDraw.h" #include "AudioClient.h" @@ -344,6 +346,7 @@ bool setupEssentials(int& argc, char** argv) { #endif DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -365,6 +368,17 @@ Cube3DOverlay* _keyboardFocusHighlight{ nullptr }; int _keyboardFocusHighlightID{ -1 }; PluginContainer* _pluginContainer; + +// FIXME hack access to the internal share context for the Chromium helper +// Normally we'd want to use QWebEngine::initialize(), but we can't because +// our primary context is a QGLWidget, which can't easily be initialized to share +// from a QOpenGLContext. +// +// So instead we create a new offscreen context to share with the QGLWidget, +// and manually set THAT to be the shared context for the Chromium helper +OffscreenGLCanvas* _chromiumShareContext { nullptr }; +Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); + Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : QApplication(argc, argv), _dependencyManagerIsSetup(setupEssentials(argc, argv)), @@ -626,6 +640,11 @@ 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(); @@ -689,13 +708,37 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) { VrMenu::toggle(); // show context menu even on non-stereo displays } else if (action == controller::toInt(controller::Action::RETICLE_X)) { - auto globalPos = QCursor::pos(); - globalPos.setX(globalPos.x() + state); - QCursor::setPos(globalPos); + auto oldPos = QCursor::pos(); + auto newPos = oldPos; + newPos.setX(oldPos.x() + state); + QCursor::setPos(newPos); + + + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPosG = { oldPos.x(), oldPos.y() }; + glm::vec2 newPosG = { newPos.x(), newPos.y() }; + auto distance = glm::distance(oldPosG, newPosG); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Action::RETICLE_X... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG; + } + } else if (action == controller::toInt(controller::Action::RETICLE_Y)) { - auto globalPos = QCursor::pos(); - globalPos.setY(globalPos.y() + state); - QCursor::setPos(globalPos); + auto oldPos = QCursor::pos(); + auto newPos = oldPos; + newPos.setY(oldPos.y() + state); + QCursor::setPos(newPos); + + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPosG = { oldPos.x(), oldPos.y() }; + glm::vec2 newPosG = { newPos.x(), newPos.y() }; + auto distance = glm::distance(oldPosG, newPosG); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Action::RETICLE_Y... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG; + } } } }); @@ -709,9 +752,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float { return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); })); - _applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float { - return (float)qApp->getMyAvatar()->getCharacterController()->onGround(); - })); + _applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float { + return (float)qApp->getMyAvatar()->getCharacterController()->onGround(); + })); userInputMapper->registerDevice(_applicationStateDevice); @@ -1164,26 +1207,15 @@ void Application::paintGL() { if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { PerformanceTimer perfTimer("Mirror"); - auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); + auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + renderArgs._blitFramebuffer = DependencyManager::get()->getSelfieFramebuffer(); + renderRearViewMirror(&renderArgs, _mirrorViewRect); + + renderArgs._blitFramebuffer.reset(); renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - - { - float ratio = ((float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale()); - // Flip the src and destination rect horizontally to do the mirror - auto mirrorRect = glm::ivec4(0, 0, _mirrorViewRect.width() * ratio, _mirrorViewRect.height() * ratio); - auto mirrorRectDest = glm::ivec4(mirrorRect.z, mirrorRect.y, mirrorRect.x, mirrorRect.w); - - auto selfieFbo = DependencyManager::get()->getSelfieFramebuffer(); - gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { - batch.setFramebuffer(selfieFbo); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f)); - batch.blit(primaryFbo, mirrorRect, selfieFbo, mirrorRectDest); - batch.setFramebuffer(nullptr); - }); - } } { @@ -1263,15 +1295,14 @@ void Application::paintGL() { hmdOffset.x = -hmdOffset.x; _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0) + + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + mirrorBodyOrientation * hmdOffset); - } else { _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0) + + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); } @@ -1355,65 +1386,11 @@ void Application::paintGL() { renderArgs._context->setStereoProjections(eyeProjections); renderArgs._context->setStereoViews(eyeOffsets); } + renderArgs._blitFramebuffer = finalFramebuffer; displaySide(&renderArgs, _myCamera); + + renderArgs._blitFramebuffer.reset(); renderArgs._context->enableStereo(false); - - // Blit primary to final FBO - auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); - - if (renderArgs._renderMode == RenderArgs::MIRROR_RENDER_MODE) { - if (displayPlugin->isStereo()) { - gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { - gpu::Vec4i srcRectLeft; - srcRectLeft.z = size.width() / 2; - srcRectLeft.w = size.height(); - - gpu::Vec4i srcRectRight; - srcRectRight.x = size.width() / 2; - srcRectRight.z = size.width(); - srcRectRight.w = size.height(); - - gpu::Vec4i destRectLeft; - destRectLeft.x = srcRectLeft.z; - destRectLeft.z = srcRectLeft.x; - destRectLeft.y = srcRectLeft.y; - destRectLeft.w = srcRectLeft.w; - - gpu::Vec4i destRectRight; - destRectRight.x = srcRectRight.z; - destRectRight.z = srcRectRight.x; - destRectRight.y = srcRectRight.y; - destRectRight.w = srcRectRight.w; - - batch.setFramebuffer(finalFramebuffer); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); - // BLit left to right and right to left in stereo - batch.blit(primaryFbo, srcRectRight, finalFramebuffer, destRectLeft); - batch.blit(primaryFbo, srcRectLeft, finalFramebuffer, destRectRight); - }); - } else { - gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { - gpu::Vec4i srcRect; - srcRect.z = size.width(); - srcRect.w = size.height(); - gpu::Vec4i destRect; - destRect.x = size.width(); - destRect.y = 0; - destRect.z = 0; - destRect.w = size.height(); - batch.setFramebuffer(finalFramebuffer); - batch.blit(primaryFbo, srcRect, finalFramebuffer, destRect); - }); - } - } else { - gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { - gpu::Vec4i rect; - rect.z = size.width(); - rect.w = size.height(); - batch.setFramebuffer(finalFramebuffer); - batch.blit(primaryFbo, rect, finalFramebuffer, rect); - }); - } } // Overlay Composition, needs to occur after screen space effects have completed @@ -1421,7 +1398,9 @@ void Application::paintGL() { { PROFILE_RANGE(__FUNCTION__ "/compositor"); PerformanceTimer perfTimer("compositor"); + auto primaryFbo = finalFramebuffer; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFbo)); if (displayPlugin->isStereo()) { QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height())); @@ -1446,7 +1425,9 @@ void Application::paintGL() { { PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); PerformanceTimer perfTimer("pluginOutput"); + auto finalTexturePointer = finalFramebuffer->getRenderBuffer(0); + GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer); Q_ASSERT(0 != finalTexture); @@ -2574,7 +2555,7 @@ void Application::init() { _environment.init(); - DependencyManager::get()->init(this); + DependencyManager::get()->init(); DependencyManager::get()->init(); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); @@ -2992,6 +2973,9 @@ void Application::update(float deltaTime) { _physicsEngine->changeObjects(motionStates); myAvatar->prepareForPhysicsSimulation(); + _physicsEngine->forEachAction([&](EntityActionPointer action) { + action->prepareForPhysicsSimulation(); + }); getEntities()->getTree()->withWriteLock([&] { _physicsEngine->stepSimulation(); @@ -3404,7 +3388,7 @@ QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { renderArgs->_renderMode = RenderArgs::DEFAULT_RENDER_MODE; renderRearViewMirror(renderArgs, QRect(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), true); - auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); + auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); renderArgs->_context->downloadFramebuffer(primaryFbo, glm::ivec4(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), image); @@ -3674,34 +3658,19 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // For now every frame pass the renderContext { PerformanceTimer perfTimer("EngineRun"); - render::RenderContext renderContext; - auto sceneInterface = DependencyManager::get(); - - renderContext._cullOpaque = sceneInterface->doEngineCullOpaque(); - renderContext._sortOpaque = sceneInterface->doEngineSortOpaque(); - renderContext._renderOpaque = sceneInterface->doEngineRenderOpaque(); - renderContext._cullTransparent = sceneInterface->doEngineCullTransparent(); - renderContext._sortTransparent = sceneInterface->doEngineSortTransparent(); - renderContext._renderTransparent = sceneInterface->doEngineRenderTransparent(); - - renderContext._maxDrawnOpaqueItems = sceneInterface->getEngineMaxDrawnOpaqueItems(); - renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems(); - renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems(); - - renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus(); - if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) { - renderContext._drawItemStatus |= render::showNetworkStatusFlag; - } - renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect(); - - renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion); - renderContext._fxaaStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing); + auto renderInterface = DependencyManager::get(); + auto renderContext = renderInterface->getRenderContext(); renderArgs->_shouldRender = LODManager::shouldRender; - - renderContext.args = renderArgs; renderArgs->_viewFrustum = getDisplayViewFrustum(); + renderContext.setArgs(renderArgs); + + bool occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion); + bool antialiasingStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing); + bool showOwnedStatus = Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned); + renderContext.setOptions(occlusionStatus, antialiasingStatus, showOwnedStatus); + _renderEngine->setRenderContext(renderContext); // Before the deferred pass, let's try to use the render engine @@ -3709,15 +3678,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se _renderEngine->run(); myAvatar->endRenderRun(); - auto engineRC = _renderEngine->getRenderContext(); - sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems); - sceneInterface->setEngineDrawnOpaqueItems(engineRC->_numDrawnOpaqueItems); - - sceneInterface->setEngineFeedTransparentItems(engineRC->_numFeedTransparentItems); - sceneInterface->setEngineDrawnTransparentItems(engineRC->_numDrawnTransparentItems); - - sceneInterface->setEngineFeedOverlay3DItems(engineRC->_numFeedOverlay3DItems); - sceneInterface->setEngineDrawnOverlay3DItems(engineRC->_numDrawnOverlay3DItems); + auto engineContext = _renderEngine->getRenderContext(); + renderInterface->setItemCounts(engineContext->getItemsConfig()); } activeRenderingThread = nullptr; @@ -4143,6 +4105,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri LocationScriptingInterface::locationSetter); scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1); + scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor); scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Stats", Stats::getInstance()); @@ -4171,11 +4134,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); scriptEngine->registerGlobalObject("Scene", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Render", DependencyManager::get().data()); scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget()); } -bool Application::canAcceptURL(const QString& urlString) { +bool Application::canAcceptURL(const QString& urlString) const { QUrl url(urlString); if (urlString.startsWith(HIFI_URL_SCHEME)) { return true; diff --git a/interface/src/Application.h b/interface/src/Application.h index bb5c73f81b..9b1cffc659 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -38,6 +38,7 @@ #include #include #include +#include #include "avatar/AvatarUpdate.h" #include "avatar/MyAvatar.h" @@ -88,7 +89,7 @@ class Application; #endif #define qApp (static_cast(QCoreApplication::instance())) -class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface { +class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, public AbstractUriHandler { Q_OBJECT // TODO? Get rid of those @@ -219,8 +220,8 @@ public: QString getScriptsLocation(); void setScriptsLocation(const QString& scriptsLocation); - bool canAcceptURL(const QString& url); - bool acceptURL(const QString& url, bool defaultUpload = false); + virtual bool canAcceptURL(const QString& url) const override; + virtual bool acceptURL(const QString& url, bool defaultUpload = false) override; void setMaxOctreePacketsPerSecond(int maxOctreePPS); int getMaxOctreePacketsPerSecond(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ae22869273..85af912892 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1085,6 +1085,26 @@ void Menu::setGroupingIsVisible(const QString& grouping, bool isVisible) { QMenuBar::repaint(); } +void Menu::addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected) { + auto menu = addMenu(groupName); + + QActionGroup* actionGroup = new QActionGroup(menu); + actionGroup->setExclusive(true); + + auto menuScriptingInterface = MenuScriptingInterface::getInstance(); + for (auto action : actionList) { + auto item = addCheckableActionToQMenuAndActionHash(menu, action, 0, action == selected, + menuScriptingInterface, + SLOT(menuItemTriggered())); + actionGroup->addAction(item); + } + + QMenuBar::repaint(); +} + +void Menu::removeActionGroup(const QString& groupName) { + removeMenu(groupName); +} MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) { VrMenu::executeOrQueue([=](VrMenu* vrMenu) { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 55266cf062..4b77e96f79 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -105,6 +105,8 @@ public slots: void addMenuItem(const MenuItemProperties& properties); void removeMenuItem(const QString& menuName, const QString& menuitem); bool menuItemExists(const QString& menuName, const QString& menuitem); + void addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected = QString()); + void removeActionGroup(const QString& groupName); bool isOptionChecked(const QString& menuOption) const; void setIsOptionChecked(const QString& menuOption, bool isChecked); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 7a234f2b47..6094a0b9fe 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -99,7 +99,8 @@ Avatar::Avatar(RigPointer rig) : // we may have been created in the network thread, but we live in the main thread moveToThread(qApp->thread()); - setAvatarScale(1.0f); + setScale(glm::vec3(1.0f)); // avatar scale is uniform + // give the pointer to our head to inherited _headData variable from AvatarData _headData = static_cast(new Head(this)); _handData = static_cast(new Hand(this)); @@ -143,15 +144,35 @@ AABox Avatar::getBounds() const { float Avatar::getLODDistance() const { return DependencyManager::get()->getAvatarLODDistanceMultiplier() * - glm::distance(qApp->getCamera()->getPosition(), getPosition()) / getAvatarScale(); + glm::distance(qApp->getCamera()->getPosition(), getPosition()) / getUniformScale(); +} + +void Avatar::animateScaleChanges(float deltaTime) { + float currentScale = getUniformScale(); + if (currentScale != _targetScale) { + // use exponential decay toward _targetScale + const float SCALE_ANIMATION_TIMESCALE = 0.5f; + float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f); + float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * _targetScale; + + // snap to the end when we get close enough + const float MIN_RELATIVE_SCALE_ERROR = 0.03f; + if (fabsf(_targetScale - currentScale) / _targetScale < MIN_RELATIVE_SCALE_ERROR) { + animatedScale = _targetScale; + } + + setScale(glm::vec3(animatedScale)); // avatar scale is uniform + rebuildCollisionShape(); + } } void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); - if (getAvatarScale() != _targetScale) { - setAvatarScale(_targetScale); + if (!isDead() && !_motionState) { + DependencyManager::get()->addAvatarToSimulation(this); } + animateScaleChanges(deltaTime); // update the billboard render flag const float BILLBOARD_HYSTERESIS_PROPORTION = 0.1f; @@ -164,7 +185,7 @@ void Avatar::simulate(float deltaTime) { _shouldRenderBillboard = true; qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); } - + const bool isControllerLogging = DependencyManager::get()->getRenderDistanceControllerIsLogging(); float renderDistance = DependencyManager::get()->getRenderDistance(); const float SKIP_HYSTERESIS_PROPORTION = isControllerLogging ? 0.0f : BILLBOARD_HYSTERESIS_PROPORTION; @@ -210,7 +231,7 @@ void Avatar::simulate(float deltaTime) { _skeletonModel.getHeadPosition(headPosition); Head* head = getHead(); head->setPosition(headPosition); - head->setScale(getAvatarScale()); + head->setScale(getUniformScale()); head->simulate(deltaTime, false, _shouldRenderBillboard); } } @@ -238,12 +259,12 @@ void Avatar::simulate(float deltaTime) { measureMotionDerivatives(deltaTime); } -bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) { +bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const { const float HEAD_SPHERE_RADIUS = 0.1f; glm::vec3 theirLookAt = dynamic_pointer_cast(avatar)->getHead()->getLookAtPosition(); glm::vec3 myEyePosition = getHead()->getEyePosition(); - return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getAvatarScale()); + return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getUniformScale()); } void Avatar::slamPosition(const glm::vec3& newPosition) { @@ -423,7 +444,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { const float BASE_LIGHT_DISTANCE = 2.0f; const float LIGHT_EXPONENT = 1.0f; const float LIGHT_CUTOFF = glm::radians(80.0f); - float distance = BASE_LIGHT_DISTANCE * getAvatarScale(); + float distance = BASE_LIGHT_DISTANCE * getUniformScale(); glm::vec3 position = glm::mix(_skeletonModel.getTranslation(), getHead()->getFaceModel().getTranslation(), 0.9f); glm::quat orientation = getOrientation(); foreach (const AvatarManager::LocalLight& light, DependencyManager::get()->getLocalLights()) { @@ -436,7 +457,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel.isRenderable()) { PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); - _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); + const float BOUNDING_SHAPE_ALPHA = 0.7f; + _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA); } // If this is the avatar being looked at, render a little ball above their head @@ -479,7 +501,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } DependencyManager::get()->renderSolidSphereInstance(batch, - Transform(transform).postScale(eyeDiameter * getAvatarScale() / 2.0f + RADIUS_INCREMENT), + Transform(transform).postScale(eyeDiameter * getUniformScale() / 2.0f + RADIUS_INCREMENT), glm::vec4(LOOKING_AT_ME_COLOR, alpha)); position = getHead()->getRightEyePosition(); @@ -489,7 +511,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { eyeDiameter = DEFAULT_EYE_DIAMETER; } DependencyManager::get()->renderSolidSphereInstance(batch, - Transform(transform).postScale(eyeDiameter * getAvatarScale() / 2.0f + RADIUS_INCREMENT), + Transform(transform).postScale(eyeDiameter * getUniformScale() / 2.0f + RADIUS_INCREMENT), glm::vec4(LOOKING_AT_ME_COLOR, alpha)); } @@ -590,9 +612,9 @@ void Avatar::simulateAttachments(float deltaTime) { glm::quat jointRotation; if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) && _skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) { - model->setTranslation(jointPosition + jointRotation * attachment.translation * getAvatarScale()); + model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale()); model->setRotation(jointRotation * attachment.rotation); - model->setScaleToFit(true, getAvatarScale() * attachment.scale, true); // hack to force rescale + model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale model->setSnapModelToCenter(false); // hack to force resnap model->setSnapModelToCenter(true); model->simulate(deltaTime); @@ -647,7 +669,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) { } float Avatar::getBillboardSize() const { - return getAvatarScale() * BILLBOARD_DISTANCE * glm::tan(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f)); + return getUniformScale() * BILLBOARD_DISTANCE * glm::tan(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f)); } #ifdef DEBUG @@ -796,7 +818,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co } void Avatar::setSkeletonOffset(const glm::vec3& offset) { - const float MAX_OFFSET_LENGTH = getAvatarScale() * 0.5f; + const float MAX_OFFSET_LENGTH = getUniformScale() * 0.5f; float offsetLength = glm::length(offset); if (offsetLength > MAX_OFFSET_LENGTH) { _skeletonOffset = (MAX_OFFSET_LENGTH / offsetLength) * offset; @@ -905,7 +927,7 @@ glm::vec3 Avatar::getJointPosition(const QString& name) const { void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const { //Scale a world space vector as if it was relative to the position - positionToScale = getPosition() + getAvatarScale() * (positionToScale - getPosition()); + positionToScale = getPosition() + getUniformScale() * (positionToScale - getPosition()); } void Avatar::setFaceModelURL(const QUrl& faceModelURL) { @@ -945,7 +967,7 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { for (int i = 0; i < attachmentData.size(); i++) { _attachmentModels[i]->setURL(attachmentData.at(i).modelURL); _attachmentModels[i]->setSnapModelToCenter(true); - _attachmentModels[i]->setScaleToFit(true, getAvatarScale() * _attachmentData.at(i).scale); + _attachmentModels[i]->setScaleToFit(true, getUniformScale() * _attachmentData.at(i).scale); } } @@ -1033,15 +1055,6 @@ void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, g } } -void Avatar::setAvatarScale(float scale) { - if (_targetScale * (1.0f - RESCALING_TOLERANCE) < scale && - scale < _targetScale * (1.0f + RESCALING_TOLERANCE)) { - setScale(glm::vec3(_targetScale)); - } else { - setScale(glm::vec3(scale)); - } -} - float Avatar::getSkeletonHeight() const { Extents extents = _skeletonModel.getBindExtents(); return extents.maximum.y - extents.minimum.y; @@ -1053,7 +1066,7 @@ float Avatar::getHeadHeight() const { // HACK: We have a really odd case when fading out for some models where this value explodes float result = extents.maximum.y - extents.minimum.y; - if (result >= 0.0f && result < 100.0f * getAvatarScale() ) { + if (result >= 0.0f && result < 100.0f * getUniformScale() ) { return result; } } @@ -1096,13 +1109,21 @@ void Avatar::setShowDisplayName(bool showDisplayName) { // virtual void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { - shapeInfo.setCapsuleY(_skeletonModel.getBoundingCapsuleRadius(), 0.5f * _skeletonModel.getBoundingCapsuleHeight()); - shapeInfo.setOffset(_skeletonModel.getBoundingCapsuleOffset()); + float uniformScale = getUniformScale(); + shapeInfo.setCapsuleY(uniformScale * _skeletonModel.getBoundingCapsuleRadius(), + 0.5f * uniformScale * _skeletonModel.getBoundingCapsuleHeight()); + shapeInfo.setOffset(uniformScale * _skeletonModel.getBoundingCapsuleOffset()); +} + +void Avatar::setMotionState(AvatarMotionState* motionState) { + _motionState = motionState; } // virtual -void Avatar::rebuildSkeletonBody() { - DependencyManager::get()->updateAvatarPhysicsShape(this); +void Avatar::rebuildCollisionShape() { + if (_motionState) { + _motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); + } } glm::vec3 Avatar::getLeftPalmPosition() { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index cdb995efea..4926212b4d 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -36,7 +36,6 @@ namespace render { static const float SCALING_RATIO = .05f; static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1 -static const float RESCALING_TOLERANCE = .02f; static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees static const float BILLBOARD_DISTANCE = 5.56f; // meters @@ -89,7 +88,7 @@ public: SkeletonModel& getSkeletonModel() { return _skeletonModel; } const SkeletonModel& getSkeletonModel() const { return _skeletonModel; } glm::vec3 getChestPosition() const; - float getAvatarScale() const { return getScale().y; } + float getUniformScale() const { return getScale().y; } const Head* getHead() const { return static_cast(_headData); } Head* getHead() { return static_cast(_headData); } Hand* getHand() { return static_cast(_handData); } @@ -154,11 +153,10 @@ public: // (otherwise floating point error will cause problems at large positions). void applyPositionDelta(const glm::vec3& delta); - virtual void rebuildSkeletonBody(); + virtual void rebuildCollisionShape(); virtual void computeShapeInfo(ShapeInfo& shapeInfo); - void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; } AvatarMotionState* getMotionState() { return _motionState; } virtual void setPosition(const glm::vec3& position) override; @@ -173,6 +171,10 @@ public slots: glm::quat getRightPalmRotation(); protected: + friend class AvatarManager; + + void setMotionState(AvatarMotionState* motionState); + SkeletonModel _skeletonModel; glm::vec3 _skeletonOffset; QVector _attachmentModels; @@ -199,14 +201,15 @@ protected: float _stringLength; bool _moving; ///< set when position is changing - bool isLookingAtMe(AvatarSharedPointer avatar); - // protected methods... + bool isLookingAtMe(AvatarSharedPointer avatar) const; + + virtual void animateScaleChanges(float deltaTime); + glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; - void setAvatarScale(float scale); void measureMotionDerivatives(float deltaTime); float getSkeletonHeight() const; diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index fab838aa68..689d557c48 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -14,6 +14,7 @@ #include #include "avatar/AvatarManager.h" +#include "CharacterController.h" const uint16_t AvatarActionHold::holdVersion = 1; @@ -32,6 +33,64 @@ AvatarActionHold::~AvatarActionHold() { #endif } +bool AvatarActionHold::getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + MyCharacterController* controller = myAvatar ? myAvatar->getCharacterController() : nullptr; + if (!controller) { + qDebug() << "AvatarActionHold::getAvatarRigidBodyLocation failed to get character controller"; + return false; + } + controller->getRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation); + return true; +} + +void AvatarActionHold::prepareForPhysicsSimulation() { + auto avatarManager = DependencyManager::get(); + auto holdingAvatar = std::static_pointer_cast(avatarManager->getAvatarBySessionID(_holderID)); + + if (!holdingAvatar || !holdingAvatar->isMyAvatar()) { + return; + } + + withWriteLock([&]{ + if (_ignoreIK) { + return; + } + + glm::vec3 palmPosition; + glm::quat palmRotation; + if (_hand == "right") { + palmPosition = holdingAvatar->getRightPalmPosition(); + palmRotation = holdingAvatar->getRightPalmRotation(); + } else { + palmPosition = holdingAvatar->getLeftPalmPosition(); + palmRotation = holdingAvatar->getLeftPalmRotation(); + } + + glm::vec3 avatarRigidBodyPosition; + glm::quat avatarRigidBodyRotation; + getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation); + + // determine the difference in translation and rotation between the avatar's + // rigid body and the palm position. The avatar's rigid body will be moved by bullet + // between this call and the call to getTarget, below. A call to get*PalmPosition in + // getTarget would get the palm position of the previous location of the avatar (because + // bullet has moved the av's rigid body but the rigid body's location has not yet been + // copied out into the Avatar class. + //glm::quat avatarRotationInverse = glm::inverse(avatarRigidBodyRotation); + + // the offset should be in the frame of the avatar, but something about the order + // things are updated makes this wrong: + // _palmOffsetFromRigidBody = avatarRotationInverse * (palmPosition - avatarRigidBodyPosition); + // I'll leave it here as a comment in case avatar handling changes. + _palmOffsetFromRigidBody = palmPosition - avatarRigidBodyPosition; + + // rotation should also be needed, but again, the order of updates makes this unneeded. leaving + // code here for future reference. + // _palmRotationFromRigidBody = avatarRotationInverse * palmRotation; + }); +} + std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) { auto avatarManager = DependencyManager::get(); auto holdingAvatar = std::static_pointer_cast(avatarManager->getAvatarBySessionID(_holderID)); @@ -40,11 +99,11 @@ std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::ve return holdingAvatar; } - withTryReadLock([&]{ + withReadLock([&]{ bool isRightHand = (_hand == "right"); glm::vec3 palmPosition { Vectors::ZERO }; glm::quat palmRotation { Quaternions::IDENTITY }; - + if (_ignoreIK && holdingAvatar->isMyAvatar()) { // We cannot ignore other avatars IK and this is not the point of this option // This is meant to make the grabbing behavior more reactive. @@ -55,6 +114,31 @@ std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::ve palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getPosition(); palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getRotation(); } + } else if (holdingAvatar->isMyAvatar()) { + glm::vec3 avatarRigidBodyPosition; + glm::quat avatarRigidBodyRotation; + getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation); + + // the offset and rotation between the avatar's rigid body and the palm were determined earlier + // in prepareForPhysicsSimulation. At this point, the avatar's rigid body has been moved by bullet + // and the data in the Avatar class is stale. This means that the result of get*PalmPosition will + // be stale. Instead, determine the current palm position with the current avatar's rigid body + // location and the saved offsets. + + // this line is more correct but breaks for the current way avatar data is updated. + // palmPosition = avatarRigidBodyPosition + avatarRigidBodyRotation * _palmOffsetFromRigidBody; + // instead, use this for now: + palmPosition = avatarRigidBodyPosition + _palmOffsetFromRigidBody; + + // the item jitters the least by getting the rotation based on the opinion of Avatar.h rather + // than that of the rigid body. leaving this next line here for future reference: + // palmRotation = avatarRigidBodyRotation * _palmRotationFromRigidBody; + + if (isRightHand) { + palmRotation = holdingAvatar->getRightPalmRotation(); + } else { + palmRotation = holdingAvatar->getLeftPalmRotation(); + } } else { if (isRightHand) { palmPosition = holdingAvatar->getRightPalmPosition(); @@ -103,21 +187,19 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { if (valid && holdCount > 0) { position /= holdCount; - bool gotLock = withTryWriteLock([&]{ + withWriteLock([&]{ _positionalTarget = position; _rotationalTarget = rotation; _positionalTargetSet = true; _rotationalTargetSet = true; _active = true; }); - if (gotLock) { - if (_kinematic) { - doKinematicUpdate(deltaTimeStep); - } else { - activateBody(); - forceBodyNonStatic(); - ObjectActionSpring::updateActionWorker(deltaTimeStep); - } + if (_kinematic) { + doKinematicUpdate(deltaTimeStep); + } else { + activateBody(); + forceBodyNonStatic(); + ObjectActionSpring::updateActionWorker(deltaTimeStep); } } } diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 63f30a75d9..b97ec59780 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -25,18 +25,21 @@ public: AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity); virtual ~AvatarActionHold(); - virtual bool updateArguments(QVariantMap arguments); - virtual QVariantMap getArguments(); + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; - virtual void updateActionWorker(float deltaTimeStep); + virtual void updateActionWorker(float deltaTimeStep) override; QByteArray serialize() const; - virtual void deserialize(QByteArray serializedArguments); + virtual void deserialize(QByteArray serializedArguments) override; - virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); } + virtual bool shouldSuppressLocationEdits() override { return _active && !_ownerEntity.expired(); } + bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); std::shared_ptr getTarget(glm::quat& rotation, glm::vec3& position); + virtual void prepareForPhysicsSimulation() override; + private: void doKinematicUpdate(float deltaTimeStep); @@ -56,6 +59,10 @@ private: float _previousDeltaTimeStep = 0.0f; glm::vec3 _previousPositionalDelta; + + glm::vec3 _palmOffsetFromRigidBody; + // leaving this here for future refernece. + // glm::quat _palmRotationFromRigidBody; }; #endif // hifi_AvatarActionHold_h diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 7c1a52f1b3..312742e778 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -203,7 +203,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { while (fadingIterator != _avatarFades.end()) { auto avatar = std::static_pointer_cast(*fadingIterator); avatar->startUpdate(); - avatar->setTargetScale(avatar->getAvatarScale() * SHRINK_RATE); + avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE); if (avatar->getTargetScale() <= MIN_FADE_SCALE) { avatar->removeFromScene(*fadingIterator, scene, pendingChanges); fadingIterator = _avatarFades.erase(fadingIterator); @@ -223,14 +223,14 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { auto newAvatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); auto rawRenderableAvatar = std::static_pointer_cast(newAvatar); - + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; if (DependencyManager::get()->shouldRenderAvatars()) { rawRenderableAvatar->addToScene(rawRenderableAvatar, scene, pendingChanges); } scene->enqueuePendingChanges(pendingChanges); - + return newAvatar; } @@ -251,7 +251,7 @@ void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) { // virtual void AvatarManager::removeAvatar(const QUuid& sessionUUID) { QWriteLocker locker(&_hashLock); - + auto removedAvatar = _avatarHash.take(sessionUUID); if (removedAvatar) { handleRemovedAvatar(removedAvatar); @@ -260,7 +260,8 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) { void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar) { AvatarHashMap::handleRemovedAvatar(removedAvatar); - + + removedAvatar->die(); removeAvatarMotionState(removedAvatar); _avatarFades.push_back(removedAvatar); } @@ -268,7 +269,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar void AvatarManager::clearOtherAvatars() { // clear any avatars that came from an avatar-mixer QWriteLocker locker(&_hashLock); - + AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { auto avatar = std::static_pointer_cast(avatarIterator.value()); @@ -278,7 +279,7 @@ void AvatarManager::clearOtherAvatars() { } else { auto removedAvatar = avatarIterator.value(); avatarIterator = _avatarHash.erase(avatarIterator); - + handleRemovedAvatar(removedAvatar); } } @@ -374,20 +375,18 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents } } -void AvatarManager::updateAvatarPhysicsShape(Avatar* avatar) { - AvatarMotionState* motionState = avatar->getMotionState(); - if (motionState) { - motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); - } else { - ShapeInfo shapeInfo; - avatar->computeShapeInfo(shapeInfo); - btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); - if (shape) { - AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); - avatar->setMotionState(motionState); - _motionStatesToAdd.insert(motionState); - _avatarMotionStates.insert(motionState); - } +void AvatarManager::addAvatarToSimulation(Avatar* avatar) { + assert(!avatar->getMotionState()); + + ShapeInfo shapeInfo; + avatar->computeShapeInfo(shapeInfo); + btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); + if (shape) { + // we don't add to the simulation now, we put it on a list to be added later + AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); + avatar->setMotionState(motionState); + _motionStatesToAdd.insert(motionState); + _avatarMotionStates.insert(motionState); } } @@ -416,6 +415,6 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) if (sessionID == _myAvatar->getSessionUUID()) { return _myAvatar; } - + return findAvatar(sessionID); } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 84a4bc44b8..1b165495c3 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -52,7 +52,7 @@ public: glm::vec3 color; glm::vec3 direction; }; - + Q_INVOKABLE void setLocalLights(const QVector& localLights); Q_INVOKABLE QVector getLocalLights() const; // Currently, your own avatar will be included as the null avatar id. @@ -66,8 +66,8 @@ public: void handleOutgoingChanges(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); - void updateAvatarPhysicsShape(Avatar* avatar); - + void addAvatarToSimulation(Avatar* avatar); + // Expose results and parameter-tuning operations to other systems, such as stats and javascript. Q_INVOKABLE float getRenderDistance() { return _renderDistance; } Q_INVOKABLE float getRenderDistanceInverseLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); } @@ -80,7 +80,7 @@ public: Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); } Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); } Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue); - + public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } void updateAvatarRenderStatus(bool shouldRenderAvatars); @@ -90,19 +90,19 @@ private: AvatarManager(const AvatarManager& other); void simulateAvatarFades(float deltaTime); - + // virtual overrides virtual AvatarSharedPointer newSharedAvatar(); virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); void removeAvatarMotionState(AvatarSharedPointer avatar); - + virtual void removeAvatar(const QUuid& sessionUUID); virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar); - + QVector _avatarFades; std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. - + QVector _localLights; bool _shouldShowReceiveStats = false; diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 1c49705f23..b5101d2c70 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -46,7 +46,7 @@ public: virtual float getObjectFriction() const; virtual float getObjectLinearDamping() const; virtual float getObjectAngularDamping() const; - + virtual glm::vec3 getObjectPosition() const; virtual glm::quat getObjectRotation() const; virtual glm::vec3 getObjectLinearVelocity() const; diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 4f16449aa2..fe03d22f0b 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -37,7 +37,7 @@ void Hand::simulate(float deltaTime, bool isMine) { void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { float avatarScale = 1.0f; if (_owningAvatar) { - avatarScale = _owningAvatar->getAvatarScale(); + avatarScale = _owningAvatar->getUniformScale(); } const float alpha = 1.0f; @@ -62,7 +62,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { transform.setRotation(palm.getRotation()); transform.postScale(SPHERE_RADIUS); DependencyManager::get()->renderSolidSphereInstance(batch, transform, grayColor); - + // draw a green sphere at the old "finger tip" transform = Transform(); position = palm.getTipPosition(); @@ -72,7 +72,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { DependencyManager::get()->renderSolidSphereInstance(batch, transform, greenColor); } } - + const float AXIS_RADIUS = 0.1f * SPHERE_RADIUS; const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9331682714..c989f1dc71 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -287,10 +287,7 @@ extern void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avata void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); - if (getAvatarScale() != _targetScale) { - float scale = (1.0f - SMOOTHING_RATIO) * getAvatarScale() + SMOOTHING_RATIO * _targetScale; - setAvatarScale(scale); - } + animateScaleChanges(deltaTime); { PerformanceTimer perfTimer("transform"); @@ -337,7 +334,7 @@ void MyAvatar::simulate(float deltaTime) { headPosition = getPosition(); } head->setPosition(headPosition); - head->setScale(getAvatarScale()); + head->setScale(getUniformScale()); head->simulate(deltaTime, true); } @@ -512,25 +509,25 @@ glm::vec3 MyAvatar::getRightHandTipPosition() const { controller::Pose MyAvatar::getLeftHandPose() const { auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(), - palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); + palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandPose() const { auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(), - palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); + palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose(); } controller::Pose MyAvatar::getLeftHandTipPose() const { auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(), - palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); + palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandTipPose() const { auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(), - palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); + palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose(); } // virtual @@ -539,7 +536,7 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { if (!_shouldRender) { return; // exit early } - + Avatar::render(renderArgs, cameraPosition); } @@ -681,7 +678,7 @@ void MyAvatar::loadData() { _leanScale = loadSetting(settings, "leanScale", 0.05f); _targetScale = loadSetting(settings, "scale", 1.0f); - setAvatarScale(getAvatarScale()); + setScale(glm::vec3(_targetScale)); _animGraphUrl = settings.value("animGraphURL", "").toString(); _fullAvatarURLFromPreferences = settings.value("fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()).toUrl(); @@ -802,14 +799,14 @@ void MyAvatar::updateLookAtTargetAvatar() { const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; AvatarHash hash = DependencyManager::get()->getHashCopy(); - + foreach (const AvatarSharedPointer& avatarPointer, hash) { auto avatar = static_pointer_cast(avatarPointer); bool isCurrentTarget = avatar->getIsLookAtTarget(); float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition); avatar->setIsLookAtTarget(false); if (!avatar->isMyAvatar() && avatar->isInitialized() && - (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getAvatarScale())) { + (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getUniformScale())) { float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - cameraPosition)); if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) { _lookAtTargetAvatar = avatarPointer; @@ -1036,14 +1033,15 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { return Avatar::getPosition(); } -void MyAvatar::rebuildSkeletonBody() { +void MyAvatar::rebuildCollisionShape() { // compute localAABox - float radius = _skeletonModel.getBoundingCapsuleRadius(); - float height = _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius; + float scale = getUniformScale(); + float radius = scale * _skeletonModel.getBoundingCapsuleRadius(); + float height = scale * _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius; glm::vec3 corner(-radius, -0.5f * height, -radius); - corner += _skeletonModel.getBoundingCapsuleOffset(); - glm::vec3 scale(2.0f * radius, height, 2.0f * radius); - _characterController.setLocalBoundingBox(corner, scale); + corner += scale * _skeletonModel.getBoundingCapsuleOffset(); + glm::vec3 diagonal(2.0f * radius, height, 2.0f * radius); + _characterController.setLocalBoundingBox(corner, diagonal); } void MyAvatar::prepareForPhysicsSimulation() { @@ -1177,7 +1175,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl if (!_skeletonModel.isRenderable()) { return; // wait until all models are loaded } - + fixupModelsInScene(); // Render head so long as the camera isn't inside it @@ -1331,7 +1329,7 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; bool MyAvatar::cameraInsideHead() const { const Head* head = getHead(); const glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); - return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getAvatarScale()); + return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { @@ -1455,11 +1453,11 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe if (isHovering) { // we're flying --> complex acceleration curve with high max speed float motorSpeed = glm::length(_keyboardMotorVelocity); - float finalMaxMotorSpeed = getAvatarScale() * MAX_KEYBOARD_MOTOR_SPEED; + float finalMaxMotorSpeed = getUniformScale() * MAX_KEYBOARD_MOTOR_SPEED; float speedGrowthTimescale = 2.0f; float speedIncreaseFactor = 1.8f; motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor; - const float maxBoostSpeed = getAvatarScale() * MAX_BOOST_SPEED; + const float maxBoostSpeed = getUniformScale() * MAX_BOOST_SPEED; if (motorSpeed < maxBoostSpeed) { // an active keyboard motor should never be slower than this float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 514261bf16..019ba0f992 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -249,7 +249,7 @@ public slots: Q_INVOKABLE void updateMotionBehaviorFromMenu(); - virtual void rebuildSkeletonBody() override; + virtual void rebuildCollisionShape() override; Q_INVOKABLE QUrl getAnimGraphUrl() const { return _animGraphUrl; } diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index 23d601e58e..c7f2945757 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -32,7 +32,7 @@ MyCharacterController::~MyCharacterController() { void MyCharacterController::updateShapeIfNecessary() { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { - _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; + _pendingFlags &= ~PENDING_FLAG_UPDATE_SHAPE; // compute new dimensions from avatar's bounding box float x = _boxScale.x; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 342f8315e1..e8d952973b 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -69,7 +69,7 @@ void SkeletonModel::initJointStates() { _headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z); _headClipDistance = std::max(_headClipDistance, DEFAULT_NEAR_CLIP); - _owningAvatar->rebuildSkeletonBody(); + _owningAvatar->rebuildCollisionShape(); emit skeletonLoaded(); } @@ -338,35 +338,38 @@ void SkeletonModel::computeBoundingShape() { return; } - _rig->computeAvatarBoundingCapsule(geometry, - _boundingCapsuleRadius, - _boundingCapsuleHeight, - _boundingCapsuleLocalOffset); + float radius, height; + glm::vec3 offset; + _rig->computeAvatarBoundingCapsule(geometry, radius, height, offset); + float invScale = 1.0f / _owningAvatar->getUniformScale(); + _boundingCapsuleRadius = invScale * radius; + _boundingCapsuleHeight = invScale * height; + _boundingCapsuleLocalOffset = invScale * offset; } -void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { +void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float scale, float alpha) { auto geometryCache = DependencyManager::get(); auto deferredLighting = DependencyManager::get(); // draw a blue sphere at the capsule top point - glm::vec3 topPoint = _translation + getRotation() * (_boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f)); + glm::vec3 topPoint = _translation + getRotation() * (scale * (_boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * Vectors::UNIT_Y)); deferredLighting->renderSolidSphereInstance(batch, - Transform().setTranslation(topPoint).postScale(_boundingCapsuleRadius), + Transform().setTranslation(topPoint).postScale(scale * _boundingCapsuleRadius), glm::vec4(0.6f, 0.6f, 0.8f, alpha)); // draw a yellow sphere at the capsule bottom point - glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, _boundingCapsuleHeight, 0.0f); + glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, scale * _boundingCapsuleHeight, 0.0f); glm::vec3 axis = topPoint - bottomPoint; deferredLighting->renderSolidSphereInstance(batch, - Transform().setTranslation(bottomPoint).postScale(_boundingCapsuleRadius), + Transform().setTranslation(bottomPoint).postScale(scale * _boundingCapsuleRadius), glm::vec4(0.8f, 0.8f, 0.6f, alpha)); // draw a green cylinder between the two points glm::vec3 origin(0.0f); batch.setModelTransform(Transform().setTranslation(bottomPoint)); deferredLighting->bindSimpleProgram(batch); - Avatar::renderJointConnectingCone(batch, origin, axis, _boundingCapsuleRadius, _boundingCapsuleRadius, + Avatar::renderJointConnectingCone(batch, origin, axis, scale * _boundingCapsuleRadius, scale * _boundingCapsuleRadius, glm::vec4(0.6f, 0.8f, 0.6f, alpha)); } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 9b73119238..7541a002dc 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -89,7 +89,7 @@ public: /// \return whether or not the head was found. glm::vec3 getDefaultEyeModelPosition() const; - void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha); + void renderBoundingCollisionShapes(gpu::Batch& batch, float scale, float alpha); float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; } float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; } const glm::vec3 getBoundingCapsuleOffset() const { return _boundingCapsuleLocalOffset; } diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index ff7784b9ae..087d391daa 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -84,6 +84,19 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& return result; } +void MenuScriptingInterface::addActionGroup(const QString& groupName, const QStringList& actionList, + const QString& selected) { + QMetaObject::invokeMethod(Menu::getInstance(), "addActionGroup", + Q_ARG(const QString&, groupName), + Q_ARG(const QStringList&, actionList), + Q_ARG(const QString&, selected)); +} + +void MenuScriptingInterface::removeActionGroup(const QString& groupName) { + QMetaObject::invokeMethod(Menu::getInstance(), "removeActionGroup", + Q_ARG(const QString&, groupName)); +} + bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { bool result; QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection, diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index 5c01318a38..51399c2fa5 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -42,6 +42,10 @@ public slots: void removeMenuItem(const QString& menuName, const QString& menuitem); bool menuItemExists(const QString& menuName, const QString& menuitem); + void addActionGroup(const QString& groupName, const QStringList& actionList, + const QString& selected = QString()); + void removeActionGroup(const QString& groupName); + bool isOptionChecked(const QString& menuOption); void setIsOptionChecked(const QString& menuOption, bool isChecked); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 99285c6558..abc3520c83 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -34,7 +34,6 @@ static const quint64 MSECS_TO_USECS = 1000ULL; static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS; -static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; static const float reticleSize = TWO_PI / 100.0f; static const float CURSOR_PIXEL_SIZE = 32.0f; @@ -386,7 +385,7 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction); float t; - if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getAvatarScale(), &t)){ + if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getUniformScale(), &t)){ result = position + direction * t; return true; } diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 9a995263cd..a38cc13100 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -199,7 +199,7 @@ void PreferencesDialog::loadPreferences() { ui.leanScaleSpin->setValue(myAvatar->getLeanScale()); - ui.avatarScaleSpin->setValue(myAvatar->getAvatarScale()); + ui.avatarScaleSpin->setValue(myAvatar->getUniformScale()); ui.avatarAnimationEdit->setText(myAvatar->getAnimGraphUrl().toString()); ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond()); @@ -256,7 +256,7 @@ void PreferencesDialog::savePreferences() { myAvatar->getHead()->setPupilDilation(ui.pupilDilationSlider->value() / (float)ui.pupilDilationSlider->maximum()); myAvatar->setLeanScale(ui.leanScaleSpin->value()); - myAvatar->setClampedTargetScale(ui.avatarScaleSpin->value()); + myAvatar->setTargetScaleVerbose(ui.avatarScaleSpin->value()); if (myAvatar->getAnimGraphUrl() != ui.avatarAnimationEdit->text()) { // If changed, destroy the old and start with the new myAvatar->setAnimGraphUrl(ui.avatarAnimationEdit->text()); } diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 11fb647f01..f1e0f08a3a 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -26,13 +26,15 @@ QString const Image3DOverlay::TYPE = "image3d"; Image3DOverlay::Image3DOverlay() { - _isLoaded = false; + _isLoaded = false; + _emissive = false; } Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) : Billboard3DOverlay(image3DOverlay), _url(image3DOverlay->_url), _texture(image3DOverlay->_texture), + _emissive(image3DOverlay->_emissive), _fromImage(image3DOverlay->_fromImage) { } @@ -93,8 +95,8 @@ void Image3DOverlay::render(RenderArgs* args) { batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); - - DependencyManager::get()->bindSimpleProgram(*batch, true, false, false, true); + + DependencyManager::get()->bindSimpleProgram(*batch, true, false, _emissive, true); DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) @@ -144,6 +146,11 @@ void Image3DOverlay::setProperties(const QScriptValue &properties) { setClipFromSource(subImageRect); } } + + QScriptValue emissiveValue = properties.property("emissive"); + if (emissiveValue.isValid()) { + _emissive = emissiveValue.toBool(); + } } QScriptValue Image3DOverlay::getProperty(const QString& property) { @@ -156,6 +163,9 @@ QScriptValue Image3DOverlay::getProperty(const QString& property) { if (property == "offsetPosition") { return vec3toScriptValue(_scriptEngine, getOffsetPosition()); } + if (property == "emissive") { + return _emissive; + } return Billboard3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/Image3DOverlay.h b/interface/src/ui/overlays/Image3DOverlay.h index f848023fbe..f2cc3789ee 100644 --- a/interface/src/ui/overlays/Image3DOverlay.h +++ b/interface/src/ui/overlays/Image3DOverlay.h @@ -46,6 +46,7 @@ public: private: QString _url; NetworkTexturePointer _texture; + bool _emissive; QRect _fromImage; // where from in the image to sample }; diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index 1b1c48c3ca..753106fa0a 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -68,7 +68,7 @@ namespace render { glm::vec3 myAvatarPosition = avatar->getPosition(); float angle = glm::degrees(glm::angle(myAvatarRotation)); glm::vec3 axis = glm::axis(myAvatarRotation); - float myAvatarScale = avatar->getAvatarScale(); + float myAvatarScale = avatar->getUniformScale(); Transform transform = Transform(); transform.setTranslation(myAvatarPosition); transform.setRotation(glm::angleAxis(angle, axis)); diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 3be6a0f1b8..609b464512 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -122,5 +122,5 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) { auto clipNode = std::dynamic_pointer_cast(_children.front()); assert(clipNode); const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; - _phase = fmodf(frame, NUM_FRAMES); + _phase = fmodf(frame / NUM_FRAMES, 1.0f); } diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index c9c6e43a02..8e3d716aac 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -140,6 +140,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) } #ifndef NDEBUG +#define DUMP_FBX_JOINTS void AnimSkeleton::dump() const { qCDebug(animation) << "["; for (int i = 0; i < getNumJoints(); i++) { @@ -151,21 +152,22 @@ void AnimSkeleton::dump() const { qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i); qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i); #ifdef DUMP_FBX_JOINTS - qCDebug(animation) << " isFree =" << _joints[i].isFree; - qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage; - qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex; - qCDebug(animation) << " translation =" << _joints[i].translation; - qCDebug(animation) << " preTransform =" << _joints[i].preTransform; - qCDebug(animation) << " preRotation =" << _joints[i].preRotation; - qCDebug(animation) << " rotation =" << _joints[i].rotation; - qCDebug(animation) << " postRotation =" << _joints[i].postRotation; - qCDebug(animation) << " postTransform =" << _joints[i].postTransform; - qCDebug(animation) << " transform =" << _joints[i].transform; - qCDebug(animation) << " rotationMin =" << _joints[i].rotationMin << ", rotationMax =" << _joints[i].rotationMax; - qCDebug(animation) << " inverseDefaultRotation" << _joints[i].inverseDefaultRotation; - qCDebug(animation) << " inverseBindRotation" << _joints[i].inverseBindRotation; - qCDebug(animation) << " bindTransform" << _joints[i].bindTransform; - qCDebug(animation) << " isSkeletonJoint" << _joints[i].isSkeletonJoint; + qCDebug(animation) << " fbxJoint ="; + qCDebug(animation) << " isFree =" << _joints[i].isFree; + qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage; + qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex; + qCDebug(animation) << " translation =" << _joints[i].translation; + qCDebug(animation) << " preTransform =" << _joints[i].preTransform; + qCDebug(animation) << " preRotation =" << _joints[i].preRotation; + qCDebug(animation) << " rotation =" << _joints[i].rotation; + qCDebug(animation) << " postRotation =" << _joints[i].postRotation; + qCDebug(animation) << " postTransform =" << _joints[i].postTransform; + qCDebug(animation) << " transform =" << _joints[i].transform; + qCDebug(animation) << " rotationMin =" << _joints[i].rotationMin << ", rotationMax =" << _joints[i].rotationMax; + qCDebug(animation) << " inverseDefaultRotation" << _joints[i].inverseDefaultRotation; + qCDebug(animation) << " inverseBindRotation" << _joints[i].inverseBindRotation; + qCDebug(animation) << " bindTransform" << _joints[i].bindTransform; + qCDebug(animation) << " isSkeletonJoint" << _joints[i].isSkeletonJoint; #endif if (getParentIndex(i) >= 0) { qCDebug(animation) << " parent =" << getJointName(getParentIndex(i)); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index a0d78da7f8..1f15007339 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -289,7 +289,6 @@ uint64_t AudioInjector::injectNextFrame() { _currentSendOffset = 0; } else { // we weren't to loop, say that we're done now - qDebug() << "AudioInjector::injectNextFrame has sent all data and was not asked to loop - calling finish()."; finish(); return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d241df7e0e..2ed9a47e02 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -90,7 +90,7 @@ const QUrl& AvatarData::defaultFullAvatarModelUrl() { // There are a number of possible strategies for this set of tools through endRender, below. void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) { avatarLock.lock(); - Transform trans; + Transform trans = getTransform(); trans.setTranslation(position); trans.setRotation(orientation); SpatiallyNestable::setTransform(trans); @@ -129,13 +129,10 @@ float AvatarData::getTargetScale() const { } void AvatarData::setTargetScale(float targetScale) { - _targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, targetScale)); + _targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); } -void AvatarData::setClampedTargetScale(float targetScale) { - - targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); - +void AvatarData::setTargetScaleVerbose(float targetScale) { setTargetScale(targetScale); qCDebug(avatars) << "Changed scale to " << _targetScale; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1fa33ff606..64c215cee7 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -172,7 +172,7 @@ public: AvatarData(); virtual ~AvatarData(); - + static const QUrl& defaultFullAvatarModelUrl(); virtual bool isMyAvatar() const { return false; } @@ -237,7 +237,7 @@ public: // Scale float getTargetScale() const; void setTargetScale(float targetScale); - void setClampedTargetScale(float targetScale); + void setTargetScaleVerbose(float targetScale); // Hand State Q_INVOKABLE void setHandState(char s) { _handState = s; } @@ -261,7 +261,7 @@ public: Q_INVOKABLE bool isJointDataValid(const QString& name) const; Q_INVOKABLE glm::quat getJointRotation(const QString& name) const; Q_INVOKABLE glm::vec3 getJointTranslation(const QString& name) const; - + Q_INVOKABLE virtual QVector getJointRotations() const; Q_INVOKABLE virtual void setJointRotations(QVector jointRotations); Q_INVOKABLE virtual void setJointTranslations(QVector jointTranslations); @@ -342,6 +342,9 @@ public: glm::vec3 getClientGlobalPosition() { return _globalPosition; } + void die() { _isDead = true; } + bool isDead() const { return _isDead; } + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); @@ -413,6 +416,8 @@ protected: // updates about one avatar to another. glm::vec3 _globalPosition; + bool _isDead { false }; + private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 5d783a671e..7ba23b01ad 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -14,7 +14,7 @@ #include #include -#include "AvatarData.h" +#include "AvatarData.h" #include "HandData.h" @@ -38,7 +38,7 @@ PalmData& HandData::addNewPalm(Hand whichHand) { PalmData HandData::getCopyOfPalmData(Hand hand) const { QReadLocker locker(&_palmsLock); - // the palms are not necessarily added in left-right order, + // the palms are not necessarily added in left-right order, // so we have to search for the correct hand for (const auto& palm : _palms) { if (palm.whichHand() == hand && palm.isActive()) { @@ -64,7 +64,7 @@ void PalmData::addToPosition(const glm::vec3& delta) { _rawPosition += _owningHandData->worldToLocalVector(delta); } -bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, +bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, const PalmData*& collidingPalm) const { QReadLocker locker(&_palmsLock); @@ -93,7 +93,7 @@ glm::vec3 HandData::getBasePosition() const { float HandData::getBaseScale() const { return _owningAvatarData->getTargetScale(); } - + glm::vec3 PalmData::getFingerDirection() const { // finger points along yAxis in hand-frame const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 1.0f, 0.0f); diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index d782f240ee..34ed610f80 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -38,7 +38,7 @@ public: HandData(AvatarData* owningAvatar); virtual ~HandData() {} - + // position conversion glm::vec3 localToWorldPosition(const glm::vec3& localPosition) { return getBasePosition() + getBaseOrientation() * localPosition * getBaseScale(); @@ -60,7 +60,7 @@ public: /// \param penetration[out] the vector in which to store the penetration /// \param collidingPalm[out] a const PalmData* to the palm that was collided with /// \return whether or not the sphere penetrated - bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, + bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, const PalmData*& collidingPalm) const; glm::quat getBaseOrientation() const; @@ -74,7 +74,7 @@ protected: AvatarData* _owningAvatarData; std::vector _palms; mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive }; - + glm::vec3 getBasePosition() const; float getBaseScale() const; @@ -112,13 +112,12 @@ public: void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; } const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; } - glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(_rawAngularVelocity); } void addToPosition(const glm::vec3& delta); void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; } void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.0f); } - + void setTipPosition(const glm::vec3& position) { _tipPosition = position; } const glm::vec3 getTipPosition() const { return _owningHandData->localToWorldPosition(_tipPosition); } const glm::vec3& getTipRawPosition() const { return _tipPosition; } @@ -126,16 +125,16 @@ public: void setTipVelocity(const glm::vec3& velocity) { _tipVelocity = velocity; } const glm::vec3 getTipVelocity() const { return _owningHandData->localToWorldDirection(_tipVelocity); } const glm::vec3& getTipRawVelocity() const { return _tipVelocity; } - + void incrementFramesWithoutData() { _numFramesWithoutData++; } void resetFramesWithoutData() { _numFramesWithoutData = 0; } int getFramesWithoutData() const { return _numFramesWithoutData; } - + // FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information // from an action and/or the UserInputMapper instead of piping it through here. void setTrigger(float trigger) { _trigger = trigger; } float getTrigger() const { return _trigger; } - + // return world-frame: glm::vec3 getFingerDirection() const; glm::vec3 getNormal() const; @@ -148,13 +147,13 @@ private: glm::vec3 _rawAngularVelocity; glm::quat _rawDeltaRotation; glm::quat _lastRotation; - + glm::vec3 _tipPosition; glm::vec3 _tipVelocity; glm::vec3 _totalPenetration; /// accumulator for per-frame penetrations float _trigger; - + bool _isActive; /// This has current valid data int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost. HandData* _owningHandData; diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 2281fc98ff..d9641618c1 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -16,7 +16,7 @@ namespace controller { Pose::Pose(const vec3& translation, const quat& rotation, - const vec3& velocity, const quat& angularVelocity) : + const vec3& velocity, const vec3& angularVelocity) : translation(translation), rotation(rotation), velocity(velocity), angularVelocity(angularVelocity), valid (true) { } bool Pose::operator==(const Pose& right) const { @@ -26,7 +26,7 @@ namespace controller { } // FIXME add margin of error? Or add an additional withinEpsilon function? - return translation == right.getTranslation() && rotation == right.getRotation() && + return translation == right.getTranslation() && rotation == right.getRotation() && velocity == right.getVelocity() && angularVelocity == right.getAngularVelocity(); } @@ -35,14 +35,29 @@ namespace controller { obj.setProperty("translation", vec3toScriptValue(engine, pose.translation)); obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation)); obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity)); - obj.setProperty("angularVelocity", quatToScriptValue(engine, pose.angularVelocity)); + obj.setProperty("angularVelocity", vec3toScriptValue(engine, pose.angularVelocity)); obj.setProperty("valid", pose.valid); return obj; } void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) { - // nothing for now... + auto translation = object.property("translation"); + auto rotation = object.property("rotation"); + auto velocity = object.property("velocity"); + auto angularVelocity = object.property("angularVelocity"); + if (translation.isValid() && + rotation.isValid() && + velocity.isValid() && + angularVelocity.isValid()) { + vec3FromScriptValue(translation, pose.translation); + quatFromScriptValue(rotation, pose.rotation); + vec3FromScriptValue(velocity, pose.velocity); + vec3FromScriptValue(angularVelocity, pose.angularVelocity); + pose.valid = true; + } else { + pose.valid = false; + } } } diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index b8d27824f3..a8a4452758 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -23,12 +23,12 @@ namespace controller { vec3 translation; quat rotation; vec3 velocity; - quat angularVelocity; + vec3 angularVelocity; bool valid{ false }; Pose() {} Pose(const vec3& translation, const quat& rotation, - const vec3& velocity = vec3(), const quat& angularVelocity = quat()); + const vec3& velocity = vec3(), const vec3& angularVelocity = vec3()); Pose(const Pose&) = default; Pose& operator = (const Pose&) = default; @@ -38,7 +38,7 @@ namespace controller { vec3 getTranslation() const { return translation; } quat getRotation() const { return rotation; } vec3 getVelocity() const { return velocity; } - quat getAngularVelocity() const { return angularVelocity; } + vec3 getAngularVelocity() const { return angularVelocity; } static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event); static void fromScriptValue(const QScriptValue& object, Pose& event); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 9af478e709..52ec52e852 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include +#include #include "UserInputMapper.h" #include "StandardControls.h" @@ -87,6 +89,21 @@ namespace controller { Q_INVOKABLE QObject* parseMapping(const QString& json); Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl); + Q_INVOKABLE glm::vec2 getReticlePosition() { + return toGlm(QCursor::pos()); + } + Q_INVOKABLE void setReticlePosition(glm::vec2 position) { + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPos = toGlm(QCursor::pos()); + auto distance = glm::distance(oldPos, position); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPos << " newPos:" << position; + } + + QCursor::setPos(position.x, position.y); + } //Q_INVOKABLE bool isPrimaryButtonPressed() const; //Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp index bb9517b136..3e7fde347e 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -10,6 +10,8 @@ #include +#include + using namespace controller; float ScriptEndpoint::peek() const { @@ -23,7 +25,16 @@ void ScriptEndpoint::updateValue() { return; } - _lastValueRead = (float)_callable.call().toNumber(); + QScriptValue result = _callable.call(); + + // If the callable ever returns a non-number, we assume it's a pose + // and start reporting ourselves as a pose. + if (result.isNumber()) { + _lastValueRead = (float)_callable.call().toNumber(); + } else { + Pose::fromScriptValue(result, _lastPoseRead); + _returnPose = true; + } } void ScriptEndpoint::apply(float value, const Pointer& source) { @@ -44,3 +55,36 @@ void ScriptEndpoint::internalApply(float value, int sourceID) { _callable.call(QScriptValue(), QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) })); } + +Pose ScriptEndpoint::peekPose() const { + const_cast(this)->updatePose(); + return _lastPoseRead; +} + +void ScriptEndpoint::updatePose() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updatePose", Qt::QueuedConnection); + return; + } + QScriptValue result = _callable.call(); + Pose::fromScriptValue(result, _lastPoseRead); +} + +void ScriptEndpoint::apply(const Pose& newPose, const Pointer& source) { + if (newPose == _lastPoseWritten) { + return; + } + internalApply(newPose, source->getInput().getID()); +} + +void ScriptEndpoint::internalApply(const Pose& newPose, int sourceID) { + _lastPoseWritten = newPose; + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, + Q_ARG(const Pose&, newPose), + Q_ARG(int, sourceID)); + return; + } + _callable.call(QScriptValue(), + QScriptValueList({ Pose::toScriptValue(_callable.engine(), newPose), QScriptValue(sourceID) })); +} diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h index 836af721f6..00439f5560 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -27,13 +27,26 @@ public: virtual float peek() const override; virtual void apply(float newValue, const Pointer& source) override; + + virtual Pose peekPose() const override; + virtual void apply(const Pose& newValue, const Pointer& source) override; + + virtual bool isPose() const override { return _returnPose; } + protected: Q_INVOKABLE void updateValue(); Q_INVOKABLE virtual void internalApply(float newValue, int sourceID); + + Q_INVOKABLE void updatePose(); + Q_INVOKABLE virtual void internalApply(const Pose& newValue, int sourceID); private: QScriptValue _callable; float _lastValueRead { 0.0f }; float _lastValueWritten { 0.0f }; + + bool _returnPose { false }; + Pose _lastPoseRead; + Pose _lastPoseWritten; }; } diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 1f1dfa1735..4c804f2e7b 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -145,9 +145,14 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, localFileData = localFileString.toLocal8Bit(); } - - connection->respond(HTTPConnection::StatusCode200, localFileData, - qPrintable(mimeDatabase.mimeTypeForFile(filePath).name())); + + // if this is an shtml file just make the MIME type match HTML so browsers aren't confused + // otherwise use the mimeDatabase to look it up + auto mimeType = localFileInfo.suffix() == "shtml" + ? QString { "text/html" } + : mimeDatabase.mimeTypeForFile(filePath).name(); + + connection->respond(HTTPConnection::StatusCode200, localFileData, qPrintable(mimeType)); return true; } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e3618d0e2a..f517c49b00 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -256,33 +256,38 @@ void EntityTreeRenderer::forceRecheckEntities() { void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr zone) { QSharedPointer scene = DependencyManager::get(); + auto sceneStage = scene->getStage(); + auto sceneKeyLight = sceneStage->getKeyLight(); + auto sceneLocation = sceneStage->getLocation(); + auto sceneTime = sceneStage->getTime(); + if (zone) { if (!_hasPreviousZone) { - _previousKeyLightColor = scene->getKeyLightColor(); - _previousKeyLightIntensity = scene->getKeyLightIntensity(); - _previousKeyLightAmbientIntensity = scene->getKeyLightAmbientIntensity(); - _previousKeyLightDirection = scene->getKeyLightDirection(); - _previousStageSunModelEnabled = scene->isStageSunModelEnabled(); - _previousStageLongitude = scene->getStageLocationLongitude(); - _previousStageLatitude = scene->getStageLocationLatitude(); - _previousStageAltitude = scene->getStageLocationAltitude(); - _previousStageHour = scene->getStageDayTime(); - _previousStageDay = scene->getStageYearTime(); + _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; } - scene->setKeyLightColor(ColorUtils::toVec3(zone->getKeyLightProperties().getColor())); - scene->setKeyLightIntensity(zone->getKeyLightProperties().getIntensity()); - scene->setKeyLightAmbientIntensity(zone->getKeyLightProperties().getAmbientIntensity()); - scene->setKeyLightDirection(zone->getKeyLightProperties().getDirection()); - scene->setStageSunModelEnable(zone->getStageProperties().getSunModelEnabled()); - scene->setStageLocation(zone->getStageProperties().getLongitude(), zone->getStageProperties().getLatitude(), + 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()); - scene->setStageDayTime(zone->getStageProperties().calculateHour()); - scene->setStageYearTime(zone->getStageProperties().calculateDay()); + sceneTime->setHour(zone->getStageProperties().calculateHour()); + sceneTime->setDay(zone->getStageProperties().calculateDay()); if (zone->getBackgroundMode() == BACKGROUND_MODE_ATMOSPHERE) { EnvironmentData data = zone->getEnvironmentData(); - glm::vec3 keyLightDirection = scene->getKeyLightDirection(); + glm::vec3 keyLightDirection = sceneKeyLight->getDirection(); glm::vec3 inverseKeyLightDirection = keyLightDirection * -1.0f; // NOTE: is this right? It seems like the "sun" should be based on the center of the @@ -293,7 +298,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetKeyLightIntensity() * KEY_LIGHT_INTENSITY_TO_SUN_BRIGHTNESS_RATIO; + float sunBrightness = sceneKeyLight->getIntensity() * KEY_LIGHT_INTENSITY_TO_SUN_BRIGHTNESS_RATIO; data.setSunBrightness(sunBrightness); _viewState->overrideEnvironmentData(data); @@ -339,15 +344,15 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrsetKeyLightColor(_previousKeyLightColor); - scene->setKeyLightIntensity(_previousKeyLightIntensity); - scene->setKeyLightAmbientIntensity(_previousKeyLightAmbientIntensity); - scene->setKeyLightDirection(_previousKeyLightDirection); - scene->setStageSunModelEnable(_previousStageSunModelEnabled); - scene->setStageLocation(_previousStageLongitude, _previousStageLatitude, + sceneKeyLight->setColor(_previousKeyLightColor); + sceneKeyLight->setIntensity(_previousKeyLightIntensity); + sceneKeyLight->setAmbientIntensity(_previousKeyLightAmbientIntensity); + sceneKeyLight->setDirection(_previousKeyLightDirection); + sceneStage->setSunModelEnable(_previousStageSunModelEnabled); + sceneStage->setLocation(_previousStageLongitude, _previousStageLatitude, _previousStageAltitude); - scene->setStageDayTime(_previousStageHour); - scene->setStageYearTime(_previousStageDay); + sceneTime->setHour(_previousStageHour); + sceneTime->setDay(_previousStageDay); _hasPreviousZone = false; } _viewState->endOverrideEnvironmentData(); @@ -482,7 +487,8 @@ void EntityTreeRenderer::deleteReleasedModels() { } RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude) { + bool precisionPicking, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard) { RayToEntityIntersectionResult result; if (_tree) { EntityTreePointer entityTree = std::static_pointer_cast(_tree); @@ -490,7 +496,7 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons OctreeElementPointer element; EntityItemPointer intersectedEntity = NULL; result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, - result.face, result.surfaceNormal, entityIdsToInclude, + result.face, result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard, (void**)&intersectedEntity, lockType, &result.accurate, precisionPicking); if (result.intersects && intersectedEntity) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 076fe26d6f..2c205336c0 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -130,7 +130,8 @@ private: QList _releasedModels; RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude = QVector()); + bool precisionPicking, const QVector& entityIdsToInclude = QVector(), + const QVector& entityIdsToDiscard = QVector()); EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index b5203ea460..9127e4ca22 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -385,7 +385,7 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { _needsInitialSimulation = true; } } - + return result; } @@ -398,14 +398,14 @@ void RenderableModelEntityItem::update(const quint64& now) { EntityItemProperties properties; auto extents = _model->getMeshExtents(); properties.setDimensions(extents.maximum - extents.minimum); - + qCDebug(entitiesrenderer) << "Autoresizing:" << (!getName().isEmpty() ? getName() : getModelURL()); QMetaObject::invokeMethod(DependencyManager::get().data(), "editEntity", Qt::QueuedConnection, Q_ARG(QUuid, getEntityItemID()), Q_ARG(EntityItemProperties, properties)); } - + ModelEntityItem::update(now); } @@ -427,7 +427,7 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori // << precisionPicking; QString extraInfo; - return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, + return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking); } @@ -447,24 +447,22 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { ShapeType type = getShapeType(); if (type == SHAPE_TYPE_COMPOUND) { - if (!_model) { + if (!_model || _model->getCollisionURL().isEmpty()) { EntityTreePointer tree = getTree(); if (tree) { QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); } - return false; // hmm... + return false; } - assert(!_model->getCollisionURL().isEmpty()); - if (_model->getURL().isEmpty()) { // we need a render geometry with a scale to proceed, so give up. return false; } - + const QSharedPointer collisionNetworkGeometry = _model->getCollisionGeometry(); const QSharedPointer renderNetworkGeometry = _model->getGeometry(); - + if ((collisionNetworkGeometry && collisionNetworkGeometry->isLoaded()) && (renderNetworkGeometry && renderNetworkGeometry->isLoaded())) { // we have both URLs AND both geometries AND they are both fully loaded. @@ -625,3 +623,11 @@ glm::vec3 RenderableModelEntityItem::getAbsoluteJointTranslationInObjectFrame(in } return glm::vec3(0.0f); } + +void RenderableModelEntityItem::locationChanged() { + EntityItem::locationChanged(); + if (_model && _model->isActive()) { + _model->setRotation(getRotation()); + _model->setTranslation(getPosition()); + } +} diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 143113146e..cf948bd7a0 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -73,6 +73,7 @@ public: virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; virtual void loader() override; + virtual void locationChanged() override; private: void remapTextures(); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 4abd8dbafd..567d2bc24a 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -223,10 +223,10 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { particleUniforms.radius.middle = getParticleRadius(); particleUniforms.radius.finish = getRadiusFinish(); particleUniforms.radius.spread = getRadiusSpread(); - particleUniforms.color.start = toGlm(getColorStart(), getAlphaStart()); - particleUniforms.color.middle = toGlm(getXColor(), getAlpha()); - particleUniforms.color.finish = toGlm(getColorFinish(), getAlphaFinish()); - particleUniforms.color.spread = toGlm(getColorSpread(), getAlphaSpread()); + particleUniforms.color.start = glm::vec4(getColorStartRGB(), getAlphaStart()); + particleUniforms.color.middle = glm::vec4(getColorRGB(), getAlpha()); + particleUniforms.color.finish = glm::vec4(getColorFinishRGB(), getAlphaFinish()); + particleUniforms.color.spread = glm::vec4(getColorSpreadRGB(), getAlphaSpread()); particleUniforms.lifespan = getLifespan(); // Build particle primitives @@ -240,8 +240,11 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { auto position = getPosition(); auto rotation = getRotation(); Transform transform; - transform.setTranslation(position); - transform.setRotation(rotation); + if (!getEmitterShouldTrail()) { + transform.setTranslation(position); + transform.setRotation(rotation); + } + render::PendingChanges pendingChanges; pendingChanges.updateItem(_renderItemId, [=](ParticlePayloadData& payload) { diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 256e36ebc3..7b3bbc4c02 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -23,6 +23,13 @@ #include "paintStroke_frag.h" + +struct PolyLineUniforms { + glm::vec3 color; +}; + + + EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity{ new RenderablePolyLineEntityItem(entityID) }; entity->setProperties(properties); @@ -30,9 +37,12 @@ EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& enti } RenderablePolyLineEntityItem::RenderablePolyLineEntityItem(const EntityItemID& entityItemID) : - PolyLineEntityItem(entityItemID) { - _numVertices = 0; +PolyLineEntityItem(entityItemID), +_numVertices(0) +{ _vertices = QVector(0.0f); + PolyLineUniforms uniforms; + _uniformBuffer = std::make_shared(sizeof(PolyLineUniforms), (const gpu::Byte*) &uniforms); } gpu::PipelinePointer RenderablePolyLineEntityItem::_pipeline; @@ -41,13 +51,11 @@ int32_t RenderablePolyLineEntityItem::PAINTSTROKE_GPU_SLOT; void RenderablePolyLineEntityItem::createPipeline() { static const int NORMAL_OFFSET = 12; - static const int COLOR_OFFSET = 24; - static const int TEXTURE_OFFSET = 28; + static const int TEXTURE_OFFSET = 24; _format.reset(new gpu::Stream::Format()); _format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); _format->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), NORMAL_OFFSET); - _format->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, COLOR_OFFSET); _format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), TEXTURE_OFFSET); auto VS = gpu::Shader::createVertex(std::string(paintStroke_vert)); @@ -72,47 +80,28 @@ void RenderablePolyLineEntityItem::updateGeometry() { _verticesBuffer.reset(new gpu::Buffer()); int vertexIndex = 0; vec2 uv; - float tailStart = 0.0f; - float tailEnd = 0.25f; - float tailLength = tailEnd - tailStart; - - float headStart = 0.76f; - float headEnd = 1.0f; - float headLength = headEnd - headStart; float uCoord, vCoord; - - int numTailStrips = 5; - int numHeadStrips = 10; - int startHeadIndex = _vertices.size() / 2 - numHeadStrips; + uCoord = 0.0f; + float uCoordInc = 1.0 / (_vertices.size() / 2); for (int i = 0; i < _vertices.size() / 2; i++) { - uCoord = 0.26f; vCoord = 0.0f; - //tail - if (i < numTailStrips) { - uCoord = float(i) / numTailStrips * tailLength + tailStart; - } - - //head - if (i > startHeadIndex) { - uCoord = float((i + 1) - startHeadIndex) / numHeadStrips * headLength + headStart; - } + uv = vec2(uCoord, vCoord); _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); - _verticesBuffer->append(sizeof(int), (gpu::Byte*)&_color); _verticesBuffer->append(sizeof(glm::vec2), (gpu::Byte*)&uv); vertexIndex++; uv.y = 1.0f; _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); - _verticesBuffer->append(sizeof(int), (gpu::Byte*)_color); _verticesBuffer->append(sizeof(glm::vec2), (const gpu::Byte*)&uv); vertexIndex++; _numVertices += 2; + uCoord += uCoordInc; } _pointsChanged = false; _normalsChanged = false; @@ -152,6 +141,11 @@ void RenderablePolyLineEntityItem::updateVertices() { _vertices << v1 << v2; } + // Guard against an empty polyline + if (finalIndex < 0) { + return; + } + // For last point we can assume binormals are the same since it represents the last two vertices of quad point = _points.at(finalIndex); v1 = point + binormal; @@ -161,6 +155,16 @@ void RenderablePolyLineEntityItem::updateVertices() { } +void RenderablePolyLineEntityItem::update(const quint64& now) { + PolyLineUniforms uniforms; + uniforms.color = toGlm(getXColor()); + memcpy(&_uniformBuffer.edit(), &uniforms, sizeof(PolyLineUniforms)); + if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) { + updateVertices(); + updateGeometry(); + } + +} void RenderablePolyLineEntityItem::render(RenderArgs* args) { QWriteLocker lock(&_quadReadWriteLock); @@ -181,17 +185,13 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render"); Q_ASSERT(getType() == EntityTypes::PolyLine); - Q_ASSERT(args->_batch); - if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) { - updateVertices(); - updateGeometry(); - } gpu::Batch& batch = *args->_batch; Transform transform = Transform(); transform.setTranslation(getPosition()); transform.setRotation(getRotation()); + batch.setUniformBuffer(0, _uniformBuffer); batch.setModelTransform(transform); batch.setPipeline(_pipeline); @@ -200,7 +200,7 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { } else { batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, args->_whiteTexture); } - + batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index eca7c59fef..d320610d83 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -29,6 +29,8 @@ public: RenderablePolyLineEntityItem(const EntityItemID& entityItemID); virtual void render(RenderArgs* args); + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const { return true; }; SIMPLE_RENDERABLE(); @@ -42,6 +44,7 @@ protected: void updateGeometry(); void updateVertices(); gpu::BufferPointer _verticesBuffer; + gpu::BufferView _uniformBuffer; unsigned int _numVertices; QVector _vertices; diff --git a/libraries/entities-renderer/src/RenderableProceduralItemShader.h b/libraries/entities-renderer/src/RenderableProceduralItemShader.h index 8c9e7c77dd..a01a6b8571 100644 --- a/libraries/entities-renderer/src/RenderableProceduralItemShader.h +++ b/libraries/entities-renderer/src/RenderableProceduralItemShader.h @@ -23,8 +23,6 @@ layout(location = 0) out vec4 _fragColor0; layout(location = 1) out vec4 _fragColor1; layout(location = 2) out vec4 _fragColor2; -// the glow intensity -uniform float glowIntensity; // the alpha threshold uniform float alphaThreshold; uniform sampler2D normalFittingMap; @@ -318,8 +316,8 @@ const QString SHADER_TEMPLATE_V1 = SHADER_COMMON + R"SCRIBE( void main(void) { vec4 emissive = getProceduralColor(); - float alpha = glowIntensity * emissive.a; - if (alpha != glowIntensity) { + float alpha = emissive.a; + if (alpha != 1.0) { discard; } diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 4e2bc8d097..fc659d5928 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -23,23 +23,24 @@ in vec3 interpolatedNormal; in vec2 varTexcoord; in vec4 varColor; -float rand(vec2 point){ - return fract(sin(dot(point.xy ,vec2(12.9898,78.233))) * 43758.5453); -} - +struct PolyLineUniforms { + vec3 color; +}; +uniform polyLineBuffer { + PolyLineUniforms polyline; +}; void main(void) { vec4 texel = texture(originalTexture, varTexcoord); int frontCondition = 1 -int(gl_FrontFacing) * 2; - vec3 color = varColor.rgb; - //vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess + vec3 color = varColor.rgb; packDeferredFragmentTranslucent( interpolatedNormal * frontCondition, texel.a, - color *texel.rgb, + polyline.color * texel.rgb, vec3(0.01, 0.01, 0.01), 10.0); } diff --git a/libraries/entities-renderer/src/paintStroke.slv b/libraries/entities-renderer/src/paintStroke.slv index 7d7523deb9..769b87f2a9 100644 --- a/libraries/entities-renderer/src/paintStroke.slv +++ b/libraries/entities-renderer/src/paintStroke.slv @@ -13,8 +13,8 @@ // <@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> // the interpolated normal @@ -30,7 +30,8 @@ void main(void) { varTexcoord = inTexCoord0.st; // pass along the diffuse color - varColor = inColor; + varColor = colorToLinearRGBA(inColor); + // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index 1e9275ec72..e1f18c2b5f 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -82,7 +82,7 @@ void main(void) { varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age); // anchor point in eye space - float radius = bezierInterpolate(particle.radius.start, particle.radius.middle, particle.radius.finish , age); + float radius = bezierInterpolate(particle.radius.start, particle.radius.middle, particle.radius.finish, age); vec4 quadPos = radius * UNIT_QUAD[twoTriID]; vec4 anchorPoint; diff --git a/libraries/entities-renderer/src/untextured_particle.slv b/libraries/entities-renderer/src/untextured_particle.slv index 3d1d99c0dc..ab0ea15219 100644 --- a/libraries/entities-renderer/src/untextured_particle.slv +++ b/libraries/entities-renderer/src/untextured_particle.slv @@ -9,16 +9,15 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> out vec4 _color; void main(void) { // pass along the diffuse color - _color = inColor; + _color = colorToLinearRGBA(inColor); TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index a192661e52..ba59d66cf4 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -58,6 +58,8 @@ public: virtual bool shouldSuppressLocationEdits() { return false; } + virtual void prepareForPhysicsSimulation() { } + // these look in the arguments map for a named argument. if it's not found or isn't well formed, // ok will be set to false (note that it's never set to true -- set it to true before calling these). // if required is true, failure to extract an argument will cause a warning to be printed. diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 98a099fcaa..3a2fdf55d4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -195,7 +195,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ALPHA_SPREAD, alphaSpread); CHECK_PROPERTY_CHANGE(PROP_ALPHA_START, alphaStart); CHECK_PROPERTY_CHANGE(PROP_ALPHA_FINISH, alphaFinish); - CHECK_PROPERTY_CHANGE(PROP_ADDITIVE_BLENDING, additiveBlending); + CHECK_PROPERTY_CHANGE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible); @@ -354,7 +354,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ADDITIVE_BLENDING, additiveBlending); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation); } @@ -515,7 +515,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaFinish, float, setAlphaFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(additiveBlending, bool, setAdditiveBlending); + COPY_PROPERTY_FROM_QSCRIPTVALUE(emitterShouldTrail , bool, setEmitterShouldTrail); COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(glowLevel, float, setGlowLevel); @@ -670,7 +670,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_START, AlphaStart, alphaStart, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float); - ADD_PROPERTY_TO_MAP(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool); + ADD_PROPERTY_TO_MAP(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool); ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3); @@ -980,7 +980,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, properties.getAlphaSpread()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish()); - APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, properties.getAdditiveBlending()); + APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, properties.getEmitterShouldTrail()); } if (properties.getType() == EntityTypes::Zone) { @@ -1265,7 +1265,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); } if (properties.getType() == EntityTypes::Zone) { @@ -1608,8 +1608,8 @@ QList EntityItemProperties::listChangedProperties() { if (alphaFinishChanged()) { out += "alphaFinish"; } - if (additiveBlendingChanged()) { - out += "additiveBlending"; + if (emitterShouldTrailChanged()) { + out += "emitterShouldTrail"; } if (modelURLChanged()) { out += "modelURL"; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index c13519996a..5420e75aed 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -161,7 +161,7 @@ public: DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD); DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, ParticleEffectEntityItem::DEFAULT_RADIUS_START); DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH); - DEFINE_PROPERTY(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool, ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING); + DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID); DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 3bca911a56..2ba86a491e 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -149,7 +149,7 @@ enum EntityPropertyList { PROP_ANIMATION_HOLD, PROP_ANIMATION_START_AUTOMATICALLY, - PROP_ADDITIVE_BLENDING, + PROP_EMITTER_SHOULD_TRAIL, PROP_PARENT_ID, PROP_PARENT_JOINT_INDEX, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index a0a6719521..0746c2a824 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -357,19 +357,21 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn return result; } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { - QVector entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); - return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entities); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { + QVector entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); + QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); + return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entitiesToInclude, entitiesToDiscard); } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { - const QVector& entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); - return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entities); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { + const QVector& entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); + const QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); + return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard); } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude) { + bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) { RayToEntityIntersectionResult result; @@ -377,7 +379,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke OctreeElementPointer element; EntityItemPointer intersectedEntity = NULL; result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, - result.surfaceNormal, entityIdsToInclude, (void**)&intersectedEntity, lockType, &result.accurate, + result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard, (void**)&intersectedEntity, lockType, &result.accurate, precisionPicking); if (result.intersects && intersectedEntity) { result.entityID = intersectedEntity->getEntityItemID(); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index f745b6b644..d08a1b7e36 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -112,11 +112,11 @@ public slots: /// If the scripting context has visible entities, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue()); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); /// If the scripting context has visible entities, this will determine a ray intersection, and will block in /// order to return an accurate result - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue()); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); Q_INVOKABLE void setLightsArePickable(bool value); Q_INVOKABLE bool getLightsArePickable() const; @@ -189,7 +189,7 @@ private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude); + bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); EntityTreePointer _entityTree; EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ba6294f8a8..f0a03623c2 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -498,6 +498,7 @@ public: BoxFace& face; glm::vec3& surfaceNormal; const QVector& entityIdsToInclude; + const QVector& entityIdsToDiscard; void** intersectedObject; bool found; bool precisionPicking; @@ -510,7 +511,7 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast(element); if (entityTreeElementPointer ->findRayIntersection(args->origin, args->direction, keepSearching, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, - args->intersectedObject, args->precisionPicking)) { + args->entityIdsToDiscard, args->intersectedObject, args->precisionPicking)) { args->found = true; } return keepSearching; @@ -518,9 +519,9 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, void** intersectedObject, Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { - RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking }; + RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, false, precisionPicking }; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 5e54e562a0..f68e2d59e9 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -84,6 +84,7 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude = QVector(), + const QVector& entityIdsToDiscard = QVector(), void** intersectedObject = NULL, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL, diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index ff2e97e8fe..8944c95cbc 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -475,8 +475,8 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - void** intersectedObject, bool precisionPicking) { + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard, void** intersectedObject, bool precisionPicking) { keepSearching = true; // assume that we will continue searching after this. @@ -501,7 +501,7 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm:: if (_cube.contains(origin) || distanceToElementCube < distance) { if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails, - face, localSurfaceNormal, entityIdsToInclude, intersectedObject, precisionPicking, distanceToElementCube)) { + face, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, precisionPicking, distanceToElementCube)) { if (distanceToElementDetails < distance) { distance = distanceToElementDetails; @@ -516,13 +516,13 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm:: bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { + const QVector& entityIdsToInclude, const QVector& entityIDsToDiscard, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { // only called if we do intersect our bounding cube, but find if we actually intersect with entities... int entityNumber = 0; bool somethingIntersected = false; forEachEntity([&](EntityItemPointer entity) { - if (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) { + if ( (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) || (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) { return; } diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index d8a182156d..aa05438bde 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -144,11 +144,13 @@ public: virtual bool canRayIntersect() const { 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, 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; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 352ab4d701..a1f16ee251 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "EntitiesLogging.h" #include "EntityItemProperties.h" @@ -243,13 +244,22 @@ void ModelEntityItem::getAnimationFrame(bool& newFrame, _lastKnownFrameDataRotations.resize(_jointMapping.size()); _lastKnownFrameDataTranslations.resize(_jointMapping.size()); + for (int j = 0; j < _jointMapping.size(); j++) { int index = _jointMapping[j]; - if (index != -1 && index < rotations.size()) { - _lastKnownFrameDataRotations[j] = fbxJoints[index].preRotation * rotations[index]; - } - if (index != -1 && index < translations.size()) { - _lastKnownFrameDataTranslations[j] = translations[index]; + if (index >= 0) { + glm::mat4 translationMat; + if (index < translations.size()) { + translationMat = glm::translate(translations[index]); + } + glm::mat4 rotationMat; + if (index < rotations.size()) { + rotationMat = glm::mat4_cast(rotations[index]); + } + glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform * + rotationMat * fbxJoints[index].postTransform); + _lastKnownFrameDataTranslations[j] = extractTranslation(finalMat); + _lastKnownFrameDataRotations[j] = glmExtractRotation(finalMat); } } } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 5600c85650..16196aa129 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -52,13 +52,13 @@ const float ParticleEffectEntityItem::MINIMUM_ALPHA = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_ALPHA = 1.0f; const quint32 ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES = 1000; const quint32 ParticleEffectEntityItem::MINIMUM_MAX_PARTICLES = 1; -const quint32 ParticleEffectEntityItem::MAXIMUM_MAX_PARTICLES = 10000; +const quint32 ParticleEffectEntityItem::MAXIMUM_MAX_PARTICLES = 100000; const float ParticleEffectEntityItem::DEFAULT_LIFESPAN = 3.0f; const float ParticleEffectEntityItem::MINIMUM_LIFESPAN = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_LIFESPAN = 86400.0f; // 1 day const float ParticleEffectEntityItem::DEFAULT_EMIT_RATE = 15.0f; const float ParticleEffectEntityItem::MINIMUM_EMIT_RATE = 0.0f; -const float ParticleEffectEntityItem::MAXIMUM_EMIT_RATE = 1000.0f; +const float ParticleEffectEntityItem::MAXIMUM_EMIT_RATE = 100000.0f; const float ParticleEffectEntityItem::DEFAULT_EMIT_SPEED = 5.0f; const float ParticleEffectEntityItem::MINIMUM_EMIT_SPEED = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3 @@ -91,7 +91,7 @@ const float ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD = 0.0f; const float ParticleEffectEntityItem::DEFAULT_RADIUS_START = DEFAULT_PARTICLE_RADIUS; const float ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH = DEFAULT_PARTICLE_RADIUS; const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = ""; -const bool ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING = false; +const bool ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL = false; EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -331,7 +331,7 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaStart, getAlphaStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(additiveBlending, getAdditiveBlending); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitterShouldTrail, getEmitterShouldTrail); return properties; @@ -370,7 +370,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaStart, setAlphaStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(additiveBlending, setAdditiveBlending); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitterShouldTrail, setEmitterShouldTrail); if (somethingChanged) { bool wantDebug = false; @@ -463,7 +463,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch } if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING) { - READ_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); + READ_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); } return bytesRead; @@ -503,7 +503,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea requestedProperties += PROP_POLAR_FINISH; requestedProperties += PROP_AZIMUTH_START; requestedProperties += PROP_AZIMUTH_FINISH; - requestedProperties += PROP_ADDITIVE_BLENDING; + requestedProperties += PROP_EMITTER_SHOULD_TRAIL; return requestedProperties; } @@ -546,7 +546,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, getPolarFinish()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish()); - APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, getAdditiveBlending()); + APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, getEmitterShouldTrail()); } bool ParticleEffectEntityItem::isEmittingParticles() const { @@ -595,18 +595,20 @@ void ParticleEffectEntityItem::integrateParticle(Particle& particle, float delta void ParticleEffectEntityItem::stepSimulation(float deltaTime) { // update particles between head and tail + int popCount = 0; for (Particle& particle : _particles) { particle.lifetime += deltaTime; // if particle has died. if (particle.lifetime >= _lifespan) { // move head forward - _particles.pop_front(); + popCount++; } else { // Otherwise update it integrateParticle(particle, deltaTime); } } + _particles.erase(_particles.begin(), _particles.begin() + popCount); // emit new particles, but only if we are emmitting if (getIsEmitting() && _emitRate > 0.0f && _lifespan > 0.0f && _polarStart <= _polarFinish) { @@ -639,13 +641,17 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { Particle particle; - - particle.seed = randFloatInRange(0.0f, 1.0f); - + + + particle.seed = randFloatInRange(-1.0f, 1.0f); + if (getEmitterShouldTrail()) { + particle.position = getPosition(); + } // Position, velocity, and acceleration if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) { // Emit along z-axis from position - particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z); + + particle.velocity = (_emitSpeed + 0.2f * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z); particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; } else { @@ -656,11 +662,12 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { float elevationMinZ = sin(PI_OVER_TWO - _polarFinish); float elevationMaxZ = sin(PI_OVER_TWO - _polarStart); - float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); + // float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); + float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) *randFloat()); float azimuth; if (_azimuthFinish >= _azimuthStart) { - azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); + azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); } else { azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat(); } @@ -691,7 +698,12 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f )); - particle.position = _emitOrientation * emitPosition; + if (getEmitterShouldTrail()) { + particle.position += _emitOrientation * emitPosition; + } + else { + particle.position = _emitOrientation * emitPosition; + } } particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection); diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index c35a45baeb..5afcbe2ae1 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -15,6 +15,8 @@ #include "EntityItem.h" +#include "ColorUtils.h" + class ParticleEffectEntityItem : public EntityItem { public: ALLOW_INSTANTIATION // This class can be instantiated @@ -47,6 +49,7 @@ public: const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } + glm::vec3 getColorRGB() const { return ColorUtils::toLinearVec3(toGlm(getXColor())); } static const xColor DEFAULT_COLOR; void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } @@ -59,14 +62,17 @@ public: bool _isColorStartInitialized = false; void setColorStart(const xColor& colorStart) { _colorStart = colorStart; _isColorStartInitialized = true; } xColor getColorStart() const { return _isColorStartInitialized ? _colorStart : getXColor(); } + glm::vec3 getColorStartRGB() const { return _isColorStartInitialized ? ColorUtils::toLinearVec3(toGlm(_colorStart)) : getColorRGB(); } bool _isColorFinishInitialized = false; void setColorFinish(const xColor& colorFinish) { _colorFinish = colorFinish; _isColorFinishInitialized = true; } xColor getColorFinish() const { return _isColorFinishInitialized ? _colorFinish : getXColor(); } + glm::vec3 getColorFinishRGB() const { return _isColorStartInitialized ? ColorUtils::toLinearVec3(toGlm(_colorFinish)) : getColorRGB(); } static const xColor DEFAULT_COLOR_SPREAD; void setColorSpread(const xColor& colorSpread) { _colorSpread = colorSpread; } xColor getColorSpread() const { return _colorSpread; } + glm::vec3 getColorSpreadRGB() const { return ColorUtils::toLinearVec3(toGlm(_colorSpread)); } static const float MAXIMUM_ALPHA; static const float MINIMUM_ALPHA; @@ -207,10 +213,10 @@ public: } } - static const bool DEFAULT_ADDITIVE_BLENDING; - bool getAdditiveBlending() const { return _additiveBlending; } - void setAdditiveBlending(bool additiveBlending) { - _additiveBlending = additiveBlending; + static const bool DEFAULT_EMITTER_SHOULD_TRAIL; + bool getEmitterShouldTrail() const { return _emitterShouldTrail; } + void setEmitterShouldTrail(bool emitterShouldTrail) { + _emitterShouldTrail = emitterShouldTrail; } virtual bool supportsDetailedRayIntersection() const { return false; } @@ -280,7 +286,7 @@ protected: float _timeUntilNextEmit { 0.0f }; - bool _additiveBlending { DEFAULT_ADDITIVE_BLENDING }; + bool _emitterShouldTrail { DEFAULT_EMITTER_SHOULD_TRAIL }; }; #endif // hifi_ParticleEffectEntityItem_h diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index e7281d8157..b0b78bc9c5 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -89,6 +89,7 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } + bool PolyLineEntityItem::appendPoint(const glm::vec3& point) { if (_points.size() > MAX_POINTS_PER_LINE - 1) { qDebug() << "MAX POINTS REACHED!"; @@ -104,6 +105,7 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) { return true; } + bool PolyLineEntityItem::setStrokeWidths(const QVector& strokeWidths) { _strokeWidths = strokeWidths; _strokeWidthsChanged = true; diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index dfe062bbdb..cf7b14dbf1 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -49,7 +49,7 @@ 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; @@ -75,7 +75,9 @@ class PolyLineEntityItem : public EntityItem { _texturesChangedFlag = true; } } - + + virtual bool needsToCallUpdate() const { return true; } + virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; } // never have a ray intersection pick a PolyLineEntityItem. diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index bddc7d19ae..26a564e20b 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -320,10 +320,13 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { void OffscreenQmlSurface::resize(const QSize& newSize) { if (!_renderer || !_renderer->_quickWindow) { - QSize currentSize = _renderer->_quickWindow->geometry().size(); - if (newSize == currentSize) { - return; - } + return; + } + + + QSize currentSize = _renderer->_quickWindow->geometry().size(); + if (newSize == currentSize) { + return; } _qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize); @@ -437,7 +440,9 @@ void OffscreenQmlSurface::updateQuick() { } if (_render) { + QMutexLocker lock(&(_renderer->_mutex)); _renderer->post(RENDER); + _renderer->_cond.wait(&(_renderer->_mutex)); _render = false; } diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index 67315b0783..d66cbeb285 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -40,8 +40,8 @@ public: void create(QOpenGLContext* context); void resize(const QSize& size); QSize size() const; - QObject* load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); - QObject* load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { + Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); + Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { return load(QUrl(qmlSourceFile), f); } diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp index 3e879df7af..6397d30e13 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp @@ -39,6 +39,11 @@ void QOpenGLContextWrapper::doneCurrent() { _context->doneCurrent(); } +void QOpenGLContextWrapper::setShareContext(QOpenGLContext* otherContext) { + _context->setShareContext(otherContext); +} + bool isCurrentContext(QOpenGLContext* context) { return QOpenGLContext::currentContext() == context; -} \ No newline at end of file +} + diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.h b/libraries/gl/src/gl/QOpenGLContextWrapper.h index 832119162c..b736253213 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.h +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.h @@ -25,6 +25,12 @@ public: void swapBuffers(QSurface* surface); bool makeCurrent(QSurface* surface); void doneCurrent(); + void setShareContext(QOpenGLContext* otherContext); + + QOpenGLContext* getContext() { + return _context; + } + private: QOpenGLContext* _context { nullptr }; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 14871aafd1..f9c4131a1b 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -311,6 +311,12 @@ void Batch::blit(const FramebufferPointer& src, const Vec4i& srcViewport, _params.push_back(dstViewport.w); } +void Batch::generateTextureMips(const TexturePointer& texture) { + ADD_COMMAND(generateTextureMips); + + _params.push_back(_textures.cache(texture)); +} + void Batch::beginQuery(const QueryPointer& query) { ADD_COMMAND(beginQuery); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 2afcc7caa9..88d28f9d10 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -211,6 +211,9 @@ public: // with xy and zw the bounding corners of the rect region. void blit(const FramebufferPointer& src, const Vec4i& srcRect, const FramebufferPointer& dst, const Vec4i& dstRect); + // Generate the mips for a texture + void generateTextureMips(const TexturePointer& texture); + // Query Section void beginQuery(const QueryPointer& query); void endQuery(const QueryPointer& query); @@ -292,6 +295,7 @@ public: COMMAND_setFramebuffer, COMMAND_clearFramebuffer, COMMAND_blit, + COMMAND_generateTextureMips, COMMAND_beginQuery, COMMAND_endQuery, diff --git a/libraries/gpu/src/gpu/Color.slh b/libraries/gpu/src/gpu/Color.slh new file mode 100644 index 0000000000..f633e6e2d4 --- /dev/null +++ b/libraries/gpu/src/gpu/Color.slh @@ -0,0 +1,52 @@ + +<@if not GPU_COLOR_SLH@> +<@def GPU_COLOR_SLH@> + 0.04045 + // constants: + // T = 0.04045 + // A = 1 / 1.055 = 0.94786729857 + // B = 0.055 * A = 0.05213270142 + // C = 1 / 12.92 = 0.0773993808 + // G = 2.4 + const float T = 0.04045; + const float A = 0.947867; + const float B = 0.052132; + const float C = 0.077399; + const float G = 2.4; + + if (cs > T) { + return pow((cs * A + B), G); + } else { + return cs * C; + } +} + +vec3 colorToLinear(vec3 srgb) { + return vec3(colorComponentToLinear(srgb.x), colorComponentToLinear(srgb.y), colorComponentToLinear(srgb.z)); +} +!> + +vec3 colorToLinearRGB(vec3 srgb) { + const float GAMMA_22 = 2.2; + return pow(srgb, vec3(GAMMA_22)); +} + +vec4 colorToLinearRGBA(vec4 srgba) { + return vec4(colorToLinearRGB(srgba.xyz), srgba.w); +} + +<@endif@> \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 3022f47b51..41a95e2578 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -160,6 +160,7 @@ enum Semantic { RGB, RGBA, BGRA, + XY, XYZ, XYZW, @@ -176,6 +177,8 @@ enum Semantic { SRGBA, SBGRA, + R11G11B10, + UNIFORM, UNIFORM_BUFFER, SAMPLER, diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index d4f3c5c4b3..a730c06bd9 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -52,6 +52,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_setFramebuffer), (&::gpu::GLBackend::do_clearFramebuffer), (&::gpu::GLBackend::do_blit), + (&::gpu::GLBackend::do_generateTextureMips), (&::gpu::GLBackend::do_beginQuery), (&::gpu::GLBackend::do_endQuery), diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index f44fbe6c0d..400c9f08f8 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -376,12 +376,16 @@ protected: // Resource Stage void do_setResourceTexture(Batch& batch, size_t paramOffset); - + + // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s void releaseResourceTexture(uint32_t slot); + void resetResourceStage(); struct ResourceStageState { Textures _textures; + int findEmptyTextureSlot() const; + ResourceStageState(): _textures(MAX_NUM_RESOURCE_TEXTURES, nullptr) {} @@ -432,6 +436,7 @@ protected: void do_setFramebuffer(Batch& batch, size_t paramOffset); void do_clearFramebuffer(Batch& batch, size_t paramOffset); void do_blit(Batch& batch, size_t paramOffset); + void do_generateTextureMips(Batch& batch, size_t paramOffset); // Synchronize the state cache of this Backend with the actual real state of the GL Context void syncOutputStateCache(); diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index 6d8ce7b2c6..5f226141a8 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -361,4 +361,4 @@ void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, co glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); (void) CHECK_GL_ERROR(); -} \ No newline at end of file +} diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index 8601c7512b..8c9647e0f2 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -231,6 +231,7 @@ void GLBackend::releaseResourceTexture(uint32_t slot) { } } + void GLBackend::resetResourceStage() { for (uint32_t i = 0; i < _resource._textures.size(); i++) { releaseResourceTexture(i); @@ -268,3 +269,13 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) { } } +int GLBackend::ResourceStageState::findEmptyTextureSlot() const { + // start from the end of the slots, try to find an empty one that can be used + for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) { + if (!_textures[i]) { + return i; + } + } + return -1; +} + diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 65f2e5ca8a..17802ae6ed 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -224,6 +224,11 @@ public: case gpu::SRGBA: texel.internalFormat = GL_SRGB; // standard 2.2 gamma correction color break; + case gpu::R11G11B10: { + // the type should be float + texel.internalFormat = GL_R11F_G11F_B10F; + break; + } default: qCDebug(gpulogging) << "Unknown combination of texel format"; } @@ -240,6 +245,59 @@ public: break; case gpu::RGBA: texel.internalFormat = GL_RGBA; + switch (dstFormat.getType()) { + case gpu::UINT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32UI; + break; + case gpu::INT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32I; + break; + case gpu::FLOAT: + texel.internalFormat = GL_RGBA32F; + break; + case gpu::UINT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16UI; + break; + case gpu::INT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16I; + break; + case gpu::NUINT16: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16; + break; + case gpu::NINT16: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16_SNORM; + break; + case gpu::HALF: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16F; + break; + case gpu::UINT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8UI; + break; + case gpu::INT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8I; + break; + case gpu::NUINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8; + break; + case gpu::NINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } break; case gpu::SRGB: texel.internalFormat = GL_SRGB; @@ -347,7 +405,6 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) { glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } - object->_target = GL_TEXTURE_2D; syncSampler(texture.getSampler(), texture.getType(), object); @@ -530,3 +587,37 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); } + + + +void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + if (!resourceTexture) { + return; + } + + GLTexture* object = GLBackend::syncGPUObject(*resourceTexture); + if (!object) { + return; + } + + // IN 4.1 we still need to find an available slot + auto freeSlot = _resource.findEmptyTextureSlot(); + auto bindingSlot = (freeSlot < 0 ? 0 : freeSlot); + glActiveTexture(GL_TEXTURE0 + bindingSlot); + glBindTexture(object->_target, object->_texture); + + glGenerateMipmap(object->_target); + + if (freeSlot < 0) { + // If had to use slot 0 then restore state + GLTexture* boundObject = GLBackend::syncGPUObject(*_resource._textures[0]); + if (boundObject) { + glBindTexture(boundObject->_target, boundObject->_texture); + } + } else { + // clean up + glBindTexture(object->_target, 0); + } + (void)CHECK_GL_ERROR(); +} diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index ddb3a0d755..74b7734618 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -23,7 +23,7 @@ Shader::Shader(Type type, const Source& source): { } -Shader::Shader(Type type, Pointer& vertex, Pointer& pixel): +Shader::Shader(Type type, const Pointer& vertex, const Pointer& pixel): _type(type) { _shaders.resize(2); @@ -44,7 +44,7 @@ Shader::Pointer Shader::createPixel(const Source& source) { return Pointer(new Shader(PIXEL, source)); } -Shader::Pointer Shader::createProgram(Pointer& vertexShader, Pointer& pixelShader) { +Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& pixelShader) { if (vertexShader && vertexShader->getType() == VERTEX && pixelShader && pixelShader->getType() == PIXEL) { return Pointer(new Shader(PROGRAM, vertexShader, pixelShader)); diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index bceb00c71e..b737a42e12 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -111,7 +111,7 @@ public: static Pointer createVertex(const Source& source); static Pointer createPixel(const Source& source); - static Pointer createProgram(Pointer& vertexShader, Pointer& pixelShader); + static Pointer createProgram(const Pointer& vertexShader, const Pointer& pixelShader); ~Shader(); @@ -157,7 +157,7 @@ public: protected: Shader(Type type, const Source& source); - Shader(Type type, Pointer& vertex, Pointer& pixel); + Shader(Type type, const Pointer& vertex, const Pointer& pixel); Shader(const Shader& shader); // deep copy of the sysmem shader Shader& operator=(const Shader& shader); // deep copy of the sysmem texture diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index c3e2809c4b..6e8eb10380 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -407,7 +407,7 @@ public: TexturePointer _texture = TexturePointer(NULL); uint16 _subresource = 0; - Element _element = Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); + Element _element = Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); TextureView() {}; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index e1aeb92e41..4fd47affc2 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -308,10 +308,9 @@ static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const material._material->setTextureMap(model::MaterialKey::GLOSS_MAP, glossMap); } if (!material.emissiveTexture.filename.isEmpty()) { - networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), EMISSIVE_TEXTURE, material.emissiveTexture.content); + networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), LIGHTMAP_TEXTURE, material.emissiveTexture.content); networkMaterial->emissiveTextureName = material.emissiveTexture.name; - //checkForTexcoordLightmap = true; auto lightmapMap = model::TextureMapPointer(new model::TextureMap()); lightmapMap->setTextureSource(networkMaterial->emissiveTexture->_textureSource); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 3e80b6c7aa..ae705faf86 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -85,7 +85,7 @@ const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() { data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f; } - _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB), 256, 2)); + _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), 256, 2)); _permutationNormalTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(data), data); } return _permutationNormalTexture; @@ -98,7 +98,7 @@ const unsigned char OPAQUE_BLACK[] = { 0x00, 0x00, 0x00, 0xFF }; const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (!_whiteTexture) { - _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); + _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } return _whiteTexture; @@ -106,7 +106,7 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() { const gpu::TexturePointer& TextureCache::getGrayTexture() { if (!_grayTexture) { - _grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); + _grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _grayTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_GRAY); } return _grayTexture; @@ -114,7 +114,7 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() { const gpu::TexturePointer& TextureCache::getBlueTexture() { if (!_blueTexture) { - _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); + _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } return _blueTexture; @@ -122,7 +122,7 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() { const gpu::TexturePointer& TextureCache::getBlackTexture() { if (!_blackTexture) { - _blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); + _blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); _blackTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK); } return _blackTexture; @@ -151,11 +151,11 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type /// Returns a texture version of an image file gpu::TexturePointer TextureCache::getImageTexture(const QString& path) { QImage image = QImage(path).mirrored(false, true); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA); } gpu::TexturePointer texture = gpu::TexturePointer( gpu::Texture::create2D(formatGPU, image.width(), image.height(), diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index cc22509631..4171b6c1cb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -29,7 +29,7 @@ class NetworkTexture; typedef QSharedPointer NetworkTexturePointer; -enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, CUBE_TEXTURE, CUSTOM_TEXTURE }; +enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, CUBE_TEXTURE, LIGHTMAP_TEXTURE, CUSTOM_TEXTURE }; /// Stores cached textures, including render-to-texture targets. class TextureCache : public ResourceCache, public Dependency { diff --git a/libraries/model/src/model/Material.cpp b/libraries/model/src/model/Material.cpp index c2ff828af3..1d0f6ee5d9 100755 --- a/libraries/model/src/model/Material.cpp +++ b/libraries/model/src/model/Material.cpp @@ -44,9 +44,9 @@ Material& Material::operator= (const Material& material) { Material::~Material() { } -void Material::setDiffuse(const Color& diffuse) { +void Material::setDiffuse(const Color& diffuse, bool isSRGB) { _key.setDiffuse(glm::any(glm::greaterThan(diffuse, Color(0.0f)))); - _schemaBuffer.edit()._diffuse = diffuse; + _schemaBuffer.edit()._diffuse = (isSRGB ? ColorUtils::toLinearVec3(diffuse) : diffuse); } void Material::setMetallic(float metallic) { @@ -54,9 +54,9 @@ void Material::setMetallic(float metallic) { _schemaBuffer.edit()._metallic = glm::vec3(metallic); } -void Material::setEmissive(const Color& emissive) { +void Material::setEmissive(const Color& emissive, bool isSRGB) { _key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f)))); - _schemaBuffer.edit()._emissive = emissive; + _schemaBuffer.edit()._emissive = (isSRGB ? ColorUtils::toLinearVec3(emissive) : emissive); } void Material::setGloss(float gloss) { diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index b0725d9908..cb5a285dba 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include @@ -219,28 +219,31 @@ public: virtual ~Material(); const MaterialKey& getKey() const { return _key; } - - const Color& getEmissive() const { return _schemaBuffer.get()._emissive; } - const Color& getDiffuse() const { return _schemaBuffer.get()._diffuse; } - float getMetallic() const { return _schemaBuffer.get()._metallic.x; } - float getGloss() const { return _schemaBuffer.get()._gloss; } - float getOpacity() const { return _schemaBuffer.get()._opacity; } - void setEmissive(const Color& emissive); - void setDiffuse(const Color& diffuse); + void setEmissive(const Color& emissive, bool isSRGB = true); + Color getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::toGamma22Vec3(_schemaBuffer.get()._emissive) : _schemaBuffer.get()._emissive); } + + void setDiffuse(const Color& diffuse, bool isSRGB = true); + Color getDiffuse(bool SRGB = true) const { return (SRGB ? ColorUtils::toGamma22Vec3(_schemaBuffer.get()._diffuse) : _schemaBuffer.get()._diffuse); } + void setMetallic(float metallic); + float getMetallic() const { return _schemaBuffer.get()._metallic.x; } + void setGloss(float gloss); + float getGloss() const { return _schemaBuffer.get()._gloss; } + void setOpacity(float opacity); + float getOpacity() const { return _schemaBuffer.get()._opacity; } // Schema to access the attribute values of the material class Schema { public: - Color _diffuse{0.5f}; + glm::vec3 _diffuse{ 0.5f }; float _opacity{1.f}; - Color _metallic{0.03f}; + glm::vec3 _metallic{ 0.03f }; float _gloss{0.1f}; - Color _emissive{0.0f}; + glm::vec3 _emissive{ 0.0f }; float _spare0{0.0f}; glm::vec4 _spareVec4{0.0f}; // for alignment beauty, Material size == Mat4x4 diff --git a/libraries/model/src/model/Skybox.slf b/libraries/model/src/model/Skybox.slf index 6246bbd9d3..f8a568bcf9 100755 --- a/libraries/model/src/model/Skybox.slf +++ b/libraries/model/src/model/Skybox.slf @@ -52,8 +52,7 @@ void main(void) { } } - vec3 pixel = pow(color, vec3(1.0/2.2)); // manual Gamma correction - _fragColor = vec4(pixel, 0.0); + _fragColor = vec4(color, 0.0); #endif diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 67f0262b61..47d3ba3063 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -225,6 +225,21 @@ void SunSkyStage::setOriginOrientation(const Quat& orientation) { invalidate(); } +void SunSkyStage::setOriginLongitude(float longitude) { + _earthSunModel.setLongitude(longitude); + invalidate(); +} + +void SunSkyStage::setOriginLatitude(float latitude) { + _earthSunModel.setLatitude(latitude); + invalidate(); +} + +void SunSkyStage::setOriginSurfaceAltitude(float altitude) { + _earthSunModel.setAltitude(altitude); + invalidate(); +} + void SunSkyStage::setOriginLocation(float longitude, float latitude, float altitude) { _earthSunModel.setLongitude(longitude); _earthSunModel.setLatitude(latitude); diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index 978c308ac6..bf586b6b55 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -184,6 +184,9 @@ public: const Quat& getOriginOrientation() const { return _earthSunModel.getSurfaceOrientation(); } // Location used to define the sun & sky is a longitude and latitude [rad] and a earth surface altitude [km] + void setOriginLatitude(float latitude); + void setOriginLongitude(float longitude); + void setOriginSurfaceAltitude(float surfaceAltitude); void setOriginLocation(float longitude, float latitude, float surfaceAltitude); float getOriginLatitude() const { return _earthSunModel.getLatitude(); } float getOriginLongitude() const { return _earthSunModel.getLongitude(); } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d443522a6f..a86c7cdbec 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -119,13 +119,13 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con if ((image.width() > 0) && (image.height() > 0)) { // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); } @@ -156,11 +156,11 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src bool isLinearRGB = true; - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); } theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); @@ -246,11 +246,11 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); } @@ -368,11 +368,11 @@ gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, c // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); } @@ -520,3 +520,89 @@ gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, c return theTexture; } + + +gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { + QImage image = srcImage; + + int imageArea = image.width() * image.height(); + + int opaquePixels = 0; + int translucentPixels = 0; + //bool isTransparent = false; + int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; + const int EIGHT_BIT_MAXIMUM = 255; + QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); + + if (!image.hasAlphaChannel()) { + if (image.format() != QImage::Format_RGB888) { + image = image.convertToFormat(QImage::Format_RGB888); + } + // int redTotal = 0, greenTotal = 0, blueTotal = 0; + for (int y = 0; y < image.height(); y++) { + for (int x = 0; x < image.width(); x++) { + QRgb rgb = image.pixel(x, y); + redTotal += qRed(rgb); + greenTotal += qGreen(rgb); + blueTotal += qBlue(rgb); + } + } + if (imageArea > 0) { + averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); + } + } else { + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + + // check for translucency/false transparency + // int opaquePixels = 0; + // int translucentPixels = 0; + // int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; + for (int y = 0; y < image.height(); y++) { + for (int x = 0; x < image.width(); x++) { + QRgb rgb = image.pixel(x, y); + redTotal += qRed(rgb); + greenTotal += qGreen(rgb); + blueTotal += qBlue(rgb); + int alpha = qAlpha(rgb); + alphaTotal += alpha; + if (alpha == EIGHT_BIT_MAXIMUM) { + opaquePixels++; + } else if (alpha != 0) { + translucentPixels++; + } + } + } + if (opaquePixels == imageArea) { + qCDebug(modelLog) << "Image with alpha channel is completely opaque:" << QString(srcImageName.c_str()); + image = image.convertToFormat(QImage::Format_RGB888); + } + + averageColor = QColor(redTotal / imageArea, + greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea); + + //isTransparent = (translucentPixels >= imageArea / 2); + } + + gpu::Texture* theTexture = nullptr; + if ((image.width() > 0) && (image.height() > 0)) { + + // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + if (image.hasAlphaChannel()) { + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + } + + + 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); + } + + return theTexture; +} diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index 107ca2e879..6bc5b8228c 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -35,6 +35,8 @@ public: static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName); + static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName); + }; diff --git a/libraries/networking/src/AbstractUriHandler.h b/libraries/networking/src/AbstractUriHandler.h new file mode 100644 index 0000000000..3d6ed472ec --- /dev/null +++ b/libraries/networking/src/AbstractUriHandler.h @@ -0,0 +1,19 @@ +// +// Created by Bradley Austin Davis on 2015/12/17 +// Copyright 2013-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 +#ifndef hifi_network_AbstractUriHandler_h +#define hifi_network_AbstractUriHandler_h + +class AbstractUriHandler { +public: + virtual bool canAcceptURL(const QString& url) const = 0; + virtual bool acceptURL(const QString& url, bool defaultUpload = false) = 0; +}; + +#endif diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 4c72a16f1d..e6163c776b 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -91,7 +91,7 @@ Assignment::Assignment(ReceivedMessage& message) : #endif -Assignment::Assignment(const Assignment& otherAssignment) { +Assignment::Assignment(const Assignment& otherAssignment) : QObject() { _uuid = otherAssignment._uuid; _command = otherAssignment._command; _type = otherAssignment._type; diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index 399deaa314..97caedcfb3 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -23,7 +23,7 @@ const int MAX_PAYLOAD_BYTES = 1024; const QString emptyPool = QString(); /// Holds information used for request, creation, and deployment of assignments -class Assignment : public NodeData { +class Assignment : public QObject { Q_OBJECT public: diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 4419ba882a..0b194b017e 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -40,7 +40,6 @@ const char SOLO_NODE_TYPES[2] = { }; LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) : - linkedDataCreateCallback(NULL), _sessionUUID(), _nodeHash(), _nodeMutex(QReadWriteLock::Recursive), diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index c89949b9fd..87ed12ac66 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -129,7 +129,7 @@ public: qint64 sendPacketList(std::unique_ptr packetList, const HifiSockAddr& sockAddr); qint64 sendPacketList(std::unique_ptr packetList, const Node& destinationNode); - void (*linkedDataCreateCallback)(Node *); + std::function linkedDataCreateCallback; size_t size() const { return _nodeHash.size(); } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e591ee8a31..5075d78597 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -94,7 +94,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings); connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start())); - connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::stopKeepalivePingTimer); + connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop); // we definitely want STUN to update our public socket, so call the LNL to kick that off startSTUNPublicSocketUpdate(); diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 9aeddbc99c..2f5ab7a015 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -122,8 +122,10 @@ void ThreadedAssignment::startSendingStats() { } void ThreadedAssignment::stopSendingStats() { - // stop sending stats, we just disconnected from domain - _statsTimer->stop(); + if (_statsTimer) { + // stop sending stats, we just disconnected from domain + _statsTimer->stop(); + } } void ThreadedAssignment::checkInWithDomainServerOrExit() { diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 11f1b043c8..8fb5db3853 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -236,8 +236,14 @@ void Socket::readPendingDatagrams() { auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); // pull the datagram - _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader, - senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); + auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader, + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); + + if (sizeRead <= 0) { + // we either didn't pull anything for this packet or there was an error reading (this seems to trigger + // on windows even if there's not a packet available) + continue; + } auto it = _unfilteredHandlers.find(senderSockAddr); @@ -248,7 +254,7 @@ void Socket::readPendingDatagrams() { it->second(std::move(basePacket)); } - return; + continue; } // check if this was a control packet or a data packet @@ -276,7 +282,7 @@ void Socket::readPendingDatagrams() { packet->getDataSize(), packet->getPayloadSize())) { // the connection indicated that we should not continue processing this packet - return; + continue; } } diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 636d1a9a1a..652089ebb1 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1867,7 +1867,7 @@ bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputSt QByteArray jsonBuffer; char* rawData = new char[READ_JSON_BUFFER_SIZE]; - while (true) { + while (!inputStream.atEnd()) { int got = inputStream.readRawData(rawData, READ_JSON_BUFFER_SIZE - 1); if (got < 0) { qCritical() << "error while reading from json stream"; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 44d4269e0e..86d57b7ee9 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -15,6 +15,7 @@ #include "BulletUtil.h" #include "PhysicsCollisionGroups.h" +#include "ObjectMotionState.h" const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float JUMP_SPEED = 3.5f; @@ -379,3 +380,15 @@ void CharacterController::preSimulation() { void CharacterController::postSimulation() { // postSimulation() exists for symmetry and just in case we need to do something here later } + + +bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) { + if (!_rigidBody) { + return false; + } + + const btTransform& worldTrans = _rigidBody->getCenterOfMassTransform(); + avatarRigidBodyPosition = bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(); + avatarRigidBodyRotation = bulletToGLM(worldTrans.getRotation()); + return true; +} diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 88c02d0940..7bdc35fc0b 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -79,6 +79,8 @@ public: void setEnabled(bool enabled); bool isEnabled() const { return _enabled && _dynamicsWorld; } + bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); + protected: void updateUpAxis(const glm::quat& rotation); diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index e44036eadc..4ca13f2fbf 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -29,12 +29,12 @@ public: ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity); virtual ~ObjectAction(); - virtual void removeFromSimulation(EntitySimulation* simulation) const; - virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; } - virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; } + virtual void removeFromSimulation(EntitySimulation* simulation) const override; + virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; } + virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; } - virtual bool updateArguments(QVariantMap arguments); - virtual QVariantMap getArguments(); + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; // this is called from updateAction and should be overridden by subclasses virtual void updateActionWorker(float deltaTimeStep) = 0; @@ -43,25 +43,25 @@ public: virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); virtual void debugDraw(btIDebugDraw* debugDrawer); - virtual QByteArray serialize() const = 0; - virtual void deserialize(QByteArray serializedArguments) = 0; + virtual QByteArray serialize() const override = 0; + virtual void deserialize(QByteArray serializedArguments) override = 0; - virtual bool lifetimeIsOver(); - virtual quint64 getExpires() { return _expires; } + virtual bool lifetimeIsOver() override; + virtual quint64 getExpires() override { return _expires; } protected: quint64 localTimeToServerTime(quint64 timeValue) const; quint64 serverTimeToLocalTime(quint64 timeValue) const; virtual btRigidBody* getRigidBody(); - 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; virtual void activateBody(); virtual void forceBodyNonStatic(); diff --git a/libraries/physics/src/ObjectActionOffset.h b/libraries/physics/src/ObjectActionOffset.h index 1918da6996..ea01b10d33 100644 --- a/libraries/physics/src/ObjectActionOffset.h +++ b/libraries/physics/src/ObjectActionOffset.h @@ -22,13 +22,13 @@ public: ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity); virtual ~ObjectActionOffset(); - virtual bool updateArguments(QVariantMap arguments); - virtual QVariantMap getArguments(); + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; - virtual void updateActionWorker(float deltaTimeStep); + virtual void updateActionWorker(float deltaTimeStep) override; - virtual QByteArray serialize() const; - virtual void deserialize(QByteArray serializedArguments); + virtual QByteArray serialize() const override; + virtual void deserialize(QByteArray serializedArguments) override; private: static const uint16_t offsetVersion; diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h index caa64c3d3a..96bb900bf6 100644 --- a/libraries/physics/src/ObjectActionSpring.h +++ b/libraries/physics/src/ObjectActionSpring.h @@ -19,13 +19,13 @@ public: ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity); virtual ~ObjectActionSpring(); - virtual bool updateArguments(QVariantMap arguments); - virtual QVariantMap getArguments(); + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; - virtual void updateActionWorker(float deltaTimeStep); + virtual void updateActionWorker(float deltaTimeStep) override; - virtual QByteArray serialize() const; - virtual void deserialize(QByteArray serializedArguments); + virtual QByteArray serialize() const override; + virtual void deserialize(QByteArray serializedArguments) override; protected: static const uint16_t springVersion; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 40366257df..5a12627abd 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -44,7 +44,7 @@ void PhysicalEntitySimulation::updateEntitiesInternal(const quint64& now) { void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { assert(entity); - if (entity->shouldBePhysical()) { + if (entity->shouldBePhysical()) { EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); if (!motionState) { _pendingAdds.insert(entity); @@ -117,6 +117,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() { _pendingRemoves.clear(); _pendingAdds.clear(); _pendingChanges.clear(); + _outgoingChanges.clear(); } // end EntitySimulation overrides diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 10e285186c..22695a1b66 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -497,3 +497,11 @@ void PhysicsEngine::removeAction(const QUuid actionID) { _objectActions.remove(actionID); } } + +void PhysicsEngine::forEachAction(std::function actor) { + QHashIterator iter(_objectActions); + while (iter.hasNext()) { + iter.next(); + actor(iter.value()); + } +} diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index e7b5fd79d4..05032ccae2 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -97,6 +97,7 @@ public: EntityActionPointer getActionByID(const QUuid& actionID) const; void addAction(EntityActionPointer action); void removeAction(const QUuid actionID); + void forEachAction(std::function actor); private: void removeContacts(ObjectMotionState* motionState); diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.slf b/libraries/procedural/src/procedural/ProceduralSkybox.slf index 382801f52d..7ad6f6b5a1 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.slf +++ b/libraries/procedural/src/procedural/ProceduralSkybox.slf @@ -35,6 +35,8 @@ void main(void) { #ifdef PROCEDURAL vec3 color = getSkyboxColor(); + // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline + color = pow(color, vec3(2.2)); _fragColor = vec4(color, 0.0); #else @@ -42,8 +44,7 @@ void main(void) { vec3 coord = normalize(_normal); vec3 texel = texture(cubeMap, coord).rgb; vec3 color = texel * _skybox._color.rgb; - vec3 pixel = pow(color, vec3(1.0/2.2)); // manual Gamma correction - _fragColor = vec4(pixel, 0.0); + _fragColor = vec4(color, 0.0); #endif diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index e54f55fc94..c32bf2654d 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -175,10 +175,10 @@ const gpu::PipelinePointer& AmbientOcclusion::getBlendPipeline() { } void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto framebufferCache = DependencyManager::get(); QSize framebufferSize = framebufferCache->getFrameBufferSize(); @@ -201,7 +201,7 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons // Occlusion step getOcclusionPipeline(); batch.setResourceTexture(0, framebufferCache->getPrimaryDepthTexture()); - batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture()); + batch.setResourceTexture(1, framebufferCache->getDeferredNormalTexture()); _occlusionBuffer->setRenderBuffer(0, _occlusionTexture); batch.setFramebuffer(_occlusionBuffer); @@ -276,7 +276,7 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons // Blend step getBlendPipeline(); batch.setResourceTexture(0, _hBlurTexture); - batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer()); + batch.setFramebuffer(framebufferCache->getDeferredFramebuffer()); // Bind the fourth gpu::Pipeline we need - for blending the primary color buffer with blurred occlusion texture batch.setPipeline(getBlendPipeline()); diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index d4707c172d..3acc88f953 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -93,14 +93,14 @@ const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { } void Antialiasing::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - if (renderContext->args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + if (renderContext->getArgs()->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { return; } - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); @@ -123,7 +123,7 @@ void Antialiasing::run(const render::SceneContextPointer& sceneContext, const re // FXAA step getAntialiasingPipeline(); - batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); + batch.setResourceTexture(0, framebufferCache->getDeferredColorTexture()); _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); batch.setFramebuffer(_antialiasingBuffer); batch.setPipeline(getAntialiasingPipeline()); @@ -153,7 +153,7 @@ void Antialiasing::run(const render::SceneContextPointer& sceneContext, const re // Blend step getBlendPipeline(); batch.setResourceTexture(0, _antialiasingTexture); - batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer()); + batch.setFramebuffer(framebufferCache->getDeferredFramebuffer()); batch.setPipeline(getBlendPipeline()); DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp new file mode 100644 index 0000000000..ca678770fb --- /dev/null +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -0,0 +1,214 @@ +// +// DebugDeferredBuffer.cpp +// libraries/render-utils/src +// +// Created by Clement on 12/3/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 "DebugDeferredBuffer.h" + +#include + +#include +#include +#include +#include + +#include "GeometryCache.h" +#include "FramebufferCache.h" + +#include "debug_deferred_buffer_vert.h" +#include "debug_deferred_buffer_frag.h" + +using namespace render; + +enum Slots { + Diffuse = 0, + Normal, + Specular, + Depth, + Lighting +}; + +static const std::string DEEFAULT_DIFFUSE_SHADER { + "vec4 getFragmentColor() {" + " return vec4(pow(texture(diffuseMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);" + " }" +}; +static const std::string DEEFAULT_ALPHA_SHADER { + "vec4 getFragmentColor() {" + " return vec4(vec3(texture(diffuseMap, uv).a), 1.0);" + " }" +}; +static const std::string DEEFAULT_SPECULAR_SHADER { + "vec4 getFragmentColor() {" + " return vec4(texture(specularMap, uv).xyz, 1.0);" + " }" +}; +static const std::string DEEFAULT_ROUGHNESS_SHADER { + "vec4 getFragmentColor() {" + " return vec4(vec3(texture(specularMap, uv).a), 1.0);" + " }" +}; +static const std::string DEEFAULT_NORMAL_SHADER { + "vec4 getFragmentColor() {" + " return vec4(normalize(texture(normalMap, uv).xyz), 1.0);" + " }" +}; +static const std::string DEEFAULT_DEPTH_SHADER { + "vec4 getFragmentColor() {" + " return vec4(vec3(texture(depthMap, uv).x), 1.0);" + " }" +}; +static const std::string DEEFAULT_LIGHTING_SHADER { + "vec4 getFragmentColor() {" + " return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);" + " }" +}; +static const std::string DEEFAULT_CUSTOM_SHADER { + "vec4 getFragmentColor() {" + " return vec4(1.0, 0.0, 0.0, 1.0);" + " }" +}; + +static std::string getFileContent(std::string fileName, std::string defaultContent = std::string()) { + QFile customFile(QString::fromStdString(fileName)); + if (customFile.open(QIODevice::ReadOnly)) { + return customFile.readAll().toStdString(); + } + qWarning() << "DebugDeferredBuffer::getFileContent(): Could not open" + << QString::fromStdString(fileName); + return defaultContent; +} + +#include // TODO REMOVE: Temporary until UI +DebugDeferredBuffer::DebugDeferredBuffer() { + // TODO REMOVE: Temporary until UI + static const auto DESKTOP_PATH = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); + static const auto CUSTOM_FILE = DESKTOP_PATH.toStdString() + "/custom.slh"; + CustomPipeline pipeline; + pipeline.info = QFileInfo(QString::fromStdString(CUSTOM_FILE)); + _customPipelines.emplace(CUSTOM_FILE, pipeline); +} + +std::string DebugDeferredBuffer::getShaderSourceCode(Modes mode, std::string customFile) { + switch (mode) { + case DiffuseMode: + return DEEFAULT_DIFFUSE_SHADER; + case AlphaMode: + return DEEFAULT_ALPHA_SHADER; + case SpecularMode: + return DEEFAULT_SPECULAR_SHADER; + case RoughnessMode: + return DEEFAULT_ROUGHNESS_SHADER; + case NormalMode: + return DEEFAULT_NORMAL_SHADER; + case DepthMode: + return DEEFAULT_DEPTH_SHADER; + case LightingMode: + return DEEFAULT_LIGHTING_SHADER; + case CustomMode: + return getFileContent(customFile, DEEFAULT_CUSTOM_SHADER); + } + Q_UNREACHABLE(); + return std::string(); +} + +bool DebugDeferredBuffer::pipelineNeedsUpdate(Modes mode, std::string customFile) const { + if (mode != CustomMode) { + return !_pipelines[mode]; + } + + auto it = _customPipelines.find(customFile); + if (it != _customPipelines.end() && it->second.pipeline) { + auto& info = it->second.info; + + auto lastModified = info.lastModified(); + info.refresh(); + return lastModified != info.lastModified(); + } + + return true; +} + +const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Modes mode, std::string customFile) { + if (pipelineNeedsUpdate(mode, customFile)) { + static const std::string VERTEX_SHADER { debug_deferred_buffer_vert }; + static const std::string FRAGMENT_SHADER { debug_deferred_buffer_frag }; + static const std::string SOURCE_PLACEHOLDER { "//SOURCE_PLACEHOLDER" }; + static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER); + Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, + "Could not find source placeholder"); + + auto bakedFragmentShader = FRAGMENT_SHADER; + bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), + getShaderSourceCode(mode, customFile)); + + static const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); + const auto ps = gpu::Shader::createPixel(bakedFragmentShader); + const auto program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("diffuseMap", Diffuse)); + slotBindings.insert(gpu::Shader::Binding("normalMap", Normal)); + slotBindings.insert(gpu::Shader::Binding("specularMap", Specular)); + slotBindings.insert(gpu::Shader::Binding("depthMap", Depth)); + slotBindings.insert(gpu::Shader::Binding("lightingMap", Lighting)); + gpu::Shader::makeProgram(*program, slotBindings); + + auto pipeline = gpu::Pipeline::create(program, std::make_shared()); + + // Good to go add the brand new pipeline + if (mode != CustomMode) { + _pipelines[mode] = pipeline; + } else { + _customPipelines[customFile].pipeline = pipeline; + } + } + + if (mode != CustomMode) { + return _pipelines[mode]; + } else { + return _customPipelines[customFile].pipeline; + } +} + + +void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); + RenderArgs* args = renderContext->getArgs(); + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + const auto geometryBuffer = DependencyManager::get(); + const auto framebufferCache = DependencyManager::get(); + + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // TODO REMOVE: Temporary until UI + auto first = _customPipelines.begin()->first; + + batch.setPipeline(getPipeline(Modes(renderContext->_deferredDebugMode), first)); + + batch.setResourceTexture(Diffuse, framebufferCache->getDeferredColorTexture()); + batch.setResourceTexture(Normal, framebufferCache->getDeferredNormalTexture()); + batch.setResourceTexture(Specular, framebufferCache->getDeferredSpecularTexture()); + batch.setResourceTexture(Depth, framebufferCache->getPrimaryDepthTexture()); + batch.setResourceTexture(Lighting, framebufferCache->getLightingTexture()); + + const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); + const glm::vec2 bottomLeft(renderContext->_deferredDebugSize.x, renderContext->_deferredDebugSize.y); + const glm::vec2 topRight(renderContext->_deferredDebugSize.z, renderContext->_deferredDebugSize.w); + geometryBuffer->renderQuad(batch, bottomLeft, topRight, color); + }); +} diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h new file mode 100644 index 0000000000..682888b2af --- /dev/null +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -0,0 +1,54 @@ +// +// DebugDeferredBuffer.h +// libraries/render-utils/src +// +// Created by Clement on 12/3/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_DebugDeferredBuffer_h +#define hifi_DebugDeferredBuffer_h + +#include + +#include + +class DebugDeferredBuffer { +public: + using JobModel = render::Job::Model; + + DebugDeferredBuffer(); + + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + +private: + enum Modes : uint8_t { + DiffuseMode = 0, + AlphaMode, + SpecularMode, + RoughnessMode, + NormalMode, + DepthMode, + LightingMode, + + CustomMode // Needs to stay last + }; + struct CustomPipeline { + gpu::PipelinePointer pipeline; + mutable QFileInfo info; + }; + using StandardPipelines = std::array; + using CustomPipelines = std::unordered_map; + + bool pipelineNeedsUpdate(Modes mode, std::string customFile = std::string()) const; + const gpu::PipelinePointer& getPipeline(Modes mode, std::string customFile = std::string()); + std::string getShaderSourceCode(Modes mode, std::string customFile = std::string()); + + StandardPipelines _pipelines; + CustomPipelines _customPipelines; +}; + +#endif // hifi_DebugDeferredBuffer_h \ No newline at end of file diff --git a/libraries/render-utils/src/DeferredBuffer.slh b/libraries/render-utils/src/DeferredBuffer.slh index 275966534a..18606f2525 100755 --- a/libraries/render-utils/src/DeferredBuffer.slh +++ b/libraries/render-utils/src/DeferredBuffer.slh @@ -24,6 +24,9 @@ uniform sampler2D specularMap; // the depth texture uniform sampler2D depthMap; +// the lighting texture +uniform sampler2D lightingMap; + struct DeferredTransform { mat4 projection; diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh index 1c1330f0c0..1045c4afc7 100755 --- a/libraries/render-utils/src/DeferredBufferWrite.slh +++ b/libraries/render-utils/src/DeferredBufferWrite.slh @@ -15,12 +15,6 @@ layout(location = 0) out vec4 _fragColor0; layout(location = 1) out vec4 _fragColor1; layout(location = 2) out vec4 _fragColor2; -// the glow intensity -uniform float glowIntensity; - -// the alpha threshold -uniform float alphaThreshold; - uniform sampler2D normalFittingMap; vec3 bestFitNormal(vec3 normal) { @@ -39,36 +33,38 @@ vec3 bestFitNormal(vec3 normal) { return (cN * 0.5 + 0.5); } + +// the alpha threshold +const float alphaThreshold = 0.5; float evalOpaqueFinalAlpha(float alpha, float mapAlpha) { - return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold)); + return mix(alpha, 1.0 - alpha, step(mapAlpha, alphaThreshold)); } const vec3 DEFAULT_SPECULAR = vec3(0.1); const float DEFAULT_SHININESS = 10; void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) { - if (alpha != glowIntensity) { + if (alpha != 1.0) { discard; } - _fragColor0 = vec4(diffuse.rgb, alpha); + _fragColor0 = vec4(diffuse.rgb, 1.0); // Opaque _fragColor1 = vec4(bestFitNormal(normal), 1.0); _fragColor2 = vec4(specular, shininess / 128.0); } void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess, vec3 emissive) { - if (alpha != glowIntensity) { + if (alpha != 1.0) { discard; } - _fragColor0 = vec4(diffuse.rgb, alpha); - //_fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); + _fragColor0 = vec4(diffuse.rgb, 0.5); _fragColor1 = vec4(bestFitNormal(normal), 0.5); _fragColor2 = vec4(emissive, shininess / 128.0); } void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) { - if (alpha <= alphaThreshold) { + if (alpha <= 0.0) { discard; } diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index 983b8002f7..11e157a50c 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -83,7 +83,7 @@ vec3 evalAmbienGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 positi vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); - color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + color += vec3(diffuse * shading.w + shading.rgb) * shadowAttenuation * getLightColor(light) * getLightIntensity(light); return color; } @@ -106,7 +106,7 @@ vec3 evalAmbienSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); - color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + color += vec3(diffuse * shading.w + shading.rgb) * shadowAttenuation * getLightColor(light) * getLightIntensity(light); return color; } @@ -129,7 +129,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 positi vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); - color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + color += vec3(diffuse * shading.w + shading.rgb) * shadowAttenuation * getLightColor(light) * getLightIntensity(light); return color; } diff --git a/libraries/render-utils/src/DeferredLighting.slh b/libraries/render-utils/src/DeferredLighting.slh index 888742bb18..cf03861c2f 100755 --- a/libraries/render-utils/src/DeferredLighting.slh +++ b/libraries/render-utils/src/DeferredLighting.slh @@ -23,17 +23,16 @@ vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 sp // Specular Lighting depends on the half vector and the gloss vec3 halfDir = normalize(fragEyeDir + fragLightDir); - // float specularPower = pow(facingLight * max(0.0, dot(halfDir, fragNormal)), gloss * 128.0); float specularPower = pow(max(0.0, dot(halfDir, fragNormal)), gloss * 128.0); specularPower *= (gloss * 128.0 * 0.125 + 0.25); float shlickPower = (1.0 - dot(fragLightDir,halfDir)); float shlickPower2 = shlickPower * shlickPower; float shlickPower5 = shlickPower2 * shlickPower2 * shlickPower; - vec3 schlick = specular * (1.0 - shlickPower5) + vec3(shlickPower5); - vec3 reflect = specularPower * schlick; + vec3 fresnel = specular * (1.0 - shlickPower5) + vec3(shlickPower5); + vec3 reflect = specularPower * fresnel * diffuse; - return vec4(reflect, diffuse); + return vec4(reflect, diffuse * (1 - fresnel.x)); } <@endfunc@> @@ -49,7 +48,7 @@ vec4 evalBlinnShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 vec3 halfDir = normalize(fragEyeDir + fragLightDir); float specularPower = pow(facingLight * max(0.0, dot(halfDir, fragNormal)), gloss * 128.0); - vec3 reflect = specularPower * specular; + vec3 reflect = specularPower * specular * diffuse; return vec4(reflect, diffuse); } @@ -59,14 +58,9 @@ vec4 evalBlinnShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 <$declareEvalPBRShading()$> - +// Return xyz the specular/reflection component and w the diffuse component vec4 evalFragShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 specular, float gloss) { - - /*if (gl_FragCoord.x > 1000) { - return evalBlinnShading(fragNormal, fragLightDir, fragEyeDir, specular, gloss); - } else {*/ - return evalPBRShading(fragNormal, fragLightDir, fragEyeDir, specular, gloss); - //} + return evalPBRShading(fragNormal, fragLightDir, fragEyeDir, specular, gloss); } <@endif@> diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 10b58b156b..db0e47de5e 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -34,25 +34,13 @@ #include "deferred_light_spot_vert.h" #include "directional_light_frag.h" -#include "directional_light_shadow_map_frag.h" -#include "directional_light_cascaded_shadow_map_frag.h" - #include "directional_ambient_light_frag.h" -#include "directional_ambient_light_shadow_map_frag.h" -#include "directional_ambient_light_cascaded_shadow_map_frag.h" - #include "directional_skybox_light_frag.h" -#include "directional_skybox_light_shadow_map_frag.h" -#include "directional_skybox_light_cascaded_shadow_map_frag.h" #include "point_light_frag.h" #include "spot_light_frag.h" -static const std::string glowIntensityShaderHandle = "glowIntensity"; - struct LightLocations { - int shadowDistances; - int shadowScale; int radius; int ambientSphere; int lightBufferUnit; @@ -92,7 +80,7 @@ gpu::PipelinePointer DeferredLightingEffect::getPipeline(SimpleProgramKey config return pipeline; } -void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { +void DeferredLightingEffect::init() { auto VS = gpu::Shader::createVertex(std::string(simple_vert)); auto PS = gpu::Shader::createPixel(std::string(simple_textured_frag)); auto PSEmissive = gpu::Shader::createPixel(std::string(simple_textured_emisive_frag)); @@ -105,54 +93,23 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { gpu::Shader::makeProgram(*_simpleShader, slotBindings); gpu::Shader::makeProgram(*_emissiveShader, slotBindings); - _viewState = viewState; + _directionalLightLocations = std::make_shared(); - _directionalLightShadowMapLocations = std::make_shared(); - _directionalLightCascadedShadowMapLocations = std::make_shared(); _directionalAmbientSphereLightLocations = std::make_shared(); - _directionalAmbientSphereLightShadowMapLocations = std::make_shared(); - _directionalAmbientSphereLightCascadedShadowMapLocations = std::make_shared(); _directionalSkyboxLightLocations = std::make_shared(); - _directionalSkyboxLightShadowMapLocations = std::make_shared(); - _directionalSkyboxLightCascadedShadowMapLocations = std::make_shared(); _pointLightLocations = std::make_shared(); _spotLightLocations = std::make_shared(); loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations); - loadLightProgram(deferred_light_vert, directional_light_shadow_map_frag, false, _directionalLightShadowMap, - _directionalLightShadowMapLocations); - loadLightProgram(deferred_light_vert, directional_light_cascaded_shadow_map_frag, false, _directionalLightCascadedShadowMap, - _directionalLightCascadedShadowMapLocations); loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations); - loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_map_frag, false, _directionalAmbientSphereLightShadowMap, - _directionalAmbientSphereLightShadowMapLocations); - loadLightProgram(deferred_light_vert, directional_ambient_light_cascaded_shadow_map_frag, false, _directionalAmbientSphereLightCascadedShadowMap, - _directionalAmbientSphereLightCascadedShadowMapLocations); loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_map_frag, false, _directionalSkyboxLightShadowMap, - _directionalSkyboxLightShadowMapLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_cascaded_shadow_map_frag, false, _directionalSkyboxLightCascadedShadowMap, - _directionalSkyboxLightCascadedShadowMapLocations); loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations); loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations); - { - //auto VSFS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - //auto PSBlit = gpu::StandardShaderLib::getDrawTexturePS(); - auto blitProgram = gpu::StandardShaderLib::getProgram(gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS, gpu::StandardShaderLib::getDrawTexturePS); - gpu::Shader::makeProgram(*blitProgram); - auto blitState = std::make_shared(); - blitState->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); - blitState->setColorWriteMask(true, true, true, false); - _blitLightBuffer = gpu::Pipeline::create(blitProgram, blitState); - } - // Allocate a global light representing the Global Directional light casting shadow (the sun) and the ambient light _globalLights.push_back(0); _allocatedLights.push_back(std::make_shared()); @@ -169,14 +126,12 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { gpu::PipelinePointer DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, - bool emmisive, bool depthBias) { - SimpleProgramKey config{textured, culled, emmisive, depthBias}; + bool emissive, bool depthBias) { + SimpleProgramKey config{textured, culled, emissive, depthBias}; gpu::PipelinePointer pipeline = getPipeline(config); batch.setPipeline(pipeline); gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader; - int glowIntensity = program->getUniforms().findLocation("glowIntensity"); - batch._glUniform1f(glowIntensity, 1.0f); if (!config.isTextured()) { // If it is not textured, bind white texture and keep using textured pipeline @@ -342,11 +297,27 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu void DeferredLightingEffect::prepare(RenderArgs* args) { gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); + batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); - auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); + // Clear Lighting buffer + auto lightingFbo = DependencyManager::get()->getLightingFramebuffer(); + + batch.setFramebuffer(lightingFbo); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(vec3(0), 0), true); + + // Clear deferred + auto deferredFbo = DependencyManager::get()->getDeferredFramebuffer(); + + batch.setFramebuffer(deferredFbo); + + // Clear Color, Depth and Stencil + batch.clearFramebuffer( + gpu::Framebuffer::BUFFER_COLOR0 | + gpu::Framebuffer::BUFFER_DEPTH | + gpu::Framebuffer::BUFFER_STENCIL, + vec4(vec3(0), 1), 1.0, 0.0, true); - batch.setFramebuffer(primaryFbo); // clear the normal and specular buffers batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); const float MAX_SPECULAR_EXPONENT = 128.0f; @@ -354,8 +325,6 @@ void DeferredLightingEffect::prepare(RenderArgs* args) { }); } -gpu::FramebufferPointer _copyFBO; - void DeferredLightingEffect::render(RenderArgs* args) { gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { @@ -375,18 +344,16 @@ void DeferredLightingEffect::render(RenderArgs* args) { QSize framebufferSize = framebufferCache->getFrameBufferSize(); // binding the first framebuffer - _copyFBO = framebufferCache->getFramebuffer(); - batch.setFramebuffer(_copyFBO); + auto lightingFBO = framebufferCache->getLightingFramebuffer(); + batch.setFramebuffer(lightingFBO); - // Clearing it batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); - batch.clearColorFramebuffer(_copyFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); // BInd the G-Buffer surfaces - batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); - batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture()); - batch.setResourceTexture(2, framebufferCache->getPrimarySpecularTexture()); + batch.setResourceTexture(0, framebufferCache->getDeferredColorTexture()); + batch.setResourceTexture(1, framebufferCache->getDeferredNormalTexture()); + batch.setResourceTexture(2, framebufferCache->getDeferredSpecularTexture()); batch.setResourceTexture(3, framebufferCache->getPrimaryDepthTexture()); // THe main viewport is assumed to be the mono viewport (or the 2 stereo faces side by side within that viewport) @@ -679,42 +646,6 @@ void DeferredLightingEffect::render(RenderArgs* args) { } } - -void DeferredLightingEffect::copyBack(RenderArgs* args) { - auto framebufferCache = DependencyManager::get(); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { - batch.enableStereo(false); - QSize framebufferSize = framebufferCache->getFrameBufferSize(); - - // TODO why doesn't this blit work? It only seems to affect a small area below the rear view mirror. - // auto destFbo = framebufferCache->getPrimaryFramebuffer(); - auto destFbo = framebufferCache->getPrimaryFramebufferDepthColor(); - // gpu::Vec4i vp = args->_viewport; - // batch.blit(_copyFBO, vp, framebufferCache->getPrimaryFramebuffer(), vp); - batch.setFramebuffer(destFbo); - batch.setViewportTransform(args->_viewport); - batch.setProjectionTransform(glm::mat4()); - batch.setViewTransform(Transform()); - { - float sMin = args->_viewport.x / (float)framebufferSize.width(); - float sWidth = args->_viewport.z / (float)framebufferSize.width(); - float tMin = args->_viewport.y / (float)framebufferSize.height(); - float tHeight = args->_viewport.w / (float)framebufferSize.height(); - Transform model; - batch.setPipeline(_blitLightBuffer); - model.setTranslation(glm::vec3(sMin, tMin, 0.0)); - model.setScale(glm::vec3(sWidth, tHeight, 1.0)); - batch.setModelTransform(model); - } - - batch.setResourceTexture(0, _copyFBO->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - args->_context->render(batch); - }); - framebufferCache->releaseFramebuffer(_copyFBO); -} - void DeferredLightingEffect::setupTransparent(RenderArgs* args, int lightBufferUnit) { auto globalLight = _allocatedLights[_globalLights.front()]; args->_batch->setUniformBuffer(lightBufferUnit, globalLight->getSchemaBuffer()); @@ -731,7 +662,6 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), 1)); slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2)); slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 3)); - slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), 4)); slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), 5)); const int LIGHT_GPU_SLOT = 3; slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT)); @@ -742,8 +672,6 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo gpu::Shader::makeProgram(*program, slotBindings); - locations->shadowDistances = program->getUniforms().findLocation("shadowDistances"); - locations->shadowScale = program->getUniforms().findLocation("shadowScale"); locations->radius = program->getUniforms().findLocation("radius"); locations->ambientSphere = program->getUniforms().findLocation("ambientSphere.L00"); @@ -756,6 +684,11 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo locations->deferredTransformBuffer = program->getBuffers().findLocation("deferredTransformBuffer"); auto state = std::make_shared(); + state->setColorWriteMask(true, true, true, false); + + // Stencil test all the light passes for objects pixels only, not the background + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); + if (lightVolume) { state->setCullMode(gpu::State::CULL_BACK); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 9c4809a82e..dfb0ecbffb 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -21,7 +21,6 @@ #include "model/Stage.h" #include "model/Geometry.h" -class AbstractViewStateInterface; class RenderArgs; class SimpleProgramKey; struct LightLocations; @@ -34,11 +33,11 @@ public: static const int NORMAL_FITTING_MAP_SLOT = 10; static const int DEFERRED_TRANSFORM_BUFFER_SLOT = 2; - void init(AbstractViewStateInterface* viewState); + void init(); /// Sets up the state necessary to render static untextured geometry with the simple program. gpu::PipelinePointer bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true, - bool emmisive = false, bool depthBias = false); + bool emissive = false, bool depthBias = false); void renderSolidSphereInstance(gpu::Batch& batch, const Transform& xfm, const glm::vec4& color); void renderSolidSphereInstance(gpu::Batch& batch, const Transform& xfm, const glm::vec3& color) { @@ -78,7 +77,6 @@ public: void prepare(RenderArgs* args); void render(RenderArgs* args); - void copyBack(RenderArgs* args); void setupTransparent(RenderArgs* args, int lightBufferUnit); @@ -101,29 +99,15 @@ private: gpu::ShaderPointer _simpleShader; gpu::ShaderPointer _emissiveShader; QHash _simplePrograms; - - gpu::PipelinePointer _blitLightBuffer; - + gpu::PipelinePointer _directionalSkyboxLight; LightLocationsPtr _directionalSkyboxLightLocations; - gpu::PipelinePointer _directionalSkyboxLightShadowMap; - LightLocationsPtr _directionalSkyboxLightShadowMapLocations; - gpu::PipelinePointer _directionalSkyboxLightCascadedShadowMap; - LightLocationsPtr _directionalSkyboxLightCascadedShadowMapLocations; gpu::PipelinePointer _directionalAmbientSphereLight; LightLocationsPtr _directionalAmbientSphereLightLocations; - gpu::PipelinePointer _directionalAmbientSphereLightShadowMap; - LightLocationsPtr _directionalAmbientSphereLightShadowMapLocations; - gpu::PipelinePointer _directionalAmbientSphereLightCascadedShadowMap; - LightLocationsPtr _directionalAmbientSphereLightCascadedShadowMapLocations; gpu::PipelinePointer _directionalLight; LightLocationsPtr _directionalLightLocations; - gpu::PipelinePointer _directionalLightShadowMap; - LightLocationsPtr _directionalLightShadowMapLocations; - gpu::PipelinePointer _directionalLightCascadedShadowMap; - LightLocationsPtr _directionalLightCascadedShadowMapLocations; gpu::PipelinePointer _pointLight; LightLocationsPtr _pointLightLocations; @@ -155,8 +139,6 @@ private: std::vector _globalLights; std::vector _pointLights; std::vector _spotLights; - - AbstractViewStateInterface* _viewState; int _ambientLightMode = 0; model::AtmospherePointer _atmosphere; diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 5907d3fa27..9f3d4ba0fe 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -33,61 +33,76 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { //If the size changed, we need to delete our FBOs if (_frameBufferSize != frameBufferSize) { _frameBufferSize = frameBufferSize; - _primaryFramebufferFull.reset(); - _primaryFramebufferDepthColor.reset(); + _primaryFramebuffer.reset(); _primaryDepthTexture.reset(); _primaryColorTexture.reset(); - _primaryNormalTexture.reset(); - _primarySpecularTexture.reset(); + _deferredFramebuffer.reset(); + _deferredFramebufferDepthColor.reset(); + _deferredColorTexture.reset(); + _deferredNormalTexture.reset(); + _deferredSpecularTexture.reset(); _selfieFramebuffer.reset(); _cachedFramebuffers.clear(); + _lightingTexture.reset(); + _lightingFramebuffer.reset(); } } void FramebufferCache::createPrimaryFramebuffer() { - _primaryFramebufferFull = gpu::FramebufferPointer(gpu::Framebuffer::create()); - _primaryFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _primaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _deferredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _deferredFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create()); - auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + auto colorFormat = gpu::Element::COLOR_RGBA_32; auto width = _frameBufferSize.width(); auto height = _frameBufferSize.height(); auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); _primaryColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _primaryNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _primarySpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _primaryFramebufferFull->setRenderBuffer(0, _primaryColorTexture); - _primaryFramebufferFull->setRenderBuffer(1, _primaryNormalTexture); - _primaryFramebufferFull->setRenderBuffer(2, _primarySpecularTexture); + _primaryFramebuffer->setRenderBuffer(0, _primaryColorTexture); - _primaryFramebufferDepthColor->setRenderBuffer(0, _primaryColorTexture); + _deferredColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + + _deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture); + _deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture); + _deferredFramebuffer->setRenderBuffer(2, _deferredSpecularTexture); + + _deferredFramebufferDepthColor->setRenderBuffer(0, _deferredColorTexture); // auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler)); - - _primaryFramebufferFull->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); - _primaryFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); - + _primaryFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + _deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + _deferredFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + _selfieFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); auto tex = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width * 0.5, height * 0.5, defaultSampler)); _selfieFramebuffer->setRenderBuffer(0, tex); + + auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); + + // FIXME: Decide on the proper one, let s stick to R11G11B10 for now + //_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, defaultSampler)); + _lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::R11G11B10), width, height, defaultSampler)); + //_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::HALF, gpu::RGBA), width, height, defaultSampler)); + _lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _lightingFramebuffer->setRenderBuffer(0, _lightingTexture); + _lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); } gpu::FramebufferPointer FramebufferCache::getPrimaryFramebuffer() { - if (!_primaryFramebufferFull) { + if (!_primaryFramebuffer) { createPrimaryFramebuffer(); } - return _primaryFramebufferFull; -} - -gpu::FramebufferPointer FramebufferCache::getPrimaryFramebufferDepthColor() { - if (!_primaryFramebufferDepthColor) { - createPrimaryFramebuffer(); - } - return _primaryFramebufferDepthColor; + return _primaryFramebuffer; } gpu::TexturePointer FramebufferCache::getPrimaryDepthTexture() { @@ -104,18 +119,53 @@ gpu::TexturePointer FramebufferCache::getPrimaryColorTexture() { return _primaryColorTexture; } -gpu::TexturePointer FramebufferCache::getPrimaryNormalTexture() { - if (!_primaryNormalTexture) { +gpu::FramebufferPointer FramebufferCache::getDeferredFramebuffer() { + if (!_deferredFramebuffer) { createPrimaryFramebuffer(); } - return _primaryNormalTexture; + return _deferredFramebuffer; } -gpu::TexturePointer FramebufferCache::getPrimarySpecularTexture() { - if (!_primarySpecularTexture) { +gpu::FramebufferPointer FramebufferCache::getDeferredFramebufferDepthColor() { + if (!_deferredFramebufferDepthColor) { createPrimaryFramebuffer(); } - return _primarySpecularTexture; + return _deferredFramebufferDepthColor; +} + +gpu::TexturePointer FramebufferCache::getDeferredColorTexture() { + if (!_deferredColorTexture) { + createPrimaryFramebuffer(); + } + return _deferredColorTexture; +} + +gpu::TexturePointer FramebufferCache::getDeferredNormalTexture() { + if (!_deferredNormalTexture) { + createPrimaryFramebuffer(); + } + return _deferredNormalTexture; +} + +gpu::TexturePointer FramebufferCache::getDeferredSpecularTexture() { + if (!_deferredSpecularTexture) { + createPrimaryFramebuffer(); + } + return _deferredSpecularTexture; +} + +gpu::FramebufferPointer FramebufferCache::getLightingFramebuffer() { + if (!_lightingFramebuffer) { + createPrimaryFramebuffer(); + } + return _lightingFramebuffer; +} + +gpu::TexturePointer FramebufferCache::getLightingTexture() { + if (!_lightingTexture) { + createPrimaryFramebuffer(); + } + return _lightingTexture; } gpu::FramebufferPointer FramebufferCache::getFramebuffer() { diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h index e9a1bbf8e8..7ac5e4af63 100644 --- a/libraries/render-utils/src/FramebufferCache.h +++ b/libraries/render-utils/src/FramebufferCache.h @@ -30,12 +30,20 @@ public: /// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is /// used for scene rendering. gpu::FramebufferPointer getPrimaryFramebuffer(); - gpu::FramebufferPointer getPrimaryFramebufferDepthColor(); gpu::TexturePointer getPrimaryDepthTexture(); gpu::TexturePointer getPrimaryColorTexture(); - gpu::TexturePointer getPrimaryNormalTexture(); - gpu::TexturePointer getPrimarySpecularTexture(); + + gpu::FramebufferPointer getDeferredFramebuffer(); + gpu::FramebufferPointer getDeferredFramebufferDepthColor(); + + gpu::TexturePointer getDeferredColorTexture(); + gpu::TexturePointer getDeferredNormalTexture(); + gpu::TexturePointer getDeferredSpecularTexture(); + + + gpu::TexturePointer getLightingTexture(); + gpu::FramebufferPointer getLightingFramebuffer(); /// Returns the framebuffer object used to render shadow maps; gpu::FramebufferPointer getShadowFramebuffer(); @@ -56,14 +64,21 @@ private: void createPrimaryFramebuffer(); - gpu::FramebufferPointer _primaryFramebufferFull; - gpu::FramebufferPointer _primaryFramebufferDepthColor; + gpu::FramebufferPointer _primaryFramebuffer; gpu::TexturePointer _primaryDepthTexture; gpu::TexturePointer _primaryColorTexture; - gpu::TexturePointer _primaryNormalTexture; - gpu::TexturePointer _primarySpecularTexture; - + + gpu::FramebufferPointer _deferredFramebuffer; + gpu::FramebufferPointer _deferredFramebufferDepthColor; + + gpu::TexturePointer _deferredColorTexture; + gpu::TexturePointer _deferredNormalTexture; + gpu::TexturePointer _deferredSpecularTexture; + + gpu::TexturePointer _lightingTexture; + gpu::FramebufferPointer _lightingFramebuffer; + gpu::FramebufferPointer _shadowFramebuffer; gpu::FramebufferPointer _selfieFramebuffer; diff --git a/libraries/render-utils/src/HitEffect.cpp b/libraries/render-utils/src/HitEffect.cpp index 7e8b5f5fa0..bf65eaf059 100644 --- a/libraries/render-utils/src/HitEffect.cpp +++ b/libraries/render-utils/src/HitEffect.cpp @@ -14,6 +14,7 @@ #include +#include #include #include @@ -60,9 +61,9 @@ const gpu::PipelinePointer& HitEffect::getHitEffectPipeline() { } void HitEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->args; + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { glm::mat4 projMat; diff --git a/libraries/render-utils/src/HitEffect.h b/libraries/render-utils/src/HitEffect.h index b560cf5550..0a96a5300d 100644 --- a/libraries/render-utils/src/HitEffect.h +++ b/libraries/render-utils/src/HitEffect.h @@ -9,11 +9,7 @@ #ifndef hifi_hitEffect_h #define hifi_hitEffect_h -#include -#include "render/DrawTask.h" - -class AbstractViewStateInterface; -class ProgramObject; +#include class HitEffect { public: @@ -23,7 +19,7 @@ public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); typedef render::Job::Model JobModel; - const gpu::PipelinePointer& getHitEffectPipeline(); + const gpu::PipelinePointer& getHitEffectPipeline(); private: gpu::PipelinePointer _hitEffectPipeline; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 2d8d7b598b..414ad9cf97 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -199,8 +199,6 @@ void MeshPartPayload::render(RenderArgs* args) const { gpu::Batch& batch = *(args->_batch); auto mode = args->_renderMode; - auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME - model::MaterialKey drawMaterialKey; if (_drawMaterial) { drawMaterialKey = _drawMaterial->getKey(); @@ -217,7 +215,7 @@ void MeshPartPayload::render(RenderArgs* args) const { } ModelRender::Locations* locations = nullptr; - ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, args, locations); @@ -395,9 +393,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { gpu::Batch& batch = *(args->_batch); auto mode = args->_renderMode; - - auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME - + const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); const std::vector>& networkMeshes = _model->_geometry->getMeshes(); @@ -467,9 +463,12 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } ModelRender::Locations* locations = nullptr; - ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, args, locations); + if (!locations) { // the pipeline could not be found + return; + } // Bind the model transform and the skinCLusterMatrices if needed bindTransform(batch, locations); diff --git a/libraries/render-utils/src/ModelRender.cpp b/libraries/render-utils/src/ModelRender.cpp index 10c9d738d2..312d34e41b 100644 --- a/libraries/render-utils/src/ModelRender.cpp +++ b/libraries/render-utils/src/ModelRender.cpp @@ -228,10 +228,8 @@ void ModelRender::RenderPipelineLib::addRenderPipeline(ModelRender::RenderKey ke void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, ModelRender::Locations& locations) { - locations.alphaThreshold = program->getUniforms().findLocation("alphaThreshold"); locations.texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices"); locations.emissiveParams = program->getUniforms().findLocation("emissiveParams"); - locations.glowIntensity = program->getUniforms().findLocation("glowIntensity"); locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap"); locations.diffuseTextureUnit = program->getTextures().findLocation("diffuseMap"); locations.normalTextureUnit = program->getTextures().findLocation("normalMap"); @@ -244,14 +242,14 @@ void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, } -void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, +void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations) { PerformanceTimer perfTimer("Model::pickPrograms"); getRenderPipelineLib(); - RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); + RenderKey key(mode, translucent, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); auto pipeline = _renderPipelineLib.find(key.getRaw()); if (pipeline == _renderPipelineLib.end()) { qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw(); @@ -266,15 +264,6 @@ void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, b // Setup the One pipeline batch.setPipeline((*pipeline).second._pipeline); - if ((locations->alphaThreshold > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { - batch._glUniform1f(locations->alphaThreshold, alphaThreshold); - } - - if ((locations->glowIntensity > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { - const float DEFAULT_GLOW_INTENSITY = 1.0f; // FIXME - glow is removed - batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY); - } - if ((locations->normalFittingMapUnit > -1)) { batch.setResourceTexture(locations->normalFittingMapUnit, DependencyManager::get()->getNormalFittingTexture()); diff --git a/libraries/render-utils/src/ModelRender.h b/libraries/render-utils/src/ModelRender.h index 39fe05378d..8331440fb0 100644 --- a/libraries/render-utils/src/ModelRender.h +++ b/libraries/render-utils/src/ModelRender.h @@ -29,21 +29,19 @@ public: class Locations { public: - int alphaThreshold; int texcoordMatrices; int diffuseTextureUnit; int normalTextureUnit; int specularTextureUnit; int emissiveTextureUnit; int emissiveParams; - int glowIntensity; int normalFittingMapUnit; int skinClusterBufferUnit; int materialBufferUnit; int lightBufferUnit; }; - static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, + static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations); @@ -111,9 +109,9 @@ public: ) {} RenderKey(RenderArgs::RenderMode mode, - bool translucent, float alphaThreshold, bool hasLightmap, + bool translucent, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) : - RenderKey(((translucent && (alphaThreshold == 0.0f) && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0) + RenderKey(((translucent && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0) | (hasLightmap && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_LIGHTMAP : 0) // Lightmap, tangents and specular don't matter for depthOnly | (hasTangents && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_TANGENTS : 0) | (hasSpecular && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_SPECULAR : 0) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 275e8af29c..1b1d08f353 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -18,10 +18,11 @@ #include #include -#include "FramebufferCache.h" +#include "DebugDeferredBuffer.h" #include "DeferredLightingEffect.h" -#include "TextureCache.h" +#include "FramebufferCache.h" #include "HitEffect.h" +#include "TextureCache.h" #include "render/DrawStatus.h" #include "AmbientOcclusionEffect.h" @@ -34,92 +35,91 @@ using namespace render; -void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - RenderArgs* args = renderContext->args; - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { - - auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); - - batch.enableStereo(false); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - batch.setFramebuffer(primaryFbo); - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | - gpu::Framebuffer::BUFFER_DEPTH | - gpu::Framebuffer::BUFFER_STENCIL, - vec4(vec3(0), 1), 1.0, 0.0, true); - }); -} void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - DependencyManager::get()->prepare(renderContext->args); + DependencyManager::get()->prepare(renderContext->getArgs()); } void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - DependencyManager::get()->render(renderContext->args); + DependencyManager::get()->render(renderContext->getArgs()); } -void ResolveDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - PerformanceTimer perfTimer("ResolveDeferred"); - DependencyManager::get()->copyBack(renderContext->args); +void ToneMappingDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("ToneMappingDeferred"); + _toneMappingEffect.render(renderContext->getArgs()); } RenderDeferredTask::RenderDeferredTask() : Task() { - _jobs.push_back(Job(new SetupDeferred::JobModel("SetupFramebuffer"))); - - _jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred"))); + // CPU only, create the list of renderedOpaques items _jobs.push_back(Job(new FetchItems::JobModel("FetchOpaque", - FetchItems( - [] (const RenderContextPointer& context, int count) { - context->_numFeedOpaqueItems = count; - } - ) + FetchItems([](const RenderContextPointer& context, int count) { + context->getItemsConfig().opaque.numFeed = count; + }) ))); _jobs.push_back(Job(new CullItemsOpaque::JobModel("CullOpaque", _jobs.back().getOutput()))); _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortOpaque", _jobs.back().getOutput()))); auto& renderedOpaques = _jobs.back().getOutput(); - _jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput()))); + // CPU only, create the list of renderedTransparents items + _jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent", + FetchItems(ItemFilter::Builder::transparentShape().withoutLayered(), + [](const RenderContextPointer& context, int count) { + context->getItemsConfig().transparent.numFeed = count; + }) + ))); + _jobs.push_back(Job(new CullItemsTransparent::JobModel("CullTransparent", _jobs.back().getOutput()))); + _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); + auto& renderedTransparents = _jobs.back().getOutput(); + + // GPU Jobs: Start preparing the deferred and lighting buffer + _jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred"))); + + // Render opaque objects in DeferredBuffer + _jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", renderedOpaques))); + + // Once opaque is all rendered create stencil background _jobs.push_back(Job(new DrawStencilDeferred::JobModel("DrawOpaqueStencil"))); + + // Use Stencil and start drawing background in Lighting buffer _jobs.push_back(Job(new DrawBackgroundDeferred::JobModel("DrawBackgroundDeferred"))); + // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. _jobs.push_back(Job(new DrawLight::JobModel("DrawLight"))); - _jobs.push_back(Job(new RenderDeferred::JobModel("RenderDeferred"))); - _jobs.push_back(Job(new ResolveDeferred::JobModel("ResolveDeferred"))); - _jobs.push_back(Job(new AmbientOcclusion::JobModel("AmbientOcclusion"))); + // DeferredBuffer is complete, now let's shade it into the LightingBuffer + _jobs.push_back(Job(new RenderDeferred::JobModel("RenderDeferred"))); + + // AO job, to be revisited + _jobs.push_back(Job(new AmbientOcclusion::JobModel("AmbientOcclusion"))); _jobs.back().setEnabled(false); _occlusionJobIndex = (int)_jobs.size() - 1; + // AA job to be revisited _jobs.push_back(Job(new Antialiasing::JobModel("Antialiasing"))); - _jobs.back().setEnabled(false); _antialiasingJobIndex = (int)_jobs.size() - 1; - _jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent", - FetchItems( - ItemFilter::Builder::transparentShape().withoutLayered(), - [] (const RenderContextPointer& context, int count) { - context->_numFeedTransparentItems = count; - } - ) - ))); - _jobs.push_back(Job(new CullItemsTransparent::JobModel("CullTransparent", _jobs.back().getOutput()))); - - - _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); - _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", _jobs.back().getOutput()))); + // Render transparent objects forward in LigthingBuffer + _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", renderedTransparents))); - // Grab a texture map representing the different status icons and assign that to the drawStatsuJob - auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; - - auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); - _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap)))); + // Lighting Buffer ready for tone mapping + _jobs.push_back(Job(new ToneMappingDeferred::JobModel("ToneMapping"))); + _toneMappingJobIndex = (int)_jobs.size() - 1; + // Debugging Deferred buffer job + _jobs.push_back(Job(new DebugDeferredBuffer::JobModel("DebugDeferredBuffer"))); _jobs.back().setEnabled(false); - _drawStatusJobIndex = (int)_jobs.size() - 1; + _drawDebugDeferredBufferIndex = (int)_jobs.size() - 1; + + // Status icon rendering job + { + // Grab a texture map representing the different status icons and assign that to the drawStatsuJob + auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; + auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); + _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap)))); + _jobs.back().setEnabled(false); + _drawStatusJobIndex = (int)_jobs.size() - 1; + } _jobs.push_back(Job(new DrawOverlay3D::JobModel("DrawOverlay3D"))); @@ -127,12 +127,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.back().setEnabled(false); _drawHitEffectJobIndex = (int)_jobs.size() -1; - - // Give ourselves 3 frmaes of timer queries - _timerQueries.push_back(std::make_shared()); - _timerQueries.push_back(std::make_shared()); - _timerQueries.push_back(std::make_shared()); - _currentTimerQueryIndex = 0; + _jobs.push_back(Job(new Blit::JobModel("Blit"))); } RenderDeferredTask::~RenderDeferredTask() { @@ -147,23 +142,29 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend // Is it possible that we render without a viewFrustum ? - if (!(renderContext->args && renderContext->args->_viewFrustum)) { + if (!(renderContext->getArgs() && renderContext->getArgs()->_viewFrustum)) { return; } - // Make sure we turn the displayItemStatus on/off - setDrawItemStatus(renderContext->_drawItemStatus); + // Make sure we turn the deferred buffer debug on/off + setDrawDebugDeferredBuffer(renderContext->_deferredDebugMode); - //Make sure we display hit effect on screen, as desired from a script - setDrawHitEffect(renderContext->_drawHitEffect); + // Make sure we turn the displayItemStatus on/off + setDrawItemStatus(renderContext->getDrawStatus()); + + // Make sure we display hit effect on screen, as desired from a script + setDrawHitEffect(renderContext->getDrawHitEffect()); // TODO: turn on/off AO through menu item - setOcclusionStatus(renderContext->_occlusionStatus); + setOcclusionStatus(renderContext->getOcclusionStatus()); - setAntialiasingStatus(renderContext->_fxaaStatus); + setAntialiasingStatus(renderContext->getFxaaStatus()); - renderContext->args->_context->syncCache(); + setToneMappingExposure(renderContext->getTone().exposure); + setToneMappingToneCurve(renderContext->getTone().toneCurve); + + renderContext->getArgs()->_context->syncCache(); for (auto job : _jobs) { job.run(sceneContext, renderContext); @@ -172,16 +173,17 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend }; void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); args->_batch = &batch; - renderContext->_numDrawnOpaqueItems = (int)inItems.size(); + auto& opaque = renderContext->getItemsConfig().opaque; + opaque.numDrawn = (int)inItems.size(); glm::mat4 projMat; Transform viewMat; @@ -191,26 +193,23 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - { - const float OPAQUE_ALPHA_THRESHOLD = 0.5f; - args->_alphaThreshold = OPAQUE_ALPHA_THRESHOLD; - } - renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOpaqueItems); + renderItems(sceneContext, renderContext, inItems, opaque.maxDrawn); args->_batch = nullptr; }); } void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); args->_batch = &batch; - renderContext->_numDrawnTransparentItems = (int)inItems.size(); + auto& transparent = renderContext->getItemsConfig().transparent; + transparent.numDrawn = (int)inItems.size(); glm::mat4 projMat; Transform viewMat; @@ -219,11 +218,8 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - - const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; - args->_alphaThreshold = TRANSPARENT_ALPHA_THRESHOLD; - - renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnTransparentItems); + + renderItems(sceneContext, renderContext, inItems, transparent.maxDrawn); args->_batch = nullptr; }); } @@ -246,8 +242,8 @@ const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() { } void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // render backgrounds auto& scene = sceneContext->_scene; @@ -262,11 +258,12 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon inItems.emplace_back(id); } } - renderContext->_numFeedOverlay3DItems = (int)inItems.size(); - renderContext->_numDrawnOverlay3DItems = (int)inItems.size(); + auto& overlay3D = renderContext->getItemsConfig().overlay3D; + overlay3D.numFeed = (int)inItems.size(); + overlay3D.numDrawn = (int)inItems.size(); if (!inItems.empty()) { - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); // Clear the framebuffer without stereo // Needs to be distinct from the other batch because using the clear call @@ -295,7 +292,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon batch.setPipeline(getOpaquePipeline()); batch.setResourceTexture(0, args->_whiteTexture); - renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems); + renderItems(sceneContext, renderContext, inItems, renderContext->getItemsConfig().overlay3D.maxDrawn); }); args->_batch = nullptr; args->_whiteTexture.reset(); @@ -324,19 +321,19 @@ const gpu::PipelinePointer& DrawStencilDeferred::getOpaquePipeline() { } void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // from the touched pixel generate the stencil buffer - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; - auto primaryFboColorDepthStencil = DependencyManager::get()->getPrimaryFramebufferDepthColor(); + auto deferredFboColorDepthStencil = DependencyManager::get()->getDeferredFramebufferDepthColor(); batch.enableStereo(false); - batch.setFramebuffer(primaryFboColorDepthStencil); + batch.setFramebuffer(deferredFboColorDepthStencil); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); @@ -350,8 +347,8 @@ void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const Ren } void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // render backgrounds auto& scene = sceneContext->_scene; @@ -363,16 +360,15 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const for (auto id : items) { inItems.emplace_back(id); } - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; - auto primaryFboColorDepthStencil = DependencyManager::get()->getPrimaryFramebufferDepthColor(); - auto primaryFboFull = DependencyManager::get()->getPrimaryFramebuffer(); + auto lightingFBO = DependencyManager::get()->getLightingFramebuffer(); batch.enableSkybox(true); - batch.setFramebuffer(primaryFboColorDepthStencil); + batch.setFramebuffer(lightingFBO); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); @@ -387,8 +383,106 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const renderItems(sceneContext, renderContext, inItems); - batch.setFramebuffer(primaryFboFull); - }); args->_batch = nullptr; } + +void Blit::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_context); + + RenderArgs* renderArgs = renderContext->getArgs(); + auto blitFbo = renderArgs->_blitFramebuffer; + + if (!blitFbo) { + return; + } + + // Determine size from viewport + int width = renderArgs->_viewport.z; + int height = renderArgs->_viewport.w; + + // Blit primary to blit FBO + auto framebufferCache = DependencyManager::get(); + auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); + + gpu::doInBatch(renderArgs->_context, [=](gpu::Batch& batch) { + batch.setFramebuffer(blitFbo); + + if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + if (renderArgs->_context->isStereo()) { + gpu::Vec4i srcRectLeft; + srcRectLeft.z = width / 2; + srcRectLeft.w = height; + + gpu::Vec4i srcRectRight; + srcRectRight.x = width / 2; + srcRectRight.z = width; + srcRectRight.w = height; + + gpu::Vec4i destRectLeft; + destRectLeft.x = srcRectLeft.z; + destRectLeft.z = srcRectLeft.x; + destRectLeft.y = srcRectLeft.y; + destRectLeft.w = srcRectLeft.w; + + gpu::Vec4i destRectRight; + destRectRight.x = srcRectRight.z; + destRectRight.z = srcRectRight.x; + destRectRight.y = srcRectRight.y; + destRectRight.w = srcRectRight.w; + + // Blit left to right and right to left in stereo + batch.blit(primaryFbo, srcRectRight, blitFbo, destRectLeft); + batch.blit(primaryFbo, srcRectLeft, blitFbo, destRectRight); + } else { + gpu::Vec4i srcRect; + srcRect.z = width; + srcRect.w = height; + + gpu::Vec4i destRect; + destRect.x = width; + destRect.y = 0; + destRect.z = 0; + destRect.w = height; + + batch.blit(primaryFbo, srcRect, blitFbo, destRect); + } + } else { + gpu::Vec4i rect; + rect.z = width; + rect.w = height; + + batch.blit(primaryFbo, rect, blitFbo, rect); + } + }); +} + +void RenderDeferredTask::setToneMappingExposure(float exposure) { + if (_toneMappingJobIndex >= 0) { + _jobs[_toneMappingJobIndex].edit()._toneMappingEffect.setExposure(exposure); + } +} + +float RenderDeferredTask::getToneMappingExposure() const { + if (_toneMappingJobIndex >= 0) { + return _jobs[_toneMappingJobIndex].get()._toneMappingEffect.getExposure(); + } else { + return 0.0f; + } +} + +void RenderDeferredTask::setToneMappingToneCurve(int toneCurve) { + if (_toneMappingJobIndex >= 0) { + _jobs[_toneMappingJobIndex].edit()._toneMappingEffect.setToneCurve((ToneMappingEffect::ToneCurve)toneCurve); + } +} + +int RenderDeferredTask::getToneMappingToneCurve() const { + if (_toneMappingJobIndex >= 0) { + return _jobs[_toneMappingJobIndex].get()._toneMappingEffect.getToneCurve(); + } else { + return 0.0f; + } +} + diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 6daa90b1ed..051faa3238 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -16,6 +16,8 @@ #include "gpu/Pipeline.h" +#include "ToneMappingEffect.h" + class SetupDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); @@ -38,11 +40,13 @@ public: typedef render::Job::Model JobModel; }; -class ResolveDeferred { +class ToneMappingDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + ToneMappingEffect _toneMappingEffect; + + typedef render::Job::Model JobModel; }; class DrawOpaqueDeferred { @@ -80,10 +84,17 @@ class DrawOverlay3D { static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable public: static const gpu::PipelinePointer& getOpaquePipeline(); + + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + + typedef render::Job::Model JobModel; +}; +class Blit { +public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + typedef render::Job::Model JobModel; }; class RenderDeferredTask : public render::Task { @@ -93,16 +104,23 @@ public: ~RenderDeferredTask(); render::Jobs _jobs; - + + int _drawDebugDeferredBufferIndex = -1; int _drawStatusJobIndex = -1; int _drawHitEffectJobIndex = -1; - + + void setDrawDebugDeferredBuffer(int draw) { + if (_drawDebugDeferredBufferIndex >= 0) { + _jobs[_drawDebugDeferredBufferIndex].setEnabled(draw >= 0); + } + } + bool doDrawDebugDeferredBuffer() const { if (_drawDebugDeferredBufferIndex >= 0) { return _jobs[_drawDebugDeferredBufferIndex].isEnabled(); } else { return false; } } + void setDrawItemStatus(int draw) { if (_drawStatusJobIndex >= 0) { _jobs[_drawStatusJobIndex].setEnabled(draw > 0); } } - bool doDrawItemStatus() const { if (_drawStatusJobIndex >= 0) { return _jobs[_drawStatusJobIndex].isEnabled(); } else { return false; } } void setDrawHitEffect(bool draw) { if (_drawHitEffectJobIndex >= 0) { _jobs[_drawHitEffectJobIndex].setEnabled(draw); } } @@ -118,6 +136,14 @@ public: void setAntialiasingStatus(bool draw) { if (_antialiasingJobIndex >= 0) { _jobs[_antialiasingJobIndex].setEnabled(draw); } } bool doAntialiasingStatus() const { if (_antialiasingJobIndex >= 0) { return _jobs[_antialiasingJobIndex].isEnabled(); } else { return false; } } + int _toneMappingJobIndex = -1; + + void setToneMappingExposure(float exposure); + float getToneMappingExposure() const; + + void setToneMappingToneCurve(int toneCurve); + int getToneMappingToneCurve() const; + virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); diff --git a/libraries/render-utils/src/RenderScriptingInterface.cpp b/libraries/render-utils/src/RenderScriptingInterface.cpp new file mode 100644 index 0000000000..a99dc814d5 --- /dev/null +++ b/libraries/render-utils/src/RenderScriptingInterface.cpp @@ -0,0 +1,52 @@ +// +// RenderScriptingInterface.cpp +// libraries/render-utils +// +// Created by Zach Pomerantz on 12/16/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 "RenderScriptingInterface.h" + +RenderScriptingInterface::RenderScriptingInterface() {}; + +void RenderScripting::Tone::setCurve(const QString& curve) { + if (curve == QString("None")) { + toneCurve = 0; + } else if (curve == QString("Gamma22")) { + toneCurve = 1; + } else if (curve == QString("Reinhard")) { + toneCurve = 2; + } else if (curve == QString("Filmic")) { + toneCurve = 3; + } +} + +QString RenderScripting::Tone::getCurve() const { + switch (toneCurve) { + case 0: + return QString("None"); + case 1: + return QString("Gamma22"); + case 2: + return QString("Reinhard"); + case 3: + return QString("Filmic"); + default: + return QString("Filmic"); + }; +} + +render::RenderContext RenderScriptingInterface::getRenderContext() { + render::RenderContext::ItemsConfig items{ *_opaque, *_transparent, *_overlay3D }; + return render::RenderContext{ items, *_tone, _drawStatus, _drawHitEffect, _deferredDebugSize, _deferredDebugMode }; +} + +void RenderScriptingInterface::setItemCounts(const render::RenderContext::ItemsConfig& items) { + _opaque->setCounts(items.opaque); + _transparent->setCounts(items.transparent); + _overlay3D->setCounts(items.overlay3D); +} \ No newline at end of file diff --git a/libraries/render-utils/src/RenderScriptingInterface.h b/libraries/render-utils/src/RenderScriptingInterface.h new file mode 100644 index 0000000000..08226ef6df --- /dev/null +++ b/libraries/render-utils/src/RenderScriptingInterface.h @@ -0,0 +1,115 @@ +// +// RenderScriptingInterface.h +// libraries/render-utils +// +// Created by Zach Pomerantz on 12/16/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderScriptingInterface_h +#define hifi_RenderScriptingInterface_h + +#include // QObject +#include // Dependency + +#include "render/Engine.h" + +namespace RenderScripting { + using State = render::RenderContext::ItemsConfig::State; + using Counter = render::RenderContext::ItemsConfig::Counter; + + class ItemCounter : public QObject, public Counter { + Q_OBJECT + + public: + Q_PROPERTY(int numFeed READ getNumFeed) + Q_PROPERTY(int numDrawn READ getNumDrawn) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn) + + protected: + int getNumFeed() const { return numFeed; } + int getNumDrawn() const { return numDrawn; } + }; + using ItemCounterPointer = std::unique_ptr; + + class ItemState : public QObject, public State { + Q_OBJECT + + public: + Q_PROPERTY(bool render MEMBER render) + Q_PROPERTY(bool cull MEMBER cull) + Q_PROPERTY(bool sort MEMBER sort) + + Q_PROPERTY(int numFeed READ getNumFeed) + Q_PROPERTY(int numDrawn READ getNumDrawn) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn) + + protected: + int getNumFeed() const { return numFeed; } + int getNumDrawn() const { return numDrawn; } + }; + using ItemStatePointer = std::unique_ptr; + + class Tone : public QObject, public render::RenderContext::Tone { + Q_OBJECT + + public: + Q_PROPERTY(float exposure MEMBER exposure) + Q_PROPERTY(QString curve READ getCurve WRITE setCurve) + + QString getCurve() const; + int getCurveValue() const { return toneCurve; } + void setCurve(const QString& curve); + }; + using TonePointer = std::unique_ptr; +}; + +class RenderScriptingInterface : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + + public: + Q_PROPERTY(RenderScripting::ItemState* opaque READ getOpaque) + Q_PROPERTY(RenderScripting::ItemState* transparent READ getTransparent) + Q_PROPERTY(RenderScripting::ItemCounter* overlay3D READ getOverlay3D) + + Q_PROPERTY(RenderScripting::Tone* tone READ getTone) + + Q_PROPERTY(int displayItemStatus MEMBER _drawStatus) + Q_PROPERTY(bool displayHitEffect MEMBER _drawHitEffect) + + Q_PROPERTY(int deferredDebugMode MEMBER _deferredDebugMode) + Q_PROPERTY(glm::vec4 deferredDebugSize MEMBER _deferredDebugSize) + + render::RenderContext getRenderContext(); + void setItemCounts(const render::RenderContext::ItemsConfig& items); + +protected: + RenderScriptingInterface(); + ~RenderScriptingInterface() {}; + + RenderScripting::ItemState* getOpaque() const { return _opaque.get(); } + RenderScripting::ItemState* getTransparent() const { return _transparent.get(); } + RenderScripting::ItemCounter* getOverlay3D() const { return _overlay3D.get(); } + + RenderScripting::Tone* getTone() const { return _tone.get(); } + + RenderScripting::ItemStatePointer _opaque = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}}; + RenderScripting::ItemStatePointer _transparent = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}}; + RenderScripting::ItemCounterPointer _overlay3D = RenderScripting::ItemCounterPointer{new RenderScripting::ItemCounter{}}; + + RenderScripting::TonePointer _tone = RenderScripting::TonePointer{ new RenderScripting::Tone{} }; + + // Options + int _drawStatus = 0; + bool _drawHitEffect = false; + + // Debugging + int _deferredDebugMode = -1; + glm::vec4 _deferredDebugSize { 0.0f, -1.0f, 1.0f, 1.0f }; +}; + +#endif // hifi_RenderScriptingInterface_h diff --git a/libraries/render-utils/src/SkyFromAtmosphere.slf b/libraries/render-utils/src/SkyFromAtmosphere.slf index c2a3635f07..10b39dc210 100755 --- a/libraries/render-utils/src/SkyFromAtmosphere.slf +++ b/libraries/render-utils/src/SkyFromAtmosphere.slf @@ -108,5 +108,6 @@ void main (void) vec3 finalColor = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; outFragColor.a = finalColor.b; - outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + // outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + outFragColor.rgb = finalColor.rgb; } diff --git a/libraries/render-utils/src/SkyFromSpace.slf b/libraries/render-utils/src/SkyFromSpace.slf index e3569a2914..42282fe08b 100755 --- a/libraries/render-utils/src/SkyFromSpace.slf +++ b/libraries/render-utils/src/SkyFromSpace.slf @@ -114,5 +114,6 @@ void main (void) vec3 finalColor = color + fMiePhase * secondaryColor; outFragColor.a = finalColor.b; - outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + // outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + outFragColor.rgb = finalColor.rgb; } diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp new file mode 100644 index 0000000000..2583719424 --- /dev/null +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -0,0 +1,145 @@ +// +// ToneMappingEffect.cpp +// libraries/render-utils/src +// +// Created by Sam Gateau on 12/7/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 "ToneMappingEffect.h" + +#include +#include + +#include + +#include "FramebufferCache.h" + + +ToneMappingEffect::ToneMappingEffect() { + Parameters parameters; + _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); +} + +void ToneMappingEffect::init() { + const char BlitTextureGamma_frag[] = R"SCRIBE(#version 410 core + // Generated on Sat Oct 24 09:34:37 2015 + // + // Draw texture 0 fetched at texcoord.xy + // + // Created by Sam Gateau on 6/22/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 + // + + struct ToneMappingParams { + vec4 _exp_2powExp_s0_s1; + ivec4 _toneCurve_s0_s1_s2; + }; + + const float INV_GAMMA_22 = 1.0 / 2.2; + const int ToneCurveNone = 0; + const int ToneCurveGamma22 = 1; + const int ToneCurveReinhard = 2; + const int ToneCurveFilmic = 3; + + uniform toneMappingParamsBuffer { + ToneMappingParams params; + }; + float getTwoPowExposure() { + return params._exp_2powExp_s0_s1.y; + } + int getToneCurve() { + return params._toneCurve_s0_s1_s2.x; + } + + uniform sampler2D colorMap; + + in vec2 varTexCoord0; + out vec4 outFragColor; + + void main(void) { + vec4 fragColorRaw = texture(colorMap, varTexCoord0); + vec3 fragColor = fragColorRaw.xyz; + + vec3 srcColor = fragColor * getTwoPowExposure(); + + int toneCurve = getToneCurve(); + vec3 tonedColor = srcColor; + if (toneCurve == ToneCurveFilmic) { + vec3 x = max(vec3(0.0), srcColor-0.004); + tonedColor = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06); + } else if (toneCurve == ToneCurveReinhard) { + tonedColor = srcColor/(1.0 + srcColor); + tonedColor = pow(tonedColor, vec3(INV_GAMMA_22)); + } else if (toneCurve == ToneCurveGamma22) { + tonedColor = pow(srcColor, vec3(INV_GAMMA_22)); + } // else None toned = src + + outFragColor = vec4(tonedColor, 1.0); + } + + )SCRIBE"; + auto blitPS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(BlitTextureGamma_frag))); + + auto blitVS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + auto blitProgram = gpu::ShaderPointer(gpu::Shader::createProgram(blitVS, blitPS)); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("toneMappingParamsBuffer"), 3)); + gpu::Shader::makeProgram(*blitProgram, slotBindings); + auto blitState = std::make_shared(); + blitState->setColorWriteMask(true, true, true, true); + _blitLightBuffer = gpu::PipelinePointer(gpu::Pipeline::create(blitProgram, blitState)); +} + +void ToneMappingEffect::setExposure(float exposure) { + _parametersBuffer.edit()._exposure = exposure; + _parametersBuffer.edit()._twoPowExposure = pow(2.0, exposure); +} + +void ToneMappingEffect::setToneCurve(ToneCurve curve) { + _parametersBuffer.edit()._toneCurve = curve; +} + +void ToneMappingEffect::render(RenderArgs* args) { + if (!_blitLightBuffer) { + init(); + } + auto framebufferCache = DependencyManager::get(); + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + batch.enableStereo(false); + QSize framebufferSize = framebufferCache->getFrameBufferSize(); + + auto lightingBuffer = framebufferCache->getLightingTexture(); + auto destFbo = framebufferCache->getPrimaryFramebuffer(); + batch.setFramebuffer(destFbo); + + // FIXME: Generate the Luminosity map + //batch.generateTextureMips(lightingBuffer); + + batch.setViewportTransform(args->_viewport); + batch.setProjectionTransform(glm::mat4()); + batch.setViewTransform(Transform()); + { + float sMin = args->_viewport.x / (float)framebufferSize.width(); + float sWidth = args->_viewport.z / (float)framebufferSize.width(); + float tMin = args->_viewport.y / (float)framebufferSize.height(); + float tHeight = args->_viewport.w / (float)framebufferSize.height(); + Transform model; + batch.setPipeline(_blitLightBuffer); + model.setTranslation(glm::vec3(sMin, tMin, 0.0)); + model.setScale(glm::vec3(sWidth, tHeight, 1.0)); + batch.setModelTransform(model); + } + + batch.setUniformBuffer(3, _parametersBuffer); + batch.setResourceTexture(0, lightingBuffer); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); +} \ No newline at end of file diff --git a/libraries/render-utils/src/ToneMappingEffect.h b/libraries/render-utils/src/ToneMappingEffect.h new file mode 100644 index 0000000000..20ee9024cf --- /dev/null +++ b/libraries/render-utils/src/ToneMappingEffect.h @@ -0,0 +1,64 @@ +// +// ToneMappingEffect.h +// libraries/render-utils/src +// +// Created by Sam Gateau on 12/7/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 +// + +#ifndef hifi_ToneMappingEffect_h +#define hifi_ToneMappingEffect_h + +#include +#include + +#include +#include + +class RenderArgs; + +class ToneMappingEffect { +public: + ToneMappingEffect(); + virtual ~ToneMappingEffect() {} + + void render(RenderArgs* args); + + void setExposure(float exposure); + float getExposure() const { return _parametersBuffer.get()._exposure; } + + // Different tone curve available + enum ToneCurve { + None = 0, + Gamma22, + Reinhard, + Filmic, + }; + void setToneCurve(ToneCurve curve); + ToneCurve getToneCurve() const { return (ToneCurve)_parametersBuffer.get()._toneCurve; } + +private: + + gpu::PipelinePointer _blitLightBuffer; + + // Class describing the uniform buffer with all the parameters common to the tone mapping shaders + class Parameters { + public: + float _exposure = 0.0f; + float _twoPowExposure = 1.0f; + glm::vec2 spareA; + int _toneCurve = Filmic; + glm::vec3 spareB; + + Parameters() {} + }; + typedef gpu::BufferView UniformBufferView; + gpu::BufferView _parametersBuffer; + + void init(); +}; + +#endif // hifi_ToneMappingEffect_h diff --git a/libraries/render-utils/src/animdebugdraw.slv b/libraries/render-utils/src/animdebugdraw.slv index f3117714b0..3cb356c055 100644 --- a/libraries/render-utils/src/animdebugdraw.slv +++ b/libraries/render-utils/src/animdebugdraw.slv @@ -9,16 +9,15 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> out vec4 _color; void main(void) { // pass along the diffuse color - _color = inColor.rgba; + _color = colorToLinearRGBA(inColor.rgba); TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); diff --git a/libraries/render-utils/src/debug_deferred_buffer.slf b/libraries/render-utils/src/debug_deferred_buffer.slf new file mode 100644 index 0000000000..375972cdc3 --- /dev/null +++ b/libraries/render-utils/src/debug_deferred_buffer.slf @@ -0,0 +1,24 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// debug_deferred_buffer.slf +// fragment shader +// +// Created by Clement on 12/3 +// 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 DeferredBuffer.slh@> + +in vec2 uv; +out vec4 outFragColor; + +//SOURCE_PLACEHOLDER + +void main(void) { + outFragColor = getFragmentColor(); +} \ No newline at end of file diff --git a/libraries/render-utils/src/debug_deferred_buffer.slv b/libraries/render-utils/src/debug_deferred_buffer.slv new file mode 100644 index 0000000000..85ff2b651d --- /dev/null +++ b/libraries/render-utils/src/debug_deferred_buffer.slv @@ -0,0 +1,22 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// debug_deferred_buffer.slv +// vertex shader +// +// Created by Clement on 12/3 +// 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@> + +out vec2 uv; + +void main(void) { + uv = (inPosition.xy + 1.0) * 0.5; + gl_Position = inPosition; +} \ No newline at end of file diff --git a/libraries/render-utils/src/directional_ambient_light.slf b/libraries/render-utils/src/directional_ambient_light.slf index 52ecc71a14..ae3b05862e 100755 --- a/libraries/render-utils/src/directional_ambient_light.slf +++ b/libraries/render-utils/src/directional_ambient_light.slf @@ -27,7 +27,6 @@ void main(void) { DeferredTransform deferredTransform = getDeferredTransform(); DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - // Light mapped or not ? if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { vec3 color = evalLightmappedColor( deferredTransform.viewInverse, diff --git a/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf b/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf deleted file mode 100755 index 8b0212636e..0000000000 --- a/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf +++ /dev/null @@ -1,57 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Andrzej Kapolka on 9/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalAmbientSphereGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalCascadedShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalAmbienSphereGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_ambient_light_shadow_map.slf b/libraries/render-utils/src/directional_ambient_light_shadow_map.slf deleted file mode 100755 index 97d69f2e63..0000000000 --- a/libraries/render-utils/src/directional_ambient_light_shadow_map.slf +++ /dev/null @@ -1,56 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Andrzej Kapolka on 9/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> -<$declareEvalLightmappedColor()$> -<$declareEvalAmbientSphereGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalAmbienSphereGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf b/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf deleted file mode 100644 index 4abe8e2e9d..0000000000 --- a/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf +++ /dev/null @@ -1,59 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Andrzej Kapolka on 9/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalAmbientGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalCascadedShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalAmbienGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_light_shadow_map.slf b/libraries/render-utils/src/directional_light_shadow_map.slf deleted file mode 100644 index 4249b2787c..0000000000 --- a/libraries/render-utils/src/directional_light_shadow_map.slf +++ /dev/null @@ -1,58 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Andrzej Kapolka on 9/3/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalAmbientGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalAmbienGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_skybox_light_cascaded_shadow_map.slf b/libraries/render-utils/src/directional_skybox_light_cascaded_shadow_map.slf deleted file mode 100755 index 3c09bf62b6..0000000000 --- a/libraries/render-utils/src/directional_skybox_light_cascaded_shadow_map.slf +++ /dev/null @@ -1,59 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Sam Gateau on 5/8/2015. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalSkyboxGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalCascadedShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalSkyboxGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/directional_skybox_light_shadow_map.slf b/libraries/render-utils/src/directional_skybox_light_shadow_map.slf deleted file mode 100755 index 6f709f31fa..0000000000 --- a/libraries/render-utils/src/directional_skybox_light_shadow_map.slf +++ /dev/null @@ -1,58 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// directional_light.frag -// fragment shader -// -// Created by Sam Gateau on 5/8/2015. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// Everything about deferred buffer -<@include DeferredBuffer.slh@> - -<@include DeferredGlobalLight.slh@> - -<$declareEvalLightmappedColor()$> -<$declareEvalSkyboxGlobalColor()$> - -// Everything about shadow -<@include Shadow.slh@> - -in vec2 _texCoord0; -out vec4 _fragColor; - -void main(void) { - DeferredTransform deferredTransform = getDeferredTransform(); - DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - - // Eval shadow Texcoord and then Attenuation - vec4 shadowTexcoord = evalShadowTexcoord(frag.position); - float shadowAttenuation = evalShadowAttenuation(shadowTexcoord); - - // Light mapped or not ? - if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - vec3 color = evalLightmappedColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.normal, - frag.diffuse, - frag.specularVal.xyz); - _fragColor = vec4(color, 1.0); - } else { - vec3 color = evalSkyboxGlobalColor( - deferredTransform.viewInverse, - shadowAttenuation, - frag.position.xyz, - frag.normal, - frag.diffuse, - frag.specular, - frag.gloss); - - _fragColor = vec4(color, frag.normalVal.a); - } -} diff --git a/libraries/render-utils/src/hit_effect.slf b/libraries/render-utils/src/hit_effect.slf index c059488ba1..cc4484442f 100644 --- a/libraries/render-utils/src/hit_effect.slf +++ b/libraries/render-utils/src/hit_effect.slf @@ -20,8 +20,8 @@ in vec2 varQuadPosition; out vec4 outFragColor; void main(void) { - vec2 center = vec2(0.0, 0.0); - float distFromCenter = distance( vec2(0.0, 0.0), varQuadPosition); - float alpha = mix(0.0, 0.5, pow(distFromCenter,5.)); - outFragColor = vec4(1.0, 0.0, 0.0, alpha); + vec2 center = vec2(0.0, 0.0); + float distFromCenter = distance( vec2(0.0, 0.0), varQuadPosition); + float alpha = mix(0.0, 0.5, pow(distFromCenter,5.)); + outFragColor = vec4(1.0, 0.0, 0.0, alpha); } \ No newline at end of file diff --git a/libraries/render-utils/src/model.slv b/libraries/render-utils/src/model.slv index 6d56833de1..47de20a5d9 100755 --- a/libraries/render-utils/src/model.slv +++ b/libraries/render-utils/src/model.slv @@ -12,9 +12,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; @@ -27,9 +26,8 @@ out vec3 _color; out vec2 _texCoord0; void main(void) { - - // pass along the diffuse color - _color = inColor.xyz; + // pass along the diffuse color in linear space + _color = colorToLinearRGB(inColor.xyz); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/model_lightmap.slv b/libraries/render-utils/src/model_lightmap.slv index 7742896228..eacc91245c 100755 --- a/libraries/render-utils/src/model_lightmap.slv +++ b/libraries/render-utils/src/model_lightmap.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; @@ -29,7 +28,8 @@ out vec3 _normal; out vec3 _color; void main(void) { - _color = inColor.xyz; + // pass along the diffuse color in linear space + _color = colorToLinearRGB(inColor.xyz); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/model_lightmap_normal_map.slv b/libraries/render-utils/src/model_lightmap_normal_map.slv index f63030301f..6046a67e10 100755 --- a/libraries/render-utils/src/model_lightmap_normal_map.slv +++ b/libraries/render-utils/src/model_lightmap_normal_map.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; @@ -30,7 +29,8 @@ out vec3 _tangent; out vec3 _color; void main(void) { - _color = inColor.xyz; + // pass along the diffuse color in linear space + _color = colorToLinearRGB(inColor.xyz); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/model_normal_map.slv b/libraries/render-utils/src/model_normal_map.slv index e989c1c294..5ed4e37278 100755 --- a/libraries/render-utils/src/model_normal_map.slv +++ b/libraries/render-utils/src/model_normal_map.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> const int MAX_TEXCOORDS = 2; @@ -30,7 +29,7 @@ out vec3 _color; void main(void) { // pass along the diffuse color - _color = inColor.rgb; + _color = colorToLinearRGB(inColor.xyz); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.xy, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/overlay3D.slv b/libraries/render-utils/src/overlay3D.slv index a57f02d854..74416f0c1f 100644 --- a/libraries/render-utils/src/overlay3D.slv +++ b/libraries/render-utils/src/overlay3D.slv @@ -11,9 +11,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> out vec2 varTexcoord; @@ -30,7 +29,7 @@ void main(void) { varTexcoord = inTexCoord0.xy; // pass along the color - varColor = inColor; + varColor = colorToLinearRGBA(inColor); // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/point_light.slf b/libraries/render-utils/src/point_light.slf index e9045e18c5..716a39aee9 100644 --- a/libraries/render-utils/src/point_light.slf +++ b/libraries/render-utils/src/point_light.slf @@ -66,7 +66,7 @@ void main(void) { float radialAttenuation = evalLightAttenuation(light, fragLightDistance); // Final Lighting color - vec3 fragColor = shading.w * (frag.diffuse + shading.xyz); + vec3 fragColor = (shading.w * frag.diffuse + shading.xyz); _fragColor = vec4(fragColor * radialAttenuation * getLightColor(light) * getLightIntensity(light), 0.0); if (getLightShowContour(light) > 0.0) { diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 576acf9340..b24e4f92ff 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -40,6 +40,8 @@ void main(void) { #ifdef PROCEDURAL_V1 specular = getProceduralColor().rgb; + // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline + specular = pow(specular, vec3(2.2)); emissiveAmount = 1.0; #else emissiveAmount = getProceduralColors(diffuse, specular, shininess); @@ -49,9 +51,9 @@ void main(void) { if (emissiveAmount > 0.0) { packDeferredFragmentLightmap( - normal, glowIntensity, diffuse, specular, shininess, specular); + normal, 1.0, diffuse, specular, shininess, specular); } else { packDeferredFragment( - normal, glowIntensity, diffuse, specular, shininess); + normal, 1.0, diffuse, specular, shininess); } } diff --git a/libraries/render-utils/src/simple.slv b/libraries/render-utils/src/simple.slv index 823654ec27..59b16cf0e5 100644 --- a/libraries/render-utils/src/simple.slv +++ b/libraries/render-utils/src/simple.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> uniform bool Instanced = false; @@ -28,7 +27,7 @@ out vec2 _texCoord0; out vec4 _position; void main(void) { - _color = inColor.rgb; + _color = colorToLinearRGB(inColor.rgb); _texCoord0 = inTexCoord0.st; _position = inPosition; _modelNormal = inNormal.xyz; diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf index d567b043f4..727c029bbe 100644 --- a/libraries/render-utils/src/simple_textured.slf +++ b/libraries/render-utils/src/simple_textured.slf @@ -29,7 +29,7 @@ void main(void) { packDeferredFragment( normalize(_normal.xyz), - glowIntensity * texel.a, + texel.a, _color.rgb * texel.rgb, DEFAULT_SPECULAR, DEFAULT_SHININESS); } \ No newline at end of file diff --git a/libraries/render-utils/src/simple_textured_emisive.slf b/libraries/render-utils/src/simple_textured_emisive.slf index 96119e98f0..1dd3d667a6 100644 --- a/libraries/render-utils/src/simple_textured_emisive.slf +++ b/libraries/render-utils/src/simple_textured_emisive.slf @@ -27,7 +27,7 @@ void main(void) { packDeferredFragmentLightmap( normalize(_normal), - glowIntensity * texel.a, + texel.a, _color.rgb, DEFAULT_SPECULAR, DEFAULT_SHININESS, texel.rgb); diff --git a/libraries/render-utils/src/skin_model.slv b/libraries/render-utils/src/skin_model.slv index cba419dd4d..8ab475e1fd 100755 --- a/libraries/render-utils/src/skin_model.slv +++ b/libraries/render-utils/src/skin_model.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> <@include Skinning.slh@> @@ -34,7 +33,7 @@ void main(void) { skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); // pass along the diffuse color - _color = inColor.rgb; + _color = colorToLinearRGB(inColor.rgb); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/skin_model_normal_map.slv b/libraries/render-utils/src/skin_model_normal_map.slv index 5dfe5ba957..cec93a024e 100755 --- a/libraries/render-utils/src/skin_model_normal_map.slv +++ b/libraries/render-utils/src/skin_model_normal_map.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> <@include Skinning.slh@> @@ -36,7 +35,7 @@ void main(void) { skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); // pass along the diffuse color - _color = inColor.rgb; + _color = colorToLinearRGB(inColor.rgb); // and the texture coordinates _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; diff --git a/libraries/render-utils/src/spot_light.slf b/libraries/render-utils/src/spot_light.slf index 73b081260e..d7a20fc5e5 100644 --- a/libraries/render-utils/src/spot_light.slf +++ b/libraries/render-utils/src/spot_light.slf @@ -73,7 +73,7 @@ void main(void) { float angularAttenuation = evalLightSpotAttenuation(light, cosSpotAngle); // Final Lighting color - vec3 fragColor = shading.w * (frag.diffuse + shading.xyz); + vec3 fragColor = (shading.w * frag.diffuse + shading.xyz); _fragColor = vec4(fragColor * angularAttenuation * radialAttenuation * getLightColor(light) * getLightIntensity(light), 0.0); if (getLightShowContour(light) > 0.0) { diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv index 340dfd9c8e..f7e72b6997 100644 --- a/libraries/render-utils/src/standardTransformPNTC.slv +++ b/libraries/render-utils/src/standardTransformPNTC.slv @@ -13,9 +13,8 @@ // <@include gpu/Inputs.slh@> - +<@include gpu/Color.slh@> <@include gpu/Transform.slh@> - <$declareStandardTransform()$> out vec3 varPosition; @@ -25,7 +24,7 @@ out vec4 varColor; void main(void) { varTexCoord0 = inTexCoord0.st; - varColor = inColor; + varColor = colorToLinearRGBA(inColor); // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/stars.slv b/libraries/render-utils/src/stars.slv index b9ad736d5e..d00bb8b7dd 100644 --- a/libraries/render-utils/src/stars.slv +++ b/libraries/render-utils/src/stars.slv @@ -13,9 +13,8 @@ // <@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 @@ -26,7 +25,7 @@ out vec4 varColor; out float varSize; void main(void) { - varColor = inColor.rgba; + varColor = colorToLinearRGBA(inColor); // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 5587185ead..43202c29c2 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -198,11 +198,11 @@ void Font::read(QIODevice& in) { image = image.convertToFormat(QImage::Format_RGBA8888); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA); + formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); + formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA); } _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 3f37ce378b..fa177bd0d4 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -97,9 +97,9 @@ const gpu::TexturePointer DrawStatus::getStatusIconMap() const { void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->args; + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); + RenderArgs* args = renderContext->getArgs(); auto& scene = sceneContext->_scene; const int NUM_STATUS_VEC4_PER_ITEM = 2; const int VEC4_LENGTH = 4; @@ -179,7 +179,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const unsigned int VEC3_ADRESS_OFFSET = 3; - if ((renderContext->_drawItemStatus & showDisplayStatusFlag) > 0) { + if ((renderContext->getDrawStatus() & showDisplayStatusFlag) > 0) { for (int i = 0; i < nbItems; i++) { batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); @@ -192,7 +192,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, batch.setPipeline(getDrawItemStatusPipeline()); - if ((renderContext->_drawItemStatus & showNetworkStatusFlag) > 0) { + if ((renderContext->getDrawStatus() & showNetworkStatusFlag) > 0) { for (int i = 0; i < nbItems; i++) { batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index c0e50037e0..44ba57143f 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -37,7 +37,7 @@ void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderCon // Is it possible that we render without a viewFrustum ? - if (!(renderContext->args && renderContext->args->_viewFrustum)) { + if (!(renderContext->getArgs() && renderContext->getArgs()->_viewFrustum)) { return; } @@ -54,11 +54,11 @@ Job::~Job() { void render::cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); - RenderArgs* args = renderContext->args; - auto renderDetails = renderContext->args->_details._item; + RenderArgs* args = renderContext->getArgs(); + auto renderDetails = renderContext->getArgs()->_details._item; renderDetails->_considered += inItems.size(); @@ -115,7 +115,7 @@ void CullItems::run(const SceneContextPointer& sceneContext, const RenderContext outItems.clear(); outItems.reserve(inItems.size()); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); args->_details.pointTo(RenderDetails::OTHER_ITEM); cullItems(sceneContext, renderContext, inItems, outItems); } @@ -124,7 +124,7 @@ void CullItemsOpaque::run(const SceneContextPointer& sceneContext, const RenderC outItems.clear(); outItems.reserve(inItems.size()); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); args->_details.pointTo(RenderDetails::OPAQUE_ITEM); cullItems(sceneContext, renderContext, inItems, outItems); } @@ -133,7 +133,7 @@ void CullItemsTransparent::run(const SceneContextPointer& sceneContext, const Re outItems.clear(); outItems.reserve(inItems.size()); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); args->_details.pointTo(RenderDetails::TRANSLUCENT_ITEM); cullItems(sceneContext, renderContext, inItems, outItems); } @@ -163,11 +163,11 @@ struct BackToFrontSort { }; void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); auto& scene = sceneContext->_scene; - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); // Allocate and simply copy @@ -211,7 +211,7 @@ void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderCo void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, int maxDrawnItems) { auto& scene = sceneContext->_scene; - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); // render if ((maxDrawnItems < 0) || (maxDrawnItems > (int) inItems.size())) { for (auto itemDetails : inItems) { @@ -236,8 +236,8 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo } void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // render lights auto& scene = sceneContext->_scene; @@ -253,7 +253,7 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext ItemIDsBounds culledItems; culledItems.reserve(inItems.size()); - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); args->_details.pointTo(RenderDetails::OTHER_ITEM); cullItems(sceneContext, renderContext, inItems, culledItems); @@ -265,8 +265,8 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext } void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); + assert(renderContext->getArgs()); + assert(renderContext->getArgs()->_viewFrustum); // render backgrounds auto& scene = sceneContext->_scene; @@ -278,7 +278,7 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo for (auto id : items) { inItems.emplace_back(id); } - RenderArgs* args = renderContext->args; + RenderArgs* args = renderContext->getArgs(); doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; batch.enableSkybox(true); diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 3f628c3a02..ab102e32a7 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -84,6 +84,17 @@ public: const Varying getInput() const { return _concept->getInput(); } const Varying getOutput() const { return _concept->getOutput(); } + template T& edit() { + auto theConcept = std::dynamic_pointer_cast(_concept); + assert(theConcept); + return theConcept->_data; + } + template const T& get() const { + auto theConcept = std::dynamic_pointer_cast(_concept); + assert(theConcept); + return theConcept->_data; + } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { PerformanceTimer perfTimer(getName().c_str()); PROFILE_RANGE(getName().c_str()); diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index 86f35eb831..907c836347 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -13,6 +13,20 @@ #include "DrawTask.h" using namespace render; +RenderContext::RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode) + : _deferredDebugMode{ deferredDebugMode }, _deferredDebugSize{ deferredDebugSize }, + _args{ nullptr }, + _drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect }, + _items{ items }, _tone{ tone } {} + +void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned) { + _occlusionStatus = occlusion; + _fxaaStatus = fxaa; + + if (showOwned) { + _drawStatus |= render::showNetworkStatusFlag; + } +}; Engine::Engine() : _sceneContext(std::make_shared()), diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 7c11246cff..4192dd3ed9 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -23,7 +23,7 @@ public: SceneContext() {} }; -typedef std::shared_ptr SceneContextPointer; +using SceneContextPointer = std::shared_ptr; // see examples/utilities/tools/renderEngineDebug.js const int showDisplayStatusFlag = 1; @@ -32,38 +32,83 @@ const int showNetworkStatusFlag = 2; class RenderContext { public: - RenderArgs* args; + class ItemsConfig { + public: + class Counter { + public: + Counter() {}; + Counter(const Counter& counter) { + numFeed = numDrawn = 0; + maxDrawn = counter.maxDrawn; + }; - bool _cullOpaque = true; - bool _sortOpaque = true; - bool _renderOpaque = true; - bool _cullTransparent = true; - bool _sortTransparent = true; - bool _renderTransparent = true; + void setCounts(const Counter& counter) { + numFeed = counter.numFeed; + numDrawn = counter.numDrawn; + }; - int _numFeedOpaqueItems = 0; - int _numDrawnOpaqueItems = 0; - int _maxDrawnOpaqueItems = -1; + int numFeed = 0; + int numDrawn = 0; + int maxDrawn = -1; + }; + + class State : public Counter { + public: + bool render = true; + bool cull = true; + bool sort = true; + + Counter counter{}; + }; + + ItemsConfig(State opaqueState, State transparentState, Counter overlay3DCounter) + : opaque{ opaqueState }, transparent{ transparentState }, overlay3D{ overlay3DCounter } {} + ItemsConfig() : ItemsConfig{ {}, {}, {} } {} + + // TODO: If member count increases, store counters in a map instead of multiple members + State opaque{}; + State transparent{}; + Counter overlay3D{}; + }; + + class Tone { + public: + int toneCurve = 1; // Means just Gamma 2.2 correction + float exposure = 0.0; + }; - int _numFeedTransparentItems = 0; - int _numDrawnTransparentItems = 0; - int _maxDrawnTransparentItems = -1; + RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode); + RenderContext() : RenderContext({}, {}, {}, {}, {}, {}) {}; - int _numFeedOverlay3DItems = 0; - int _numDrawnOverlay3DItems = 0; - int _maxDrawnOverlay3DItems = -1; + void setArgs(RenderArgs* args) { _args = args; } + inline RenderArgs* getArgs() { return _args; } + inline ItemsConfig& getItemsConfig() { return _items; } + inline Tone& getTone() { return _tone; } + inline int getDrawStatus() { return _drawStatus; } + inline bool getDrawHitEffect() { return _drawHitEffect; } + inline bool getOcclusionStatus() { return _occlusionStatus; } + inline bool getFxaaStatus() { return _fxaaStatus; } + void setOptions(bool occlusion, bool fxaa, bool showOwned); - int _drawItemStatus = 0; - bool _drawHitEffect = false; + // Debugging + int _deferredDebugMode; + glm::vec4 _deferredDebugSize; +protected: + RenderArgs* _args; + + // Options + int _drawStatus; // bitflag + bool _drawHitEffect; bool _occlusionStatus = false; bool _fxaaStatus = false; - RenderContext() {} + ItemsConfig _items; + Tone _tone; }; typedef std::shared_ptr RenderContextPointer; -// THe base class for a task that runs on the SceneContext +// The base class for a task that runs on the SceneContext class Task { public: Task() {} @@ -76,7 +121,7 @@ protected: typedef std::shared_ptr TaskPointer; typedef std::vector Tasks; -// The root of the takss, the Engine, should not be known from the Tasks, +// The root of the tasks, the Engine, should not be known from the Tasks, // The SceneContext is what navigates from the engine down to the Tasks class Engine { public: diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index bb74b20be0..f6b1726770 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -22,6 +22,10 @@ quat Quat::normalize(const glm::quat& q) { return glm::normalize(q); } +quat Quat::conjugate(const glm::quat& q) { + return glm::conjugate(q); +} + glm::quat Quat::rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { return ::rotationBetween(v1, v2); } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 543c93401f..bc7f11ab01 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -26,6 +26,7 @@ class Quat : public QObject { public slots: glm::quat multiply(const glm::quat& q1, const glm::quat& q2); glm::quat normalize(const glm::quat& q); + glm::quat conjugate(const glm::quat& q); glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up); glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center); glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); diff --git a/libraries/script-engine/src/SceneScriptingInterface.cpp b/libraries/script-engine/src/SceneScriptingInterface.cpp index 59d2765659..062e502d6e 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.cpp +++ b/libraries/script-engine/src/SceneScriptingInterface.cpp @@ -1,6 +1,6 @@ // // SceneScriptingInterface.cpp -// interface/src/scripting +// libraries/script-engine // // Created by Sam Gateau on 2/24/15. // Copyright 2014 High Fidelity, Inc. @@ -11,90 +11,97 @@ #include "SceneScriptingInterface.h" -#include - - #include -SceneScriptingInterface::SceneScriptingInterface() { - // Let's make sure the sunSkyStage is using a proceduralSKybox - _skyStage->setSkybox(model::SkyboxPointer(new ProceduralSkybox())); -} - -void SceneScriptingInterface::setStageOrientation(const glm::quat& orientation) { - _skyStage->setOriginOrientation(orientation); -} -void SceneScriptingInterface::setStageLocation(float longitude, float latitude, float altitude) { - _skyStage->setOriginLocation(longitude, latitude, altitude); -} - -float SceneScriptingInterface::getStageLocationLongitude() const { +float SceneScripting::Location::getLongitude() const { return _skyStage->getOriginLongitude(); } -float SceneScriptingInterface::getStageLocationLatitude() const { + +float SceneScripting::Location::getLatitude() const { return _skyStage->getOriginLatitude(); } -float SceneScriptingInterface::getStageLocationAltitude() const { + +float SceneScripting::Location::getAltitude() const { return _skyStage->getOriginSurfaceAltitude(); } -void SceneScriptingInterface::setStageDayTime(float hour) { +void SceneScripting::Location::setLongitude(float longitude) { + _skyStage->setOriginLongitude(longitude); +} + +void SceneScripting::Location::setLatitude(float latitude) { + _skyStage->setOriginLatitude(latitude); +} + +void SceneScripting::Location::setAltitude(float altitude) { + _skyStage->setOriginSurfaceAltitude(altitude); +} + +void SceneScripting::Time::setHour(float hour) { _skyStage->setDayTime(hour); } -float SceneScriptingInterface::getStageDayTime() const { +float SceneScripting::Time::getHour() const { return _skyStage->getDayTime(); } -void SceneScriptingInterface::setStageYearTime(int day) { +void SceneScripting::Time::setDay(int day) { _skyStage->setYearTime(day); } -int SceneScriptingInterface::getStageYearTime() const { +int SceneScripting::Time::getDay() const { return _skyStage->getYearTime(); } -void SceneScriptingInterface::setKeyLightColor(const glm::vec3& color) { - _skyStage->setSunColor(color); -} - -glm::vec3 SceneScriptingInterface::getKeyLightColor() const { +glm::vec3 SceneScripting::KeyLight::getColor() const { return _skyStage->getSunColor(); } -void SceneScriptingInterface::setKeyLightIntensity(float intensity) { - _skyStage->setSunIntensity(intensity); +void SceneScripting::KeyLight::setColor(const glm::vec3& color) { + _skyStage->setSunColor(color); } -float SceneScriptingInterface::getKeyLightIntensity() const { +float SceneScripting::KeyLight::getIntensity() const { return _skyStage->getSunIntensity(); } -void SceneScriptingInterface::setKeyLightAmbientIntensity(float intensity) { - _skyStage->setSunAmbientIntensity(intensity); +void SceneScripting::KeyLight::setIntensity(float intensity) { + _skyStage->setSunIntensity(intensity); } -float SceneScriptingInterface::getKeyLightAmbientIntensity() const { +float SceneScripting::KeyLight::getAmbientIntensity() const { return _skyStage->getSunAmbientIntensity(); } -void SceneScriptingInterface::setKeyLightDirection(const glm::vec3& direction) { - _skyStage->setSunDirection(direction); +void SceneScripting::KeyLight::setAmbientIntensity(float intensity) { + _skyStage->setSunAmbientIntensity(intensity); } -glm::vec3 SceneScriptingInterface::getKeyLightDirection() const { +glm::vec3 SceneScripting::KeyLight::getDirection() const { return _skyStage->getSunDirection(); } -void SceneScriptingInterface::setStageSunModelEnable(bool isEnabled) { +void SceneScripting::KeyLight::setDirection(const glm::vec3& direction) { + _skyStage->setSunDirection(direction); +} + +void SceneScripting::Stage::setOrientation(const glm::quat& orientation) const { + _skyStage->setOriginOrientation(orientation); +} + +void SceneScripting::Stage::setLocation(float longitude, float latitude, float altitude) { + _skyStage->setOriginLocation(longitude, latitude, altitude); +} + +void SceneScripting::Stage::setSunModelEnable(bool isEnabled) { _skyStage->setSunModelEnable(isEnabled); } -bool SceneScriptingInterface::isStageSunModelEnabled() const { +bool SceneScripting::Stage::isSunModelEnabled() const { return _skyStage->isSunModelEnabled(); } -void SceneScriptingInterface::setBackgroundMode(const QString& mode) { +void SceneScripting::Stage::setBackgroundMode(const QString& mode) { if (mode == QString("inherit")) { _skyStage->setBackgroundMode(model::SunSkyStage::NO_BACKGROUND); } else if (mode == QString("atmosphere")) { @@ -104,7 +111,7 @@ void SceneScriptingInterface::setBackgroundMode(const QString& mode) { } } -QString SceneScriptingInterface::getBackgroundMode() const { +QString SceneScripting::Stage::getBackgroundMode() const { switch (_skyStage->getBackgroundMode()) { case model::SunSkyStage::NO_BACKGROUND: return QString("inherit"); @@ -117,8 +124,9 @@ QString SceneScriptingInterface::getBackgroundMode() const { }; } -model::SunSkyStagePointer SceneScriptingInterface::getSkyStage() const { - return _skyStage; +SceneScriptingInterface::SceneScriptingInterface() : _stage{ new SceneScripting::Stage{ _skyStage } } { + // Let's make sure the sunSkyStage is using a proceduralSkybox + _skyStage->setSkybox(model::SkyboxPointer(new ProceduralSkybox())); } void SceneScriptingInterface::setShouldRenderAvatars(bool shouldRenderAvatars) { @@ -135,35 +143,6 @@ void SceneScriptingInterface::setShouldRenderEntities(bool shouldRenderEntities) } } -void SceneScriptingInterface::setEngineRenderOpaque(bool renderOpaque) { - _engineRenderOpaque = renderOpaque; -} - -void SceneScriptingInterface::setEngineRenderTransparent(bool renderTransparent) { - _engineRenderTransparent = renderTransparent; -} - -void SceneScriptingInterface::setEngineCullOpaque(bool cullOpaque) { - _engineCullOpaque = cullOpaque; -} - -void SceneScriptingInterface::setEngineCullTransparent(bool cullTransparent) { - _engineCullTransparent = cullTransparent; -} - -void SceneScriptingInterface::setEngineSortOpaque(bool sortOpaque) { - _engineSortOpaque = sortOpaque; -} - -void SceneScriptingInterface::setEngineSortTransparent(bool sortTransparent) { - _engineSortOpaque = sortTransparent; -} - -void SceneScriptingInterface::clearEngineCounters() { - _numFeedOpaqueItems = 0; - _numDrawnOpaqueItems = 0; - _numFeedTransparentItems = 0; - _numDrawnTransparentItems = 0; - _numFeedOverlay3DItems = 0; - _numDrawnOverlay3DItems = 0; +model::SunSkyStagePointer SceneScriptingInterface::getSkyStage() const { + return _skyStage; } diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 8cd48d9ddd..0be8b066aa 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -1,6 +1,6 @@ // // SceneScriptingInterface.h -// interface/src/scripting +// libraries/script-engine // // Created by Sam Gateau on 2/24/15. // Copyright 2014 High Fidelity, Inc. @@ -12,141 +12,149 @@ #ifndef hifi_SceneScriptingInterface_h #define hifi_SceneScriptingInterface_h -#include - -#include +#include // QObject +#include // Dependency #include "model/Stage.h" +// TODO: if QT moc ever supports nested classes, subclass these to the interface instead of namespacing +namespace SceneScripting { + class Location : public QObject { + Q_OBJECT + + public: + Location(model::SunSkyStagePointer skyStage) : _skyStage{ skyStage } {} + + Q_PROPERTY(float longitude READ getLongitude WRITE setLongitude) + Q_PROPERTY(float latitude READ getLatitude WRITE setLatitude) + Q_PROPERTY(float altitude READ getAltitude WRITE setAltitude) + + float getLongitude() const; + float getLatitude() const; + float getAltitude() const; + void setLongitude(float longitude); + void setLatitude(float latitude); + void setAltitude(float altitude); + + protected: + model::SunSkyStagePointer _skyStage; + }; + using LocationPointer = std::unique_ptr; + + class Time : public QObject { + Q_OBJECT + + public: + Time(model::SunSkyStagePointer skyStage) : _skyStage{ skyStage } {} + + Q_PROPERTY(float hour READ getHour WRITE setHour) + Q_PROPERTY(int day READ getDay WRITE setDay) + + float getHour() const; + void setHour(float hour); + int getDay() const; + void setDay(int day); + + protected: + model::SunSkyStagePointer _skyStage; + }; + using TimePointer = std::unique_ptr