mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 14:03:17 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into 20736
This commit is contained in:
commit
5d1e5b054a
201 changed files with 3393 additions and 1692 deletions
29
BUILD_WIN.md
29
BUILD_WIN.md
|
@ -1,5 +1,7 @@
|
|||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Windows specific instructions are found in this file.
|
||||
|
||||
Interface can be built as 32 or 64 bit.
|
||||
|
||||
###Visual Studio 2013
|
||||
|
||||
You can use the Community or Professional editions of Visual Studio 2013.
|
||||
|
@ -25,25 +27,26 @@ We expect nmake.exe to be located at the following path.
|
|||
###Qt
|
||||
You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version.
|
||||
|
||||
NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit version of libraries for interface.exe to run. The 32-bit version of the static library is the one linked by our CMake find modules.
|
||||
|
||||
* [Download the online installer](http://qt-project.org/downloads)
|
||||
* When it asks you to select components, ONLY select the following:
|
||||
* When it asks you to select components, ONLY select one of the following, 32- or 64-bit to match your build preference:
|
||||
* Qt > Qt 5.5.1 > **msvc2013 32-bit**
|
||||
* Qt > Qt 5.5.1 > **msvc2013 64-bit**
|
||||
|
||||
* [Download the offline installer](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe)
|
||||
* Download the offline installer, 32- or 64-bit to match your build preference:
|
||||
* [32-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe)
|
||||
* [64-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013_64-5.5.1.exe)
|
||||
|
||||
Once Qt is installed, you need to manually configure the following:
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.5.1\msvc2013\lib\cmake` directory.
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.5.1\msvc2013\lib\cmake` or `Qt\5.5.1\msvc2013_64\lib\cmake` directory.
|
||||
* You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New
|
||||
|
||||
###External Libraries
|
||||
|
||||
As it stands, Hifi/Interface is a 32-bit application, so all libraries should also be 32-bit.
|
||||
All libraries should be 32- or 64-bit to match your build preference.
|
||||
|
||||
CMake will need to know where the headers and libraries for required external dependencies are.
|
||||
|
||||
We use CMake's `fixup_bundle` to find the DLLs all of our exectuable targets require, and then copy them beside the executable in a post-build step. If `fixup_bundle` is having problems finding a DLL, you can fix it manually on your end by adding the folder containing that DLL to your path. Let us know which DLL CMake had trouble finding, as it is possible a tweak to our CMake files is required.
|
||||
We use CMake's `fixup_bundle` to find the DLLs all of our executable targets require, and then copy them beside the executable in a post-build step. If `fixup_bundle` is having problems finding a DLL, you can fix it manually on your end by adding the folder containing that DLL to your path. Let us know which DLL CMake had trouble finding, as it is possible a tweak to our CMake files is required.
|
||||
|
||||
The recommended route for CMake to find the external dependencies is to place all of the dependencies in one folder and set one ENV variable - HIFI_LIB_DIR. That ENV variable should point to a directory with the following structure:
|
||||
|
||||
|
@ -69,17 +72,23 @@ Your system may already have several versions of the OpenSSL DLL's (ssleay32.dll
|
|||
QSslSocket: cannot resolve SSL_CTX_set_next_proto_select_cb
|
||||
QSslSocket: cannot resolve SSL_get0_next_proto_negotiated
|
||||
|
||||
To prevent these problems, install OpenSSL yourself. Download the following binary packages [from this website](http://slproweb.com/products/Win32OpenSSL.html):
|
||||
* Visual C++ 2008 Redistributables
|
||||
* Win32 OpenSSL v1.0.1p
|
||||
To prevent these problems, install OpenSSL yourself. Download one of the following binary packages [from this website](http://slproweb.com/products/Win32OpenSSL.html):
|
||||
* Win32 OpenSSL v1.0.1q
|
||||
* Win64 OpenSSL v1.0.1q
|
||||
|
||||
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.
|
||||
|
||||
###Build High Fidelity using Visual Studio
|
||||
Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake.
|
||||
|
||||
For 32-bit builds:
|
||||
|
||||
cmake .. -G "Visual Studio 12"
|
||||
|
||||
For 64-bit builds:
|
||||
|
||||
cmake .. -G "Visual Studio 12 Win64"
|
||||
|
||||
Open %HIFI_DIR%\build\hifi.sln and compile.
|
||||
|
||||
###Running Interface
|
||||
|
|
|
@ -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")
|
||||
|
@ -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()
|
||||
|
@ -228,34 +228,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()
|
|
@ -37,6 +37,7 @@
|
|||
#include <EntityScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
||||
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
#include "entities/AssignmentParentFinder.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
#include "AbstractAudioInterface.h"
|
||||
|
||||
|
@ -62,6 +63,8 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
|
||||
assetThread->start();
|
||||
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
@ -284,6 +287,8 @@ void Agent::executeScript() {
|
|||
|
||||
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
|
||||
|
||||
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
|
||||
|
||||
// wire up our additional agent related processing to the update signal
|
||||
QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio);
|
||||
|
||||
|
@ -384,7 +389,7 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) {
|
|||
int numAvailableBytes = (soundByteArray.size() - _numAvatarSoundSentBytes) > SCRIPT_AUDIO_BUFFER_BYTES
|
||||
? SCRIPT_AUDIO_BUFFER_BYTES
|
||||
: soundByteArray.size() - _numAvatarSoundSentBytes;
|
||||
numAvailableSamples = numAvailableBytes / sizeof(int16_t);
|
||||
numAvailableSamples = (int16_t)numAvailableBytes / sizeof(int16_t);
|
||||
|
||||
|
||||
// check if the all of the _numAvatarAudioBufferSamples to be sent are silence
|
||||
|
|
|
@ -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<ReceivedMessage> 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<NodeList>();
|
||||
|
@ -242,7 +243,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
|
|||
nodeList->getDomainHandler().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(QSharedPointer<ReceivedMessa
|
|||
// Starts an event loop, and emits workerThread->started()
|
||||
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<ReceivedMessage> 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<NodeList>();
|
||||
|
||||
|
|
14
assignment-client/src/AssignmentClientLogging.cpp
Normal file
14
assignment-client/src/AssignmentClientLogging.cpp
Normal file
|
@ -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")
|
19
assignment-client/src/AssignmentClientLogging.h
Normal file
19
assignment-client/src/AssignmentClientLogging.h
Normal file
|
@ -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 <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(assigmnentclient)
|
||||
|
||||
#endif // hifi_AssignmentClientLogging_h
|
|
@ -171,7 +171,7 @@ void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer&
|
|||
statsPacket->writePrimitive(appendFlag);
|
||||
appendFlag = 1;
|
||||
|
||||
int numStreamStatsRoomFor = (statsPacket->size() - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats);
|
||||
int numStreamStatsRoomFor = (int)(statsPacket->size() - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats);
|
||||
|
||||
// calculate and pack the number of stream stats to follow
|
||||
quint16 numStreamStatsToPack = std::min(numStreamStatsRemaining, numStreamStatsRoomFor);
|
||||
|
|
|
@ -293,7 +293,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
|
||||
qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID;
|
||||
|
||||
packetsSent += nackPacketList->getNumPackets();
|
||||
packetsSent += (int)nackPacketList->getNumPackets();
|
||||
|
||||
// send the list of nack packets
|
||||
totalBytesSent += nodeList->sendPacketList(std::move(nackPacketList), *destinationNode);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<NLPacket> _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<OCTREE_PACKET_SEQUENCE> _nackedSequenceNumbers;
|
||||
|
||||
quint64 _sceneSendStartTime = 0;
|
||||
|
||||
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
|
||||
};
|
||||
|
||||
#endif // hifi_OctreeQueryNode_h
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <udt/PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#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<OctreeQueryNode*>(_node->getLinkedData());
|
||||
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(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<NodeList>()->sendUnreliablePacket(statsPacket, *_node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(statsPacket, *node);
|
||||
packetSent = true;
|
||||
} else {
|
||||
// not enough room in the packet, send two packets
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(statsPacket, *_node);
|
||||
DependencyManager::get<NodeList>()->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<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *_node);
|
||||
DependencyManager::get<NodeList>()->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<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *_node);
|
||||
DependencyManager::get<NodeList>()->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<NodeList>()->sendUnreliablePacket(*packet, *_node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(*packet, *node);
|
||||
truePacketsSent++;
|
||||
packetsSentThisInterval++;
|
||||
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
|
||||
#include <GenericThread.h>
|
||||
|
||||
#include "OctreeQueryNode.h"
|
||||
|
||||
class OctreeQueryNode;
|
||||
class OctreeServer;
|
||||
|
||||
using AtomicUIntStat = std::atomic<uintmax_t>;
|
||||
|
@ -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> _node;
|
||||
QUuid _nodeUuid;
|
||||
|
||||
OctreePacketData _packetData;
|
||||
|
||||
int _nodeMissingCount;
|
||||
bool _isShuttingDown;
|
||||
int _nodeMissingCount { 0 };
|
||||
bool _isShuttingDown { false };
|
||||
};
|
||||
|
||||
#endif // hifi_OctreeSendThread_h
|
||||
|
|
|
@ -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<OctreeSendThread>(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<OctreeSendThread*>(sender())) {
|
||||
// This deletes the unique_ptr, so sendThread is destructed after that line
|
||||
_sendThreads.erase(sendThread->getNodeUuid());
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeServer::handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> 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>();
|
||||
nodeList->updateNodeWithDataFromPacket(message, senderNode);
|
||||
|
||||
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(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<OctreeQueryNode*>(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>();
|
||||
nodeList->linkedDataCreateCallback = NULL;
|
||||
// This ensures that we don't get any more newly connecting nodes
|
||||
DependencyManager::get<NodeList>()->linkedDataCreateCallback = nullptr;
|
||||
|
||||
if (_octreeInboundPacketProcessor) {
|
||||
_octreeInboundPacketProcessor->terminating();
|
||||
|
@ -1226,21 +1226,15 @@ void OctreeServer::aboutToFinish() {
|
|||
_jurisdictionSender->terminating();
|
||||
}
|
||||
|
||||
QSet<SharedNodePointer> 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<OctreeSendThread*, quint64>& something, quint64 since) {
|
||||
|
|
|
@ -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<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void removeSendThread();
|
||||
|
||||
protected:
|
||||
using UniqueSendThread = std::unique_ptr<OctreeSendThread>;
|
||||
using SendThreads = std::unordered_map<QUuid, UniqueSendThread>;
|
||||
|
||||
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;
|
||||
|
|
30
cmake/externals/bullet/CMakeLists.txt
vendored
30
cmake/externals/bullet/CMakeLists.txt
vendored
|
@ -4,7 +4,7 @@ if (WIN32)
|
|||
set(PLATFORM_CMAKE_ARGS "-DUSE_MSVC_RUNTIME_LIBRARY_DLL=1")
|
||||
else ()
|
||||
set(PLATFORM_CMAKE_ARGS "-DBUILD_SHARED_LIBS=1")
|
||||
|
||||
|
||||
if (ANDROID)
|
||||
list(APPEND PLATFORM_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
|
||||
elseif (APPLE)
|
||||
|
@ -14,30 +14,28 @@ endif ()
|
|||
|
||||
include(ExternalProject)
|
||||
|
||||
if (WIN32)
|
||||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
# URL https://bullet.googlecode.com/files/bullet-2.82-r2704.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.zip
|
||||
URL_MD5 d95b07eb120de7dd7786361c0b5a8d9f
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 -DUSE_DX11=0
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz
|
||||
URL_MD5 03051bf112dcc78ddd296f9cab38fd68
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 -DUSE_DX11=0
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
)
|
||||
)
|
||||
else ()
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
#URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.tgz
|
||||
URL_MD5 fb140a4983b4109aa1c825a162aa8d64
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz
|
||||
URL_MD5 03051bf112dcc78ddd296f9cab38fd68
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
)
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
|
@ -55,7 +53,7 @@ if (APPLE OR UNIX OR ANDROID)
|
|||
else ()
|
||||
set(BULLET_LIB_EXT "so")
|
||||
endif ()
|
||||
|
||||
|
||||
set(LIB_PREFIX "lib")
|
||||
elseif (WIN32)
|
||||
set(BULLET_LIB_EXT "lib")
|
||||
|
@ -63,13 +61,13 @@ endif ()
|
|||
|
||||
if (DEFINED BULLET_LIB_EXT)
|
||||
set(_BULLET_LIB_PAIRS "DYNAMICS_LIBRARY\;BulletDynamics" "COLLISION_LIBRARY\;BulletCollision" "MATH_LIBRARY\;LinearMath" "SOFTBODY_LIBRARY\;BulletSoftBody")
|
||||
|
||||
|
||||
foreach(_LIB_PAIR ${_BULLET_LIB_PAIRS})
|
||||
list(GET _LIB_PAIR 0 _LIB_VAR_NAME)
|
||||
list(GET _LIB_PAIR 1 _LIB_NAME)
|
||||
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_${_LIB_VAR_NAME}_RELEASE ${BULLET_LIB_DIR}/${LIB_PREFIX}${_LIB_NAME}.${BULLET_LIB_EXT} CACHE FILEPATH "${_LIB_NAME} release library location")
|
||||
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_${_LIB_VAR_NAME}_DEBUG ${BULLET_LIB_DIR}/${LIB_PREFIX}${_LIB_NAME}_Debug.${BULLET_LIB_EXT} CACHE FILEPATH "${_LIB_NAME} debug library location")
|
||||
else ()
|
||||
|
|
|
@ -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 $<TARGET_FILE_DIR:${TARGET_NAME}> ${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 $<TARGET_FILE_DIR:${TARGET_NAME}> ${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 ()
|
||||
|
||||
|
|
30
cmake/macros/GenerateInstallers.cmake
Normal file
30
cmake/macros/GenerateInstallers.cmake
Normal file
|
@ -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()
|
|
@ -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")
|
||||
|
|
|
@ -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} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> $<TARGET_FILE:${TARGET_NAME}>"
|
||||
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> $<TARGET_FILE:${TARGET_NAME}>"
|
||||
)
|
||||
elseif (DEFINED BUILD_BUNDLE AND BUILD_BUNDLE AND APPLE)
|
||||
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH)
|
||||
|
|
|
@ -10,24 +10,30 @@ macro(SETUP_HIFI_PLUGIN)
|
|||
setup_hifi_library(${ARGV})
|
||||
add_dependencies(interface ${TARGET_NAME})
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins")
|
||||
|
||||
if (APPLE)
|
||||
|
||||
if (APPLE)
|
||||
set(PLUGIN_PATH "interface.app/Contents/MacOS/plugins")
|
||||
else()
|
||||
set(PLUGIN_PATH "plugins")
|
||||
endif()
|
||||
|
||||
|
||||
IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
set(PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/${PLUGIN_PATH}/")
|
||||
else()
|
||||
set(PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${PLUGIN_PATH}/")
|
||||
endif()
|
||||
|
||||
# create the destination for the plugin binaries
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory
|
||||
"${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${PLUGIN_PATH}/"
|
||||
${PLUGIN_FULL_PATH}
|
||||
)
|
||||
|
||||
|
||||
add_custom_command(TARGET ${DIR} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"$<TARGET_FILE:${TARGET_NAME}>"
|
||||
"${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${PLUGIN_PATH}/"
|
||||
${PLUGIN_FULL_PATH}
|
||||
)
|
||||
|
||||
endmacro()
|
||||
endmacro()
|
||||
|
|
|
@ -15,4 +15,4 @@ macro(TARGET_BULLET)
|
|||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
|
||||
endif()
|
||||
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
||||
endmacro()
|
||||
endmacro()
|
||||
|
|
|
@ -88,4 +88,4 @@ find_package_handle_standard_args(Bullet "Could NOT find Bullet, try to set the
|
|||
BULLET_INCLUDE_DIRS
|
||||
BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY BULLET_SOFTBODY_LIBRARY
|
||||
BULLET_LIBRARIES
|
||||
)
|
||||
)
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
|
||||
var BAT_MODEL = "atp:c47deaae09cca927f6bc9cca0e8bbe77fc618f8c3f2b49899406a63a59f885cb.fbx";
|
||||
var BAT_COLLISION_HULL = "atp:9eafceb7510c41d50661130090de7e0632aa4da236ebda84a0059a4be2130e0c.obj";
|
||||
var SCRIPT_URL = "http://rawgit.com/birarda/hifi/baseball/examples/baseball/bat.js"
|
||||
var SCRIPT_RELATIVE_PATH = "bat.js"
|
||||
|
||||
var batUserData = {
|
||||
grabbableKey: {
|
||||
|
@ -69,7 +69,7 @@
|
|||
velocity: { x: 0, y: 0.05, z: 0}, // workaround for gravity not taking effect on add
|
||||
gravity: { x: 0, y: -9.81, z: 0},
|
||||
rotation: Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0),
|
||||
script: SCRIPT_URL,
|
||||
script: Script.resolvePath(SCRIPT_RELATIVE_PATH),
|
||||
userData: JSON.stringify(batUserData)
|
||||
});
|
||||
};
|
||||
|
|
87
examples/controllers/philipsVersion.js
Normal file
87
examples/controllers/philipsVersion.js
Normal file
|
@ -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();
|
||||
});
|
76
examples/controllers/proceduralHandPoseExample.js
Normal file
76
examples/controllers/proceduralHandPoseExample.js
Normal file
|
@ -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();
|
||||
});
|
121
examples/controllers/reticleHandAngularVelocityTest.js
Normal file
121
examples/controllers/reticleHandAngularVelocityTest.js
Normal file
|
@ -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();
|
||||
});
|
||||
|
||||
|
103
examples/controllers/reticleHandRotationTest.js
Normal file
103
examples/controllers/reticleHandRotationTest.js
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
Math.clamp=function(a,b,c) {
|
||||
return Math.max(b,Math.min(c,a));
|
||||
}
|
||||
|
||||
var whichHand = Controller.Standard.RightHand;
|
||||
var whichTrigger = Controller.Standard.RT;
|
||||
|
||||
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) {
|
||||
print("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------");
|
||||
}
|
||||
|
||||
if (Math.abs(dX) > EXPECTED_CHANGE) {
|
||||
print("surpressing unexpectedly large change dX:" + dX + "----------------------------");
|
||||
}
|
||||
if (Math.abs(dY) > EXPECTED_CHANGE) {
|
||||
print("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(whichTrigger).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
|
||||
mapping.from(whichHand).peek().to(function(pose) {
|
||||
|
||||
// NOTE: hack for now
|
||||
var screenSizeX = 1920;
|
||||
var screenSizeY = 1080;
|
||||
|
||||
var rotated = Vec3.multiplyQbyV(pose.rotation, Vec3.UNIT_NEG_Y); //
|
||||
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.z; // from -1 left to 1 right
|
||||
//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);
|
||||
//var clampYaw = absoluteYaw;
|
||||
//print("clampYaw:" + clampYaw);
|
||||
//print("clampPitch:" + clampPitch);
|
||||
|
||||
// if using entire span...
|
||||
//var xRatio = (absoluteYaw + 1) / 2;
|
||||
//var yRatio = (absolutePitch + 1) / 2;
|
||||
|
||||
// if using only from -0.5 to 0.5
|
||||
var xRatio = (clampYaw + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||
var yRatio = (clampPitch + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||
|
||||
//print("xRatio:" + xRatio);
|
||||
//print("yRatio:" + yRatio);
|
||||
|
||||
//print("ratio x:" + xRatio + " y:" + yRatio);
|
||||
|
||||
var x = screenSizeX * xRatio;
|
||||
var y = screenSizeY * yRatio;
|
||||
|
||||
//print("position x:" + x + " y:" + y);
|
||||
if (!(xRatio == 0.5 && yRatio == 0)) {
|
||||
moveReticleAbsolute(x, y);
|
||||
}
|
||||
});
|
||||
mapping.enable();
|
||||
|
||||
Script.scriptEnding.connect(function(){
|
||||
mapping.disable();
|
||||
});
|
||||
|
||||
|
38
examples/controllers/reticleTests.js
Normal file
38
examples/controllers/reticleTests.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
var mappingJSON = {
|
||||
"name": "com.highfidelity.testing.reticleWithJoystick",
|
||||
"channels": [
|
||||
{ "from": "Standard.RT", "to": "Actions.ReticleClick", "filters": "constrainToInteger" },
|
||||
|
||||
{ "from": "Standard.RX", "to": "Actions.ReticleX",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "pulse", "interval": 0.05 },
|
||||
{ "type": "scale", "scale": 20 }
|
||||
]
|
||||
},
|
||||
{ "from": "Standard.RY", "to": "Actions.ReticleY",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "pulse", "interval": 0.05 },
|
||||
{ "type": "scale", "scale": 20 }
|
||||
]
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
mapping = Controller.parseMapping(JSON.stringify(mappingJSON));
|
||||
mapping.enable();
|
||||
Script.scriptEnding.connect(function(){
|
||||
mapping.disable();
|
||||
});
|
|
@ -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", {
|
||||
|
|
|
@ -1002,7 +1002,8 @@ function setupModelMenus() {
|
|||
menuName: "Edit",
|
||||
menuItemName: "Models",
|
||||
isSeparator: true,
|
||||
beforeItem: "Physics"
|
||||
beforeItem: "Physics",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
if (!Menu.menuItemExists("Edit", "Delete")) {
|
||||
print("no delete... adding ours");
|
||||
|
@ -1012,7 +1013,8 @@ function setupModelMenus() {
|
|||
shortcutKeyEvent: {
|
||||
text: "backspace"
|
||||
},
|
||||
afterItem: "Models"
|
||||
afterItem: "Models",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
modelMenuAddedDelete = true;
|
||||
} else {
|
||||
|
@ -1023,7 +1025,8 @@ function setupModelMenus() {
|
|||
menuName: "Edit",
|
||||
menuItemName: "Entity List...",
|
||||
shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Models"
|
||||
afterItem: "Models",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
|
@ -1031,7 +1034,8 @@ function setupModelMenus() {
|
|||
shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Entity List...",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
isChecked: true,
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
|
@ -1039,79 +1043,91 @@ function setupModelMenus() {
|
|||
shortcutKey: "CTRL+META+S",
|
||||
afterItem: "Allow Selecting of Large Models",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
isChecked: true,
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Allow Selecting of Lights",
|
||||
shortcutKey: "CTRL+SHIFT+META+L",
|
||||
afterItem: "Allow Selecting of Small Models",
|
||||
isCheckable: true
|
||||
isCheckable: true,
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Select All Entities In Box",
|
||||
shortcutKey: "CTRL+SHIFT+META+A",
|
||||
afterItem: "Allow Selecting of Lights"
|
||||
afterItem: "Allow Selecting of Lights",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Select All Entities Touching Box",
|
||||
shortcutKey: "CTRL+SHIFT+META+T",
|
||||
afterItem: "Select All Entities In Box"
|
||||
afterItem: "Select All Entities In Box",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "File",
|
||||
menuItemName: "Models",
|
||||
isSeparator: true,
|
||||
beforeItem: "Settings"
|
||||
beforeItem: "Settings",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "File",
|
||||
menuItemName: "Export Entities",
|
||||
shortcutKey: "CTRL+META+E",
|
||||
afterItem: "Models"
|
||||
afterItem: "Models",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "File",
|
||||
menuItemName: "Import Entities",
|
||||
shortcutKey: "CTRL+META+I",
|
||||
afterItem: "Export Entities"
|
||||
afterItem: "Export Entities",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "File",
|
||||
menuItemName: "Import Entities from URL",
|
||||
shortcutKey: "CTRL+META+U",
|
||||
afterItem: "Import Entities"
|
||||
afterItem: "Import Entities",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "View",
|
||||
menuName: "Edit",
|
||||
menuItemName: MENU_AUTO_FOCUS_ON_SELECT,
|
||||
isCheckable: true,
|
||||
isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true"
|
||||
isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "View",
|
||||
menuName: "Edit",
|
||||
menuItemName: MENU_EASE_ON_FOCUS,
|
||||
afterItem: MENU_AUTO_FOCUS_ON_SELECT,
|
||||
isCheckable: true,
|
||||
isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true"
|
||||
isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "View",
|
||||
menuName: "Edit",
|
||||
menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE,
|
||||
afterItem: MENU_EASE_ON_FOCUS,
|
||||
isCheckable: true,
|
||||
isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true"
|
||||
isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "View",
|
||||
menuName: "Edit",
|
||||
menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE,
|
||||
afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE,
|
||||
isCheckable: true,
|
||||
isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true"
|
||||
isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
|
||||
Entities.setLightsArePickable(false);
|
||||
|
@ -1138,10 +1154,10 @@ function cleanupModelMenus() {
|
|||
Menu.removeMenuItem("File", "Import Entities");
|
||||
Menu.removeMenuItem("File", "Import Entities from URL");
|
||||
|
||||
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
|
||||
Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS);
|
||||
Menu.removeMenuItem("View", MENU_SHOW_LIGHTS_IN_EDIT_MODE);
|
||||
Menu.removeMenuItem("View", MENU_SHOW_ZONES_IN_EDIT_MODE);
|
||||
Menu.removeMenuItem("Edit", MENU_AUTO_FOCUS_ON_SELECT);
|
||||
Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS);
|
||||
Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_IN_EDIT_MODE);
|
||||
Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
|
|
|
@ -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
|
||||
|
|
59
examples/html/eventBridgeLoader.js
Normal file
59
examples/html/eventBridgeLoader.js
Normal file
|
@ -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); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
31
examples/html/qmlWebTest.html
Normal file
31
examples/html/qmlWebTest.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Properties</title>
|
||||
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||
|
||||
<script>
|
||||
var myBridge;
|
||||
|
||||
window.onload = function() {
|
||||
openEventBridge(function(eventBridge) {
|
||||
myBridge = eventBridge;
|
||||
myBridge.scriptEventReceived.connect(function(message) {
|
||||
console.log("HTML side received message: " + message);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
testClick = function() {
|
||||
myBridge.emitWebEvent("HTML side sending message - button click");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="properties">
|
||||
<button name="Test" title="Test" onclick="testClick()">Test</button>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</body>
|
84
examples/tPose.js
Normal file
84
examples/tPose.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// tPose.js
|
||||
// examples
|
||||
//
|
||||
// Created by Anthony Thibault on 12/10/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Example of how to put the avatar into it's default tpose.
|
||||
//
|
||||
|
||||
// TODO: CHANGE
|
||||
var buttonImageUrl = "https://s3.amazonaws.com/hifi-public/images/tools/tpose.svg";
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
|
||||
var buttonWidth = 37;
|
||||
var buttonHeight = 46;
|
||||
var buttonPadding = 10;
|
||||
|
||||
var buttonPositionX = windowDimensions.x - buttonPadding - buttonWidth;
|
||||
var buttonPositionY = (windowDimensions.y - buttonHeight) / 2 - (buttonHeight + buttonPadding);
|
||||
|
||||
var tPoseEnterImageOverlay = {
|
||||
x: buttonPositionX,
|
||||
y: buttonPositionY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight,
|
||||
subImage: { x: 0, y: buttonHeight, width: buttonWidth, height: buttonHeight },
|
||||
imageURL: buttonImageUrl,
|
||||
visible: true,
|
||||
alpha: 1.0
|
||||
};
|
||||
|
||||
var tPoseExitImageOverlay = {
|
||||
x: buttonPositionX,
|
||||
y: buttonPositionY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight,
|
||||
subImage: { x: buttonWidth, y: buttonHeight, width: buttonWidth, height: buttonHeight },
|
||||
imageURL: buttonImageUrl,
|
||||
visible: false,
|
||||
alpha: 1.0
|
||||
};
|
||||
|
||||
var tPoseEnterButton = Overlays.addOverlay("image", tPoseEnterImageOverlay);
|
||||
var tPoseExitButton = Overlays.addOverlay("image", tPoseExitImageOverlay);
|
||||
var tPose = false;
|
||||
|
||||
function enterDefaultPose() {
|
||||
tPose = true;
|
||||
var i, l = MyAvatar.getJointNames().length;
|
||||
var rot, trans;
|
||||
for (i = 0; i < l; i++) {
|
||||
rot = MyAvatar.getDefaultJointRotation(i);
|
||||
trans = MyAvatar.getDefaultJointTranslation(i);
|
||||
MyAvatar.setJointData(i, rot, trans);
|
||||
}
|
||||
Overlays.editOverlay(tPoseEnterButton, { visible: false });
|
||||
Overlays.editOverlay(tPoseExitButton, { visible: true });
|
||||
}
|
||||
|
||||
function exitDefaultPose() {
|
||||
tPose = false;
|
||||
MyAvatar.clearJointsData();
|
||||
Overlays.editOverlay(tPoseEnterButton, { visible: true });
|
||||
Overlays.editOverlay(tPoseExitButton, { visible: false });
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(function (event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
if (clickedOverlay == tPoseEnterButton) {
|
||||
enterDefaultPose();
|
||||
} else if (clickedOverlay == tPoseExitButton) {
|
||||
exitDefaultPose();
|
||||
}
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
Overlays.deleteOverlay(tPoseEnterButton);
|
||||
Overlays.deleteOverlay(tPoseExitButton);
|
||||
});
|
||||
|
32
examples/tests/qmlWebTest.js
Normal file
32
examples/tests/qmlWebTest.js
Normal file
|
@ -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)
|
|
@ -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()
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
{ "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" },
|
||||
{ "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" },
|
||||
|
||||
|
||||
|
||||
{ "comment" : "Mouse turn need to be small continuous increments",
|
||||
"from": { "makeAxis" : [
|
||||
[ "Keyboard.MouseMoveLeft" ],
|
||||
|
@ -75,7 +75,6 @@
|
|||
{ "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
|
||||
{ "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" },
|
||||
|
||||
{ "from": "Keyboard.Left", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.Right", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
{ "type": "scale", "scale": 22.5 }
|
||||
]
|
||||
},
|
||||
{ "from": "Standard.RX", "to": "Actions.Yaw" },
|
||||
|
||||
{ "from": "Standard.RX", "to": "Actions.Yaw" },
|
||||
{ "from": "Standard.RY",
|
||||
"when": "Application.Grounded",
|
||||
"to": "Actions.Up",
|
||||
|
@ -24,6 +24,7 @@
|
|||
"invert"
|
||||
]
|
||||
},
|
||||
|
||||
{ "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"},
|
||||
|
||||
{ "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" },
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
62
interface/resources/qml/QmlWebWindow.qml
Normal file
62
interface/resources/qml/QmlWebWindow.qml
Normal file
|
@ -0,0 +1,62 @@
|
|||
|
||||
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
|
||||
contentImplicitWidth: clientArea.implicitWidth
|
||||
contentImplicitHeight: clientArea.implicitHeight
|
||||
backgroundColor: "#7f000000"
|
||||
property url source: "about:blank"
|
||||
|
||||
signal navigating(string url)
|
||||
|
||||
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) {
|
||||
var HIFI_URL_PATTERN = /^hifi:\/\//;
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var newUrl = loadRequest.url.toString();
|
||||
if (newUrl.match(HIFI_URL_PATTERN)) {
|
||||
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
|
||||
profile: WebEngineProfile {
|
||||
httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)"
|
||||
}
|
||||
}
|
||||
} // item
|
||||
} // dialog
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,9 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
implicitHeight: label.implicitHeight * 1.5
|
||||
implicitHeight: source.visible ? label.implicitHeight * 1.5 : 0
|
||||
implicitWidth: label.implicitWidth + label.height * 2.5
|
||||
visible: source.visible
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
|
@ -66,6 +67,7 @@ Item {
|
|||
color: label.color
|
||||
text: checkText()
|
||||
size: label.height
|
||||
visible: source.visible
|
||||
font.pixelSize: size
|
||||
function checkText() {
|
||||
if (!source || source.type != 1 || !source.checkable) {
|
||||
|
@ -89,6 +91,7 @@ Item {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
color: source.enabled ? hifi.colors.text : hifi.colors.disabledText
|
||||
enabled: source.enabled && source.visible
|
||||
visible: source.visible
|
||||
function typedText() {
|
||||
if (source) {
|
||||
switch (source.type) {
|
||||
|
@ -109,7 +112,7 @@ Item {
|
|||
x: listView.width - width - 4
|
||||
size: label.height
|
||||
width: implicitWidth
|
||||
visible: source.type == 2
|
||||
visible: source.visible && (source.type == 2)
|
||||
text: "\uF0DA"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: label.color
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
#include <QtNetwork/QNetworkDiskCache>
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <QOpenGLContextWrapper.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
|
@ -101,6 +101,7 @@
|
|||
#include <VrMenu.h>
|
||||
#include <recording/Deck.h>
|
||||
#include <recording/Recorder.h>
|
||||
#include <QmlWebWindowClass.h>
|
||||
|
||||
#include "AnimDebugDraw.h"
|
||||
#include "AudioClient.h"
|
||||
|
@ -362,6 +363,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)),
|
||||
|
@ -382,7 +394,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_scaleMirror(1.0f),
|
||||
_rotateMirror(0.0f),
|
||||
_raiseMirror(0.0f),
|
||||
_lastMouseMoveWasSimulated(false),
|
||||
_enableProcessOctreeThread(true),
|
||||
_runningScriptsWidget(NULL),
|
||||
_runningScriptsWidgetWasVisible(false),
|
||||
|
@ -624,6 +635,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();
|
||||
|
@ -664,6 +680,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
// Setup the userInputMapper with the actions
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
|
||||
if (action == controller::toInt(controller::Action::RETICLE_CLICK)) {
|
||||
auto globalPos = QCursor::pos();
|
||||
auto localPos = _glWidget->mapFromGlobal(globalPos);
|
||||
if (state) {
|
||||
QMouseEvent mousePress(QEvent::MouseButtonPress, localPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
|
||||
sendEvent(_glWidget, &mousePress);
|
||||
_reticleClickPressed = true;
|
||||
} else {
|
||||
QMouseEvent mouseRelease(QEvent::MouseButtonRelease, localPos, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
|
||||
sendEvent(_glWidget, &mouseRelease);
|
||||
_reticleClickPressed = false;
|
||||
}
|
||||
return; // nothing else to do
|
||||
}
|
||||
|
||||
if (state) {
|
||||
if (action == controller::toInt(controller::Action::TOGGLE_MUTE)) {
|
||||
DependencyManager::get<AudioClient>()->toggleMute();
|
||||
|
@ -671,6 +702,38 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
cycleCamera();
|
||||
} 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 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -692,8 +755,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
|
||||
// Setup the keyboardMouseDevice and the user input mapper with the default bindings
|
||||
userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice());
|
||||
|
||||
|
||||
userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID());
|
||||
|
||||
// check first run...
|
||||
|
@ -745,15 +806,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
setActiveEyeTracker();
|
||||
#endif
|
||||
|
||||
_oldHandMouseX[0] = -1;
|
||||
_oldHandMouseY[0] = -1;
|
||||
_oldHandMouseX[1] = -1;
|
||||
_oldHandMouseY[1] = -1;
|
||||
_oldHandLeftClick[0] = false;
|
||||
_oldHandRightClick[0] = false;
|
||||
_oldHandLeftClick[1] = false;
|
||||
_oldHandRightClick[1] = false;
|
||||
|
||||
auto applicationUpdater = DependencyManager::get<AutoUpdater>();
|
||||
connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);
|
||||
applicationUpdater->checkForUpdate();
|
||||
|
@ -1231,20 +1283,32 @@ void Application::paintGL() {
|
|||
}
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
if (isHMDMode()) {
|
||||
auto mirrorBodyOrientation = myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
|
||||
|
||||
glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setRotation(myAvatar->getWorldAlignedOrientation()
|
||||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)) * hmdRotation);
|
||||
// Mirror HMD yaw and roll
|
||||
glm::vec3 mirrorHmdEulers = glm::eulerAngles(hmdRotation);
|
||||
mirrorHmdEulers.y = -mirrorHmdEulers.y;
|
||||
mirrorHmdEulers.z = -mirrorHmdEulers.z;
|
||||
glm::quat mirrorHmdRotation = glm::quat(mirrorHmdEulers);
|
||||
|
||||
glm::quat worldMirrorRotation = mirrorBodyOrientation * mirrorHmdRotation;
|
||||
|
||||
_myCamera.setRotation(worldMirrorRotation);
|
||||
|
||||
glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix());
|
||||
// Mirror HMD lateral offsets
|
||||
hmdOffset.x = -hmdOffset.x;
|
||||
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0)
|
||||
+ (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror
|
||||
+ (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset);
|
||||
+ 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);
|
||||
}
|
||||
|
@ -1273,6 +1337,10 @@ void Application::paintGL() {
|
|||
// Primary rendering pass
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
const QSize size = framebufferCache->getFrameBufferSize();
|
||||
|
||||
// Final framebuffer that will be handled to the display-plugin
|
||||
auto finalFramebuffer = framebufferCache->getFramebuffer();
|
||||
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/mainRender");
|
||||
PerformanceTimer perfTimer("mainRender");
|
||||
|
@ -1326,9 +1394,63 @@ void Application::paintGL() {
|
|||
}
|
||||
displaySide(&renderArgs, _myCamera);
|
||||
renderArgs._context->enableStereo(false);
|
||||
gpu::doInBatch(renderArgs._context, [](gpu::Batch& batch) {
|
||||
batch.setFramebuffer(nullptr);
|
||||
});
|
||||
|
||||
// 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
|
||||
|
@ -1336,7 +1458,7 @@ void Application::paintGL() {
|
|||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/compositor");
|
||||
PerformanceTimer perfTimer("compositor");
|
||||
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
|
||||
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()));
|
||||
|
@ -1361,23 +1483,12 @@ void Application::paintGL() {
|
|||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
|
||||
PerformanceTimer perfTimer("pluginOutput");
|
||||
auto primaryFramebuffer = framebufferCache->getPrimaryFramebuffer();
|
||||
auto scratchFramebuffer = framebufferCache->getFramebuffer();
|
||||
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
|
||||
gpu::Vec4i rect;
|
||||
rect.z = size.width();
|
||||
rect.w = size.height();
|
||||
batch.setFramebuffer(scratchFramebuffer);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
batch.blit(primaryFramebuffer, rect, scratchFramebuffer, rect);
|
||||
batch.setFramebuffer(nullptr);
|
||||
});
|
||||
auto finalTexturePointer = scratchFramebuffer->getRenderBuffer(0);
|
||||
auto finalTexturePointer = finalFramebuffer->getRenderBuffer(0);
|
||||
GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer);
|
||||
Q_ASSERT(0 != finalTexture);
|
||||
|
||||
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
|
||||
_lockedFramebufferMap[finalTexture] = scratchFramebuffer;
|
||||
_lockedFramebufferMap[finalTexture] = finalFramebuffer;
|
||||
|
||||
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
|
||||
{
|
||||
|
@ -1940,8 +2051,6 @@ void Application::focusOutEvent(QFocusEvent* event) {
|
|||
|
||||
void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
// Used by application overlay to determine how to draw cursor(s)
|
||||
_lastMouseMoveWasSimulated = deviceID > 0;
|
||||
|
||||
if (_aboutToQuit) {
|
||||
return;
|
||||
|
@ -1969,11 +2078,20 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
|
||||
auto button = event->button();
|
||||
auto buttons = event->buttons();
|
||||
// Determine if the ReticleClick Action is 1 and if so, fake include the LeftMouseButton
|
||||
if (_reticleClickPressed) {
|
||||
if (button == Qt::NoButton) {
|
||||
button = Qt::LeftButton;
|
||||
}
|
||||
buttons |= Qt::LeftButton;
|
||||
}
|
||||
|
||||
QMouseEvent mappedEvent(event->type(),
|
||||
transformedPos,
|
||||
event->screenPos(), event->button(),
|
||||
event->buttons(), event->modifiers());
|
||||
|
||||
event->screenPos(), button,
|
||||
buttons, event->modifiers());
|
||||
|
||||
getEntities()->mouseMoveEvent(&mappedEvent, deviceID);
|
||||
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
|
||||
|
@ -2877,13 +2995,6 @@ void Application::update(float deltaTime) {
|
|||
Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
|
||||
setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK));
|
||||
setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK));
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
|
||||
emulateMouse(hand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK),
|
||||
userInputMapper->getActionState(controller::Action::SHIFT), HandData::LeftHand);
|
||||
emulateMouse(hand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK),
|
||||
userInputMapper->getActionState(controller::Action::SHIFT), HandData::RightHand);
|
||||
}
|
||||
|
||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||
|
||||
|
@ -2918,6 +3029,9 @@ void Application::update(float deltaTime) {
|
|||
_physicsEngine->changeObjects(motionStates);
|
||||
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
_physicsEngine->forEachAction([&](EntityActionPointer action) {
|
||||
action->prepareForPhysicsSimulation();
|
||||
});
|
||||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_physicsEngine->stepSimulation();
|
||||
|
@ -3054,7 +3168,7 @@ int Application::sendNackPackets() {
|
|||
}
|
||||
|
||||
if (nackPacketList->getNumPackets()) {
|
||||
packetsSent += nackPacketList->getNumPackets();
|
||||
packetsSent += (int)nackPacketList->getNumPackets();
|
||||
|
||||
// send the packet list
|
||||
nodeList->sendPacketList(std::move(nackPacketList), *node);
|
||||
|
@ -4047,7 +4161,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
|
||||
ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface();
|
||||
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
|
||||
connect(scriptEngine, &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater);
|
||||
|
||||
connect(scriptEngine, &ScriptEngine::finished, this, &Application::scriptFinished, Qt::DirectConnection);
|
||||
|
||||
|
@ -4069,6 +4183,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());
|
||||
|
@ -5062,125 +5177,6 @@ void Application::setPalmData(Hand* hand, const controller::Pose& pose, float de
|
|||
});
|
||||
}
|
||||
|
||||
void Application::emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand) {
|
||||
auto palms = hand->getCopyOfPalms();
|
||||
|
||||
// Locate the palm, if it exists and is active
|
||||
PalmData* palm;
|
||||
bool foundHand = false;
|
||||
for (size_t j = 0; j < palms.size(); j++) {
|
||||
if (palms[j].whichHand() == whichHand) {
|
||||
palm = &(palms[j]);
|
||||
foundHand = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundHand || !palm->isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the mouse events
|
||||
QPoint pos;
|
||||
|
||||
|
||||
// FIXME - this mouse emulation stuff needs to be reworked for new controller input plugins
|
||||
unsigned int deviceID = whichHand == HandData::LeftHand ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT;
|
||||
int index = (int)whichHand; // FIXME - hack attack
|
||||
|
||||
if (isHMDMode()) {
|
||||
pos = getApplicationCompositor().getPalmClickLocation(palm);
|
||||
} else {
|
||||
// Get directon relative to avatar orientation
|
||||
glm::vec3 direction = glm::inverse(getMyAvatar()->getOrientation()) * palm->getFingerDirection();
|
||||
|
||||
// Get the angles, scaled between (-0.5,0.5)
|
||||
float xAngle = (atan2f(direction.z, direction.x) + (float)M_PI_2);
|
||||
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));
|
||||
auto canvasSize = getCanvasSize();
|
||||
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||
float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult();
|
||||
|
||||
pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle);
|
||||
pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle);
|
||||
|
||||
}
|
||||
|
||||
//If we are off screen then we should stop processing, and if a trigger or bumper is pressed,
|
||||
//we should unpress them.
|
||||
if (pos.x() == INT_MAX) {
|
||||
if (_oldHandLeftClick[index]) {
|
||||
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0);
|
||||
|
||||
mouseReleaseEvent(&mouseEvent, deviceID);
|
||||
|
||||
_oldHandLeftClick[index] = false;
|
||||
}
|
||||
if (_oldHandRightClick[index]) {
|
||||
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0);
|
||||
|
||||
mouseReleaseEvent(&mouseEvent, deviceID);
|
||||
|
||||
_oldHandRightClick[index] = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//If position has changed, emit a mouse move to the application
|
||||
if (pos.x() != _oldHandMouseX[index] || pos.y() != _oldHandMouseY[index]) {
|
||||
QMouseEvent mouseEvent(QEvent::MouseMove, pos, Qt::NoButton, Qt::NoButton, 0);
|
||||
|
||||
// Only send the mouse event if the opposite left button isnt held down.
|
||||
// Is this check necessary?
|
||||
if (!_oldHandLeftClick[(int)(!index)]) {
|
||||
mouseMoveEvent(&mouseEvent, deviceID);
|
||||
}
|
||||
}
|
||||
_oldHandMouseX[index] = pos.x();
|
||||
_oldHandMouseY[index] = pos.y();
|
||||
|
||||
//We need separate coordinates for clicks, since we need to check if
|
||||
//a magnification window was clicked on
|
||||
int clickX = pos.x();
|
||||
int clickY = pos.y();
|
||||
//Set pos to the new click location, which may be the same if no magnification window is open
|
||||
pos.setX(clickX);
|
||||
pos.setY(clickY);
|
||||
|
||||
// Right click
|
||||
if (shift == 1.0f && click == 1.0f) {
|
||||
if (!_oldHandRightClick[index]) {
|
||||
_oldHandRightClick[index] = true;
|
||||
|
||||
QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::RightButton, Qt::RightButton, 0);
|
||||
|
||||
mousePressEvent(&mouseEvent, deviceID);
|
||||
}
|
||||
} else if (_oldHandRightClick[index]) {
|
||||
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0);
|
||||
|
||||
mouseReleaseEvent(&mouseEvent, deviceID);
|
||||
|
||||
_oldHandRightClick[index] = false;
|
||||
}
|
||||
|
||||
// Left click
|
||||
if (shift != 1.0f && click == 1.0f) {
|
||||
if (!_oldHandLeftClick[index]) {
|
||||
_oldHandLeftClick[index] = true;
|
||||
|
||||
QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, 0);
|
||||
|
||||
mousePressEvent(&mouseEvent, deviceID);
|
||||
}
|
||||
} else if (_oldHandLeftClick[index]) {
|
||||
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0);
|
||||
|
||||
mouseReleaseEvent(&mouseEvent, deviceID);
|
||||
|
||||
_oldHandLeftClick[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::crashApplication() {
|
||||
qCDebug(interfaceapp) << "Intentionally crashed Interface";
|
||||
QObject* object = nullptr;
|
||||
|
|
|
@ -145,7 +145,6 @@ public:
|
|||
|
||||
ivec2 getMouse() const;
|
||||
ivec2 getTrueMouse() const;
|
||||
bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; }
|
||||
|
||||
FaceTracker* getActiveFaceTracker();
|
||||
FaceTracker* getSelectedFaceTracker();
|
||||
|
@ -361,7 +360,6 @@ private:
|
|||
void update(float deltaTime);
|
||||
|
||||
void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue);
|
||||
void emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand);
|
||||
|
||||
// Various helper functions called during update()
|
||||
void updateLOD();
|
||||
|
@ -476,8 +474,6 @@ private:
|
|||
|
||||
Environment _environment;
|
||||
|
||||
bool _lastMouseMoveWasSimulated;
|
||||
|
||||
QSet<int> _keysPressed;
|
||||
|
||||
bool _enableProcessOctreeThread;
|
||||
|
@ -537,14 +533,6 @@ private:
|
|||
ApplicationCompositor _compositor;
|
||||
OverlayConductor _overlayConductor;
|
||||
|
||||
|
||||
// FIXME - Hand Controller to mouse emulation helpers. This is crufty and should be moved
|
||||
// into the input plugins or something.
|
||||
int _oldHandMouseX[(int)HandData::NUMBER_OF_HANDS];
|
||||
int _oldHandMouseY[(int)HandData::NUMBER_OF_HANDS];
|
||||
bool _oldHandLeftClick[(int)HandData::NUMBER_OF_HANDS];
|
||||
bool _oldHandRightClick[(int)HandData::NUMBER_OF_HANDS];
|
||||
|
||||
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
|
||||
|
||||
EntityItemID _keyboardFocusedItem;
|
||||
|
@ -559,6 +547,8 @@ private:
|
|||
bool _inPaint = false;
|
||||
bool _isGLInitialized { false };
|
||||
bool _physicsEnabled { false };
|
||||
|
||||
bool _reticleClickPressed { false };
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -68,16 +68,22 @@ Menu::Menu() {
|
|||
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
|
||||
}
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts");
|
||||
// File Menu > Scripts section -- "Advanced" grouping
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts", UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O,
|
||||
qApp, SLOT(loadDialog()));
|
||||
qApp, SLOT(loadDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts()));
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
|
||||
qApp, SLOT(reloadAllScripts()));
|
||||
qApp, SLOT(reloadAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
||||
qApp, SLOT(toggleRunningScriptsWidget()));
|
||||
qApp, SLOT(toggleRunningScriptsWidget()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
|
||||
|
@ -112,9 +118,11 @@ Menu::Menu() {
|
|||
dialogsManager.data(),
|
||||
SLOT(toggleAddressBar()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0,
|
||||
addressManager.data(), SLOT(copyAddress()));
|
||||
addressManager.data(), SLOT(copyAddress()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0,
|
||||
addressManager.data(), SLOT(copyPath()));
|
||||
addressManager.data(), SLOT(copyPath()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Quit,
|
||||
|
@ -143,11 +151,13 @@ Menu::Menu() {
|
|||
QAction::PreferencesRole);
|
||||
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0,
|
||||
dialogsManager.data(), SLOT(editAttachments()));
|
||||
dialogsManager.data(), SLOT(editAttachments()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
MenuWrapper* toolsMenu = addMenu("Tools");
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S,
|
||||
dialogsManager.data(), SLOT(showScriptEditor()));
|
||||
dialogsManager.data(), SLOT(showScriptEditor()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
auto speechRecognizer = DependencyManager::get<SpeechRecognizer>();
|
||||
|
@ -155,13 +165,16 @@ Menu::Menu() {
|
|||
Qt::CTRL | Qt::SHIFT | Qt::Key_C,
|
||||
speechRecognizer->getEnabled(),
|
||||
speechRecognizer.data(),
|
||||
SLOT(setEnabled(bool)));
|
||||
SLOT(setEnabled(bool)),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
|
||||
#endif
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
|
||||
0, // QML Qt::Key_Backslash,
|
||||
dialogsManager.data(), SLOT(showIRCLink()));
|
||||
dialogsManager.data(), SLOT(showIRCLink()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0,
|
||||
qApp, SLOT(showFriendsWindow()));
|
||||
|
||||
|
@ -193,22 +206,26 @@ Menu::Menu() {
|
|||
MenuOption::ToolWindow,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_T,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleToolWindow()));
|
||||
SLOT(toggleToolWindow()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::Console,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
DependencyManager::get<StandAloneJSConsole>().data(),
|
||||
SLOT(toggleConsole()));
|
||||
SLOT(toggleConsole()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ResetSensors,
|
||||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp,
|
||||
SLOT(resetSensors()));
|
||||
SLOT(resetSensors()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()));
|
||||
qApp, SLOT(packageModel()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addMenu(DisplayPlugin::MENU_PATH());
|
||||
{
|
||||
|
@ -220,7 +237,7 @@ Menu::Menu() {
|
|||
MenuWrapper* avatarMenu = addMenu("Avatar");
|
||||
QObject* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
MenuWrapper* inputModeMenu = addMenu(MenuOption::InputMenu);
|
||||
MenuWrapper* inputModeMenu = addMenu(MenuOption::InputMenu, "Advanced");
|
||||
QActionGroup* inputModeGroup = new QActionGroup(inputModeMenu);
|
||||
inputModeGroup->setExclusive(false);
|
||||
|
||||
|
@ -241,17 +258,12 @@ Menu::Menu() {
|
|||
avatar,
|
||||
SLOT(resetSize()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::KeyboardMotorControl,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()));
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ScriptedMotorControl, 0, true,
|
||||
avatar, SLOT(updateMotionBehaviorFromMenu()));
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true,
|
||||
avatar, SLOT(updateMotionBehaviorFromMenu()));
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
|
||||
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
MenuWrapper* viewMenu = addMenu("View");
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
MenuWrapper* cameraModeMenu = viewMenu->addMenu("Camera Mode");
|
||||
QActionGroup* cameraModeGroup = new QActionGroup(cameraModeMenu);
|
||||
|
@ -275,30 +287,29 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
|
||||
0, //QML Qt::SHIFT | Qt::Key_H,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
|
||||
0, // QML Qt::Key_H,
|
||||
false, qApp, SLOT(cameraMenuChanged()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
|
||||
0, false, qApp, SLOT(rotationModeChanged()));
|
||||
0, false, qApp, SLOT(rotationModeChanged()),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::WorldAxes);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::WorldAxes, 0, false, NULL, NULL, UNSPECIFIED_POSITION, "Developer");
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, 0, false, NULL, NULL, UNSPECIFIED_POSITION, "Developer");
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()));
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::AudioNetworkStats, 0,
|
||||
dialogsManager.data(), SLOT(audioStatsDetails()));
|
||||
dialogsManager.data(), SLOT(audioStatsDetails()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()));
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0,
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()));
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, "Advanced Menus", 0, false, this, SLOT(toggleAdvancedMenus()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
|
||||
|
||||
MenuWrapper* developerMenu = addMenu("Developer");
|
||||
MenuWrapper* developerMenu = addMenu("Developer", "Developer");
|
||||
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
|
||||
|
@ -447,12 +458,25 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true,
|
||||
avatar, SLOT(setEnableMeshVisible(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()),
|
||||
UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ScriptedMotorControl, 0, true,
|
||||
avatar, SLOT(updateMotionBehaviorFromMenu()),
|
||||
UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableCharacterController, 0, true,
|
||||
avatar, SLOT(updateMotionBehaviorFromMenu()),
|
||||
UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
|
||||
|
||||
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::EnableHandMouseInput, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LowVelocityFilter, 0, true,
|
||||
qApp, SLOT(setLowVelocityFilter(bool)));
|
||||
|
||||
|
@ -573,6 +597,14 @@ Menu::Menu() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Menu::toggleAdvancedMenus() {
|
||||
setGroupingIsVisible("Advanced", !getGroupingIsVisible("Advanced"));
|
||||
}
|
||||
|
||||
void Menu::toggleDeveloperMenus() {
|
||||
setGroupingIsVisible("Developer", !getGroupingIsVisible("Developer"));
|
||||
}
|
||||
|
||||
void Menu::loadSettings() {
|
||||
scanMenuBar(&Menu::loadAction);
|
||||
}
|
||||
|
@ -610,23 +642,36 @@ void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& setting
|
|||
settings.endGroup();
|
||||
}
|
||||
|
||||
void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, int menuItemLocation) {
|
||||
void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
|
||||
int menuItemLocation, const QString& grouping) {
|
||||
QAction* actionBefore = NULL;
|
||||
QAction* separator;
|
||||
QAction* separatorText;
|
||||
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
actionBefore = destinationMenu->actions()[menuItemLocation];
|
||||
}
|
||||
if (actionBefore) {
|
||||
QAction* separator = new QAction("",destinationMenu);
|
||||
separator = new QAction("",destinationMenu);
|
||||
destinationMenu->insertAction(actionBefore, separator);
|
||||
separator->setSeparator(true);
|
||||
|
||||
QAction* separatorText = new QAction(actionName,destinationMenu);
|
||||
separatorText = new QAction(actionName,destinationMenu);
|
||||
separatorText->setEnabled(false);
|
||||
destinationMenu->insertAction(actionBefore, separatorText);
|
||||
|
||||
} else {
|
||||
destinationMenu->addSeparator();
|
||||
(destinationMenu->addAction(actionName))->setEnabled(false);
|
||||
separator = destinationMenu->addSeparator();
|
||||
separatorText = destinationMenu->addAction(actionName);
|
||||
separatorText->setEnabled(false);
|
||||
}
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << separator;
|
||||
_groupingActions[grouping] << separatorText;
|
||||
bool isVisible = getGroupingIsVisible(grouping);
|
||||
separator->setVisible(isVisible);
|
||||
separatorText->setVisible(isVisible);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -636,7 +681,8 @@ QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
|||
const QObject* receiver,
|
||||
const char* member,
|
||||
QAction::MenuRole role,
|
||||
int menuItemLocation) {
|
||||
int menuItemLocation,
|
||||
const QString& grouping) {
|
||||
QAction* action = NULL;
|
||||
QAction* actionBefore = NULL;
|
||||
|
||||
|
@ -664,6 +710,11 @@ QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
|||
|
||||
_actionHash.insert(actionName, action);
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
|
@ -672,7 +723,8 @@ QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
|||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
QAction::MenuRole role,
|
||||
int menuItemLocation) {
|
||||
int menuItemLocation,
|
||||
const QString& grouping) {
|
||||
QAction* actionBefore = NULL;
|
||||
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
|
@ -699,6 +751,11 @@ QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
|||
|
||||
_actionHash.insert(action->text(), action);
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
|
@ -708,19 +765,29 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMe
|
|||
const bool checked,
|
||||
const QObject* receiver,
|
||||
const char* member,
|
||||
int menuItemLocation) {
|
||||
int menuItemLocation,
|
||||
const QString& grouping) {
|
||||
|
||||
QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member,
|
||||
QAction::NoRole, menuItemLocation);
|
||||
action->setCheckable(true);
|
||||
action->setChecked(checked);
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
void Menu::removeAction(MenuWrapper* menu, const QString& actionName) {
|
||||
menu->removeAction(_actionHash.value(actionName));
|
||||
auto action = _actionHash.value(actionName);
|
||||
menu->removeAction(action);
|
||||
_actionHash.remove(actionName);
|
||||
for (auto& grouping : _groupingActions) {
|
||||
grouping.remove(action);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
||||
|
@ -850,7 +917,7 @@ int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPositi
|
|||
}
|
||||
|
||||
|
||||
MenuWrapper* Menu::addMenu(const QString& menuName) {
|
||||
MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
MenuWrapper* addTo = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
|
@ -866,6 +933,14 @@ MenuWrapper* Menu::addMenu(const QString& menuName) {
|
|||
addTo = menu;
|
||||
}
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
auto action = getMenuAction(menuName);
|
||||
if (action) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
return menu;
|
||||
}
|
||||
|
@ -897,7 +972,7 @@ bool Menu::menuExists(const QString& menuName) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Menu::addSeparator(const QString& menuName, const QString& separatorName) {
|
||||
void Menu::addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping) {
|
||||
MenuWrapper* menuObj = getMenu(menuName);
|
||||
if (menuObj) {
|
||||
addDisabledActionAndSeparator(menuObj, separatorName);
|
||||
|
@ -952,15 +1027,16 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
|
|||
|
||||
QAction* menuItemAction = NULL;
|
||||
if (properties.isSeparator) {
|
||||
addDisabledActionAndSeparator(menuObj, properties.menuItemName, requestedPosition);
|
||||
addDisabledActionAndSeparator(menuObj, properties.menuItemName, requestedPosition, properties.grouping);
|
||||
} else if (properties.isCheckable) {
|
||||
menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName,
|
||||
properties.shortcutKeySequence, properties.isChecked,
|
||||
MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), requestedPosition);
|
||||
MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()),
|
||||
requestedPosition, properties.grouping);
|
||||
} else {
|
||||
menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence,
|
||||
MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()),
|
||||
QAction::NoRole, requestedPosition);
|
||||
QAction::NoRole, requestedPosition, properties.grouping);
|
||||
}
|
||||
if (shortcut && menuItemAction) {
|
||||
connect(shortcut, SIGNAL(activated()), menuItemAction, SLOT(trigger()));
|
||||
|
@ -975,7 +1051,7 @@ void Menu::removeMenuItem(const QString& menu, const QString& menuitem) {
|
|||
removeAction(menuObj, menuitem);
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
|
||||
QAction* menuItemAction = _actionHash.value(menuitem);
|
||||
|
@ -983,7 +1059,31 @@ bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
|
|||
return (getMenu(menu) != NULL);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
bool Menu::getGroupingIsVisible(const QString& grouping) {
|
||||
if (grouping.isEmpty() || grouping.isNull()) {
|
||||
return true;
|
||||
}
|
||||
if (_groupingVisible.contains(grouping)) {
|
||||
return _groupingVisible[grouping];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::setGroupingIsVisible(const QString& grouping, bool isVisible) {
|
||||
// NOTE: Default grouping always visible
|
||||
if (grouping.isEmpty() || grouping.isNull()) {
|
||||
return;
|
||||
}
|
||||
_groupingVisible[grouping] = isVisible;
|
||||
|
||||
for (auto action: _groupingActions[grouping]) {
|
||||
action->setVisible(isVisible);
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
|
||||
|
||||
MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) {
|
||||
|
@ -1005,8 +1105,8 @@ void MenuWrapper::setEnabled(bool enabled) {
|
|||
_realMenu->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void MenuWrapper::addSeparator() {
|
||||
_realMenu->addSeparator();
|
||||
QAction* MenuWrapper::addSeparator() {
|
||||
return _realMenu->addSeparator();
|
||||
}
|
||||
|
||||
void MenuWrapper::addAction(QAction* action) {
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
QList<QAction*> actions();
|
||||
MenuWrapper* addMenu(const QString& menuName);
|
||||
void setEnabled(bool enabled = true);
|
||||
void addSeparator();
|
||||
QAction* addSeparator();
|
||||
void addAction(QAction* action);
|
||||
|
||||
QAction* addAction(const QString& menuName);
|
||||
|
@ -74,28 +74,33 @@ public:
|
|||
const QObject* receiver = NULL,
|
||||
const char* member = NULL,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
QAction* action,
|
||||
const QString& actionName = QString(),
|
||||
const QKeySequence& shortcut = 0,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
const bool checked = false,
|
||||
const QObject* receiver = NULL,
|
||||
const char* member = NULL,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
void removeAction(MenuWrapper* menu, const QString& actionName);
|
||||
|
||||
public slots:
|
||||
MenuWrapper* addMenu(const QString& menuName);
|
||||
MenuWrapper* addMenu(const QString& menuName, const QString& grouping = QString());
|
||||
void removeMenu(const QString& menuName);
|
||||
bool menuExists(const QString& menuName);
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
void addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping = QString());
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
void addMenuItem(const MenuItemProperties& properties);
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
|
@ -103,6 +108,12 @@ public slots:
|
|||
bool isOptionChecked(const QString& menuOption) const;
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
bool getGroupingIsVisible(const QString& grouping);
|
||||
void setGroupingIsVisible(const QString& grouping, bool isVisible); /// NOTE: the "" grouping is always visible
|
||||
|
||||
void toggleDeveloperMenus();
|
||||
void toggleAdvancedMenus();
|
||||
|
||||
private:
|
||||
typedef void(*settingsAction)(Settings&, QAction&);
|
||||
static void loadAction(Settings& settings, QAction& action);
|
||||
|
@ -111,8 +122,10 @@ private:
|
|||
void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings);
|
||||
|
||||
/// helper method to have separators with labels that are also compatible with OS X
|
||||
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
|
||||
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
|
||||
|
@ -123,6 +136,10 @@ private:
|
|||
int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition);
|
||||
|
||||
QHash<QString, QAction*> _actionHash;
|
||||
|
||||
bool isValidGrouping(const QString& grouping) const { return grouping == "Advanced" || grouping == "Developer"; }
|
||||
QHash<QString, bool> _groupingVisible;
|
||||
QHash<QString, QSet<QAction*>> _groupingActions;
|
||||
};
|
||||
|
||||
namespace MenuOption {
|
||||
|
@ -151,7 +168,6 @@ namespace MenuOption {
|
|||
const QString Back = "Back";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString BinaryEyelidControl = "Binary Eyelid Control";
|
||||
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
|
||||
const QString BookmarkLocation = "Bookmark Location";
|
||||
const QString Bookmarks = "Bookmarks";
|
||||
const QString CachesSize = "RAM Caches Size";
|
||||
|
@ -203,7 +219,6 @@ namespace MenuOption {
|
|||
const QString FrameTimer = "Show Timer";
|
||||
const QString FullscreenMirror = "Fullscreen Mirror";
|
||||
const QString GlowWhenSpeaking = "Glow When Speaking";
|
||||
const QString EnableHandMouseInput = "Enable Hand Controller Mouse Input";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString IndependentMode = "Independent Mode";
|
||||
const QString InputMenu = "Avatar>Input Devices";
|
||||
|
|
|
@ -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<HeadData*>(new Head(this));
|
||||
_handData = static_cast<HandData*>(new Hand(this));
|
||||
|
@ -143,15 +144,35 @@ AABox Avatar::getBounds() const {
|
|||
|
||||
float Avatar::getLODDistance() const {
|
||||
return DependencyManager::get<LODManager>()->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<AvatarManager>()->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<AvatarManager>()->getRenderDistanceControllerIsLogging();
|
||||
float renderDistance = DependencyManager::get<AvatarManager>()->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>(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<AvatarManager>()->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<DeferredLightingEffect>()->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,45 +511,12 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
eyeDiameter = DEFAULT_EYE_DIAMETER;
|
||||
}
|
||||
DependencyManager::get<DeferredLightingEffect>()->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));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// quick check before falling into the code below:
|
||||
// (a 10 degree breadth of an almost 2 meter avatar kicks in at about 12m)
|
||||
const float MIN_VOICE_SPHERE_DISTANCE = 12.0f;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::BlueSpeechSphere)
|
||||
&& distanceToTarget > MIN_VOICE_SPHERE_DISTANCE) {
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderVoiceSphere");
|
||||
|
||||
// render voice intensity sphere for avatars that are farther away
|
||||
const float MAX_SPHERE_ANGLE = 10.0f * RADIANS_PER_DEGREE;
|
||||
const float MIN_SPHERE_ANGLE = 0.5f * RADIANS_PER_DEGREE;
|
||||
const float MIN_SPHERE_SIZE = 0.01f;
|
||||
const float SPHERE_LOUDNESS_SCALING = 0.0005f;
|
||||
const float SPHERE_COLOR[] = { 0.5f, 0.8f, 0.8f };
|
||||
float height = getSkeletonHeight();
|
||||
glm::vec3 delta = height * (getHead()->getCameraOrientation() * IDENTITY_UP) / 2.0f;
|
||||
float angle = abs(angleBetween(toTarget + delta, toTarget - delta));
|
||||
float sphereRadius = getHead()->getAverageLoudness() * SPHERE_LOUDNESS_SCALING;
|
||||
|
||||
if (renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && (sphereRadius > MIN_SPHERE_SIZE) &&
|
||||
(angle < MAX_SPHERE_ANGLE) && (angle > MIN_SPHERE_ANGLE)) {
|
||||
batch.setModelTransform(Transform());
|
||||
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(getPosition());
|
||||
transform.setScale(height);
|
||||
transform.postScale(sphereRadius);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch,
|
||||
transform,
|
||||
glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const float DISPLAYNAME_DISTANCE = 20.0f;
|
||||
|
@ -623,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);
|
||||
|
@ -680,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
|
||||
|
@ -829,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;
|
||||
|
@ -868,6 +857,18 @@ glm::vec3 Avatar::getJointTranslation(int index) const {
|
|||
return translation;
|
||||
}
|
||||
|
||||
glm::quat Avatar::getDefaultJointRotation(int index) const {
|
||||
glm::quat rotation;
|
||||
_skeletonModel.getRelativeDefaultJointRotation(index, rotation);
|
||||
return rotation;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getDefaultJointTranslation(int index) const {
|
||||
glm::vec3 translation;
|
||||
_skeletonModel.getRelativeDefaultJointTranslation(index, translation);
|
||||
return translation;
|
||||
}
|
||||
|
||||
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
||||
glm::quat rotation;
|
||||
_skeletonModel.getAbsoluteJointRotationInRigFrame(index, rotation);
|
||||
|
@ -926,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) {
|
||||
|
@ -966,7 +967,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1054,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;
|
||||
|
@ -1074,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;
|
||||
}
|
||||
}
|
||||
|
@ -1117,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<AvatarManager>()->updateAvatarPhysicsShape(this);
|
||||
void Avatar::rebuildCollisionShape() {
|
||||
if (_motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_SHAPE);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getLeftPalmPosition() {
|
||||
|
|
|
@ -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<const Head*>(_headData); }
|
||||
Head* getHead() { return static_cast<Head*>(_headData); }
|
||||
Hand* getHand() { return static_cast<Hand*>(_handData); }
|
||||
|
@ -100,25 +99,28 @@ public:
|
|||
/// Returns the distance to use as a LOD parameter.
|
||||
float getLODDistance() const;
|
||||
|
||||
virtual bool isMyAvatar() const { return false; }
|
||||
virtual bool isMyAvatar() const override { return false; }
|
||||
|
||||
virtual QVector<glm::quat> getJointRotations() const;
|
||||
virtual glm::quat getJointRotation(int index) const;
|
||||
virtual glm::vec3 getJointTranslation(int index) const;
|
||||
virtual int getJointIndex(const QString& name) const;
|
||||
virtual QStringList getJointNames() const;
|
||||
virtual QVector<glm::quat> getJointRotations() const override;
|
||||
virtual glm::quat getJointRotation(int index) const override;
|
||||
virtual glm::vec3 getJointTranslation(int index) const override;
|
||||
virtual int getJointIndex(const QString& name) const override;
|
||||
virtual QStringList getJointNames() const override;
|
||||
|
||||
Q_INVOKABLE virtual glm::quat getDefaultJointRotation(int index) const;
|
||||
Q_INVOKABLE virtual glm::vec3 getDefaultJointTranslation(int index) const;
|
||||
|
||||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
|
||||
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
||||
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
virtual void setBillboard(const QByteArray& billboard);
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL) override;
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
|
||||
virtual void setBillboard(const QByteArray& billboard) override;
|
||||
|
||||
void setShowDisplayName(bool showDisplayName);
|
||||
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer);
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
|
||||
|
||||
static void renderJointConnectingCone( gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2,
|
||||
float radius1, float radius2, const glm::vec4& color);
|
||||
|
@ -144,18 +146,17 @@ public:
|
|||
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
|
||||
|
||||
void slamPosition(const glm::vec3& position);
|
||||
virtual void updateAttitude() { _skeletonModel.updateAttitude(); }
|
||||
virtual void updateAttitude() override { _skeletonModel.updateAttitude(); }
|
||||
|
||||
// Call this when updating Avatar position with a delta. This will allow us to
|
||||
// _accurately_ measure position changes and compute the resulting velocity
|
||||
// (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;
|
||||
|
@ -170,6 +171,10 @@ public slots:
|
|||
glm::quat getRightPalmRotation();
|
||||
|
||||
protected:
|
||||
friend class AvatarManager;
|
||||
|
||||
void setMotionState(AvatarMotionState* motionState);
|
||||
|
||||
SkeletonModel _skeletonModel;
|
||||
glm::vec3 _skeletonOffset;
|
||||
QVector<Model*> _attachmentModels;
|
||||
|
@ -196,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;
|
||||
|
@ -217,7 +223,7 @@ protected:
|
|||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
|
||||
virtual void fixupModelsInScene();
|
||||
|
||||
virtual void updateJointMappings();
|
||||
virtual void updateJointMappings() override;
|
||||
|
||||
render::ItemID _renderItemID;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QVariantGLM.h>
|
||||
|
||||
#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<AvatarManager>()->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<AvatarManager>();
|
||||
auto holdingAvatar = std::static_pointer_cast<Avatar>(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<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) {
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
|
||||
|
@ -40,11 +99,11 @@ std::shared_ptr<Avatar> 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<Avatar> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Avatar> 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
|
||||
|
|
|
@ -203,7 +203,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
while (fadingIterator != _avatarFades.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*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<Node>& mixerWeakPointer) {
|
||||
auto newAvatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer);
|
||||
auto rawRenderableAvatar = std::static_pointer_cast<Avatar>(newAvatar);
|
||||
|
||||
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->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<Avatar>(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);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
glm::vec3 color;
|
||||
glm::vec3 direction;
|
||||
};
|
||||
|
||||
|
||||
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
|
||||
Q_INVOKABLE QVector<AvatarManager::LocalLight> 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<Node>& mixerWeakPointer);
|
||||
void removeAvatarMotionState(AvatarSharedPointer avatar);
|
||||
|
||||
|
||||
virtual void removeAvatar(const QUuid& sessionUUID);
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar);
|
||||
|
||||
|
||||
QVector<AvatarSharedPointer> _avatarFades;
|
||||
std::shared_ptr<MyAvatar> _myAvatar;
|
||||
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
|
||||
|
||||
|
||||
QVector<AvatarManager::LocalLight> _localLights;
|
||||
|
||||
bool _shouldShowReceiveStats = false;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<DeferredLightingEffect>()->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<DeferredLightingEffect>()->renderSolidSphereInstance(batch, transform, greenColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const float AXIS_RADIUS = 0.1f * SPHERE_RADIUS;
|
||||
const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -441,14 +438,6 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
|
|||
estimatedPosition = tracker->getHeadTranslation();
|
||||
_trackedHeadPosition = estimatedPosition;
|
||||
estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation()));
|
||||
if (qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR) {
|
||||
// Invert yaw and roll when in mirror mode
|
||||
// NOTE: this is kinda a hack, it's the same hack we use to make the head tilt. But it's not really a mirror
|
||||
// it just makes you feel like you're looking in a mirror because the body movements of the avatar appear to
|
||||
// match your body movements.
|
||||
YAW(estimatedRotation) *= -1.0f;
|
||||
ROLL(estimatedRotation) *= -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the body if the head is turned beyond the screen
|
||||
|
@ -489,14 +478,6 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
|
|||
const float TORSO_LENGTH = 0.5f;
|
||||
glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.0f);
|
||||
|
||||
// Invert left/right lean when in mirror mode
|
||||
// NOTE: this is kinda a hack, it's the same hack we use to make the head tilt. But it's not really a mirror
|
||||
// it just makes you feel like you're looking in a mirror because the body movements of the avatar appear to
|
||||
// match your body movements.
|
||||
if ((inHmd || inFacetracker) && qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR) {
|
||||
relativePosition.x = -relativePosition.x;
|
||||
}
|
||||
|
||||
const float MAX_LEAN = 45.0f;
|
||||
head->setLeanSideways(glm::clamp(glm::degrees(atanf(relativePosition.x * _leanScale / TORSO_LENGTH)),
|
||||
-MAX_LEAN, MAX_LEAN));
|
||||
|
@ -528,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
|
||||
|
@ -555,7 +536,7 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
if (!_shouldRender) {
|
||||
return; // exit early
|
||||
}
|
||||
|
||||
|
||||
Avatar::render(renderArgs, cameraPosition);
|
||||
}
|
||||
|
||||
|
@ -697,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();
|
||||
|
@ -818,14 +799,14 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
const float GREATEST_LOOKING_AT_DISTANCE = 10.0f;
|
||||
|
||||
AvatarHash hash = DependencyManager::get<AvatarManager>()->getHashCopy();
|
||||
|
||||
|
||||
foreach (const AvatarSharedPointer& avatarPointer, hash) {
|
||||
auto avatar = static_pointer_cast<Avatar>(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;
|
||||
|
@ -966,7 +947,11 @@ void MyAvatar::clearJointData(int index) {
|
|||
}
|
||||
|
||||
void MyAvatar::clearJointsData() {
|
||||
//clearJointAnimationPriorities();
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "clearJointsData");
|
||||
return;
|
||||
}
|
||||
_rig->clearJointStates();
|
||||
}
|
||||
|
||||
void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) {
|
||||
|
@ -1048,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() {
|
||||
|
@ -1189,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
|
||||
|
@ -1343,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 {
|
||||
|
@ -1401,12 +1387,6 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
// ... so they need to be converted to degrees before we do math...
|
||||
glm::vec3 euler = glm::eulerAngles(localOrientation) * DEGREES_PER_RADIAN;
|
||||
|
||||
//Invert yaw and roll when in mirror mode
|
||||
if (qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR) {
|
||||
YAW(euler) *= -1.0f;
|
||||
ROLL(euler) *= -1.0f;
|
||||
}
|
||||
|
||||
Head* head = getHead();
|
||||
head->setBaseYaw(YAW(euler));
|
||||
head->setBasePitch(PITCH(euler));
|
||||
|
@ -1473,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;
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<GeometryCache>();
|
||||
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -20,7 +20,7 @@ DeviceTracker::SingletonData::~SingletonData() {
|
|||
}
|
||||
|
||||
int DeviceTracker::getNumDevices() {
|
||||
return Singleton::get()->_devicesMap.size();
|
||||
return (int)Singleton::get()->_devicesMap.size();
|
||||
}
|
||||
|
||||
DeviceTracker::ID DeviceTracker::getDeviceID(const Name& name) {
|
||||
|
@ -58,7 +58,7 @@ DeviceTracker::ID DeviceTracker::registerDevice(const Name& name, DeviceTracker*
|
|||
}
|
||||
|
||||
// Good to register the device
|
||||
deviceID = Singleton::get()->_devicesVector.size();
|
||||
deviceID = (ID)Singleton::get()->_devicesVector.size();
|
||||
Singleton::get()->_devicesMap.insert(Map::value_type(name, deviceID));
|
||||
Singleton::get()->_devicesVector.push_back(device);
|
||||
device->assignIDAndName(deviceID, name);
|
||||
|
|
|
@ -255,7 +255,7 @@ void Faceshift::receive(const QByteArray& buffer) {
|
|||
}
|
||||
case fsMsg::MSG_OUT_BLENDSHAPE_NAMES: {
|
||||
const vector<string>& names = static_pointer_cast<fsMsgBlendshapeNames>(msg)->blendshape_names();
|
||||
for (size_t i = 0; i < names.size(); i++) {
|
||||
for (int i = 0; i < (int)names.size(); i++) {
|
||||
if (names[i] == "EyeBlink_L") {
|
||||
_leftBlinkIndex = i;
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ MotionTracker::Index MotionTracker::addJoint(const Semantic& semantic, Index par
|
|||
|
||||
|
||||
// All good then allocate the joint
|
||||
Index newIndex = _jointsArray.size();
|
||||
Index newIndex = (Index)_jointsArray.size();
|
||||
_jointsArray.push_back(JointTracker(semantic, parent));
|
||||
_jointsMap.insert(JointTracker::Map::value_type(semantic, newIndex));
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ public:
|
|||
|
||||
virtual bool isConnected() const;
|
||||
|
||||
Index numJointTrackers() const { return _jointsArray.size(); }
|
||||
Index numJointTrackers() const { return (Index)_jointsArray.size(); }
|
||||
|
||||
/// Access a Joint from it's index.
|
||||
/// Index 0 is always the "Root".
|
||||
|
|
|
@ -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;
|
||||
|
@ -115,10 +114,6 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r,
|
|||
ApplicationCompositor::ApplicationCompositor() :
|
||||
_alphaPropertyAnimation(new QPropertyAnimation(this, "alpha"))
|
||||
{
|
||||
memset(_reticleActive, 0, sizeof(_reticleActive));
|
||||
memset(_magActive, 0, sizeof(_reticleActive));
|
||||
memset(_magSizeMult, 0, sizeof(_magSizeMult));
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
_reticleQuad = geometryCache->allocateID();
|
||||
|
@ -219,9 +214,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
|||
batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0));
|
||||
geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));
|
||||
|
||||
// Doesn't actually render
|
||||
renderPointers(batch);
|
||||
|
||||
//draw the mouse pointer
|
||||
// Get the mouse coordinates and convert to NDC [-1, 1]
|
||||
vec2 canvasSize = qApp->getCanvasSize();
|
||||
|
@ -306,8 +298,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
|||
}
|
||||
#endif
|
||||
|
||||
// Doesn't actually render
|
||||
renderPointers(batch);
|
||||
|
||||
vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);
|
||||
|
||||
bindCursorTexture(batch);
|
||||
|
@ -316,34 +307,13 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
|||
glm::mat4 overlayXfm;
|
||||
_modelTransform.getMatrix(overlayXfm);
|
||||
|
||||
// Only render the hand pointers if the EnableHandMouseInput is enabled
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
auto palms = myAvatar->getHand()->getCopyOfPalms();
|
||||
for (const auto& palm : palms) {
|
||||
if (palm.isActive()) {
|
||||
glm::vec2 polar = getPolarCoordinates(palm);
|
||||
// Convert to quaternion
|
||||
mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||
mat4 reticleXfm = overlayXfm * pointerXfm;
|
||||
reticleXfm = glm::scale(reticleXfm, reticleScale);
|
||||
batch.setModelTransform(reticleXfm);
|
||||
// Render reticle at location
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Mouse Pointer
|
||||
if (_reticleActive[MOUSE]) {
|
||||
glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),
|
||||
_reticlePosition[MOUSE].y()));
|
||||
mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||
mat4 reticleXfm = overlayXfm * pointerXfm;
|
||||
reticleXfm = glm::scale(reticleXfm, reticleScale);
|
||||
batch.setModelTransform(reticleXfm);
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
|
||||
}
|
||||
glm::vec2 projection = screenToSpherical(qApp->getTrueMouse());
|
||||
mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||
mat4 reticleXfm = overlayXfm * pointerXfm;
|
||||
reticleXfm = glm::scale(reticleXfm, reticleScale);
|
||||
batch.setModelTransform(reticleXfm);
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -415,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;
|
||||
}
|
||||
|
@ -423,124 +393,6 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi
|
|||
return false;
|
||||
}
|
||||
|
||||
//Renders optional pointers
|
||||
void ApplicationCompositor::renderPointers(gpu::Batch& batch) {
|
||||
if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated()) {
|
||||
//If we are in oculus, render reticle later
|
||||
auto trueMouse = qApp->getTrueMouse();
|
||||
trueMouse /= qApp->getCanvasSize();
|
||||
QPoint position = QPoint(qApp->getTrueMouse().x, qApp->getTrueMouse().y);
|
||||
_reticlePosition[MOUSE] = position;
|
||||
_reticleActive[MOUSE] = true;
|
||||
_magActive[MOUSE] = _magnifier;
|
||||
_reticleActive[LEFT_CONTROLLER] = false;
|
||||
_reticleActive[RIGHT_CONTROLLER] = false;
|
||||
} else if (qApp->getLastMouseMoveWasSimulated()
|
||||
&& Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
|
||||
//only render controller pointer if we aren't already rendering a mouse pointer
|
||||
_reticleActive[MOUSE] = false;
|
||||
_magActive[MOUSE] = false;
|
||||
renderControllerPointers(batch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FIXME - this is old code that likely needs to be removed and/or reworked to support the new input control model
|
||||
void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
//Static variables used for storing controller state
|
||||
static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL };
|
||||
static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false };
|
||||
static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false };
|
||||
|
||||
const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
|
||||
auto palms = handData->getCopyOfPalms();
|
||||
|
||||
for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {
|
||||
const int index = palmIndex - 1;
|
||||
|
||||
const PalmData* palmData = NULL;
|
||||
|
||||
if (palmIndex >= palms.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (palms[palmIndex].isActive()) {
|
||||
palmData = &palms[palmIndex];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isPressed[index]) {
|
||||
isPressed[index] = false;
|
||||
//If the button was only pressed for < 250 ms
|
||||
//then disable it.
|
||||
|
||||
const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS;
|
||||
if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) {
|
||||
_magActive[index] = !stateWhenPressed[index];
|
||||
}
|
||||
}
|
||||
|
||||
//if we have the oculus, we should make the cursor smaller since it will be
|
||||
//magnified
|
||||
if (qApp->isHMDMode()) {
|
||||
|
||||
QPoint point = getPalmClickLocation(palmData);
|
||||
|
||||
_reticlePosition[index] = point;
|
||||
|
||||
//When button 2 is pressed we drag the mag window
|
||||
if (isPressed[index]) {
|
||||
_magActive[index] = true;
|
||||
}
|
||||
|
||||
// If oculus is enabled, we draw the crosshairs later
|
||||
continue;
|
||||
}
|
||||
|
||||
auto canvasSize = qApp->getCanvasSize();
|
||||
int mouseX, mouseY;
|
||||
|
||||
// Get directon relative to avatar orientation
|
||||
glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection();
|
||||
|
||||
// Get the angles, scaled between (-0.5,0.5)
|
||||
float xAngle = (atan2f(direction.z, direction.x) + PI_OVER_TWO);
|
||||
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO));
|
||||
|
||||
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||
float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult();
|
||||
|
||||
mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle);
|
||||
mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle);
|
||||
|
||||
//If the cursor is out of the screen then don't render it
|
||||
if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) {
|
||||
_reticleActive[index] = false;
|
||||
continue;
|
||||
}
|
||||
_reticleActive[index] = true;
|
||||
|
||||
|
||||
const float reticleSize = 40.0f;
|
||||
|
||||
mouseX -= reticleSize / 2.0f;
|
||||
mouseY += reticleSize / 2.0f;
|
||||
|
||||
|
||||
glm::vec2 topLeft(mouseX, mouseY);
|
||||
glm::vec2 bottomRight(mouseX + reticleSize, mouseY - reticleSize);
|
||||
glm::vec2 texCoordTopLeft(0.0f, 0.0f);
|
||||
glm::vec2 texCoordBottomRight(1.0f, 1.0f);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCompositor::buildHemiVertices(
|
||||
const float fov, const float aspectRatio, const int slices, const int stacks) {
|
||||
static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
|
||||
|
|
|
@ -92,9 +92,6 @@ private:
|
|||
void drawSphereSection(gpu::Batch& batch);
|
||||
void updateTooltips();
|
||||
|
||||
void renderPointers(gpu::Batch& batch);
|
||||
void renderControllerPointers(gpu::Batch& batch);
|
||||
|
||||
vec2 getPolarCoordinates(const PalmData& palm) const;
|
||||
|
||||
// Support for hovering and tooltips
|
||||
|
@ -109,11 +106,6 @@ private:
|
|||
float _textureAspectRatio{ 1.0f };
|
||||
int _hemiVerticesID{ GeometryCache::UNKNOWN_ID };
|
||||
|
||||
enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES };
|
||||
bool _reticleActive[NUMBER_OF_RETICLES];
|
||||
QPoint _reticlePosition[NUMBER_OF_RETICLES];
|
||||
bool _magActive[NUMBER_OF_RETICLES];
|
||||
float _magSizeMult[NUMBER_OF_RETICLES];
|
||||
bool _magnifier{ true };
|
||||
|
||||
float _alpha{ 1.0f };
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
|
||||
STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange());
|
||||
STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating
|
||||
STAT_UPDATE(serverCount, nodeList->size());
|
||||
STAT_UPDATE(serverCount, (int)nodeList->size());
|
||||
STAT_UPDATE(renderrate, (int)qApp->getFps());
|
||||
if (qApp->getActiveDisplayPlugin()) {
|
||||
STAT_UPDATE(presentrate, (int)round(qApp->getActiveDisplayPlugin()->presentRate()));
|
||||
|
@ -270,7 +270,7 @@ void Stats::updateStats(bool force) {
|
|||
if (voxelPacketsToProcess == 0) {
|
||||
_resetRecentMaxPacketsSoon = true;
|
||||
} else if (voxelPacketsToProcess > _recentMaxPackets) {
|
||||
_recentMaxPackets = voxelPacketsToProcess;
|
||||
_recentMaxPackets = (int)voxelPacketsToProcess;
|
||||
}
|
||||
|
||||
// Server Octree Elements
|
||||
|
@ -344,18 +344,18 @@ void Stats::setRenderDetails(const RenderDetails& details) {
|
|||
STAT_UPDATE(triangles, details._trianglesRendered);
|
||||
STAT_UPDATE(materialSwitches, details._materialSwitches);
|
||||
if (_expanded) {
|
||||
STAT_UPDATE(opaqueConsidered, details._opaque._considered);
|
||||
STAT_UPDATE(opaqueConsidered, (int)details._opaque._considered);
|
||||
STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView);
|
||||
STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall);
|
||||
STAT_UPDATE(opaqueRendered, details._opaque._rendered);
|
||||
STAT_UPDATE(translucentConsidered, details._translucent._considered);
|
||||
STAT_UPDATE(opaqueRendered, (int)details._opaque._rendered);
|
||||
STAT_UPDATE(translucentConsidered, (int)details._translucent._considered);
|
||||
STAT_UPDATE(translucentOutOfView, details._translucent._outOfView);
|
||||
STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall);
|
||||
STAT_UPDATE(translucentRendered, details._translucent._rendered);
|
||||
STAT_UPDATE(otherConsidered, details._other._considered);
|
||||
STAT_UPDATE(translucentRendered, (int)details._translucent._rendered);
|
||||
STAT_UPDATE(otherConsidered, (int)details._other._considered);
|
||||
STAT_UPDATE(otherOutOfView, details._other._outOfView);
|
||||
STAT_UPDATE(otherTooSmall, details._other._tooSmall);
|
||||
STAT_UPDATE(otherRendered, details._other._rendered);
|
||||
STAT_UPDATE(otherRendered, (int)details._other._rendered);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<DeferredLightingEffect>()->bindSimpleProgram(*batch, true, false, false, true);
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch, true, false, _emissive, true);
|
||||
DependencyManager::get<GeometryCache>()->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);
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
private:
|
||||
QString _url;
|
||||
NetworkTexturePointer _texture;
|
||||
bool _emissive;
|
||||
|
||||
QRect _fromImage; // where from in the image to sample
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -46,10 +46,10 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
|
|||
evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
||||
} else {
|
||||
|
||||
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
|
||||
size_t prevPoseIndex = glm::floor(clampedAlpha);
|
||||
size_t nextPoseIndex = glm::ceil(clampedAlpha);
|
||||
float alpha = glm::fract(clampedAlpha);
|
||||
auto clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
|
||||
auto prevPoseIndex = glm::floor(clampedAlpha);
|
||||
auto nextPoseIndex = glm::ceil(clampedAlpha);
|
||||
auto alpha = glm::fract(clampedAlpha);
|
||||
float prevDeltaTime, nextDeltaTime;
|
||||
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
||||
evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
||||
|
@ -122,5 +122,5 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) {
|
|||
auto clipNode = std::dynamic_pointer_cast<AnimClip>(_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);
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt,
|
|||
|
||||
// It can be quite possible for the user to set _startFrame and _endFrame to
|
||||
// values before or past valid ranges. We clamp the frames here.
|
||||
int frameCount = _anim.size();
|
||||
int frameCount = (int)_anim.size();
|
||||
prevIndex = std::min(std::max(0, prevIndex), frameCount - 1);
|
||||
nextIndex = std::min(std::max(0, nextIndex), frameCount - 1);
|
||||
|
||||
|
@ -92,8 +92,8 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
// by matching joints with the same name.
|
||||
const FBXGeometry& geom = _networkAnim->getGeometry();
|
||||
AnimSkeleton animSkeleton(geom);
|
||||
const int animJointCount = animSkeleton.getNumJoints();
|
||||
const int skeletonJointCount = _skeleton->getNumJoints();
|
||||
const auto animJointCount = animSkeleton.getNumJoints();
|
||||
const auto skeletonJointCount = _skeleton->getNumJoints();
|
||||
std::vector<int> jointMap;
|
||||
jointMap.reserve(animJointCount);
|
||||
for (int i = 0; i < animJointCount; i++) {
|
||||
|
|
|
@ -114,7 +114,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
|
|||
}
|
||||
|
||||
if (removeUnfoundJoints) {
|
||||
int numVars = _targetVarVec.size();
|
||||
int numVars = (int)_targetVarVec.size();
|
||||
int i = 0;
|
||||
while (i < numVars) {
|
||||
if (_targetVarVec[i].jointIndex == -1) {
|
||||
|
@ -145,7 +145,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
int numLoops = 0;
|
||||
const int MAX_IK_LOOPS = 4;
|
||||
do {
|
||||
int lowestMovedIndex = _relativePoses.size();
|
||||
int lowestMovedIndex = (int)_relativePoses.size();
|
||||
for (auto& target: targets) {
|
||||
IKTarget::Type targetType = target.getType();
|
||||
if (targetType == IKTarget::Type::RotationOnly) {
|
||||
|
@ -283,8 +283,8 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
}
|
||||
|
||||
// only update the absolutePoses that need it: those between lowestMovedIndex and _maxTargetIndex
|
||||
for (int i = lowestMovedIndex; i <= _maxTargetIndex; ++i) {
|
||||
int parentIndex = _skeleton->getParentIndex(i);
|
||||
for (auto i = lowestMovedIndex; i <= _maxTargetIndex; ++i) {
|
||||
auto parentIndex = _skeleton->getParentIndex((int)i);
|
||||
if (parentIndex != -1) {
|
||||
absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ AnimSkeleton::AnimSkeleton(const std::vector<FBXJoint>& joints) {
|
|||
}
|
||||
|
||||
int AnimSkeleton::nameToJointIndex(const QString& jointName) const {
|
||||
for (size_t i = 0; i < _joints.size(); i++) {
|
||||
for (int i = 0; i < (int)_joints.size(); i++) {
|
||||
if (_joints[i].name == jointName) {
|
||||
return i;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ int AnimSkeleton::nameToJointIndex(const QString& jointName) const {
|
|||
}
|
||||
|
||||
int AnimSkeleton::getNumJoints() const {
|
||||
return _joints.size();
|
||||
return (int)_joints.size();
|
||||
}
|
||||
|
||||
const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const {
|
||||
|
@ -101,7 +101,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
|
|||
_relativeDefaultPoses.reserve(joints.size());
|
||||
|
||||
// iterate over FBXJoints and extract the bind pose information.
|
||||
for (size_t i = 0; i < joints.size(); i++) {
|
||||
for (int i = 0; i < (int)joints.size(); i++) {
|
||||
|
||||
// build relative and absolute default poses
|
||||
glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation);
|
||||
|
@ -140,6 +140,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& 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));
|
||||
|
|
|
@ -234,7 +234,7 @@ bool Rig::jointStatesEmpty() {
|
|||
}
|
||||
|
||||
int Rig::getJointStateCount() const {
|
||||
return _internalPoseSet._relativePoses.size();
|
||||
return (int)_internalPoseSet._relativePoses.size();
|
||||
}
|
||||
|
||||
int Rig::indexOfJoint(const QString& jointName) const {
|
||||
|
@ -283,12 +283,14 @@ bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const {
|
|||
void Rig::clearJointState(int index) {
|
||||
if (isIndexValid(index)) {
|
||||
_internalPoseSet._overrideFlags[index] = false;
|
||||
_internalPoseSet._overridePoses[index] = _animSkeleton->getRelativeDefaultPose(index);
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::clearJointStates() {
|
||||
_internalPoseSet._overrideFlags.clear();
|
||||
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints());
|
||||
_internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
|
||||
}
|
||||
|
||||
void Rig::clearJointAnimationPriority(int index) {
|
||||
|
@ -434,7 +436,7 @@ void Rig::calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds,
|
|||
|
||||
void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) {
|
||||
// TODO: use cached eye/hips indices for these calculations
|
||||
int numPoses = poses.size();
|
||||
int numPoses = (int)poses.size();
|
||||
int hipsIndex = _animSkeleton->nameToJointIndex(QString("Hips"));
|
||||
int headIndex = _animSkeleton->nameToJointIndex(QString("Head"));
|
||||
if (hipsIndex > 0 && headIndex > 0) {
|
||||
|
@ -464,6 +466,25 @@ const AnimPoseVec& Rig::getAbsoluteDefaultPoses() const {
|
|||
return _absoluteDefaultPoses;
|
||||
}
|
||||
|
||||
|
||||
bool Rig::getRelativeDefaultJointRotation(int index, glm::quat& rotationOut) const {
|
||||
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
|
||||
rotationOut = _animSkeleton->getRelativeDefaultPose(index).rot;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Rig::getRelativeDefaultJointTranslation(int index, glm::vec3& translationOut) const {
|
||||
if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) {
|
||||
translationOut = _animSkeleton->getRelativeDefaultPose(index).trans;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// animation reference speeds.
|
||||
static const std::vector<float> FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s
|
||||
static const std::vector<float> BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s
|
||||
|
@ -1066,8 +1087,8 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const {
|
|||
}
|
||||
|
||||
void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
|
||||
jointDataVec.resize(getJointStateCount());
|
||||
for (int i = 0; i < jointDataVec.size(); i++) {
|
||||
jointDataVec.resize((int)getJointStateCount());
|
||||
for (auto i = 0; i < jointDataVec.size(); i++) {
|
||||
JointData& data = jointDataVec[i];
|
||||
data.rotationSet |= getJointStateRotation(i, data.rotation);
|
||||
// geometry offset is used here so that translations are in meters.
|
||||
|
@ -1164,7 +1185,7 @@ void Rig::computeAvatarBoundingCapsule(
|
|||
// even if they do not have legs (default robot)
|
||||
totalExtents.addPoint(glm::vec3(0.0f));
|
||||
|
||||
int numPoses = finalPoses.size();
|
||||
int numPoses = (int)finalPoses.size();
|
||||
for (int i = 0; i < numPoses; i++) {
|
||||
const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo;
|
||||
AnimPose pose = finalPoses[i];
|
||||
|
|
|
@ -200,6 +200,10 @@ public:
|
|||
// rig space
|
||||
const AnimPoseVec& getAbsoluteDefaultPoses() const;
|
||||
|
||||
// geometry space
|
||||
bool getRelativeDefaultJointRotation(int index, glm::quat& rotationOut) const;
|
||||
bool getRelativeDefaultJointTranslation(int index, glm::vec3& translationOut) const;
|
||||
|
||||
void copyJointsIntoJointData(QVector<JointData>& jointDataVec) const;
|
||||
void copyJointsFromJointData(const QVector<JointData>& jointDataVec);
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ void SwingTwistConstraint::SwingLimitFunction::setCone(float maxAngle) {
|
|||
}
|
||||
|
||||
void SwingTwistConstraint::SwingLimitFunction::setMinDots(const std::vector<float>& minDots) {
|
||||
uint32_t numDots = minDots.size();
|
||||
uint32_t numDots = (uint32_t)minDots.size();
|
||||
_minDots.clear();
|
||||
if (numDots == 0) {
|
||||
// push two copies of MIN_MINDOT
|
||||
|
@ -90,7 +90,7 @@ void SwingTwistConstraint::setSwingLimits(const std::vector<glm::vec3>& swungDir
|
|||
};
|
||||
std::vector<SwingLimitData> limits;
|
||||
|
||||
uint32_t numLimits = swungDirections.size();
|
||||
uint32_t numLimits = (uint32_t)swungDirections.size();
|
||||
limits.reserve(numLimits);
|
||||
|
||||
// compute the limit pairs: <theta, minDot>
|
||||
|
@ -108,7 +108,7 @@ void SwingTwistConstraint::setSwingLimits(const std::vector<glm::vec3>& swungDir
|
|||
}
|
||||
|
||||
std::vector<float> minDots;
|
||||
numLimits = limits.size();
|
||||
numLimits = (uint32_t)limits.size();
|
||||
if (numLimits == 0) {
|
||||
// trivial case: nearly free constraint
|
||||
std::vector<float> minDots;
|
||||
|
|
|
@ -1184,11 +1184,11 @@ float AudioClient::getAudioOutputMsecsUnplayed() const {
|
|||
}
|
||||
|
||||
qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
||||
int samplesRequested = maxSize / sizeof(int16_t);
|
||||
auto samplesRequested = maxSize / sizeof(int16_t);
|
||||
int samplesPopped;
|
||||
int bytesWritten;
|
||||
|
||||
if ((samplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) {
|
||||
if ((samplesPopped = _receivedAudioStream.popSamples((int)samplesRequested, false)) > 0) {
|
||||
AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput();
|
||||
lastPopOutput.readSamples((int16_t*)data, samplesPopped);
|
||||
bytesWritten = samplesPopped * sizeof(int16_t);
|
||||
|
|
|
@ -55,7 +55,7 @@ static const float LATE_MIX_RIGHT_DEFAULT = 90.0f;
|
|||
static const float WET_DRY_MIX_DEFAULT = 50.0f;
|
||||
|
||||
static void setOption(QScriptValue arguments, const QString name, float defaultValue, float& variable) {
|
||||
variable = arguments.property(name).isNumber() ? arguments.property(name).toNumber() : defaultValue;
|
||||
variable = arguments.property(name).isNumber() ? (float)arguments.property(name).toNumber() : defaultValue;
|
||||
}
|
||||
|
||||
AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<glm::quat> getJointRotations() const;
|
||||
Q_INVOKABLE virtual void setJointRotations(QVector<glm::quat> jointRotations);
|
||||
Q_INVOKABLE virtual void setJointTranslations(QVector<glm::vec3> 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;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <GeometryUtil.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -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<PalmData> _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;
|
||||
|
|
|
@ -62,6 +62,14 @@ namespace controller {
|
|||
makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"),
|
||||
makeButtonPair(Action::CYCLE_CAMERA, "CycleCamera"),
|
||||
|
||||
makeAxisPair(Action::RETICLE_CLICK, "ReticleClick"),
|
||||
makeAxisPair(Action::RETICLE_X, "ReticleX"),
|
||||
makeAxisPair(Action::RETICLE_Y, "ReticleY"),
|
||||
makeAxisPair(Action::RETICLE_LEFT, "ReticleLeft"),
|
||||
makeAxisPair(Action::RETICLE_RIGHT, "ReticleRight"),
|
||||
makeAxisPair(Action::RETICLE_UP, "ReticleUp"),
|
||||
makeAxisPair(Action::RETICLE_DOWN, "ReticleDown"),
|
||||
|
||||
// Aliases and bisected versions
|
||||
makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"),
|
||||
makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"),
|
||||
|
|
|
@ -55,30 +55,41 @@ enum class Action {
|
|||
|
||||
SHIFT,
|
||||
|
||||
// Biseced aliases for TRANSLATE_Z
|
||||
// Pointer/Reticle control
|
||||
RETICLE_CLICK,
|
||||
RETICLE_X,
|
||||
RETICLE_Y,
|
||||
|
||||
// Bisected aliases for RETICLE_X/RETICLE_Y
|
||||
RETICLE_LEFT,
|
||||
RETICLE_RIGHT,
|
||||
RETICLE_UP,
|
||||
RETICLE_DOWN,
|
||||
|
||||
// Bisected aliases for TRANSLATE_Z
|
||||
LONGITUDINAL_BACKWARD,
|
||||
LONGITUDINAL_FORWARD,
|
||||
|
||||
// Biseced aliases for TRANSLATE_X
|
||||
// Bisected aliases for TRANSLATE_X
|
||||
LATERAL_LEFT,
|
||||
LATERAL_RIGHT,
|
||||
|
||||
// Biseced aliases for TRANSLATE_Y
|
||||
// Bisected aliases for TRANSLATE_Y
|
||||
VERTICAL_DOWN,
|
||||
VERTICAL_UP,
|
||||
|
||||
// Biseced aliases for ROTATE_Y
|
||||
// Bisected aliases for ROTATE_Y
|
||||
YAW_LEFT,
|
||||
YAW_RIGHT,
|
||||
|
||||
// Biseced aliases for ROTATE_X
|
||||
// Bisected aliases for ROTATE_X
|
||||
PITCH_DOWN,
|
||||
PITCH_UP,
|
||||
|
||||
// Biseced aliases for TRANSLATE_CAMERA_Z
|
||||
// Bisected aliases for TRANSLATE_CAMERA_Z
|
||||
BOOM_IN,
|
||||
BOOM_OUT,
|
||||
|
||||
|
||||
NUM_ACTIONS,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <QCursor>
|
||||
#include <QThread>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVariant>
|
||||
|
@ -29,6 +30,7 @@
|
|||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -255,6 +255,9 @@ void UserInputMapper::update(float deltaTime) {
|
|||
fixBisectedAxis(_actionStates[toInt(Action::ROTATE_Y)], _actionStates[toInt(Action::YAW_LEFT)], _actionStates[toInt(Action::YAW_RIGHT)]);
|
||||
fixBisectedAxis(_actionStates[toInt(Action::ROTATE_X)], _actionStates[toInt(Action::PITCH_UP)], _actionStates[toInt(Action::PITCH_DOWN)]);
|
||||
|
||||
fixBisectedAxis(_actionStates[toInt(Action::RETICLE_X)], _actionStates[toInt(Action::RETICLE_LEFT)], _actionStates[toInt(Action::RETICLE_RIGHT)]);
|
||||
fixBisectedAxis(_actionStates[toInt(Action::RETICLE_Y)], _actionStates[toInt(Action::RETICLE_UP)], _actionStates[toInt(Action::RETICLE_DOWN)]);
|
||||
|
||||
static const float EPSILON = 0.01f;
|
||||
for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
|
||||
_actionStates[i] *= _actionScales[i];
|
||||
|
@ -319,42 +322,12 @@ QVector<QString> UserInputMapper::getActionNames() const {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
/*
|
||||
void UserInputMapper::assignDefaulActionScales() {
|
||||
_actionScales[toInt(Action::LONGITUDINAL_BACKWARD)] = 1.0f; // 1m per unit
|
||||
_actionScales[toInt(Action::LONGITUDINAL_FORWARD)] = 1.0f; // 1m per unit
|
||||
_actionScales[toInt(Action::LATERAL_LEFT)] = 1.0f; // 1m per unit
|
||||
_actionScales[toInt(Action::LATERAL_RIGHT)] = 1.0f; // 1m per unit
|
||||
_actionScales[toInt(Action::VERTICAL_DOWN)] = 1.0f; // 1m per unit
|
||||
_actionScales[toInt(Action::VERTICAL_UP)] = 1.0f; // 1m per unit
|
||||
_actionScales[toInt(Action::YAW_LEFT)] = 1.0f; // 1 degree per unit
|
||||
_actionScales[toInt(Action::YAW_RIGHT)] = 1.0f; // 1 degree per unit
|
||||
_actionScales[toInt(Action::PITCH_DOWN)] = 1.0f; // 1 degree per unit
|
||||
_actionScales[toInt(Action::PITCH_UP)] = 1.0f; // 1 degree per unit
|
||||
_actionScales[toInt(Action::BOOM_IN)] = 0.5f; // .5m per unit
|
||||
_actionScales[toInt(Action::BOOM_OUT)] = 0.5f; // .5m per unit
|
||||
_actionScales[toInt(Action::LEFT_HAND)] = 1.0f; // default
|
||||
_actionScales[toInt(Action::RIGHT_HAND)] = 1.0f; // default
|
||||
_actionScales[toInt(Action::LEFT_HAND_CLICK)] = 1.0f; // on
|
||||
_actionScales[toInt(Action::RIGHT_HAND_CLICK)] = 1.0f; // on
|
||||
_actionScales[toInt(Action::SHIFT)] = 1.0f; // on
|
||||
_actionScales[toInt(Action::ACTION1)] = 1.0f; // default
|
||||
_actionScales[toInt(Action::ACTION2)] = 1.0f; // default
|
||||
_actionScales[toInt(Action::TRANSLATE_X)] = 1.0f; // default
|
||||
_actionScales[toInt(Action::TRANSLATE_Y)] = 1.0f; // default
|
||||
_actionScales[toInt(Action::TRANSLATE_Z)] = 1.0f; // default
|
||||
_actionScales[toInt(Action::ROLL)] = 1.0f; // default
|
||||
_actionScales[toInt(Action::PITCH)] = 1.0f; // default
|
||||
_actionScales[toInt(Action::YAW)] = 1.0f; // default
|
||||
}
|
||||
*/
|
||||
|
||||
static int actionMetaTypeId = qRegisterMetaType<Action>();
|
||||
static int inputMetaTypeId = qRegisterMetaType<Input>();
|
||||
static int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>();
|
||||
static int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose");
|
||||
|
||||
|
||||
QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input);
|
||||
void inputFromScriptValue(const QScriptValue& object, Input& input);
|
||||
QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action);
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <StreamUtils.h>
|
||||
|
||||
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<ScriptEndpoint*>(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) }));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
|
||||
#include <QtOpenGL/QGLWidget>
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
|
||||
#include <gl/GLWidget.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
@ -104,7 +105,7 @@ public:
|
|||
|
||||
// take the latest texture and present it
|
||||
_context->makeCurrent();
|
||||
if (QOpenGLContext::currentContext() == _context->contextHandle()) {
|
||||
if (isCurrentContext(_context->contextHandle())) {
|
||||
currentPlugin->present();
|
||||
_context->doneCurrent();
|
||||
} else {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -66,5 +66,5 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|||
DependencyManager::get<DeferredLightingEffect>()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor);
|
||||
}
|
||||
static const auto triCount = DependencyManager::get<GeometryCache>()->getCubeTriangleCount();
|
||||
args->_details._trianglesRendered += triCount;
|
||||
args->_details._trianglesRendered += (int)triCount;
|
||||
}
|
||||
|
|
|
@ -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<EntityScriptingInterface>().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<NetworkGeometry> collisionNetworkGeometry = _model->getCollisionGeometry();
|
||||
const QSharedPointer<NetworkGeometry> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
virtual ~RenderableModelEntityItem();
|
||||
|
||||
virtual void setDimensions(const glm::vec3& value) override;
|
||||
virtual void setModelURL(const QString& url);
|
||||
virtual void setModelURL(const QString& url) override;
|
||||
|
||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
@ -73,6 +73,7 @@ public:
|
|||
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
||||
|
||||
virtual void loader() override;
|
||||
virtual void locationChanged() override;
|
||||
|
||||
private:
|
||||
void remapTextures();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue