mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 16:55:26 +02:00
Merge branch 'master' into plugins
This commit is contained in:
commit
9f98547390
216 changed files with 8172 additions and 5556 deletions
CMakeLists.txt
assignment-client/src
cmake/externals
examples
interface
CMakeLists.txt
resources
images
address-bar.svgarrow.pngdarkgreyarrow.pngdarkgreyarrow.svgleft-arrow.svglightgreyarrow.pnglightgreyarrow.svgredarrow.svgredarrow_reversed.svgsepline.pngsepline.svg
qml
src
Application.cppApplication.hCamera.hGLCanvas.cppInterfaceActionFactory.cppLODManager.cppMenu.cppMenu.hModelPropertiesDialog.cppStars.cppUIUtil.cppUtil.cpp
audio
avatar
devices
CameraToolBox.cppCameraToolBox.hDdeFaceTracker.cppJoystick.cppJoystick.hKeyboardMouseDevice.cppSDL2Manager.cppSDL2Manager.hSixenseManager.cppSixenseManager.h
scripting
ControllerScriptingInterface.cppControllerScriptingInterface.hHMDScriptingInterface.cppHMDScriptingInterface.h
starfield/renderer
ui
AddressBarDialog.cppAddressBarDialog.hApplicationCompositor.cppApplicationCompositor.hApplicationOverlay.cppApplicationOverlay.hAvatarInputs.cppAvatarInputs.hDialogsManager.cppDialogsManager.hFlowLayout.cppLogDialog.cppPreferencesDialog.cppRearMirrorTools.cppRearMirrorTools.hStats.cppStats.hUpdateDialog.cppUpdateDialog.hUserInputMapper.cppUserInputMapper.h
overlays
ui
libraries/audio-client/src
|
@ -49,7 +49,7 @@ if (WIN32)
|
|||
else ()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter")
|
||||
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion")
|
||||
endif ()
|
||||
endif(WIN32)
|
||||
|
||||
|
|
|
@ -295,10 +295,10 @@ void AudioMixerClientData::printUpstreamDownstreamStats() const {
|
|||
|
||||
void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamStats) const {
|
||||
printf(" Packet loss | overall: %5.2f%% (%d lost), last_30s: %5.2f%% (%d lost)\n",
|
||||
streamStats._packetStreamStats.getLostRate() * 100.0f,
|
||||
streamStats._packetStreamStats._lost,
|
||||
streamStats._packetStreamWindowStats.getLostRate() * 100.0f,
|
||||
streamStats._packetStreamWindowStats._lost);
|
||||
(double)(streamStats._packetStreamStats.getLostRate() * 100.0f),
|
||||
streamStats._packetStreamStats._lost,
|
||||
(double)(streamStats._packetStreamWindowStats.getLostRate() * 100.0f),
|
||||
streamStats._packetStreamWindowStats._lost);
|
||||
|
||||
printf(" Ringbuffer frames | desired: %u, avg_available(10s): %u, available: %u\n",
|
||||
streamStats._desiredJitterBufferFrames,
|
||||
|
|
|
@ -127,7 +127,7 @@ void OctreeServer::resetSendingStats() {
|
|||
_noProcessWait = 0;
|
||||
}
|
||||
|
||||
void OctreeServer::trackEncodeTime(float time) {
|
||||
void OctreeServer::trackEncodeTime(float time) {
|
||||
const float MAX_SHORT_TIME = 10.0f;
|
||||
const float MAX_LONG_TIME = 100.0f;
|
||||
|
||||
|
@ -144,10 +144,10 @@ void OctreeServer::trackEncodeTime(float time) {
|
|||
_extraLongEncode++;
|
||||
_averageExtraLongEncodeTime.updateAverage(time);
|
||||
}
|
||||
_averageEncodeTime.updateAverage(time);
|
||||
_averageEncodeTime.updateAverage(time);
|
||||
}
|
||||
|
||||
void OctreeServer::trackTreeWaitTime(float time) {
|
||||
void OctreeServer::trackTreeWaitTime(float time) {
|
||||
const float MAX_SHORT_TIME = 10.0f;
|
||||
const float MAX_LONG_TIME = 100.0f;
|
||||
if (time == SKIP_TIME) {
|
||||
|
@ -166,7 +166,7 @@ void OctreeServer::trackTreeWaitTime(float time) {
|
|||
_averageTreeWaitTime.updateAverage(time);
|
||||
}
|
||||
|
||||
void OctreeServer::trackCompressAndWriteTime(float time) {
|
||||
void OctreeServer::trackCompressAndWriteTime(float time) {
|
||||
const float MAX_SHORT_TIME = 10.0f;
|
||||
const float MAX_LONG_TIME = 100.0f;
|
||||
if (time == SKIP_TIME) {
|
||||
|
@ -182,19 +182,19 @@ void OctreeServer::trackCompressAndWriteTime(float time) {
|
|||
_extraLongCompress++;
|
||||
_averageExtraLongCompressTime.updateAverage(time);
|
||||
}
|
||||
_averageCompressAndWriteTime.updateAverage(time);
|
||||
_averageCompressAndWriteTime.updateAverage(time);
|
||||
}
|
||||
|
||||
void OctreeServer::trackPacketSendingTime(float time) {
|
||||
void OctreeServer::trackPacketSendingTime(float time) {
|
||||
if (time == SKIP_TIME) {
|
||||
_noSend++;
|
||||
time = 0.0f;
|
||||
}
|
||||
_averagePacketSendingTime.updateAverage(time);
|
||||
_averagePacketSendingTime.updateAverage(time);
|
||||
}
|
||||
|
||||
|
||||
void OctreeServer::trackProcessWaitTime(float time) {
|
||||
void OctreeServer::trackProcessWaitTime(float time) {
|
||||
const float MAX_SHORT_TIME = 10.0f;
|
||||
const float MAX_LONG_TIME = 100.0f;
|
||||
if (time == SKIP_TIME) {
|
||||
|
@ -243,9 +243,9 @@ OctreeServer::OctreeServer(const QByteArray& packet) :
|
|||
|
||||
_averageLoopTime.updateAverage(0);
|
||||
qDebug() << "Octree server starting... [" << this << "]";
|
||||
|
||||
|
||||
// make sure the AccountManager has an Auth URL for payment redemptions
|
||||
|
||||
|
||||
AccountManager::getInstance().setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
||||
}
|
||||
|
||||
|
@ -278,13 +278,13 @@ OctreeServer::~OctreeServer() {
|
|||
|
||||
delete _jurisdiction;
|
||||
_jurisdiction = NULL;
|
||||
|
||||
|
||||
// cleanup our tree here...
|
||||
qDebug() << qPrintable(_safeServerName) << "server START cleaning up octree... [" << this << "]";
|
||||
delete _tree;
|
||||
_tree = NULL;
|
||||
qDebug() << qPrintable(_safeServerName) << "server DONE cleaning up octree... [" << this << "]";
|
||||
|
||||
|
||||
if (_instance == this) {
|
||||
_instance = NULL; // we are gone
|
||||
}
|
||||
|
@ -400,10 +400,10 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
statsString += QString().sprintf(" Internal Elements: %s nodes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)internalNodeCount).rightJustified(16,
|
||||
' ').toLocal8Bit().constData(),
|
||||
((float)internalNodeCount / (float)nodeCount) * AS_PERCENT);
|
||||
(double)((internalNodeCount / (float)nodeCount) * AS_PERCENT));
|
||||
statsString += QString().sprintf(" Leaf Elements: %s nodes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)leafNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
|
||||
((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
|
||||
(double)((leafNodeCount / (float)nodeCount) * AS_PERCENT));
|
||||
statsString += "\r\n";
|
||||
statsString += "\r\n";
|
||||
|
||||
|
@ -422,7 +422,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
.arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
quint64 oneSecondAgo = usecTimestampNow() - USECS_PER_SECOND;
|
||||
|
||||
|
||||
statsString += QString(" process() last second: %1 clients\r\n")
|
||||
.arg(locale.toString((uint)howManyThreadsDidProcess(oneSecondAgo)).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" packetDistributor() last second: %1 clients\r\n")
|
||||
|
@ -434,13 +434,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
|
||||
float averageLoopTime = getAverageLoopTime();
|
||||
statsString += QString().sprintf(" Average packetLoop() time: %7.2f msecs"
|
||||
" samples: %12d \r\n",
|
||||
averageLoopTime, _averageLoopTime.getSampleCount());
|
||||
" samples: %12d \r\n",
|
||||
(double)averageLoopTime, _averageLoopTime.getSampleCount());
|
||||
|
||||
float averageInsideTime = getAverageInsideTime();
|
||||
statsString += QString().sprintf(" Average 'inside' time: %9.2f usecs"
|
||||
" samples: %12d \r\n\r\n",
|
||||
averageInsideTime, _averageInsideTime.getSampleCount());
|
||||
" samples: %12d \r\n\r\n",
|
||||
(double)averageInsideTime, _averageInsideTime.getSampleCount());
|
||||
|
||||
|
||||
// Process Wait
|
||||
|
@ -450,30 +450,30 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
float averageProcessWaitTime = getAverageProcessWaitTime();
|
||||
statsString += QString().sprintf(" Average process lock wait time:"
|
||||
" %9.2f usecs samples: %12d \r\n",
|
||||
averageProcessWaitTime, allWaitTimes);
|
||||
(double)averageProcessWaitTime, allWaitTimes);
|
||||
|
||||
float zeroVsTotal = (allWaitTimes > 0) ? ((float)_noProcessWait / (float)allWaitTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" No Lock Wait:"
|
||||
" (%6.2f%%) samples: %12d \r\n",
|
||||
zeroVsTotal * AS_PERCENT, _noProcessWait);
|
||||
(double)(zeroVsTotal * AS_PERCENT), _noProcessWait);
|
||||
|
||||
float shortVsTotal = (allWaitTimes > 0) ? ((float)_shortProcessWait / (float)allWaitTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg process lock short wait time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||
_averageProcessShortWaitTime.getAverage(),
|
||||
shortVsTotal * AS_PERCENT, _shortProcessWait);
|
||||
(double)_averageProcessShortWaitTime.getAverage(),
|
||||
(double)(shortVsTotal * AS_PERCENT), _shortProcessWait);
|
||||
|
||||
float longVsTotal = (allWaitTimes > 0) ? ((float)_longProcessWait / (float)allWaitTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg process lock long wait time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||
_averageProcessLongWaitTime.getAverage(),
|
||||
longVsTotal * AS_PERCENT, _longProcessWait);
|
||||
(double)_averageProcessLongWaitTime.getAverage(),
|
||||
(double)(longVsTotal * AS_PERCENT), _longProcessWait);
|
||||
|
||||
float extraLongVsTotal = (allWaitTimes > 0) ? ((float)_extraLongProcessWait / (float)allWaitTimes) : 0.0f;
|
||||
statsString += QString().sprintf("Avg process lock extralong wait time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n\r\n",
|
||||
_averageProcessExtraLongWaitTime.getAverage(),
|
||||
extraLongVsTotal * AS_PERCENT, _extraLongProcessWait);
|
||||
(double)_averageProcessExtraLongWaitTime.getAverage(),
|
||||
(double)(extraLongVsTotal * AS_PERCENT), _extraLongProcessWait);
|
||||
}
|
||||
|
||||
// Tree Wait
|
||||
|
@ -482,122 +482,125 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
float averageTreeWaitTime = getAverageTreeWaitTime();
|
||||
statsString += QString().sprintf(" Average tree lock wait time:"
|
||||
" %9.2f usecs samples: %12d \r\n",
|
||||
averageTreeWaitTime, allWaitTimes);
|
||||
(double)averageTreeWaitTime, allWaitTimes);
|
||||
|
||||
float zeroVsTotal = (allWaitTimes > 0) ? ((float)_noTreeWait / (float)allWaitTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" No Lock Wait:"
|
||||
" (%6.2f%%) samples: %12d \r\n",
|
||||
zeroVsTotal * AS_PERCENT, _noTreeWait);
|
||||
(double)(zeroVsTotal * AS_PERCENT), _noTreeWait);
|
||||
|
||||
float shortVsTotal = (allWaitTimes > 0) ? ((float)_shortTreeWait / (float)allWaitTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg tree lock short wait time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||
_averageTreeShortWaitTime.getAverage(),
|
||||
shortVsTotal * AS_PERCENT, _shortTreeWait);
|
||||
(double)_averageTreeShortWaitTime.getAverage(),
|
||||
(double)(shortVsTotal * AS_PERCENT), _shortTreeWait);
|
||||
|
||||
float longVsTotal = (allWaitTimes > 0) ? ((float)_longTreeWait / (float)allWaitTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg tree lock long wait time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||
_averageTreeLongWaitTime.getAverage(),
|
||||
longVsTotal * AS_PERCENT, _longTreeWait);
|
||||
(double)_averageTreeLongWaitTime.getAverage(),
|
||||
(double)(longVsTotal * AS_PERCENT), _longTreeWait);
|
||||
|
||||
float extraLongVsTotal = (allWaitTimes > 0) ? ((float)_extraLongTreeWait / (float)allWaitTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg tree lock extra long wait time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n\r\n",
|
||||
_averageTreeExtraLongWaitTime.getAverage(),
|
||||
extraLongVsTotal * AS_PERCENT, _extraLongTreeWait);
|
||||
(double)_averageTreeExtraLongWaitTime.getAverage(),
|
||||
(double)(extraLongVsTotal * AS_PERCENT), _extraLongTreeWait);
|
||||
|
||||
// encode
|
||||
float averageEncodeTime = getAverageEncodeTime();
|
||||
statsString += QString().sprintf(" Average encode time: %9.2f usecs\r\n", averageEncodeTime);
|
||||
|
||||
statsString += QString().sprintf(" Average encode time: %9.2f usecs\r\n", (double)averageEncodeTime);
|
||||
|
||||
int allEncodeTimes = _noEncode + _shortEncode + _longEncode + _extraLongEncode;
|
||||
|
||||
float zeroVsTotalEncode = (allEncodeTimes > 0) ? ((float)_noEncode / (float)allEncodeTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" No Encode:"
|
||||
" (%6.2f%%) samples: %12d \r\n",
|
||||
zeroVsTotalEncode * AS_PERCENT, _noEncode);
|
||||
(double)(zeroVsTotalEncode * AS_PERCENT), _noEncode);
|
||||
|
||||
float shortVsTotalEncode = (allEncodeTimes > 0) ? ((float)_shortEncode / (float)allEncodeTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg short encode time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||
_averageShortEncodeTime.getAverage(),
|
||||
shortVsTotalEncode * AS_PERCENT, _shortEncode);
|
||||
(double)_averageShortEncodeTime.getAverage(),
|
||||
(double)(shortVsTotalEncode * AS_PERCENT), _shortEncode);
|
||||
|
||||
float longVsTotalEncode = (allEncodeTimes > 0) ? ((float)_longEncode / (float)allEncodeTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg long encode time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||
_averageLongEncodeTime.getAverage(),
|
||||
longVsTotalEncode * AS_PERCENT, _longEncode);
|
||||
(double)_averageLongEncodeTime.getAverage(),
|
||||
(double)(longVsTotalEncode * AS_PERCENT), _longEncode);
|
||||
|
||||
float extraLongVsTotalEncode = (allEncodeTimes > 0) ? ((float)_extraLongEncode / (float)allEncodeTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg extra long encode time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n\r\n",
|
||||
_averageExtraLongEncodeTime.getAverage(),
|
||||
extraLongVsTotalEncode * AS_PERCENT, _extraLongEncode);
|
||||
(double)_averageExtraLongEncodeTime.getAverage(),
|
||||
(double)(extraLongVsTotalEncode * AS_PERCENT), _extraLongEncode);
|
||||
|
||||
|
||||
float averageCompressAndWriteTime = getAverageCompressAndWriteTime();
|
||||
statsString += QString().sprintf(" Average compress and write time: %9.2f usecs\r\n",
|
||||
averageCompressAndWriteTime);
|
||||
statsString += QString().sprintf(" Average compress and write time: %9.2f usecs\r\n",
|
||||
(double)averageCompressAndWriteTime);
|
||||
|
||||
int allCompressTimes = _noCompress + _shortCompress + _longCompress + _extraLongCompress;
|
||||
|
||||
float zeroVsTotalCompress = (allCompressTimes > 0) ? ((float)_noCompress / (float)allCompressTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" No compression:"
|
||||
" (%6.2f%%) samples: %12d \r\n",
|
||||
zeroVsTotalCompress * AS_PERCENT, _noCompress);
|
||||
(double)(zeroVsTotalCompress * AS_PERCENT), _noCompress);
|
||||
|
||||
float shortVsTotalCompress = (allCompressTimes > 0) ? ((float)_shortCompress / (float)allCompressTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg short compress time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||
_averageShortCompressTime.getAverage(),
|
||||
shortVsTotalCompress * AS_PERCENT, _shortCompress);
|
||||
(double)_averageShortCompressTime.getAverage(),
|
||||
(double)(shortVsTotalCompress * AS_PERCENT), _shortCompress);
|
||||
|
||||
float longVsTotalCompress = (allCompressTimes > 0) ? ((float)_longCompress / (float)allCompressTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg long compress time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||
_averageLongCompressTime.getAverage(),
|
||||
longVsTotalCompress * AS_PERCENT, _longCompress);
|
||||
(double)_averageLongCompressTime.getAverage(),
|
||||
(double)(longVsTotalCompress * AS_PERCENT), _longCompress);
|
||||
|
||||
float extraLongVsTotalCompress = (allCompressTimes > 0) ? ((float)_extraLongCompress / (float)allCompressTimes) : 0.0f;
|
||||
statsString += QString().sprintf(" Avg extra long compress time:"
|
||||
" %9.2f usecs (%6.2f%%) samples: %12d \r\n\r\n",
|
||||
_averageExtraLongCompressTime.getAverage(),
|
||||
extraLongVsTotalCompress * AS_PERCENT, _extraLongCompress);
|
||||
(double)_averageExtraLongCompressTime.getAverage(),
|
||||
(double)(extraLongVsTotalCompress * AS_PERCENT), _extraLongCompress);
|
||||
|
||||
float averagePacketSendingTime = getAveragePacketSendingTime();
|
||||
statsString += QString().sprintf(" Average packet sending time: %9.2f usecs (includes node lock)\r\n",
|
||||
averagePacketSendingTime);
|
||||
statsString += QString().sprintf(" Average packet sending time: %9.2f usecs (includes node lock)\r\n",
|
||||
(double)averagePacketSendingTime);
|
||||
|
||||
float noVsTotalSend = (_averagePacketSendingTime.getSampleCount() > 0) ?
|
||||
float noVsTotalSend = (_averagePacketSendingTime.getSampleCount() > 0) ?
|
||||
((float)_noSend / (float)_averagePacketSendingTime.getSampleCount()) : 0.0f;
|
||||
statsString += QString().sprintf(" Not sending:"
|
||||
" (%6.2f%%) samples: %12d \r\n",
|
||||
noVsTotalSend * AS_PERCENT, _noSend);
|
||||
|
||||
(double)(noVsTotalSend * AS_PERCENT), _noSend);
|
||||
|
||||
float averageNodeWaitTime = getAverageNodeWaitTime();
|
||||
statsString += QString().sprintf(" Average node lock wait time: %9.2f usecs\r\n", averageNodeWaitTime);
|
||||
statsString += QString().sprintf(" Average node lock wait time: %9.2f usecs\r\n",
|
||||
(double)averageNodeWaitTime);
|
||||
|
||||
statsString += QString().sprintf("--------------------------------------------------------------\r\n");
|
||||
|
||||
float encodeToInsidePercent = averageInsideTime == 0.0f ? 0.0f : (averageEncodeTime / averageInsideTime) * AS_PERCENT;
|
||||
statsString += QString().sprintf(" encode ratio: %5.2f%%\r\n",
|
||||
encodeToInsidePercent);
|
||||
statsString += QString().sprintf(" encode ratio: %5.2f%%\r\n",
|
||||
(double)encodeToInsidePercent);
|
||||
|
||||
float waitToInsidePercent = averageInsideTime == 0.0f ? 0.0f
|
||||
float waitToInsidePercent = averageInsideTime == 0.0f ? 0.0f
|
||||
: ((averageTreeWaitTime + averageNodeWaitTime) / averageInsideTime) * AS_PERCENT;
|
||||
statsString += QString().sprintf(" waiting ratio: %5.2f%%\r\n", waitToInsidePercent);
|
||||
statsString += QString().sprintf(" waiting ratio: %5.2f%%\r\n",
|
||||
(double)waitToInsidePercent);
|
||||
|
||||
float compressAndWriteToInsidePercent = averageInsideTime == 0.0f ? 0.0f
|
||||
float compressAndWriteToInsidePercent = averageInsideTime == 0.0f ? 0.0f
|
||||
: (averageCompressAndWriteTime / averageInsideTime) * AS_PERCENT;
|
||||
statsString += QString().sprintf(" compress and write ratio: %5.2f%%\r\n",
|
||||
compressAndWriteToInsidePercent);
|
||||
statsString += QString().sprintf(" compress and write ratio: %5.2f%%\r\n",
|
||||
(double)compressAndWriteToInsidePercent);
|
||||
|
||||
float sendingToInsidePercent = averageInsideTime == 0.0f ? 0.0f
|
||||
float sendingToInsidePercent = averageInsideTime == 0.0f ? 0.0f
|
||||
: (averagePacketSendingTime / averageInsideTime) * AS_PERCENT;
|
||||
statsString += QString().sprintf(" sending ratio: %5.2f%%\r\n", sendingToInsidePercent);
|
||||
|
||||
statsString += QString().sprintf(" sending ratio: %5.2f%%\r\n",
|
||||
(double)sendingToInsidePercent);
|
||||
|
||||
|
||||
|
||||
statsString += QString("\r\n");
|
||||
|
@ -610,13 +613,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
.arg(locale.toString((uint)totalWastedBytes).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString().sprintf(" Total OctalCode Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)totalBytesOfOctalCodes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
|
||||
((float)totalBytesOfOctalCodes / (float)totalOutboundBytes) * AS_PERCENT);
|
||||
(double)((totalBytesOfOctalCodes / (float)totalOutboundBytes) * AS_PERCENT));
|
||||
statsString += QString().sprintf(" Total BitMasks Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)totalBytesOfBitMasks).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
|
||||
((float)totalBytesOfBitMasks / (float)totalOutboundBytes) * AS_PERCENT);
|
||||
(double)(((float)totalBytesOfBitMasks / (float)totalOutboundBytes) * AS_PERCENT));
|
||||
statsString += QString().sprintf(" Total Color Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)totalBytesOfColor).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
|
||||
((float)totalBytesOfColor / (float)totalOutboundBytes) * AS_PERCENT);
|
||||
(double)((totalBytesOfColor / (float)totalOutboundBytes) * AS_PERCENT));
|
||||
|
||||
statsString += "\r\n";
|
||||
statsString += "\r\n";
|
||||
|
@ -638,7 +641,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
.arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Inbound Elements: %1 elements\r\n")
|
||||
.arg(locale.toString((uint)totalElementsProcessed).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString().sprintf(" Average Inbound Elements/Packet: %f elements/packet\r\n", averageElementsPerPacket);
|
||||
statsString += QString().sprintf(" Average Inbound Elements/Packet: %f elements/packet\r\n",
|
||||
(double)averageElementsPerPacket);
|
||||
statsString += QString(" Average Transit Time/Packet: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Process Time/Packet: %1 usecs\r\n")
|
||||
|
@ -676,7 +680,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
statsString += QString(" Total Inbound Elements: %1 elements\r\n")
|
||||
.arg(locale.toString((uint)totalElementsProcessed).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString().sprintf(" Average Inbound Elements/Packet: %f elements/packet\r\n",
|
||||
averageElementsPerPacket);
|
||||
(double)averageElementsPerPacket);
|
||||
statsString += QString(" Average Transit Time/Packet: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Process Time/Packet: %1 usecs\r\n")
|
||||
|
@ -710,14 +714,15 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
}
|
||||
|
||||
statsString += QString().sprintf("Element Node Memory Usage: %8.2f %s\r\n",
|
||||
OctreeElement::getOctreeMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
OctreeElement::getOctreeMemoryUsage() / (double)memoryScale, memoryScaleLabel);
|
||||
statsString += QString().sprintf("Octcode Memory Usage: %8.2f %s\r\n",
|
||||
OctreeElement::getOctcodeMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
OctreeElement::getOctcodeMemoryUsage() / (double)memoryScale, memoryScaleLabel);
|
||||
statsString += QString().sprintf("External Children Memory Usage: %8.2f %s\r\n",
|
||||
OctreeElement::getExternalChildrenMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
OctreeElement::getExternalChildrenMemoryUsage() / (double)memoryScale,
|
||||
memoryScaleLabel);
|
||||
statsString += " -----------\r\n";
|
||||
statsString += QString().sprintf(" Total: %8.2f %s\r\n",
|
||||
OctreeElement::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
OctreeElement::getTotalMemoryUsage() / (double)memoryScale, memoryScaleLabel);
|
||||
statsString += "\r\n";
|
||||
|
||||
statsString += "OctreeElement Children Population Statistics...\r\n";
|
||||
|
@ -726,7 +731,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
checkSum += OctreeElement::getChildrenCount(i);
|
||||
statsString += QString().sprintf(" Nodes with %d children: %s nodes (%5.2f%%)\r\n", i,
|
||||
locale.toString((uint)OctreeElement::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
|
||||
((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
|
||||
(double)(((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT));
|
||||
}
|
||||
statsString += " ----------------------\r\n";
|
||||
statsString += QString(" Total: %1 nodes\r\n")
|
||||
|
@ -831,7 +836,7 @@ void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const H
|
|||
|
||||
// If we know we're shutting down we just drop these packets on the floor.
|
||||
// This stops us from initializing send threads we just shut down.
|
||||
|
||||
|
||||
if (!_isShuttingDown) {
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
PacketType packetType = packetTypeForPacket(receivedPacket);
|
||||
|
@ -841,7 +846,7 @@ void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const H
|
|||
// need to make sure we have it in our nodeList.
|
||||
if (matchingNode) {
|
||||
nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket);
|
||||
|
||||
|
||||
OctreeQueryNode* nodeData = (OctreeQueryNode*) matchingNode->getLinkedData();
|
||||
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
|
||||
nodeData->initializeOctreeSendThread(this, matchingNode);
|
||||
|
@ -870,33 +875,33 @@ void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const H
|
|||
|
||||
void OctreeServer::setupDatagramProcessingThread() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
||||
// we do not want this event loop to be the handler for UDP datagrams, so disconnect
|
||||
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
|
||||
|
||||
|
||||
// setup a QThread with us as parent that will house the OctreeServerDatagramProcessor
|
||||
_datagramProcessingThread = new QThread(this);
|
||||
_datagramProcessingThread->setObjectName("Octree Datagram Processor");
|
||||
|
||||
|
||||
// create an OctreeServerDatagramProcessor and move it to that thread
|
||||
OctreeServerDatagramProcessor* datagramProcessor = new OctreeServerDatagramProcessor(nodeList->getNodeSocket(), thread());
|
||||
datagramProcessor->moveToThread(_datagramProcessingThread);
|
||||
|
||||
|
||||
// remove the NodeList as the parent of the node socket
|
||||
nodeList->getNodeSocket().setParent(NULL);
|
||||
nodeList->getNodeSocket().moveToThread(_datagramProcessingThread);
|
||||
|
||||
|
||||
// let the datagram processor handle readyRead from node socket
|
||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead,
|
||||
datagramProcessor, &OctreeServerDatagramProcessor::readPendingDatagrams);
|
||||
|
||||
|
||||
// connect to the datagram processing thread signal that tells us we have to handle a packet
|
||||
connect(datagramProcessor, &OctreeServerDatagramProcessor::packetRequiresProcessing, this, &OctreeServer::readPendingDatagram);
|
||||
|
||||
|
||||
// delete the datagram processor and the associated thread when the QThread quits
|
||||
connect(_datagramProcessingThread, &QThread::finished, datagramProcessor, &QObject::deleteLater);
|
||||
connect(datagramProcessor, &QObject::destroyed, _datagramProcessingThread, &QThread::deleteLater);
|
||||
|
||||
|
||||
// start the datagram processing thread
|
||||
_datagramProcessingThread->start();
|
||||
}
|
||||
|
@ -961,16 +966,16 @@ void OctreeServer::readConfiguration() {
|
|||
// wait until we have the domain-server settings, otherwise we bail
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
||||
|
||||
qDebug() << "Waiting for domain settings from domain-server.";
|
||||
|
||||
|
||||
// block until we get the settingsRequestComplete signal
|
||||
QEventLoop loop;
|
||||
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
||||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
||||
domainHandler.requestDomainSettings();
|
||||
loop.exec();
|
||||
|
||||
|
||||
if (domainHandler.getSettingsObject().isEmpty()) {
|
||||
qDebug() << "No settings object from domain-server.";
|
||||
}
|
||||
|
@ -978,7 +983,7 @@ void OctreeServer::readConfiguration() {
|
|||
QString settingsKey = getMyDomainSettingsKey();
|
||||
QJsonObject settingsSectionObject = settingsObject[settingsKey].toObject();
|
||||
_settings = settingsSectionObject; // keep this for later
|
||||
|
||||
|
||||
if (!readOptionString(QString("statusHost"), settingsSectionObject, _statusHost) || _statusHost.isEmpty()) {
|
||||
_statusHost = getLocalAddress().toString();
|
||||
}
|
||||
|
@ -1002,7 +1007,7 @@ void OctreeServer::readConfiguration() {
|
|||
bool hasRoot = readOptionString(QString("jurisdictionRoot"), settingsSectionObject, jurisdictionRoot);
|
||||
QString jurisdictionEndNodes;
|
||||
bool hasEndNodes = readOptionString(QString("jurisdictionEndNodes"), settingsSectionObject, jurisdictionEndNodes);
|
||||
|
||||
|
||||
if (hasRoot || hasEndNodes) {
|
||||
_jurisdiction = new JurisdictionMap(qPrintable(jurisdictionRoot), qPrintable(jurisdictionEndNodes));
|
||||
}
|
||||
|
@ -1050,7 +1055,7 @@ void OctreeServer::readConfiguration() {
|
|||
qDebug() << "wantBackup=" << _wantBackup;
|
||||
|
||||
//qDebug() << "settingsSectionObject:" << settingsSectionObject;
|
||||
|
||||
|
||||
} else {
|
||||
qDebug("persistFilename= DISABLED");
|
||||
}
|
||||
|
@ -1072,7 +1077,7 @@ void OctreeServer::readConfiguration() {
|
|||
_packetsPerClientPerInterval = 1;
|
||||
}
|
||||
}
|
||||
qDebug("packetsPerSecondPerClientMax=%d _packetsPerClientPerInterval=%d",
|
||||
qDebug("packetsPerSecondPerClientMax=%d _packetsPerClientPerInterval=%d",
|
||||
packetsPerSecondPerClientMax, _packetsPerClientPerInterval);
|
||||
|
||||
// Check to see if the user passed in a command line option for setting packet send rate
|
||||
|
@ -1083,10 +1088,10 @@ void OctreeServer::readConfiguration() {
|
|||
_packetsTotalPerInterval = 1;
|
||||
}
|
||||
}
|
||||
qDebug("packetsPerSecondTotalMax=%d _packetsTotalPerInterval=%d",
|
||||
qDebug("packetsPerSecondTotalMax=%d _packetsTotalPerInterval=%d",
|
||||
packetsPerSecondTotalMax, _packetsTotalPerInterval);
|
||||
|
||||
|
||||
|
||||
|
||||
readAdditionalConfiguration(settingsSectionObject);
|
||||
}
|
||||
|
||||
|
@ -1101,8 +1106,8 @@ void OctreeServer::run() {
|
|||
// make sure our NodeList knows what type we are
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->setOwnerType(getMyNodeType());
|
||||
|
||||
|
||||
|
||||
|
||||
// use common init to setup common timers and logging
|
||||
commonInit(getMyLoggingServerTargetName(), getMyNodeType());
|
||||
|
||||
|
@ -1110,7 +1115,7 @@ void OctreeServer::run() {
|
|||
|
||||
// read the configuration from either the payload or the domain server configuration
|
||||
readConfiguration();
|
||||
|
||||
|
||||
beforeRun(); // after payload has been processed
|
||||
|
||||
connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer)));
|
||||
|
@ -1119,7 +1124,7 @@ void OctreeServer::run() {
|
|||
|
||||
// we need to ask the DS about agents so we can ping/reply with them
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
|
||||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
@ -1209,7 +1214,7 @@ void OctreeServer::forceNodeShutdown(SharedNodePointer node) {
|
|||
|
||||
quint64 end = usecTimestampNow();
|
||||
quint64 usecsElapsed = (end - start);
|
||||
qDebug() << qPrintable(_safeServerName) << "server forceNodeShutdown() took: "
|
||||
qDebug() << qPrintable(_safeServerName) << "server forceNodeShutdown() took: "
|
||||
<< usecsElapsed << " usecs for node:" << *node;
|
||||
}
|
||||
|
||||
|
@ -1225,7 +1230,7 @@ void OctreeServer::aboutToFinish() {
|
|||
// 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;
|
||||
|
||||
|
||||
if (_octreeInboundPacketProcessor) {
|
||||
_octreeInboundPacketProcessor->terminating();
|
||||
}
|
||||
|
@ -1240,7 +1245,7 @@ void OctreeServer::aboutToFinish() {
|
|||
qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node;
|
||||
forceNodeShutdown(node);
|
||||
});
|
||||
|
||||
|
||||
if (_persistThread) {
|
||||
_persistThread->aboutToFinish();
|
||||
_persistThread->terminating();
|
||||
|
@ -1282,7 +1287,7 @@ QString OctreeServer::getUptime() {
|
|||
if (hours > 0 || minutes > 0) {
|
||||
formattedUptime += QString(" ");
|
||||
}
|
||||
formattedUptime += QString().sprintf("%.3f seconds", seconds);
|
||||
formattedUptime += QString().sprintf("%.3f seconds", (double)seconds);
|
||||
}
|
||||
return formattedUptime;
|
||||
}
|
||||
|
@ -1290,13 +1295,13 @@ QString OctreeServer::getUptime() {
|
|||
QString OctreeServer::getFileLoadTime() {
|
||||
QString result;
|
||||
if (isInitialLoadComplete()) {
|
||||
|
||||
|
||||
const int USECS_PER_MSEC = 1000;
|
||||
const int MSECS_PER_SEC = 1000;
|
||||
const int SECS_PER_MIN = 60;
|
||||
const int MIN_PER_HOUR = 60;
|
||||
const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
|
||||
|
||||
|
||||
quint64 msecsElapsed = getLoadElapsedTime() / USECS_PER_MSEC;;
|
||||
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
||||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||
|
@ -1321,7 +1326,7 @@ QString OctreeServer::getFileLoadTime() {
|
|||
if (hours > 0 || minutes > 0) {
|
||||
result += QString(" ");
|
||||
}
|
||||
result += QString().sprintf("%.3f seconds", seconds);
|
||||
result += QString().sprintf("%.3f seconds", (double)seconds);
|
||||
}
|
||||
} else {
|
||||
result = "Not yet loaded...";
|
||||
|
@ -1356,27 +1361,27 @@ void OctreeServer::sendStatsPacket() {
|
|||
// 2) only send new data
|
||||
// 3) automatically break up into multiple packets
|
||||
static QJsonObject statsObject1;
|
||||
|
||||
|
||||
QString baseName = getMyServerName() + QString("Server");
|
||||
|
||||
|
||||
statsObject1[baseName + QString(".0.1.configuration")] = getConfiguration();
|
||||
|
||||
|
||||
statsObject1[baseName + QString(".0.2.detailed_stats_url")] = getStatusLink();
|
||||
|
||||
|
||||
statsObject1[baseName + QString(".0.3.uptime")] = getUptime();
|
||||
statsObject1[baseName + QString(".0.4.persistFileLoadTime")] = getFileLoadTime();
|
||||
statsObject1[baseName + QString(".0.5.clients")] = getCurrentClientCount();
|
||||
|
||||
|
||||
quint64 oneSecondAgo = usecTimestampNow() - USECS_PER_SECOND;
|
||||
|
||||
|
||||
statsObject1[baseName + QString(".0.6.threads.1.processing")] = (double)howManyThreadsDidProcess(oneSecondAgo);
|
||||
statsObject1[baseName + QString(".0.6.threads.2.packetDistributor")] =
|
||||
statsObject1[baseName + QString(".0.6.threads.2.packetDistributor")] =
|
||||
(double)howManyThreadsDidPacketDistributor(oneSecondAgo);
|
||||
statsObject1[baseName + QString(".0.6.threads.3.handlePacektSend")] =
|
||||
statsObject1[baseName + QString(".0.6.threads.3.handlePacektSend")] =
|
||||
(double)howManyThreadsDidHandlePacketSend(oneSecondAgo);
|
||||
statsObject1[baseName + QString(".0.6.threads.4.writeDatagram")] =
|
||||
(double)howManyThreadsDidCallWriteDatagram(oneSecondAgo);
|
||||
|
||||
statsObject1[baseName + QString(".0.6.threads.4.writeDatagram")] =
|
||||
(double)howManyThreadsDidCallWriteDatagram(oneSecondAgo);
|
||||
|
||||
statsObject1[baseName + QString(".1.1.octree.elementCount")] = (double)OctreeElement::getNodeCount();
|
||||
statsObject1[baseName + QString(".1.2.octree.internalElementCount")] = (double)OctreeElement::getInternalNodeCount();
|
||||
statsObject1[baseName + QString(".1.3.octree.leafElementCount")] = (double)OctreeElement::getLeafNodeCount();
|
||||
|
@ -1388,9 +1393,9 @@ void OctreeServer::sendStatsPacket() {
|
|||
statsObject2[baseName + QString(".2.outbound.data.totalPackets")] = (double)OctreeSendThread::_totalPackets;
|
||||
statsObject2[baseName + QString(".2.outbound.data.totalBytes")] = (double)OctreeSendThread::_totalBytes;
|
||||
statsObject2[baseName + QString(".2.outbound.data.totalBytesWasted")] = (double)OctreeSendThread::_totalWastedBytes;
|
||||
statsObject2[baseName + QString(".2.outbound.data.totalBytesOctalCodes")] =
|
||||
statsObject2[baseName + QString(".2.outbound.data.totalBytesOctalCodes")] =
|
||||
(double)OctreePacketData::getTotalBytesOfOctalCodes();
|
||||
statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] =
|
||||
statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] =
|
||||
(double)OctreePacketData::getTotalBytesOfBitMasks();
|
||||
statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] = (double)OctreePacketData::getTotalBytesOfColor();
|
||||
|
||||
|
@ -1406,19 +1411,19 @@ void OctreeServer::sendStatsPacket() {
|
|||
|
||||
static QJsonObject statsObject3;
|
||||
|
||||
statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] =
|
||||
statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] =
|
||||
(double)_octreeInboundPacketProcessor->getTotalPacketsProcessed();
|
||||
statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] =
|
||||
statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] =
|
||||
(double)_octreeInboundPacketProcessor->getTotalElementsProcessed();
|
||||
statsObject3[baseName + QString(".3.inbound.timing.1.avgTransitTimePerPacket")] =
|
||||
statsObject3[baseName + QString(".3.inbound.timing.1.avgTransitTimePerPacket")] =
|
||||
(double)_octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
|
||||
statsObject3[baseName + QString(".3.inbound.timing.2.avgProcessTimePerPacket")] =
|
||||
statsObject3[baseName + QString(".3.inbound.timing.2.avgProcessTimePerPacket")] =
|
||||
(double)_octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
|
||||
statsObject3[baseName + QString(".3.inbound.timing.3.avgLockWaitTimePerPacket")] =
|
||||
statsObject3[baseName + QString(".3.inbound.timing.3.avgLockWaitTimePerPacket")] =
|
||||
(double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
|
||||
statsObject3[baseName + QString(".3.inbound.timing.4.avgProcessTimePerElement")] =
|
||||
statsObject3[baseName + QString(".3.inbound.timing.4.avgProcessTimePerElement")] =
|
||||
(double)_octreeInboundPacketProcessor->getAverageProcessTimePerElement();
|
||||
statsObject3[baseName + QString(".3.inbound.timing.5.avgLockWaitTimePerElement")] =
|
||||
statsObject3[baseName + QString(".3.inbound.timing.5.avgLockWaitTimePerElement")] =
|
||||
(double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
|
||||
|
||||
DependencyManager::get<NodeList>()->sendStatsToDomainServer(statsObject3);
|
||||
|
|
14
cmake/externals/boostconfig/CMakeLists.txt
vendored
14
cmake/externals/boostconfig/CMakeLists.txt
vendored
|
@ -3,13 +3,13 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/boostorg/config/archive/boost-1.58.0.zip
|
||||
URL_MD5 42fa673bae2b7645a22736445e80eb8d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/boostorg/config/archive/boost-1.58.0.zip
|
||||
URL_MD5 42fa673bae2b7645a22736445e80eb8d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
|
14
cmake/externals/oglplus/CMakeLists.txt
vendored
14
cmake/externals/oglplus/CMakeLists.txt
vendored
|
@ -3,13 +3,13 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://softlayer-dal.dl.sourceforge.net/project/oglplus/oglplus-0.61.x/oglplus-0.61.0.zip
|
||||
URL_MD5 bb55038c36c660d2b6c7be380414fa60
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
${EXTERNAL_NAME}
|
||||
GIT_REPOSITORY https://github.com/matus-chochlik/oglplus.git
|
||||
GIT_TAG a2681383928b1166f176512cbe0f95e96fe68d08
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
Script.load("progress.js");
|
||||
Script.load("edit.js");
|
||||
Script.load("selectAudioDevice.js");
|
||||
Script.load("controllers/hydra/hydraMove.js");
|
||||
Script.load("inspect.js");
|
||||
Script.load("lobby.js");
|
||||
Script.load("notifications.js");
|
||||
Script.load("users.js");
|
||||
Script.load("grab.js");
|
||||
Script.load("pointer.js");
|
||||
Script.load("directory.js");
|
||||
Script.load("mouseLook.js");
|
||||
Script.load("dialTone.js");
|
||||
|
|
|
@ -11,19 +11,26 @@
|
|||
//
|
||||
|
||||
// setup the local sound we're going to use
|
||||
var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/hello.wav");
|
||||
var disconnectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/goodbye.wav");
|
||||
var connectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/hello.wav");
|
||||
var disconnectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/goodbye.wav");
|
||||
var micMutedSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/goodbye.wav");
|
||||
|
||||
// setup the options needed for that sound
|
||||
var connectSoundOptions = {
|
||||
var soundOptions = {
|
||||
localOnly: true
|
||||
};
|
||||
|
||||
// play the sound locally once we get the first audio packet from a mixer
|
||||
Audio.receivedFirstPacket.connect(function(){
|
||||
Audio.playSound(connectSound, connectSoundOptions);
|
||||
Audio.playSound(connectSound, soundOptions);
|
||||
});
|
||||
|
||||
Audio.disconnected.connect(function(){
|
||||
Audio.playSound(disconnectSound, connectSoundOptions);
|
||||
Audio.playSound(disconnectSound, soundOptions);
|
||||
});
|
||||
|
||||
AudioDevice.muteToggled.connect(function () {
|
||||
if (AudioDevice.getMuted()) {
|
||||
Audio.playSound(micMutedSound, soundOptions);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -354,7 +354,8 @@
|
|||
var elZoneAtmosphereScatteringWavelengthsZ = document.getElementById("property-zone-atmosphere-scattering-wavelengths-z");
|
||||
var elZoneAtmosphereHasStars = document.getElementById("property-zone-atmosphere-has-stars");
|
||||
|
||||
var elPolyVoxSelections = document.querySelectorAll(".poly-vox-section");
|
||||
var elPolyVoxSections = document.querySelectorAll(".poly-vox-section");
|
||||
allSections.push(elPolyVoxSections);
|
||||
var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x");
|
||||
var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y");
|
||||
var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z");
|
||||
|
@ -602,6 +603,10 @@
|
|||
elParticleLocalGravity.value = properties.localGravity.toFixed(2);
|
||||
elParticleRadius.value = properties.particleRadius.toFixed(3);
|
||||
} else if (properties.type == "PolyVox") {
|
||||
for (var i = 0; i < elPolyVoxSections.length; i++) {
|
||||
elPolyVoxSections[i].style.display = 'block';
|
||||
}
|
||||
|
||||
elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2);
|
||||
elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2);
|
||||
elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2);
|
||||
|
@ -1014,9 +1019,9 @@
|
|||
<div class="poly-vox-section property">
|
||||
<div class="label">Voxel Volume Size</div>
|
||||
<div class="value">
|
||||
<div class="input-area">X <br><input class="coord" type='number' id="property-voxel-volume-size-x"></input></div>
|
||||
<div class="input-area">Y <br><input class="coord" type='number' id="property-voxel-volume-size-y"></input></div>
|
||||
<div class="input-area">Z <br><input class="coord" type='number' id="property-voxel-volume-size-z"></input></div>
|
||||
<div class="input-area">X <br> <input class="coord" type='number' id="property-voxel-volume-size-x"></input></div>
|
||||
<div class="input-area">Y <br><input class="coord" type='number' id="property-voxel-volume-size-y"></input></div>
|
||||
<div class="input-area">Z <br><input class="coord" type='number' id="property-voxel-volume-size-z"></input></div>
|
||||
</div>
|
||||
|
||||
<div class="label">Surface Extractor</div>
|
||||
|
|
|
@ -8,7 +8,7 @@ body {
|
|||
background-color: rgb(76, 76, 76);
|
||||
color: rgb(204, 204, 204);
|
||||
font-family: Arial;
|
||||
font-size: 8.25pt;
|
||||
font-size: 9.0pt;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
|
@ -58,6 +58,7 @@ body {
|
|||
|
||||
.multi-property-section {
|
||||
}
|
||||
|
||||
.property-section {
|
||||
display: block;
|
||||
margin: 10 10;
|
||||
|
@ -132,7 +133,7 @@ input.no-spin::-webkit-inner-spin-button {
|
|||
table#entity-table {
|
||||
border-collapse: collapse;
|
||||
font-family: Sans-Serif;
|
||||
font-size: 7.5pt;
|
||||
font-size: 9pt;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
@ -156,7 +157,7 @@ table#entity-table {
|
|||
}
|
||||
|
||||
#entity-table td {
|
||||
font-size: 8.25pt;
|
||||
font-size: 9.0pt;
|
||||
border: 0pt black solid;
|
||||
word-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
|
@ -176,14 +177,14 @@ th#entity-type {
|
|||
|
||||
div.input-area {
|
||||
display: inline-block;
|
||||
font-size: 7.5pt;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
input {
|
||||
}
|
||||
|
||||
#type {
|
||||
font-size: 10.5pt;
|
||||
font-size: 9.0pt;
|
||||
}
|
||||
|
||||
#type label {
|
||||
|
@ -191,14 +192,14 @@ input {
|
|||
}
|
||||
|
||||
input, textarea {
|
||||
background-color: rgb(102, 102, 102);
|
||||
color: rgb(204, 204, 204);
|
||||
background-color: rgb(63, 63, 63);
|
||||
color: rgb(255, 255, 255);
|
||||
border: none;
|
||||
font-size: 7.5pt;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
input:disabled, textarea:disabled {
|
||||
background-color: rgb(102, 102, 102);
|
||||
background-color: rgb(63, 63, 63);
|
||||
color: rgb(160, 160, 160);
|
||||
}
|
||||
|
||||
|
@ -224,7 +225,7 @@ input:disabled, textarea:disabled {
|
|||
#properties-list .property {
|
||||
padding: 4pt;
|
||||
border-bottom: 0.75pt solid rgb(63, 63, 63);
|
||||
min-height: 1em;
|
||||
min-height: 12pt;
|
||||
}
|
||||
|
||||
|
||||
|
@ -245,7 +246,6 @@ table#properties-list {
|
|||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
vertical-align: middle;
|
||||
height: 1.2em;
|
||||
}
|
||||
|
@ -270,9 +270,7 @@ div.inner {
|
|||
}
|
||||
|
||||
td {
|
||||
|
||||
|
||||
vertical-align: top;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#no-entities {
|
||||
|
|
200
examples/mouseLook.js
Normal file
200
examples/mouseLook.js
Normal file
|
@ -0,0 +1,200 @@
|
|||
//
|
||||
// mouseLook.js
|
||||
// examples
|
||||
//
|
||||
// Created by Sam Gondelman on 6/16/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var lastX = Window.getCursorPositionX();
|
||||
var lastY = Window.getCursorPositionY();
|
||||
var yawFromMouse = 0;
|
||||
var pitchFromMouse = 0;
|
||||
|
||||
var yawSpeed = 0;
|
||||
var pitchSpeed = 0;
|
||||
|
||||
var DEFAULT_PARAMETERS = {
|
||||
MOUSE_YAW_SCALE: -0.125,
|
||||
MOUSE_PITCH_SCALE: -0.125,
|
||||
MOUSE_SENSITIVITY: 0.5,
|
||||
|
||||
// Damping frequency, adjust to change mouse look behavior
|
||||
W: 10,
|
||||
}
|
||||
|
||||
var movementParameters = DEFAULT_PARAMETERS;
|
||||
|
||||
var mouseLook = (function () {
|
||||
|
||||
var BUTTON_WIDTH = 50,
|
||||
BUTTON_HEIGHT = 50,
|
||||
BUTTON_ALPHA = 0.9,
|
||||
BUTTON_MARGIN = 8,
|
||||
active = false,
|
||||
keyboardID = 0;
|
||||
|
||||
function onKeyPressEvent(event) {
|
||||
if (event.text == 'm' && event.isMeta) {
|
||||
active = !active;
|
||||
updateMapping();
|
||||
}
|
||||
}
|
||||
|
||||
function findInput(name) {
|
||||
var availableInputs = Controller.getAvailableInputs(keyboardID);
|
||||
for (i = 0; i < availableInputs.length; i++) {
|
||||
if (availableInputs[i].inputName == name) {
|
||||
return availableInputs[i].input;
|
||||
}
|
||||
}
|
||||
// If the input isn't found, it will default to the first available input
|
||||
return availableInputs[0].input;
|
||||
}
|
||||
|
||||
function findAction(name) {
|
||||
var actions = Controller.getAllActions();
|
||||
for (var i = 0; i < actions.length; i++) {
|
||||
if (actions[i].actionName == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// If the action isn't found, it will default to the first available action
|
||||
return 0;
|
||||
}
|
||||
|
||||
function updateMapping() {
|
||||
if (keyboardID != 0) {
|
||||
if (active) {
|
||||
// Turn mouselook on
|
||||
yawFromMouse = 0;
|
||||
pitchFromMouse = 0;
|
||||
yawSpeed = 0;
|
||||
pitchSpeed = 0;
|
||||
|
||||
var a = findInput("A").channel;
|
||||
var d = findInput("D").channel;
|
||||
var left = findInput("Left").channel;
|
||||
var right = findInput("Right").channel;
|
||||
var shift = findInput("Shift").channel;
|
||||
|
||||
for (var i = findAction("YAW_LEFT"); i <= findAction("YAW_RIGHT"); i++) {
|
||||
var inputChannels = Controller.getAllActions()[i].inputChannels;
|
||||
for (var j = 0; j < inputChannels.length; j++) {
|
||||
var inputChannel = inputChannels[j];
|
||||
// make a, d, left, and right strafe
|
||||
if ((inputChannel.input.channel == a || inputChannel.input.channel == left) && inputChannel.modifier.device == 0) {
|
||||
Controller.removeInputChannel(inputChannel);
|
||||
inputChannel.action = findAction("LATERAL_LEFT");
|
||||
Controller.addInputChannel(inputChannel);
|
||||
} else if ((inputChannel.input.channel == d || inputChannel.input.channel == right) && inputChannel.modifier.device == 0) {
|
||||
Controller.removeInputChannel(inputChannel);
|
||||
inputChannel.action = findAction("LATERAL_RIGHT");
|
||||
Controller.addInputChannel(inputChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i = findAction("LATERAL_LEFT"); i <= findAction("LATERAL_RIGHT"); i++) {
|
||||
var inputChannels = Controller.getAllActions()[i].inputChannels;
|
||||
for (var j = 0; j < inputChannels.length; j++) {
|
||||
var inputChannel = inputChannels[j];
|
||||
// make shift + a/d/left/right change yaw/pitch
|
||||
if ((inputChannel.input.channel == a || inputChannel.input.channel == left) && inputChannel.modifier.channel == shift) {
|
||||
Controller.removeInputChannel(inputChannel);
|
||||
inputChannel.action = findAction("YAW_LEFT");
|
||||
Controller.addInputChannel(inputChannel);
|
||||
} else if ((inputChannel.input.channel == d || inputChannel.input.channel == right) && inputChannel.modifier.channel == shift) {
|
||||
Controller.removeInputChannel(inputChannel);
|
||||
inputChannel.action = findAction("YAW_RIGHT");
|
||||
Controller.addInputChannel(inputChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Controller.resetDevice(keyboardID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onScriptUpdate(dt) {
|
||||
if (active && Window.hasFocus()) {
|
||||
var x = Window.getCursorPositionX();
|
||||
// I'm not sure why this + 0.5 is necessary?
|
||||
var y = Window.getCursorPositionY() + 0.5;
|
||||
|
||||
yawFromMouse += ((x - lastX) * movementParameters.MOUSE_YAW_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||
pitchFromMouse += ((y - lastY) * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||
pitchFromMouse = Math.max(-180, Math.min(180, pitchFromMouse));
|
||||
|
||||
resetCursorPosition();
|
||||
|
||||
// Here we use a linear damping model - http://en.wikipedia.org/wiki/Damping#Linear_damping
|
||||
// Because we are using a critically damped model (no oscillation), ζ = 1 and
|
||||
// so we derive the formula: acceleration = -(2 * w0 * v) - (w0^2 * x)
|
||||
var W = movementParameters.W;
|
||||
yawAccel = (W * W * yawFromMouse) - (2 * W * yawSpeed);
|
||||
pitchAccel = (W * W * pitchFromMouse) - (2 * W * pitchSpeed);
|
||||
|
||||
yawSpeed += yawAccel * dt;
|
||||
var yawMove = yawSpeed * dt;
|
||||
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees( { x: 0, y: yawMove, z: 0 } ));
|
||||
MyAvatar.orientation = newOrientation;
|
||||
yawFromMouse -= yawMove;
|
||||
|
||||
pitchSpeed += pitchAccel * dt;
|
||||
var pitchMove = pitchSpeed * dt;
|
||||
var newPitch = MyAvatar.headPitch + pitchMove;
|
||||
MyAvatar.headPitch = newPitch;
|
||||
pitchFromMouse -= pitchMove;
|
||||
}
|
||||
}
|
||||
|
||||
function resetCursorPosition() {
|
||||
var newX = Window.x + Window.innerWidth / 2.0;
|
||||
var newY = Window.y + Window.innerHeight / 2.0;
|
||||
Window.setCursorPosition(newX, newY);
|
||||
lastX = newX;
|
||||
lastY = newY;
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
keyboardID = Controller.findDevice("Keyboard");
|
||||
|
||||
Controller.keyPressEvent.connect(onKeyPressEvent);
|
||||
|
||||
Menu.menuItemEvent.connect(handleMenuEvent);
|
||||
|
||||
Script.update.connect(onScriptUpdate);
|
||||
}
|
||||
|
||||
function setupMenu() {
|
||||
Menu.addMenuItem({ menuName: "View", menuItemName: "Mouselook Mode", shortcutKey: "META+M",
|
||||
afterItem: "Mirror", isCheckable: true, isChecked: false });
|
||||
}
|
||||
|
||||
setupMenu();
|
||||
|
||||
function cleanupMenu() {
|
||||
Menu.removeMenuItem("View", "Mouselook Mode");
|
||||
}
|
||||
|
||||
function handleMenuEvent(menuItem) {
|
||||
if (menuItem == "Mouselook Mode") {
|
||||
active = !active;
|
||||
updateMapping();
|
||||
}
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
if (keyboardID != 0) {
|
||||
Controller.resetDevice(keyboardID);
|
||||
}
|
||||
cleanupMenu();
|
||||
}
|
||||
|
||||
setUp();
|
||||
Script.scriptEnding.connect(tearDown);
|
||||
}());
|
|
@ -22,10 +22,12 @@ Script.setTimeout(function() {
|
|||
type: "Model",
|
||||
modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
|
||||
compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
|
||||
dimensions: {x: .11, y: .11, z: .59},
|
||||
dimensions: {x: .11, y: .11, z: 1.0},
|
||||
position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
|
||||
rotation: MyAvatar.orientation,
|
||||
damping: .1,
|
||||
collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav",
|
||||
restitution: 0.01,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9},
|
||||
|
|
|
@ -120,6 +120,23 @@ Slider = function(x,y,width,thumbSize) {
|
|||
|
||||
this.onValueChanged = function(value) {};
|
||||
|
||||
this.setMaxValue = function(maxValue) {
|
||||
if (this.maxValue == maxValue) {
|
||||
return;
|
||||
}
|
||||
var currentVal = this.getValue();
|
||||
this.maxValue = maxValue;
|
||||
this.setValue(currentVal);
|
||||
}
|
||||
this.setMinValue = function(minValue) {
|
||||
if (this.minValue == minValue) {
|
||||
return;
|
||||
}
|
||||
var currentVal = this.getValue();
|
||||
this.minValue = minValue;
|
||||
this.setValue(currentVal);
|
||||
}
|
||||
|
||||
this.destroy = function() {
|
||||
Overlays.deleteOverlay(this.background);
|
||||
Overlays.deleteOverlay(this.thumb);
|
||||
|
@ -613,6 +630,14 @@ Panel = function(x, y) {
|
|||
return null;
|
||||
}
|
||||
|
||||
this.getWidget = function(name) {
|
||||
var item = this.items[name];
|
||||
if (item != null) {
|
||||
return item.widget;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
this.update = function(name) {
|
||||
var item = this.items[name];
|
||||
if (item != null) {
|
||||
|
|
|
@ -10,25 +10,7 @@
|
|||
|
||||
Script.include("cookies.js");
|
||||
|
||||
var panel = new Panel(10, 400);
|
||||
|
||||
panel.newCheckbox("Enable Cull Opaque",
|
||||
function(value) { Scene.setEngineCullOpaque((value != 0)); },
|
||||
function() { return Scene.doEngineCullOpaque(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newCheckbox("Enable Sort Opaque",
|
||||
function(value) { Scene.setEngineSortOpaque((value != 0)); },
|
||||
function() { return Scene.doEngineSortOpaque(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newCheckbox("Enable Render Opaque",
|
||||
function(value) { Scene.setEngineRenderOpaque((value != 0)); },
|
||||
function() { return Scene.doEngineRenderOpaque(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
var panel = new Panel(10, 800);
|
||||
|
||||
panel.newSlider("Num Feed Opaques", 0, 1000,
|
||||
function(value) { },
|
||||
|
@ -48,24 +30,6 @@ panel.newSlider("Max Drawn Opaques", -1, 1000,
|
|||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newCheckbox("Enable Cull Transparent",
|
||||
function(value) { Scene.setEngineCullTransparent((value != 0)); },
|
||||
function() { return Scene.doEngineCullTransparent(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newCheckbox("Enable Sort Transparent",
|
||||
function(value) { Scene.setEngineSortTransparent((value != 0)); },
|
||||
function() { return Scene.doEngineSortTransparent(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newCheckbox("Enable Render Transparent",
|
||||
function(value) { Scene.setEngineRenderTransparent((value != 0)); },
|
||||
function() { return Scene.doEngineRenderTransparent(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newSlider("Num Feed Transparents", 0, 100,
|
||||
function(value) { },
|
||||
function() { return Scene.getEngineNumFeedTransparentItems(); },
|
||||
|
@ -84,13 +48,52 @@ panel.newSlider("Max Drawn Transparents", -1, 100,
|
|||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newSlider("Num Feed Overlay3Ds", 0, 100,
|
||||
function(value) { },
|
||||
function() { return Scene.getEngineNumFeedOverlay3DItems(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newSlider("Num Drawn Overlay3Ds", 0, 100,
|
||||
function(value) { },
|
||||
function() { return Scene.getEngineNumDrawnOverlay3DItems(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newSlider("Max Drawn Overlay3Ds", -1, 100,
|
||||
function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); },
|
||||
function() { return Scene.getEngineMaxDrawnOverlay3DItems(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
var tickTackPeriod = 500;
|
||||
|
||||
function updateCounters() {
|
||||
panel.set("Num Feed Opaques", panel.get("Num Feed Opaques"));
|
||||
panel.set("Num Drawn Opaques", panel.get("Num Drawn Opaques"));
|
||||
panel.set("Num Feed Transparents", panel.get("Num Feed Transparents"));
|
||||
panel.set("Num Drawn Transparents", panel.get("Num Drawn Transparents"));
|
||||
var numFeedOpaques = panel.get("Num Feed Opaques");
|
||||
var numFeedTransparents = panel.get("Num Feed Transparents");
|
||||
var numFeedOverlay3Ds = panel.get("Num Feed Overlay3Ds");
|
||||
|
||||
panel.set("Num Feed Opaques", numFeedOpaques);
|
||||
panel.set("Num Drawn Opaques", panel.get("Num Drawn Opaques"));
|
||||
panel.set("Num Feed Transparents", numFeedTransparents);
|
||||
panel.set("Num Drawn Transparents", panel.get("Num Drawn Transparents"));
|
||||
panel.set("Num Feed Overlay3Ds", numFeedOverlay3Ds);
|
||||
panel.set("Num Drawn Overlay3Ds", panel.get("Num Drawn Overlay3Ds"));
|
||||
|
||||
var numMax = Math.max(numFeedOpaques * 1.2, 1);
|
||||
panel.getWidget("Num Feed Opaques").setMaxValue(numMax);
|
||||
panel.getWidget("Num Drawn Opaques").setMaxValue(numMax);
|
||||
panel.getWidget("Max Drawn Opaques").setMaxValue(numMax);
|
||||
|
||||
numMax = Math.max(numFeedTransparents * 1.2, 1);
|
||||
panel.getWidget("Num Feed Transparents").setMaxValue(numMax);
|
||||
panel.getWidget("Num Drawn Transparents").setMaxValue(numMax);
|
||||
panel.getWidget("Max Drawn Transparents").setMaxValue(numMax);
|
||||
|
||||
numMax = Math.max(numFeedOverlay3Ds * 1.2, 1);
|
||||
panel.getWidget("Num Feed Overlay3Ds").setMaxValue(numMax);
|
||||
panel.getWidget("Num Drawn Overlay3Ds").setMaxValue(numMax);
|
||||
panel.getWidget("Max Drawn Overlay3Ds").setMaxValue(numMax);
|
||||
}
|
||||
Script.setInterval(updateCounters, tickTackPeriod);
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS})
|
|||
|
||||
# link required hifi libraries
|
||||
link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars
|
||||
audio audio-client animation script-engine physics
|
||||
audio audio-client auto-updater animation script-engine physics
|
||||
render-utils entities-renderer ui plugins display-plugins)
|
||||
|
||||
add_dependency_external_projects(sdl2)
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
viewBox="0 0 1440 200"
|
||||
id="svg4136"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="address-bar.svg">
|
||||
sodipodi:docname="address-bar.002.svg">
|
||||
<metadata
|
||||
id="metadata4144">
|
||||
<rdf:RDF>
|
||||
|
@ -39,14 +39,14 @@
|
|||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1840"
|
||||
inkscape:window-width="1835"
|
||||
inkscape:window-height="1057"
|
||||
id="namedview4140"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.8671875"
|
||||
inkscape:cx="707.02439"
|
||||
inkscape:zoom="0.61319416"
|
||||
inkscape:cx="132.58366"
|
||||
inkscape:cy="52.468468"
|
||||
inkscape:window-x="72"
|
||||
inkscape:window-x="77"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4136" />
|
||||
|
@ -59,6 +59,15 @@
|
|||
y="30"
|
||||
rx="16.025024"
|
||||
ry="17.019567" />
|
||||
<rect
|
||||
style="fill:#dadada;fill-opacity:1;stroke:#cbcbcb;stroke-width:0.33821851;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4135"
|
||||
width="292.86267"
|
||||
height="139.66179"
|
||||
x="150.32542"
|
||||
y="30.169102"
|
||||
rx="16.817432"
|
||||
ry="20.612938" />
|
||||
<circle
|
||||
style="fill:#b8b8b8;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="path4146"
|
||||
|
@ -69,4 +78,11 @@
|
|||
d="m 100,36.000005 c -22.1,0 -40,17.9 -40,39.999995 0,30 40,88 40,88 0,0 40,-58 40,-88 0,-22.099995 -17.9,-39.999995 -40,-39.999995 z m 0,22 c 9.9,0 18,8.099995 18,17.999995 0,9.9 -8.1,18 -18,18 -9.9,0 -18,-8.1 -18,-18 0,-9.9 8.1,-17.999995 18,-17.999995 z"
|
||||
id="path4138"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
style="fill:#bdbdbd;fill-opacity:1;stroke:none;stroke-width:0.30000001;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4136"
|
||||
width="4"
|
||||
height="100"
|
||||
x="310.12924"
|
||||
y="50" />
|
||||
</svg>
|
||||
|
|
Before (image error) Size: 2.2 KiB After (image error) Size: 2.8 KiB |
BIN
interface/resources/images/arrow.png
Normal file
BIN
interface/resources/images/arrow.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 3.5 KiB |
BIN
interface/resources/images/darkgreyarrow.png
Normal file
BIN
interface/resources/images/darkgreyarrow.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 369 B |
11
interface/resources/images/darkgreyarrow.svg
Normal file
11
interface/resources/images/darkgreyarrow.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:xl="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="133.1 714.2 21.3 33.4"
|
||||
enable-background="new 133.1 714.2 21.3 33.4" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#535353" d="M133.1,714.2l21.3,16.7l-21.3,16.7V714.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 501 B |
50
interface/resources/images/left-arrow.svg
Normal file
50
interface/resources/images/left-arrow.svg
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="133.1 714.2 21.3 33.4"
|
||||
enable-background="new 133.1 714.2 21.3 33.4"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="left-arrow.svg"><metadata
|
||||
id="metadata13"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs11" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1835"
|
||||
inkscape:window-height="1057"
|
||||
id="namedview9"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.0658679"
|
||||
inkscape:cx="10.65"
|
||||
inkscape:cy="16.700001"
|
||||
inkscape:window-x="77"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" /><g
|
||||
id="g3"
|
||||
transform="matrix(-1,0,0,1,287.5,0)"><g
|
||||
id="g5"><path
|
||||
d="m 133.1,714.2 21.3,16.7 -21.3,16.7 0,-33.4 z"
|
||||
id="path7"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#535353" /></g></g></svg>
|
After (image error) Size: 1.8 KiB |
BIN
interface/resources/images/lightgreyarrow.png
Normal file
BIN
interface/resources/images/lightgreyarrow.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 369 B |
11
interface/resources/images/lightgreyarrow.svg
Normal file
11
interface/resources/images/lightgreyarrow.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:xl="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="133.1 714.2 21.3 33.4"
|
||||
enable-background="new 133.1 714.2 21.3 33.4" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#7E7E7E" d="M133.1,714.2l21.3,16.7l-21.3,16.7V714.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 501 B |
11
interface/resources/images/redarrow.svg
Normal file
11
interface/resources/images/redarrow.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:xl="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="133.1 714.2 21.3 33.4"
|
||||
enable-background="new 133.1 714.2 21.3 33.4" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FF5353" d="M133.1,714.2l21.3,16.7l-21.3,16.7V714.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 501 B |
11
interface/resources/images/redarrow_reversed.svg
Normal file
11
interface/resources/images/redarrow_reversed.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:xl="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="225.8 714.2 326.5 512"
|
||||
enable-background="new 225.8 714.2 326.5 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FF5353" d="M552.4,1226.2l-326.5-256l326.5-256V1226.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 503 B |
BIN
interface/resources/images/sepline.png
Normal file
BIN
interface/resources/images/sepline.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 127 B |
3
interface/resources/images/sepline.svg
Normal file
3
interface/resources/images/sepline.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="344 454 26 74" width="26pt" height="74pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2015-06-12 18:23Z</dc:date><!-- Produced by OmniGraffle Professional 5.4.4 --></metadata><defs></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="1728" height="1466"/><g><title> Navi Bar</title><line x1="356.58927" y1="466.42861" x2="356.58927" y2="515.4286" stroke="#b3b3b3" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="3"/></g></g></svg>
|
After (image error) Size: 778 B |
|
@ -30,6 +30,7 @@ DialogContainer {
|
|||
property int maximumX: parent ? parent.width - width : 0
|
||||
property int maximumY: parent ? parent.height - height : 0
|
||||
|
||||
|
||||
AddressBarDialog {
|
||||
id: addressBarDialog
|
||||
|
||||
|
@ -45,19 +46,63 @@ DialogContainer {
|
|||
property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area
|
||||
property int inputAreaStep: (height - inputAreaHeight) / 2
|
||||
|
||||
Image {
|
||||
id: backArrow
|
||||
|
||||
source: addressBarDialog.backEnabled ? "../images/left-arrow.svg" : "../images/redarrow_reversed.svg"
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: parent.height + hifi.layout.spacing + 6
|
||||
rightMargin: parent.height + hifi.layout.spacing * 60
|
||||
topMargin: parent.inputAreaStep + parent.inputAreaStep + hifi.layout.spacing
|
||||
bottomMargin: parent.inputAreaStep + parent.inputAreaStep + hifi.layout.spacing
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
addressBarDialog.loadBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: forwardArrow
|
||||
|
||||
source: addressBarDialog.forwardEnabled ? "../images/darkgreyarrow.svg" : "../images/redarrow.svg"
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: parent.height + hifi.layout.spacing * 9
|
||||
rightMargin: parent.height + hifi.layout.spacing * 53
|
||||
topMargin: parent.inputAreaStep + parent.inputAreaStep + hifi.layout.spacing
|
||||
bottomMargin: parent.inputAreaStep + parent.inputAreaStep + hifi.layout.spacing
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
addressBarDialog.loadForward()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: addressLine
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: parent.height + hifi.layout.spacing * 2
|
||||
leftMargin: parent.height + parent.height + hifi.layout.spacing * 5
|
||||
rightMargin: hifi.layout.spacing * 2
|
||||
topMargin: parent.inputAreaStep + hifi.layout.spacing
|
||||
bottomMargin: parent.inputAreaStep + hifi.layout.spacing
|
||||
|
||||
}
|
||||
|
||||
font.pixelSize: hifi.fonts.pixelSize * root.scale
|
||||
font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75
|
||||
|
||||
helperText: "Go to: place, @user, /path, network address"
|
||||
|
||||
|
@ -66,7 +111,7 @@ DialogContainer {
|
|||
addressBarDialog.loadAddress(addressLine.text)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MouseArea {
|
||||
// Drag the icon
|
||||
width: parent.height
|
||||
|
@ -82,6 +127,8 @@ DialogContainer {
|
|||
}
|
||||
}
|
||||
|
||||
// Add this code to make text bar draggable
|
||||
/*
|
||||
MouseArea {
|
||||
// Drag the input rectangle
|
||||
width: parent.width - parent.height
|
||||
|
@ -95,7 +142,7 @@ DialogContainer {
|
|||
maximumX: root.parent ? root.maximumX : 0
|
||||
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
177
interface/resources/qml/AvatarInputs.qml
Normal file
177
interface/resources/qml/AvatarInputs.qml
Normal file
|
@ -0,0 +1,177 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/06/19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Hifi.AvatarInputs {
|
||||
id: root
|
||||
objectName: "AvatarInputs"
|
||||
anchors.fill: parent
|
||||
|
||||
// width: 800
|
||||
// height: 600
|
||||
// color: "black"
|
||||
readonly property int iconPadding: 5
|
||||
readonly property int mirrorHeight: 215
|
||||
readonly property int mirrorWidth: 265
|
||||
readonly property int mirrorTopPad: iconPadding
|
||||
readonly property int mirrorLeftPad: 10
|
||||
readonly property int iconSize: 24
|
||||
|
||||
Item {
|
||||
id: mirror
|
||||
width: root.mirrorWidth
|
||||
height: root.mirrorVisible ? root.mirrorHeight : 0
|
||||
visible: root.mirrorVisible
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.mirrorLeftPad
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: root.mirrorTopPad
|
||||
clip: true
|
||||
|
||||
MouseArea {
|
||||
id: hover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
|
||||
Image {
|
||||
id: closeMirror
|
||||
visible: hover.containsMouse
|
||||
width: root.iconSize
|
||||
height: root.iconSize
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: root.iconPadding
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.iconPadding
|
||||
source: "../images/close.svg"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.closeMirror();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: zoomIn
|
||||
visible: hover.containsMouse
|
||||
width: root.iconSize
|
||||
height: root.iconSize
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: root.iconPadding
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.iconPadding
|
||||
source: root.mirrorZoomed ? "../images/minus.svg" : "../images/plus.svg"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.toggleZoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: root.mirrorWidth
|
||||
height: 44
|
||||
|
||||
x: root.mirrorLeftPad
|
||||
y: root.mirrorVisible ? root.mirrorTopPad + root.mirrorHeight : 5
|
||||
|
||||
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: root.mirrorVisible ? (root.audioClipping ? "red" : "#696969") : "#00000000"
|
||||
|
||||
Image {
|
||||
id: faceMute
|
||||
width: root.iconSize
|
||||
height: root.iconSize
|
||||
visible: root.cameraEnabled
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.iconPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
source: root.cameraMuted ? "../images/face-mute.svg" : "../images/face.svg"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.toggleCameraMute()
|
||||
}
|
||||
onDoubleClicked: {
|
||||
root.resetSensors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: micMute
|
||||
width: root.iconSize
|
||||
height: root.iconSize
|
||||
anchors.left: root.cameraEnabled ? faceMute.right : parent.left
|
||||
anchors.leftMargin: root.iconPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
source: root.audioMuted ? "../images/mic-mute.svg" : "../images/mic.svg"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.toggleAudioMute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: audioMeter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: micMute.right
|
||||
anchors.leftMargin: root.iconPadding
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.iconPadding
|
||||
height: 8
|
||||
Rectangle {
|
||||
id: blueRect
|
||||
color: "blue"
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
width: parent.width / 4
|
||||
}
|
||||
Rectangle {
|
||||
id: greenRect
|
||||
color: "green"
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: blueRect.right
|
||||
anchors.right: redRect.left
|
||||
}
|
||||
Rectangle {
|
||||
id: redRect
|
||||
color: "red"
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
width: parent.width / 5
|
||||
}
|
||||
Rectangle {
|
||||
z: 100
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
width: (1.0 - root.audioLevel) * parent.width
|
||||
color: "black"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
258
interface/resources/qml/Stats.qml
Normal file
258
interface/resources/qml/Stats.qml
Normal file
|
@ -0,0 +1,258 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 300
|
||||
Hifi.Stats {
|
||||
id: root
|
||||
objectName: "Stats"
|
||||
implicitHeight: row.height
|
||||
implicitWidth: row.width
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
readonly property int sTATS_GENERAL_MIN_WIDTH: 165
|
||||
readonly property int sTATS_PING_MIN_WIDTH: 190
|
||||
readonly property int sTATS_GEO_MIN_WIDTH: 240
|
||||
readonly property int sTATS_OCTREE_MIN_WIDTH: 410
|
||||
readonly property int fontSize: 12
|
||||
readonly property string fontColor: "white"
|
||||
readonly property string bgColor: "#99333333"
|
||||
|
||||
Row {
|
||||
id: row
|
||||
spacing: 8
|
||||
Rectangle {
|
||||
width: generalCol.width + 8;
|
||||
height: generalCol.height + 8;
|
||||
color: root.bgColor;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: { root.expanded = !root.expanded; }
|
||||
}
|
||||
|
||||
Column {
|
||||
id: generalCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
width: sTATS_GENERAL_MIN_WIDTH
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Servers: " + root.serverCount
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Avatars: " + root.avatarCount
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Framerate: " + root.framerate
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: pingCol.width + 8
|
||||
height: pingCol.height + 8
|
||||
color: root.bgColor;
|
||||
visible: root.audioPing != -2
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: { root.expanded = !root.expanded; }
|
||||
}
|
||||
Column {
|
||||
id: pingCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
width: sTATS_PING_MIN_WIDTH
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Audio ping: " + root.audioPing
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Avatar ping: " + root.avatarPing
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Entities avg ping: " + root.entitiesPing
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "Voxel max ping: " + 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: geoCol.width + 8
|
||||
height: geoCol.height + 8
|
||||
color: root.bgColor;
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: { root.expanded = !root.expanded; }
|
||||
}
|
||||
Column {
|
||||
id: geoCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
width: sTATS_GEO_MIN_WIDTH
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Position: " + root.position.x.toFixed(1) + ", " +
|
||||
root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1)
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Velocity: " + root.velocity.toFixed(1)
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Yaw: " + root.yaw.toFixed(1)
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " +
|
||||
root.avatarMixerPps + "pps";
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "Downloads: ";
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
width: octreeCol.width + 8
|
||||
height: octreeCol.height + 8
|
||||
color: root.bgColor;
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: { root.expanded = !root.expanded; }
|
||||
}
|
||||
Column {
|
||||
id: octreeCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
width: sTATS_OCTREE_MIN_WIDTH
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Triangles: " + root.triangles +
|
||||
" / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque +
|
||||
" / Translucent: " + root.meshTranslucent;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "\tOpaque considered: " + root.opaqueConsidered +
|
||||
" / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: !root.expanded
|
||||
text: "Octree Elements Server: " + root.serverElements +
|
||||
" Local: " + root.localElements;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "Octree Sending Mode: " + root.sendingMode;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "Octree Packets to Process: " + root.packetStats;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "Octree Elements - ";
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "\tServer: " + root.serverElements +
|
||||
" Internal: " + root.serverInternal +
|
||||
" Leaves: " + root.serverLeaves;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "\tLocal: " + root.localElements +
|
||||
" Internal: " + root.localInternal +
|
||||
" Leaves: " + root.localLeaves;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "LOD: " + root.lodStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
y: 250
|
||||
visible: root.timingExpanded
|
||||
width: perfText.width + 8
|
||||
height: perfText.height + 8
|
||||
color: root.bgColor;
|
||||
Text {
|
||||
x: 4; y: 4
|
||||
id: perfText
|
||||
color: root.fontColor
|
||||
font.family: root.monospaceFont
|
||||
font.pixelSize: 12
|
||||
text: "------------------------------------------ Function " +
|
||||
"--------------------------------------- --msecs- -calls--\n" +
|
||||
root.timingStats;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.parent
|
||||
onWidthChanged: {
|
||||
root.x = root.parent.width - root.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
30
interface/resources/qml/Tooltip.qml
Normal file
30
interface/resources/qml/Tooltip.qml
Normal file
|
@ -0,0 +1,30 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.3 as Original
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
Hifi.Tooltip {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
// FIXME adjust position based on the edges of the screen
|
||||
x: lastMousePosition.x + 20
|
||||
y: lastMousePosition.y + 5
|
||||
implicitWidth: border.implicitWidth
|
||||
implicitHeight: border.implicitHeight
|
||||
|
||||
Border {
|
||||
id: border
|
||||
anchors.fill: parent
|
||||
implicitWidth: text.implicitWidth
|
||||
implicitHeight: Math.max(text.implicitHeight, 64)
|
||||
|
||||
Text {
|
||||
id: text
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
font.pixelSize: hifi.fonts.pixelSize / 2
|
||||
text: root.text
|
||||
wrapMode: Original.Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
172
interface/resources/qml/UpdateDialog.qml
Normal file
172
interface/resources/qml/UpdateDialog.qml
Normal file
|
@ -0,0 +1,172 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
DialogContainer {
|
||||
HifiConstants { id: hifi }
|
||||
id: root
|
||||
objectName: "UpdateDialog"
|
||||
implicitWidth: updateDialog.width
|
||||
implicitHeight: updateDialog.height
|
||||
x: parent ? parent.width / 2 - width / 2 : 0
|
||||
y: parent ? parent.height / 2 - height / 2 : 0
|
||||
|
||||
UpdateDialog {
|
||||
id: updateDialog
|
||||
|
||||
implicitWidth: backgroundRectangle.width
|
||||
implicitHeight: backgroundRectangle.height
|
||||
|
||||
readonly property int inputWidth: 500
|
||||
readonly property int inputHeight: 60
|
||||
readonly property int borderWidth: 30
|
||||
readonly property int closeMargin: 16
|
||||
readonly property int inputSpacing: 16
|
||||
readonly property int buttonWidth: 150
|
||||
readonly property int buttonHeight: 50
|
||||
readonly property int buttonRadius: 15
|
||||
|
||||
signal triggerBuildDownload
|
||||
signal closeUpdateDialog
|
||||
|
||||
Column {
|
||||
id: mainContent
|
||||
width: updateDialog.inputWidth
|
||||
spacing: updateDialog.inputSpacing
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: backgroundRectangle
|
||||
color: "#2c86b1"
|
||||
opacity: 0.85
|
||||
radius: updateDialog.closeMargin * 2
|
||||
|
||||
width: updateDialog.inputWidth + updateDialog.borderWidth * 2
|
||||
height: updateDialog.inputHeight * 6 + updateDialog.closeMargin * 2
|
||||
|
||||
Rectangle {
|
||||
id: dialogTitle
|
||||
width: updateDialog.inputWidth
|
||||
height: updateDialog.inputHeight
|
||||
radius: height / 2
|
||||
color: "#ebebeb"
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: updateDialog.inputSpacing
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
id: updateAvailableText
|
||||
text: "Update Available"
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
leftMargin: updateDialog.inputSpacing
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: updateDialog.updateAvailableDetails
|
||||
font.pixelSize: 14
|
||||
color: hifi.colors.text
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: updateAvailableText.right
|
||||
leftMargin: 13
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: scrollArea
|
||||
anchors {
|
||||
top: dialogTitle.bottom
|
||||
}
|
||||
contentWidth: updateDialog.inputWidth
|
||||
contentHeight: backgroundRectangle.height - (dialogTitle.height * 2.5)
|
||||
width: updateDialog.inputWidth
|
||||
height: backgroundRectangle.height - (dialogTitle.height * 2.5)
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
clip: true
|
||||
|
||||
TextEdit {
|
||||
id: releaseNotes
|
||||
wrapMode: TextEdit.Wrap
|
||||
width: parent.width
|
||||
readOnly: true
|
||||
text: updateDialog.releaseNotes
|
||||
font.pixelSize: 14
|
||||
color: hifi.colors.text
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: updateDialog.borderWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: downloadButton
|
||||
width: updateDialog.buttonWidth
|
||||
height: updateDialog.buttonHeight
|
||||
radius: updateDialog.buttonRadius
|
||||
color: "green"
|
||||
anchors {
|
||||
top: scrollArea.bottom
|
||||
topMargin: 10
|
||||
right: backgroundRectangle.right
|
||||
rightMargin: 15
|
||||
}
|
||||
Text {
|
||||
text: "Upgrade"
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: downloadButtonAction
|
||||
anchors.fill: parent
|
||||
onClicked: updateDialog.triggerUpgrade()
|
||||
cursorShape: "PointingHandCursor"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: cancelButton
|
||||
width: updateDialog.buttonWidth
|
||||
height: updateDialog.buttonHeight
|
||||
radius: updateDialog.buttonRadius
|
||||
color: "red"
|
||||
anchors {
|
||||
top: scrollArea.bottom
|
||||
topMargin: 10
|
||||
right: downloadButton.left
|
||||
rightMargin: 15
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Cancel"
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: cancelButtonAction
|
||||
anchors.fill: parent
|
||||
onClicked: updateDialog.closeDialog()
|
||||
cursorShape: "PointingHandCursor"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@
|
|||
#include <CursorManager.h>
|
||||
#include <AmbientOcclusionEffect.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <AutoUpdater.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
|
@ -87,12 +88,14 @@
|
|||
#include <PerfStat.h>
|
||||
#include <PhysicsEngine.h>
|
||||
#include <ProgramObject.h>
|
||||
#include <RenderDeferredTask.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <SceneScriptingInterface.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <SoundCache.h>
|
||||
#include <TextRenderer.h>
|
||||
#include <Tooltip.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <UUID.h>
|
||||
#include <VrMenu.h>
|
||||
|
@ -111,23 +114,20 @@
|
|||
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
||||
#include "audio/AudioToolBox.h"
|
||||
#include "audio/AudioIOStatsRenderer.h"
|
||||
#include "audio/AudioScope.h"
|
||||
|
||||
#include "devices/CameraToolBox.h"
|
||||
#include "devices/DdeFaceTracker.h"
|
||||
#include "devices/Faceshift.h"
|
||||
#include "devices/Leapmotion.h"
|
||||
#include "devices/RealSense.h"
|
||||
#include "devices/SDL2Manager.h"
|
||||
#include "devices/MIDIManager.h"
|
||||
|
||||
#include "RenderDeferredTask.h"
|
||||
#include "scripting/AccountScriptingInterface.h"
|
||||
#include "scripting/AudioDeviceScriptingInterface.h"
|
||||
#include "scripting/ClipboardScriptingInterface.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
#include "scripting/JoystickScriptingInterface.h"
|
||||
#include "scripting/GlobalServicesScriptingInterface.h"
|
||||
#include "scripting/LocationScriptingInterface.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
|
@ -139,6 +139,7 @@
|
|||
#include "SpeechRecognizer.h"
|
||||
#endif
|
||||
|
||||
#include "ui/AvatarInputs.h"
|
||||
#include "ui/DataWebDialog.h"
|
||||
#include "ui/DialogsManager.h"
|
||||
#include "ui/LoginDialog.h"
|
||||
|
@ -146,6 +147,7 @@
|
|||
#include "ui/StandAloneJSConsole.h"
|
||||
#include "ui/Stats.h"
|
||||
#include "ui/AddressBarDialog.h"
|
||||
#include "ui/UpdateDialog.h"
|
||||
|
||||
|
||||
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
|
@ -193,7 +195,6 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D
|
|||
const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
|
||||
Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
class MyNativeEventFilter : public QAbstractNativeEventFilter {
|
||||
public:
|
||||
|
@ -279,8 +280,6 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||
auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>();
|
||||
auto modelBlender = DependencyManager::set<ModelBlender>();
|
||||
auto audioToolBox = DependencyManager::set<AudioToolBox>();
|
||||
auto cameraToolBox = DependencyManager::set<CameraToolBox>();
|
||||
auto avatarManager = DependencyManager::set<AvatarManager>();
|
||||
auto lodManager = DependencyManager::set<LODManager>();
|
||||
auto jsConsole = DependencyManager::set<StandAloneJSConsole>();
|
||||
|
@ -295,6 +294,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();
|
||||
auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();
|
||||
auto offscreenUi = DependencyManager::set<OffscreenUi>();
|
||||
auto autoUpdater = DependencyManager::set<AutoUpdater>();
|
||||
auto pathUtils = DependencyManager::set<PathUtils>();
|
||||
auto actionFactory = DependencyManager::set<InterfaceActionFactory>();
|
||||
|
||||
|
@ -563,8 +563,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_toolWindow->setWindowFlags(_toolWindow->windowFlags() | Qt::WindowStaysOnTopHint);
|
||||
_toolWindow->setWindowTitle("Tools");
|
||||
|
||||
|
||||
_compositor = CompositorPtr(new ApplicationOverlayCompositor());
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
// Tell our entity edit sender about our known jurisdictions
|
||||
|
@ -575,8 +573,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
// allow you to move an entity around in your hand
|
||||
_entityEditSender.setPacketsPerSecond(3000); // super high!!
|
||||
|
||||
checkVersion();
|
||||
|
||||
_overlays.init(); // do this before scripts load
|
||||
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
|
@ -645,9 +641,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
ddeTracker->init();
|
||||
connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled);
|
||||
#endif
|
||||
|
||||
auto applicationUpdater = DependencyManager::get<AutoUpdater>();
|
||||
connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);
|
||||
applicationUpdater->checkForUpdate();
|
||||
}
|
||||
|
||||
|
||||
void Application::aboutToQuit() {
|
||||
emit beforeAboutToQuit();
|
||||
|
||||
|
@ -819,9 +818,9 @@ void Application::initializeGL() {
|
|||
_idleLoopStdev.reset();
|
||||
|
||||
if (_justStarted) {
|
||||
float startupTime = (float)_applicationStartupTime.elapsed() / 1000.0;
|
||||
float startupTime = (float)_applicationStartupTime.elapsed() / 1000.0f;
|
||||
_justStarted = false;
|
||||
qCDebug(interfaceapp, "Startup time: %4.2f seconds.", startupTime);
|
||||
qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTime);
|
||||
}
|
||||
|
||||
// update before the first render
|
||||
|
@ -836,6 +835,8 @@ void Application::initializeUi() {
|
|||
LoginDialog::registerType();
|
||||
MessageDialog::registerType();
|
||||
VrMenu::registerType();
|
||||
Tooltip::registerType();
|
||||
UpdateDialog::registerType();
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create(_offscreenContext->getContext());
|
||||
|
@ -877,6 +878,7 @@ void Application::paintGL() {
|
|||
|
||||
PerformanceTimer perfTimer("paintGL");
|
||||
|
||||
|
||||
PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::paintGL()");
|
||||
|
@ -884,10 +886,19 @@ void Application::paintGL() {
|
|||
|
||||
{
|
||||
PerformanceTimer perfTimer("renderOverlay");
|
||||
/*
|
||||
gpu::Context context(new gpu::GLBackend());
|
||||
RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(),
|
||||
lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE,
|
||||
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
|
||||
*/
|
||||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
}
|
||||
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
|
||||
Menu::getInstance()->setIsOptionChecked("First Person", _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
|
||||
Application::getInstance()->cameraMenuChanged();
|
||||
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
// Always use the default eye position, not the actual head eye position.
|
||||
|
@ -906,14 +917,18 @@ void Application::paintGL() {
|
|||
}
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
static const float THIRD_PERSON_CAMERA_DISTANCE = 1.5f;
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition() +
|
||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * THIRD_PERSON_CAMERA_DISTANCE * _myAvatar->getScale());
|
||||
if (getActiveDisplayPlugin()->isHmd()) {
|
||||
if (isHMDMode()) {
|
||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation());
|
||||
} else {
|
||||
_myCamera.setRotation(_myAvatar->getHead()->getOrientation());
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition() +
|
||||
_myCamera.getRotation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale());
|
||||
} else {
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition() +
|
||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale());
|
||||
}
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
|
@ -966,10 +981,8 @@ void Application::paintGL() {
|
|||
|
||||
displaySide(&renderArgs, eyeCamera);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
glm::vec2 mpos = getActiveDisplayPlugin()->getUiMousePosition();
|
||||
_rearMirrorTools->render(&renderArgs, true, QPoint(mpos.x, mpos.y));
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror) &&
|
||||
!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
renderRearViewMirror(&renderArgs, _mirrorViewRect);
|
||||
}
|
||||
}, [&] {
|
||||
|
@ -979,10 +992,8 @@ void Application::paintGL() {
|
|||
} else {
|
||||
glViewport(0, 0, size.width(), size.height());
|
||||
displaySide(&renderArgs, _myCamera);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
glm::vec2 mpos = getActiveDisplayPlugin()->getUiMousePosition();
|
||||
_rearMirrorTools->render(&renderArgs, true, QPoint(mpos.x, mpos.y));
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror) &&
|
||||
!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
renderRearViewMirror(&renderArgs, _mirrorViewRect);
|
||||
}
|
||||
}
|
||||
|
@ -991,9 +1002,7 @@ void Application::paintGL() {
|
|||
|
||||
#if 0
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
_rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos()));
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
renderRearViewMirror(&renderArgs, _mirrorViewRect);
|
||||
}
|
||||
|
||||
|
@ -1001,6 +1010,7 @@ void Application::paintGL() {
|
|||
|
||||
auto finalFbo = DependencyManager::get<GlowEffect>()->render(&renderArgs);
|
||||
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo));
|
||||
glBlitFramebuffer(0, 0, _renderResolution.x, _renderResolution.y,
|
||||
|
@ -1008,7 +1018,7 @@ void Application::paintGL() {
|
|||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
_applicationOverlay.displayOverlayTexture();
|
||||
_compositor.displayOverlayTexture(&renderArgs);
|
||||
}
|
||||
if (!OculusManager::isConnected() || OculusManager::allowSwap()) {
|
||||
_glWidget->swapBuffers();
|
||||
|
@ -1019,14 +1029,13 @@ if (!OculusManager::isConnected() || OculusManager::allowSwap()) {
|
|||
// has completed before we start trying to read from it in another context. However
|
||||
// once we have multi-threaded rendering, this will almost certainly be critical,
|
||||
// but may be better handled with a fence object
|
||||
// glFinish();
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
_offscreenContext->doneCurrent();
|
||||
Q_ASSERT(!QOpenGLContext::currentContext());
|
||||
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync(sync);
|
||||
|
||||
GLuint finalTexture = _compositor->composite(displayPlugin,
|
||||
gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0)), finalFbo->getSize(),
|
||||
_applicationOverlay.getOverlayTexture(), getCanvasSize());
|
||||
|
||||
GLuint finalTexture = gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0));
|
||||
displayPlugin->preDisplay();
|
||||
displayPlugin->display(finalTexture, finalFbo->getSize());
|
||||
displayPlugin->finishFrame();
|
||||
|
@ -1069,7 +1078,6 @@ void Application::resizeEvent(QResizeEvent * event) {
|
|||
}
|
||||
|
||||
void Application::resizeGL() {
|
||||
|
||||
auto displayPlugin = getActiveDisplayPlugin();
|
||||
|
||||
// Set the desired FBO texture size. If it hasn't changed, this does nothing.
|
||||
|
@ -1090,10 +1098,6 @@ void Application::resizeGL() {
|
|||
if (offscreenUi->getWindow()->geometry().size() != fromGlm(uiSize)) {
|
||||
offscreenUi->resize(fromGlm(uiSize));
|
||||
_offscreenContext->makeCurrent();
|
||||
// update Stats width
|
||||
// let's set horizontal offset to give stats some margin to mirror
|
||||
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
||||
Stats::getInstance()->resetWidth(uiSize.x, horizontalOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1504,6 +1508,8 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
|
||||
void Application::focusOutEvent(QFocusEvent* event) {
|
||||
_keyboardMouseDevice.focusOutEvent(event);
|
||||
SixenseManager::getInstance().focusOutEvent();
|
||||
SDL2Manager::getInstance()->focusOutEvent();
|
||||
|
||||
// synthesize events for keys currently pressed, since we may not get their release events
|
||||
foreach (int key, _keysPressed) {
|
||||
|
@ -1569,26 +1575,9 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
_keyboardMouseDevice.mousePressEvent(event);
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
_mouseDragStarted = getTrueMousePosition();
|
||||
_mouseDragStarted = getTrueMouse();
|
||||
_mousePressed = true;
|
||||
|
||||
if (mouseOnScreen()) {
|
||||
if (DependencyManager::get<AudioToolBox>()->mousePressEvent(getMouseX(), getMouseY())) {
|
||||
// stop propagation
|
||||
return;
|
||||
}
|
||||
|
||||
if (DependencyManager::get<CameraToolBox>()->mousePressEvent(getMouseX(), getMouseY())) {
|
||||
// stop propagation
|
||||
return;
|
||||
}
|
||||
|
||||
if (_rearMirrorTools->mousePressEvent(getMouseX(), getMouseY())) {
|
||||
// stop propagation
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// nobody handled this - make it an action event on the _window object
|
||||
HFActionEvent actionEvent(HFActionEvent::startType(),
|
||||
computePickRay(event->x(), event->y()));
|
||||
|
@ -1606,16 +1595,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceI
|
|||
return;
|
||||
}
|
||||
|
||||
if (hasFocus()) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (mouseOnScreen()) {
|
||||
if (DependencyManager::get<CameraToolBox>()->mouseDoublePressEvent(getMouseX(), getMouseY())) {
|
||||
// stop propagation
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_controllerScriptingInterface.emitMouseDoublePressEvent(event);
|
||||
}
|
||||
|
||||
void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
|
@ -1637,13 +1617,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
if (event->button() == Qt::LeftButton) {
|
||||
_mousePressed = false;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && mouseOnScreen()) {
|
||||
// let's set horizontal offset to give stats some margin to mirror
|
||||
int horizontalOffset = MIRROR_VIEW_WIDTH;
|
||||
Stats::getInstance()->checkClick(getMouseX(), getMouseY(),
|
||||
getMouseDragStartedX(), getMouseDragStartedY(), horizontalOffset);
|
||||
}
|
||||
|
||||
// fire an action end event
|
||||
HFActionEvent actionEvent(HFActionEvent::endType(),
|
||||
computePickRay(event->x(), event->y()));
|
||||
|
@ -1671,13 +1644,13 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
|
|||
bool validTouch = false;
|
||||
if (hasFocus()) {
|
||||
const QList<QTouchEvent::TouchPoint>& tPoints = event->touchPoints();
|
||||
_touchAvg = glm::vec2(0);
|
||||
_touchAvg = vec2();
|
||||
int numTouches = tPoints.count();
|
||||
if (numTouches > 1) {
|
||||
for (int i = 0; i < numTouches; ++i) {
|
||||
_touchAvg += toGlm(tPoints[i].pos());
|
||||
}
|
||||
_touchAvg /= numTouches;
|
||||
_touchAvg /= (float)(numTouches);
|
||||
validTouch = true;
|
||||
}
|
||||
}
|
||||
|
@ -1868,9 +1841,9 @@ void Application::setFullscreen(bool fullscreen) {
|
|||
Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(fullscreen);
|
||||
}
|
||||
|
||||
// The following code block is useful on platforms that can have a visible
|
||||
// app menu in a fullscreen window. However the OSX mechanism hides the
|
||||
// application menu for fullscreen apps, so the check is not required.
|
||||
// The following code block is useful on platforms that can have a visible
|
||||
// app menu in a fullscreen window. However the OSX mechanism hides the
|
||||
// application menu for fullscreen apps, so the check is not required.
|
||||
#ifndef Q_OS_MAC
|
||||
if (getActiveDisplayPlugin()->isHmd()) {
|
||||
if (fullscreen) {
|
||||
|
@ -1935,16 +1908,15 @@ void Application::setEnableVRMode(bool enableVRMode) {
|
|||
// attempt to reconnect the Oculus manager - it's possible this was a workaround
|
||||
// for the sixense crash
|
||||
OculusManager::disconnect();
|
||||
OculusManager::connect();
|
||||
OculusManager::connect(_glWidget->context()->contextHandle());
|
||||
_glWidget->setFocus();
|
||||
_glWidget->makeCurrent();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
OculusManager::recalibrate();
|
||||
} else {
|
||||
OculusManager::abandonCalibration();
|
||||
|
||||
_mirrorCamera.setHmdPosition(glm::vec3());
|
||||
_mirrorCamera.setHmdRotation(glm::quat());
|
||||
_myCamera.setHmdPosition(glm::vec3());
|
||||
_myCamera.setHmdRotation(glm::quat());
|
||||
OculusManager::disconnect();
|
||||
}
|
||||
|
||||
resizeGL();
|
||||
|
@ -1963,6 +1935,18 @@ ivec2 Application::getMouseDragStarted() const {
|
|||
return getActiveDisplayPlugin()->trueMouseToUiMouse(getTrueMouseDragStarted());
|
||||
}
|
||||
|
||||
ivec2 Application::getMouse() const {
|
||||
if (isHMDMode()) {
|
||||
return _compositor.screenToOverlay(getTrueMouse());
|
||||
}
|
||||
return getTrueMouse();
|
||||
}
|
||||
|
||||
|
||||
ivec2 Application::getTrueMouseDragStarted() const {
|
||||
return _mouseDragStarted;
|
||||
}
|
||||
|
||||
FaceTracker* Application::getActiveFaceTracker() {
|
||||
auto faceshift = DependencyManager::get<Faceshift>();
|
||||
auto dde = DependencyManager::get<DdeFaceTracker>();
|
||||
|
@ -2144,13 +2128,6 @@ void Application::init() {
|
|||
_mirrorCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
|
||||
#if 0
|
||||
OculusManager::connect();
|
||||
if (OculusManager::isConnected()) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen),
|
||||
"trigger",
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
TV3DManager::connect();
|
||||
if (TV3DManager::isConnected()) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen),
|
||||
|
@ -2215,13 +2192,6 @@ void Application::init() {
|
|||
_entityClipboardRenderer.setViewFrustum(getViewFrustum());
|
||||
_entityClipboardRenderer.setTree(&_entityClipboard);
|
||||
|
||||
_rearMirrorTools = new RearMirrorTools(_mirrorViewRect);
|
||||
|
||||
connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors()));
|
||||
|
||||
// initialize the GlowEffect with our widget
|
||||
bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect);
|
||||
DependencyManager::get<GlowEffect>()->init(glow);
|
||||
|
@ -2386,14 +2356,26 @@ void Application::cameraMenuChanged() {
|
|||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
_myAvatar->setBoomLength(MyAvatar::ZOOM_MIN);
|
||||
}
|
||||
} else {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
|
||||
if (_myAvatar->getBoomLength() == MyAvatar::ZOOM_MIN) {
|
||||
_myAvatar->setBoomLength(MyAvatar::ZOOM_DEFAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
void Application::rotationModeChanged() {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||
_myAvatar->setHeadPitch(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Application::updateCamera(float deltaTime) {
|
||||
PerformanceTimer perfTimer("updateCamera");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
|
@ -2467,7 +2449,7 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
|
||||
SixenseManager::getInstance().update(deltaTime);
|
||||
JoystickScriptingInterface::getInstance().update();
|
||||
SDL2Manager::getInstance()->update();
|
||||
}
|
||||
|
||||
_userInputMapper.update(deltaTime);
|
||||
|
@ -2488,7 +2470,8 @@ void Application::update(float deltaTime) {
|
|||
_myAvatar->setDriveKeys(ROT_DOWN, _userInputMapper.getActionState(UserInputMapper::PITCH_DOWN));
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, _userInputMapper.getActionState(UserInputMapper::YAW_LEFT));
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, _userInputMapper.getActionState(UserInputMapper::YAW_RIGHT));
|
||||
|
||||
_myAvatar->setDriveKeys(BOOM_IN, _userInputMapper.getActionState(UserInputMapper::BOOM_IN));
|
||||
_myAvatar->setDriveKeys(BOOM_OUT, _userInputMapper.getActionState(UserInputMapper::BOOM_OUT));
|
||||
|
||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||
|
||||
|
@ -3081,7 +3064,7 @@ PickRay Application::computePickRay(float x, float y) const {
|
|||
y /= size.y;
|
||||
PickRay result;
|
||||
if (isHMDMode()) {
|
||||
getApplicationOverlay().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction);
|
||||
getApplicationCompositor().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction);
|
||||
} else {
|
||||
if (QThread::currentThread() == activeRenderingThread) {
|
||||
getDisplayViewFrustum()->computePickRay(x, y, result.origin, result.direction);
|
||||
|
@ -3117,7 +3100,6 @@ QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) {
|
|||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
@ -3210,6 +3192,9 @@ namespace render {
|
|||
template <> const Item::Bound payloadGetBound(const BackgroundRenderData::Pointer& stuff) { return Item::Bound(); }
|
||||
template <> void payloadRender(const BackgroundRenderData::Pointer& background, RenderArgs* args) {
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
// Background rendering decision
|
||||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
auto skybox = model::SkyboxPointer();
|
||||
|
@ -3277,7 +3262,7 @@ namespace render {
|
|||
PerformanceTimer perfTimer("atmosphere");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... atmosphere...");
|
||||
background->_environment->renderAtmospheres(args->_viewFrustum->getPosition());
|
||||
background->_environment->renderAtmospheres(batch, args->_viewFrustum->getPosition());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3286,13 +3271,13 @@ namespace render {
|
|||
|
||||
skybox = skyStage->getSkybox();
|
||||
if (skybox) {
|
||||
gpu::Batch batch;
|
||||
model::Skybox::render(batch, *(Application::getInstance()->getDisplayViewFrustum()), *skybox);
|
||||
|
||||
gpu::GLBackend::renderBatch(batch, true);
|
||||
glUseProgram(0);
|
||||
}
|
||||
}
|
||||
// FIX ME - If I don't call this renderBatch() here, then the atmosphere and skybox don't render, but it
|
||||
// seems like these payloadRender() methods shouldn't be doing this. We need to investigate why the engine
|
||||
// isn't rendering our batch
|
||||
gpu::GLBackend::renderBatch(batch, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3316,9 +3301,8 @@ void Application::displaySide(RenderArgs* renderArgs, const Camera& theCamera, b
|
|||
// FIXME just flip the texture coordinates
|
||||
// flip x if in mirror mode (also requires reversing winding order for backface culling)
|
||||
if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
glScalef(-1.0f, 1.0f, 1.0f);
|
||||
glFrontFace(GL_CW);
|
||||
|
||||
//glScalef(-1.0f, 1.0f, 1.0f);
|
||||
//glFrontFace(GL_CW);
|
||||
} else {
|
||||
glFrontFace(GL_CCW);
|
||||
}
|
||||
|
@ -3331,7 +3315,7 @@ void Application::displaySide(RenderArgs* renderArgs, const Camera& theCamera, b
|
|||
viewTransform.setTranslation(theCamera.getPosition());
|
||||
viewTransform.setRotation(rotation);
|
||||
if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f));
|
||||
// viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
|
||||
setViewTransform(viewTransform);
|
||||
|
@ -3461,6 +3445,7 @@ void Application::displaySide(RenderArgs* renderArgs, const Camera& theCamera, b
|
|||
|
||||
renderContext._maxDrawnOpaqueItems = sceneInterface->getEngineMaxDrawnOpaqueItems();
|
||||
renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems();
|
||||
renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems();
|
||||
|
||||
renderArgs->_shouldRender = LODManager::shouldRender;
|
||||
|
||||
|
@ -3477,7 +3462,9 @@ void Application::displaySide(RenderArgs* renderArgs, const Camera& theCamera, b
|
|||
|
||||
sceneInterface->setEngineFeedTransparentItems(engineRC->_numFeedTransparentItems);
|
||||
sceneInterface->setEngineDrawnTransparentItems(engineRC->_numDrawnTransparentItems);
|
||||
|
||||
|
||||
sceneInterface->setEngineFeedOverlay3DItems(engineRC->_numFeedOverlay3DItems);
|
||||
sceneInterface->setEngineDrawnOverlay3DItems(engineRC->_numDrawnOverlay3DItems);
|
||||
}
|
||||
//Render the sixense lasers
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) {
|
||||
|
@ -3569,7 +3556,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi
|
|||
_mirrorCamera.setPosition(_myAvatar->getPosition() +
|
||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale());
|
||||
|
||||
} else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) {
|
||||
} else if (!AvatarInputs::getInstance()->mirrorZoomed()) {
|
||||
_mirrorCamera.setPosition(_myAvatar->getChestPosition() +
|
||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
|
||||
|
||||
|
@ -3604,7 +3591,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi
|
|||
} else {
|
||||
// if not rendering the billboard, the region is in device independent coordinates; must convert to device
|
||||
QSize size = DependencyManager::get<TextureCache>()->getFrameBufferSize();
|
||||
float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale();
|
||||
float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale();
|
||||
int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio;
|
||||
glViewport(x, size.height() - y - height, width, height);
|
||||
glScissor(x, size.height() - y - height, width, height);
|
||||
|
@ -3617,9 +3604,6 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi
|
|||
displaySide(renderArgs, _mirrorCamera, true, billboard);
|
||||
glPopMatrix();
|
||||
|
||||
if (!billboard) {
|
||||
_rearMirrorTools->render(renderArgs, false, getActiveDisplayPlugin()->getWindow()->mapFromGlobal(QCursor::pos()));
|
||||
}
|
||||
// reset Viewport and projection matrix
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
@ -3759,7 +3743,7 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
_entityServerJurisdictions.unlock();
|
||||
|
||||
qCDebug(interfaceapp, "model server going away...... v[%f, %f, %f, %f]",
|
||||
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
|
||||
(double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s);
|
||||
|
||||
// Add the jurisditionDetails object to the list of "fade outs"
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) {
|
||||
|
@ -3845,7 +3829,8 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin
|
|||
jurisdiction->unlock();
|
||||
|
||||
qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]",
|
||||
qPrintable(serverType), rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
|
||||
qPrintable(serverType),
|
||||
(double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s);
|
||||
|
||||
// Add the jurisditionDetails object to the list of "fade outs"
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) {
|
||||
|
@ -3979,7 +3964,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
|
||||
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("Joysticks", &JoystickScriptingInterface::getInstance());
|
||||
qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue);
|
||||
|
||||
scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface);
|
||||
|
@ -4430,72 +4414,6 @@ void Application::toggleLogDialog() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::checkVersion() {
|
||||
QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL)));
|
||||
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().get(latestVersionRequest);
|
||||
connect(reply, SIGNAL(finished()), SLOT(parseVersionXml()));
|
||||
}
|
||||
|
||||
void Application::parseVersionXml() {
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
QString operatingSystem("win");
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QString operatingSystem("mac");
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
QString operatingSystem("ubuntu");
|
||||
#endif
|
||||
|
||||
QString latestVersion;
|
||||
QUrl downloadUrl;
|
||||
QString releaseNotes("Unavailable");
|
||||
QNetworkReply* sender = qobject_cast<QNetworkReply*>(QObject::sender());
|
||||
|
||||
QXmlStreamReader xml(sender);
|
||||
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) {
|
||||
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == operatingSystem)) {
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "version") {
|
||||
xml.readNext();
|
||||
latestVersion = xml.text().toString();
|
||||
}
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "url") {
|
||||
xml.readNext();
|
||||
downloadUrl = QUrl(xml.text().toString());
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
|
||||
if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) {
|
||||
new UpdateDialog(desktop(), releaseNotes, latestVersion, downloadUrl);
|
||||
}
|
||||
sender->deleteLater();
|
||||
}
|
||||
|
||||
bool Application::shouldSkipVersion(QString latestVersion) {
|
||||
QFile skipFile(SKIP_FILENAME);
|
||||
skipFile.open(QIODevice::ReadWrite);
|
||||
QString skipVersion(skipFile.readAll());
|
||||
return (skipVersion == latestVersion || applicationVersion() == "dev");
|
||||
}
|
||||
|
||||
void Application::skipVersion(QString latestVersion) {
|
||||
QFile skipFile(SKIP_FILENAME);
|
||||
skipFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
skipFile.seek(0);
|
||||
skipFile.write(latestVersion.toStdString().c_str());
|
||||
}
|
||||
|
||||
void Application::takeSnapshot() {
|
||||
#if 0
|
||||
QMediaPlayer* player = new QMediaPlayer();
|
||||
|
@ -4695,15 +4613,9 @@ void Application::postLambdaEvent(std::function<void()> f) {
|
|||
}
|
||||
|
||||
void Application::initPlugins() {
|
||||
#if 0
|
||||
OculusManager::init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::shutdownPlugins() {
|
||||
#if 0
|
||||
OculusManager::deinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
glm::vec3 Application::getHeadPosition() const {
|
||||
|
@ -4730,6 +4642,10 @@ bool Application::isThrottleRendering() const {
|
|||
return getActiveDisplayPlugin()->isThrottled();
|
||||
}
|
||||
|
||||
ivec2 Application::getTrueMouse() const {
|
||||
return getActiveDisplayPlugin()->getTrueMousePosition();
|
||||
}
|
||||
|
||||
bool Application::hasFocus() const {
|
||||
return getActiveDisplayPlugin()->hasFocus();
|
||||
}
|
||||
|
@ -4738,10 +4654,6 @@ glm::vec2 Application::getViewportDimensions() const {
|
|||
return toGlm(getDeviceSize());
|
||||
}
|
||||
|
||||
glm::ivec2 Application::getTrueMousePosition() const {
|
||||
return getActiveDisplayPlugin()->getTrueMousePosition();
|
||||
}
|
||||
|
||||
void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) {
|
||||
if (maxOctreePPS != _maxOctreePPS) {
|
||||
_maxOctreePPS = maxOctreePPS;
|
||||
|
@ -4808,10 +4720,6 @@ void Application::updateDisplayMode() {
|
|||
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
||||
}
|
||||
|
||||
glm::ivec2 Application::getMouse() const {
|
||||
return getActiveDisplayPlugin()->getUiMousePosition();
|
||||
}
|
||||
|
||||
void Application::addMenuItem(const QString& path, std::function<void()> onClicked, bool checkable, bool checked, const QString& groupName) {
|
||||
|
||||
}
|
||||
|
@ -4819,3 +4727,19 @@ void Application::addMenuItem(const QString& path, std::function<void()> onClick
|
|||
GlWindow* Application::getVisibleWindow() {
|
||||
return _glWindow;
|
||||
}
|
||||
|
||||
mat4 Application::getEyeProjection(int eye) const {
|
||||
if (isHMDMode()) {
|
||||
return getActiveDisplayPlugin()->getProjection((Eye)eye, _viewFrustum.getProjection());
|
||||
}
|
||||
|
||||
return _viewFrustum.getProjection();
|
||||
}
|
||||
|
||||
mat4 Application::getEyePose(int eye) const {
|
||||
if (isHMDMode()) {
|
||||
return getActiveDisplayPlugin()->getEyePose((Eye)eye);
|
||||
}
|
||||
|
||||
return mat4();
|
||||
}
|
||||
|
|
|
@ -61,13 +61,12 @@
|
|||
#include "ui/ModelsBrowser.h"
|
||||
#include "ui/NodeBounds.h"
|
||||
#include "ui/OctreeStatsDialog.h"
|
||||
#include "ui/RearMirrorTools.h"
|
||||
#include "ui/SnapshotShareDialog.h"
|
||||
#include "ui/LodToolsDialog.h"
|
||||
#include "ui/LogDialog.h"
|
||||
#include "ui/UpdateDialog.h"
|
||||
#include "ui/overlays/Overlays.h"
|
||||
#include "ui/ApplicationOverlay.h"
|
||||
#include "ui/ApplicationCompositor.h"
|
||||
#include "ui/RunningScriptsWidget.h"
|
||||
#include "ui/ToolWindow.h"
|
||||
#include "ui/UserInputMapper.h"
|
||||
|
@ -92,8 +91,6 @@ class Node;
|
|||
class ProgramObject;
|
||||
class ScriptEngine;
|
||||
class GlWindow;
|
||||
class ApplicationOverlayCompositor;
|
||||
using CompositorPtr = std::shared_ptr<ApplicationOverlayCompositor>;
|
||||
|
||||
static const float NODE_ADDED_RED = 0.0f;
|
||||
static const float NODE_ADDED_GREEN = 1.0f;
|
||||
|
@ -227,22 +224,20 @@ public:
|
|||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
bool mouseOnScreen() const;
|
||||
|
||||
inline glm::ivec2 getMouse() const;
|
||||
inline int getMouseX() const { return getMouse().x; }
|
||||
inline int getMouseY() const { return getMouse().y; }
|
||||
|
||||
inline glm::ivec2 getTrueMousePosition() const;
|
||||
inline int getTrueMouseX() const { return getTrueMousePosition().x; }
|
||||
inline int getTrueMouseY() const { return getTrueMousePosition().y; }
|
||||
|
||||
inline glm::ivec2 getMouseDragStarted() const;
|
||||
inline int getMouseDragStartedX() const { return getMouseDragStarted().x; }
|
||||
inline int getMouseDragStartedY() const { return getMouseDragStarted().y; }
|
||||
|
||||
inline const glm::ivec2& getTrueMouseDragStarted() const { return _mouseDragStarted; }
|
||||
inline int getTrueMouseDragStartedX() const { return getTrueMouseDragStarted().x; }
|
||||
inline int getTrueMouseDragStartedY() const { return getTrueMouseDragStarted().y; }
|
||||
ivec2 getMouse() const;
|
||||
ivec2 getTrueMouse() const;
|
||||
ivec2 getMouseDragStarted() const;
|
||||
ivec2 getTrueMouseDragStarted() const;
|
||||
|
||||
// TODO get rid of these and use glm types directly
|
||||
int getMouseX() const { return getMouse().x; }
|
||||
int getMouseY() const { return getMouse().y; }
|
||||
int getTrueMouseX() const { return getTrueMouse().x; }
|
||||
int getTrueMouseY() const { return getTrueMouse().y; }
|
||||
int getMouseDragStartedX() const { return getMouseDragStarted().x; }
|
||||
int getMouseDragStartedY() const { return getMouseDragStarted().y; }
|
||||
int getTrueMouseDragStartedX() const { return getTrueMouseDragStarted().x; }
|
||||
int getTrueMouseDragStartedY() const { return getTrueMouseDragStarted().y; }
|
||||
bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; }
|
||||
|
||||
FaceTracker* getActiveFaceTracker();
|
||||
|
@ -251,6 +246,8 @@ public:
|
|||
QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
|
||||
ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
|
||||
const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; }
|
||||
ApplicationCompositor& getApplicationCompositor() { return _compositor; }
|
||||
const ApplicationCompositor& getApplicationCompositor() const { return _compositor; }
|
||||
Overlays& getOverlays() { return _overlays; }
|
||||
|
||||
float getFps() const { return _fps; }
|
||||
|
@ -318,8 +315,6 @@ public:
|
|||
|
||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||
|
||||
void skipVersion(QString latestVersion);
|
||||
|
||||
QStringList getRunningScripts() { return _scriptEnginesHash.keys(); }
|
||||
ScriptEngine* getScriptEngine(QString scriptHash) { return _scriptEnginesHash.contains(scriptHash) ? _scriptEnginesHash[scriptHash] : NULL; }
|
||||
|
||||
|
@ -339,6 +334,9 @@ public:
|
|||
bool isHMDMode() const;
|
||||
glm::quat getHeadOrientation() const;
|
||||
glm::vec3 getHeadPosition() const;
|
||||
glm::mat4 getHeadPose() const;
|
||||
glm::mat4 getEyePose(int eye) const;
|
||||
glm::mat4 getEyeProjection(int eye) const;
|
||||
|
||||
QRect getDesirableApplicationGeometry();
|
||||
RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; }
|
||||
|
@ -466,8 +464,6 @@ private slots:
|
|||
void restoreMirrorView();
|
||||
void shrinkMirrorView();
|
||||
|
||||
void parseVersionXml();
|
||||
|
||||
void manageRunningScriptsWidgetVisibility(bool shown);
|
||||
|
||||
void runTests();
|
||||
|
@ -557,27 +553,21 @@ private:
|
|||
ViewFrustum _shadowViewFrustum;
|
||||
quint64 _lastQueriedTime;
|
||||
|
||||
CompositorPtr _compositor;
|
||||
|
||||
float _trailingAudioLoudness;
|
||||
|
||||
OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
|
||||
|
||||
KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
|
||||
|
||||
UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer
|
||||
|
||||
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
|
||||
|
||||
Camera _myCamera; // My view onto the world
|
||||
KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
|
||||
UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer
|
||||
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
|
||||
Camera _myCamera; // My view onto the world
|
||||
Camera _mirrorCamera; // Cammera for mirror view
|
||||
QRect _mirrorViewRect;
|
||||
RearMirrorTools* _rearMirrorTools;
|
||||
|
||||
Setting::Handle<bool> _firstRun;
|
||||
Setting::Handle<QString> _previousScriptLocation;
|
||||
Setting::Handle<QString> _scriptsLocationHandle;
|
||||
Setting::Handle<float> _fieldOfView;
|
||||
Setting::Handle<bool> _firstRun;
|
||||
Setting::Handle<QString> _previousScriptLocation;
|
||||
Setting::Handle<QString> _scriptsLocationHandle;
|
||||
Setting::Handle<float> _fieldOfView;
|
||||
|
||||
Transform _viewTransform;
|
||||
glm::mat4 _projectionMatrix;
|
||||
|
@ -597,15 +587,17 @@ private:
|
|||
Environment _environment;
|
||||
|
||||
bool _cursorVisible;
|
||||
glm::ivec2 _mouseDragStarted;
|
||||
ivec2 _mouseDragStarted;
|
||||
|
||||
quint64 _lastMouseMove;
|
||||
bool _lastMouseMoveWasSimulated;
|
||||
|
||||
glm::vec3 _mouseRayOrigin;
|
||||
glm::vec3 _mouseRayDirection;
|
||||
|
||||
glm::vec2 _touchAvg;
|
||||
glm::vec2 _touchDragStartedAvg;
|
||||
vec2 _touchAvg;
|
||||
vec2 _touchDragStartedAvg;
|
||||
|
||||
bool _isTouchPressed; // true if multitouch has been pressed (clear when finished)
|
||||
|
||||
bool _mousePressed; // true if mouse has been pressed (clear when finished)
|
||||
|
@ -637,9 +629,6 @@ private:
|
|||
|
||||
FileLogger* _logger;
|
||||
|
||||
void checkVersion();
|
||||
void displayUpdateDialog();
|
||||
bool shouldSkipVersion(QString latestVersion);
|
||||
void takeSnapshot();
|
||||
|
||||
TouchEvent _lastTouchEvent;
|
||||
|
@ -685,6 +674,7 @@ private:
|
|||
|
||||
Overlays _overlays;
|
||||
ApplicationOverlay _applicationOverlay;
|
||||
ApplicationCompositor _compositor;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -46,9 +46,6 @@ public:
|
|||
CameraMode getMode() const { return _mode; }
|
||||
void setMode(CameraMode m);
|
||||
|
||||
const glm::mat4& getProjection() const { return _projection; }
|
||||
void setProjection(const glm::mat4& projection);
|
||||
|
||||
void loadViewFrustum(ViewFrustum& frustum) const;
|
||||
ViewFrustum toViewFrustum() const;
|
||||
|
||||
|
@ -56,18 +53,21 @@ public slots:
|
|||
QString getModeString() const;
|
||||
void setModeString(const QString& mode);
|
||||
|
||||
const glm::quat getRotation() const { return _rotation; }
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
void setRotation(const glm::quat& rotation);
|
||||
|
||||
const glm::vec3 getPosition() const { return _position; }
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
void setPosition(const glm::vec3& position);
|
||||
|
||||
const glm::quat getOrientation() const { return getRotation(); }
|
||||
const glm::quat& getOrientation() const { return getRotation(); }
|
||||
void setOrientation(const glm::quat& orientation) { setRotation(orientation); }
|
||||
|
||||
const glm::mat4 getTransform() const { return _transform; }
|
||||
const glm::mat4& getTransform() const { return _transform; }
|
||||
void setTransform(const glm::mat4& transform);
|
||||
|
||||
const glm::mat4& getProjection() const { return _projection; }
|
||||
void setProjection(const glm::mat4& projection);
|
||||
|
||||
PickRay computePickRay(float x, float y);
|
||||
|
||||
// These only work on independent cameras
|
||||
|
|
|
@ -36,11 +36,11 @@ bool GLCanvas::isThrottleRendering() const {
|
|||
}
|
||||
|
||||
int GLCanvas::getDeviceWidth() const {
|
||||
return width() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f);
|
||||
return width() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f);
|
||||
}
|
||||
|
||||
int GLCanvas::getDeviceHeight() const {
|
||||
return height() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f);
|
||||
return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f);
|
||||
}
|
||||
|
||||
void GLCanvas::initializeGL() {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
|
||||
#include <avatar/AvatarActionHold.h>
|
||||
#include <ObjectActionPullToPoint.h>
|
||||
#include <ObjectActionOffset.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
|
||||
#include "InterfaceActionFactory.h"
|
||||
|
@ -27,8 +27,8 @@ EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation
|
|||
switch (type) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return nullptr;
|
||||
case ACTION_TYPE_PULL_TO_POINT:
|
||||
action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
|
||||
case ACTION_TYPE_OFFSET:
|
||||
action = (EntityActionPointer) new ObjectActionOffset(id, ownerEntity);
|
||||
break;
|
||||
case ACTION_TYPE_SPRING:
|
||||
action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
|
||||
|
|
|
@ -205,11 +205,11 @@ QString LODManager::getLODFeedbackText() {
|
|||
int relativeToTwentyTwenty = 20 / relativeToDefault;
|
||||
|
||||
QString result;
|
||||
if (relativeToDefault > 1.01) {
|
||||
if (relativeToDefault > 1.01f) {
|
||||
result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',2).arg(granularityFeedback);
|
||||
} else if (relativeToDefault > 0.99) {
|
||||
} else if (relativeToDefault > 0.99f) {
|
||||
result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback);
|
||||
} else if (relativeToDefault > 0.01) {
|
||||
} else if (relativeToDefault > 0.01f) {
|
||||
result = QString("20:%1 or %2 of default distance for average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',3).arg(granularityFeedback);
|
||||
} else {
|
||||
result = QString("%2 of default distance for average vision%3").arg(relativeToDefault,0,'f',3).arg(granularityFeedback);
|
||||
|
|
|
@ -90,6 +90,30 @@ Menu::Menu() {
|
|||
addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
||||
qApp, SLOT(toggleRunningScriptsWidget()));
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "History");
|
||||
|
||||
QAction* backAction = addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Back,
|
||||
0,
|
||||
addressManager.data(),
|
||||
SLOT(goBack()));
|
||||
|
||||
QAction* forwardAction = addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Forward,
|
||||
0,
|
||||
addressManager.data(),
|
||||
SLOT(goForward()));
|
||||
|
||||
// connect to the AddressManager signal to enable and disable the back and forward menu items
|
||||
connect(addressManager.data(), &AddressManager::goBackPossible, backAction, &QAction::setEnabled);
|
||||
connect(addressManager.data(), &AddressManager::goForwardPossible, forwardAction, &QAction::setEnabled);
|
||||
|
||||
// set the two actions to start disabled since the stacks are clear on startup
|
||||
backAction->setDisabled(true);
|
||||
forwardAction->setDisabled(true);
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Location");
|
||||
qApp->getBookmarks()->setupMenus(this, fileMenu);
|
||||
|
||||
|
@ -98,7 +122,6 @@ Menu::Menu() {
|
|||
Qt::CTRL | Qt::Key_L,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleAddressBar()));
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0,
|
||||
addressManager.data(), SLOT(copyAddress()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0,
|
||||
|
@ -261,6 +284,9 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
|
||||
0, // QML Qt::Key_H,
|
||||
false, qApp, SLOT(cameraMenuChanged()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
|
||||
0, false, qApp, SLOT(rotationModeChanged()));
|
||||
|
||||
#if 0
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
|
||||
|
@ -409,7 +435,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_F, true, // DDE face tracking is on by default
|
||||
qApp, SLOT(toggleFaceTrackerMute()));
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, false);
|
||||
#endif
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
|
|
@ -149,6 +149,7 @@ namespace MenuOption {
|
|||
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
|
||||
const QString AutoMuteAudio = "Auto Mute Microphone";
|
||||
const QString AvatarReceiveStats = "Show Receive Stats";
|
||||
const QString Back = "Back";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString BinaryEyelidControl = "Binary Eyelid Control";
|
||||
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
|
||||
|
@ -193,8 +194,10 @@ namespace MenuOption {
|
|||
const QString Faceshift = "Faceshift";
|
||||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
const QString FirstPerson = "First Person";
|
||||
const QString Forward = "Forward";
|
||||
const QString FrameTimer = "Show Timer";
|
||||
const QString FullscreenMirror = "Fullscreen Mirror";
|
||||
const QString CenterPlayerInView = "Center Player In View";
|
||||
const QString GlowWhenSpeaking = "Glow When Speaking";
|
||||
const QString NamesAboveHeads = "Names Above Heads";
|
||||
const QString GoToUser = "Go To User";
|
||||
|
|
|
@ -33,19 +33,19 @@ _basePath(basePath),
|
|||
_geometry(geometry)
|
||||
{
|
||||
setWindowTitle("Set Model Properties");
|
||||
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
setLayout(form);
|
||||
|
||||
|
||||
form->addRow("Name:", _name = new QLineEdit());
|
||||
|
||||
|
||||
form->addRow("Texture Directory:", _textureDirectory = new QPushButton());
|
||||
connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory()));
|
||||
|
||||
|
||||
form->addRow("Scale:", _scale = new QDoubleSpinBox());
|
||||
_scale->setMaximum(FLT_MAX);
|
||||
_scale->setSingleStep(0.01);
|
||||
|
||||
|
||||
if (_modelType != FSTReader::ENTITY_MODEL) {
|
||||
if (_modelType == FSTReader::ATTACHMENT_MODEL) {
|
||||
QHBoxLayout* translation = new QHBoxLayout();
|
||||
|
@ -57,7 +57,7 @@ _geometry(geometry)
|
|||
form->addRow("Pivot Joint:", _pivotJoint = createJointBox());
|
||||
connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint()));
|
||||
_pivotAboutCenter->setChecked(true);
|
||||
|
||||
|
||||
} else {
|
||||
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
|
||||
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
|
||||
|
@ -69,22 +69,22 @@ _geometry(geometry)
|
|||
form->addRow("Head Joint:", _headJoint = createJointBox());
|
||||
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
|
||||
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
|
||||
|
||||
|
||||
form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
|
||||
QPushButton* newFreeJoint = new QPushButton("New Free Joint");
|
||||
_freeJoints->addWidget(newFreeJoint);
|
||||
connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel | QDialogButtonBox::Reset);
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
connect(buttons->button(QDialogButtonBox::Reset), SIGNAL(clicked(bool)), SLOT(reset()));
|
||||
|
||||
|
||||
form->addRow(buttons);
|
||||
|
||||
|
||||
// reset to initialize the fields
|
||||
reset();
|
||||
}
|
||||
|
@ -100,42 +100,42 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
|
|||
mapping.insert(NAME_FIELD, _name->text());
|
||||
mapping.insert(TEXDIR_FIELD, _textureDirectory->text());
|
||||
mapping.insert(SCALE_FIELD, QString::number(_scale->value()));
|
||||
|
||||
|
||||
// update the joint indices
|
||||
QVariantHash jointIndices;
|
||||
for (int i = 0; i < _geometry.joints.size(); i++) {
|
||||
jointIndices.insert(_geometry.joints.at(i).name, QString::number(i));
|
||||
}
|
||||
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
|
||||
|
||||
|
||||
if (_modelType != FSTReader::ENTITY_MODEL) {
|
||||
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
||||
if (_modelType == FSTReader::ATTACHMENT_MODEL) {
|
||||
glm::vec3 pivot;
|
||||
if (_pivotAboutCenter->isChecked()) {
|
||||
pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
|
||||
|
||||
|
||||
} else if (_pivotJoint->currentIndex() != 0) {
|
||||
pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
|
||||
}
|
||||
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value());
|
||||
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value());
|
||||
mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value());
|
||||
|
||||
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * (float)_scale->value() + (float)_translationX->value());
|
||||
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * (float)_scale->value() + (float)_translationY->value());
|
||||
mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * (float)_scale->value() + (float)_translationZ->value());
|
||||
|
||||
} else {
|
||||
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) {
|
||||
insertJointMapping(joints, "jointRoot", _rootJoint->currentText());
|
||||
insertJointMapping(joints, "jointLean", _leanJoint->currentText());
|
||||
insertJointMapping(joints, "jointHead", _headJoint->currentText());
|
||||
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
|
||||
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
|
||||
|
||||
|
||||
mapping.remove(FREE_JOINT_FIELD);
|
||||
for (int i = 0; i < _freeJoints->count() - 1; i++) {
|
||||
QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
|
||||
|
@ -144,7 +144,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
|
|||
}
|
||||
mapping.insert(JOINT_FIELD, joints);
|
||||
}
|
||||
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
@ -156,9 +156,9 @@ void ModelPropertiesDialog::reset() {
|
|||
_name->setText(_originalMapping.value(NAME_FIELD).toString());
|
||||
_textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString());
|
||||
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
|
||||
|
||||
|
||||
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
||||
|
||||
|
||||
if (_modelType != FSTReader::ENTITY_MODEL) {
|
||||
if (_modelType == FSTReader::ATTACHMENT_MODEL) {
|
||||
_translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble());
|
||||
|
@ -166,20 +166,20 @@ void ModelPropertiesDialog::reset() {
|
|||
_translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble());
|
||||
_pivotAboutCenter->setChecked(true);
|
||||
_pivotJoint->setCurrentIndex(0);
|
||||
|
||||
|
||||
} else {
|
||||
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
|
||||
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
|
||||
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
|
||||
}
|
||||
|
||||
|
||||
if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) {
|
||||
setJointText(_rootJoint, jointHash.value("jointRoot").toString());
|
||||
setJointText(_leanJoint, jointHash.value("jointLean").toString());
|
||||
setJointText(_headJoint, jointHash.value("jointHead").toString());
|
||||
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
|
||||
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
|
||||
|
||||
|
||||
while (_freeJoints->count() > 1) {
|
||||
delete _freeJoints->itemAt(0)->widget();
|
||||
}
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
#include "InterfaceConfig.h"
|
||||
#include "starfield/Controller.h"
|
||||
|
||||
Stars::Stars() :
|
||||
Stars::Stars() :
|
||||
_controller(0l), _starsLoaded(false) {
|
||||
_controller = new starfield::Controller;
|
||||
}
|
||||
|
||||
Stars::~Stars() {
|
||||
delete _controller;
|
||||
Stars::~Stars() {
|
||||
delete _controller;
|
||||
}
|
||||
|
||||
bool Stars::generate(unsigned numStars, unsigned seed) {
|
||||
|
@ -30,22 +30,20 @@ bool Stars::generate(unsigned numStars, unsigned seed) {
|
|||
return _starsLoaded;
|
||||
}
|
||||
|
||||
bool Stars::setResolution(unsigned k) {
|
||||
return _controller->setResolution(k);
|
||||
bool Stars::setResolution(unsigned k) {
|
||||
return _controller->setResolution(k);
|
||||
}
|
||||
|
||||
void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
|
||||
// determine length of screen diagonal from quadrant height and aspect ratio
|
||||
float quadrantHeight = nearZ * tan(RADIANS_PER_DEGREE * fovY * 0.5f);
|
||||
float quadrantHeight = nearZ * tanf(RADIANS_PER_DEGREE * fovY * 0.5f);
|
||||
float halfDiagonal = sqrt(quadrantHeight * quadrantHeight * (1.0f + aspect * aspect));
|
||||
|
||||
// determine fov angle in respect to the diagonal
|
||||
float fovDiagonal = atan(halfDiagonal / nearZ) * 2.0f;
|
||||
float fovDiagonal = atanf(halfDiagonal / nearZ) * 2.0f;
|
||||
|
||||
// pull the modelview matrix off the GL stack
|
||||
glm::mat4 view; glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(view));
|
||||
glm::mat4 view; glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(view));
|
||||
|
||||
_controller->render(fovDiagonal, aspect, glm::affineInverse(view), alpha);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -70,6 +70,6 @@ void UIUtil::internalScaleWidgetFontSizes(QWidget* widget, float scale) {
|
|||
}
|
||||
|
||||
QFont font = widget->font();
|
||||
font.setPointSizeF(font.pointSizeF() * scale);
|
||||
font.setPointSizeF(font.pointSizeF() * (double)scale);
|
||||
widget->setFont(font);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ void renderWorldBox(gpu::Batch& batch) {
|
|||
static const glm::vec3 green(0.0f, 1.0f, 0.0f);
|
||||
static const glm::vec3 blue(0.0f, 0.0f, 1.0f);
|
||||
static const glm::vec3 grey(0.5f, 0.5f, 0.5f);
|
||||
|
||||
|
||||
auto transform = Transform{};
|
||||
batch.setModelTransform(transform);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(TREE_SCALE, 0.0f, 0.0f), red);
|
||||
|
@ -49,13 +49,13 @@ void renderWorldBox(gpu::Batch& batch) {
|
|||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, TREE_SCALE), blue);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), grey);
|
||||
geometryCache->renderLine(batch, glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, 0.0f), grey);
|
||||
|
||||
|
||||
// Draw meter markers along the 3 axis to help with measuring things
|
||||
const float MARKER_DISTANCE = 1.0f;
|
||||
const float MARKER_RADIUS = 0.05f;
|
||||
|
||||
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red);
|
||||
|
||||
|
||||
transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f));
|
||||
batch.setModelTransform(transform);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red);
|
||||
|
@ -79,10 +79,10 @@ const glm::vec3 randVector() {
|
|||
}
|
||||
|
||||
static TextRenderer* textRenderer(int mono) {
|
||||
static TextRenderer* monoRenderer = TextRenderer::getInstance(MONO_FONT_FAMILY);
|
||||
static TextRenderer* monoRenderer = TextRenderer::getInstance(MONO_FONT_FAMILY);
|
||||
static TextRenderer* proportionalRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY,
|
||||
-1, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
static TextRenderer* inconsolataRenderer = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, -1, INCONSOLATA_FONT_WEIGHT,
|
||||
static TextRenderer* inconsolataRenderer = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, -1, INCONSOLATA_FONT_WEIGHT,
|
||||
false);
|
||||
switch (mono) {
|
||||
case 1:
|
||||
|
@ -133,46 +133,46 @@ void runTimingTests() {
|
|||
QElapsedTimer startTime;
|
||||
startTime.start();
|
||||
float elapsedUsecs;
|
||||
|
||||
|
||||
float NSEC_TO_USEC = 1.0f / 1000.0f;
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs);
|
||||
|
||||
qCDebug(interfaceapp, "QElapsedTimer::nsecElapsed() usecs: %f", (double)elapsedUsecs);
|
||||
|
||||
// Test sleep functions for accuracy
|
||||
startTime.start();
|
||||
QThread::msleep(1);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "QThread::msleep(1) ms: %f", elapsedUsecs / 1000.0f);
|
||||
qCDebug(interfaceapp, "QThread::msleep(1) ms: %f", (double)(elapsedUsecs / 1000.0f));
|
||||
|
||||
startTime.start();
|
||||
QThread::sleep(1);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "QThread::sleep(1) ms: %f", elapsedUsecs / 1000.0f);
|
||||
qCDebug(interfaceapp, "QThread::sleep(1) ms: %f", (double)(elapsedUsecs / 1000.0f));
|
||||
|
||||
startTime.start();
|
||||
usleep(1);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "usleep(1) ms: %f", elapsedUsecs / 1000.0f);
|
||||
qCDebug(interfaceapp, "usleep(1) ms: %f", (double)(elapsedUsecs / 1000.0f));
|
||||
|
||||
startTime.start();
|
||||
usleep(10);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "usleep(10) ms: %f", elapsedUsecs / 1000.0f);
|
||||
qCDebug(interfaceapp, "usleep(10) ms: %f", (double)(elapsedUsecs / 1000.0f));
|
||||
|
||||
startTime.start();
|
||||
usleep(100);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "usleep(100) ms: %f", elapsedUsecs / 1000.0f);
|
||||
qCDebug(interfaceapp, "usleep(100) ms: %f", (double)(elapsedUsecs / 1000.0f));
|
||||
|
||||
startTime.start();
|
||||
usleep(1000);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "usleep(1000) ms: %f", elapsedUsecs / 1000.0f);
|
||||
qCDebug(interfaceapp, "usleep(1000) ms: %f", (double)(elapsedUsecs / 1000.0f));
|
||||
|
||||
startTime.start();
|
||||
usleep(15000);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "usleep(15000) ms: %f", elapsedUsecs / 1000.0f);
|
||||
qCDebug(interfaceapp, "usleep(15000) ms: %f", (double)(elapsedUsecs / 1000.0f));
|
||||
|
||||
// Random number generation
|
||||
startTime.start();
|
||||
|
@ -180,7 +180,8 @@ void runTimingTests() {
|
|||
iResults[i] = rand();
|
||||
}
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "rand() stored in array usecs: %f, first result:%d", elapsedUsecs / (float) numTests, iResults[0]);
|
||||
qCDebug(interfaceapp, "rand() stored in array usecs: %f, first result:%d",
|
||||
(double)(elapsedUsecs / numTests), iResults[0]);
|
||||
|
||||
// Random number generation using randFloat()
|
||||
startTime.start();
|
||||
|
@ -188,7 +189,8 @@ void runTimingTests() {
|
|||
fResults[i] = randFloat();
|
||||
}
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "randFloat() stored in array usecs: %f, first result: %f", elapsedUsecs / (float) numTests, fResults[0]);
|
||||
qCDebug(interfaceapp, "randFloat() stored in array usecs: %f, first result: %f",
|
||||
(double)(elapsedUsecs / numTests), (double)(fResults[0]));
|
||||
|
||||
free(iResults);
|
||||
free(fResults);
|
||||
|
@ -200,7 +202,7 @@ void runTimingTests() {
|
|||
fTest = powf(fTest, 0.5f);
|
||||
}
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "powf(f, 0.5) usecs: %f", elapsedUsecs / (float) numTests);
|
||||
qCDebug(interfaceapp, "powf(f, 0.5) usecs: %f", (double)(elapsedUsecs / (float) numTests));
|
||||
|
||||
// Vector Math
|
||||
float distance;
|
||||
|
@ -213,19 +215,20 @@ void runTimingTests() {
|
|||
}
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "vector math usecs: %f [%f usecs total for %d tests], last result:%f",
|
||||
elapsedUsecs / (float) numTests, elapsedUsecs, numTests, distance);
|
||||
(double)(elapsedUsecs / (float) numTests), (double)elapsedUsecs, numTests, (double)distance);
|
||||
|
||||
// Vec3 test
|
||||
glm::vec3 vecA(randVector()), vecB(randVector());
|
||||
float result;
|
||||
|
||||
|
||||
startTime.start();
|
||||
for (int i = 0; i < numTests; i++) {
|
||||
glm::vec3 temp = vecA-vecB;
|
||||
result = glm::dot(temp,temp);
|
||||
}
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qCDebug(interfaceapp, "vec3 assign and dot() usecs: %f, last result:%f", elapsedUsecs / (float) numTests, result);
|
||||
qCDebug(interfaceapp, "vec3 assign and dot() usecs: %f, last result:%f",
|
||||
(double)(elapsedUsecs / numTests), (double)result);
|
||||
}
|
||||
|
||||
bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,
|
||||
|
|
|
@ -41,32 +41,32 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) {
|
|||
if (!_isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const int linesWhenCentered = _shouldShowInjectedStreams ? 34 : 27;
|
||||
const int CENTERED_BACKGROUND_HEIGHT = STATS_HEIGHT_PER_LINE * linesWhenCentered;
|
||||
|
||||
|
||||
int lines = _shouldShowInjectedStreams ? _stats->getMixerInjectedStreamStatsMap().size() * 7 + 27 : 27;
|
||||
int statsHeight = STATS_HEIGHT_PER_LINE * lines;
|
||||
|
||||
|
||||
|
||||
|
||||
static const glm::vec4 backgroundColor = { 0.2f, 0.2f, 0.2f, 0.6f };
|
||||
int x = std::max((width - (int)STATS_WIDTH) / 2, 0);
|
||||
int y = std::max((height - CENTERED_BACKGROUND_HEIGHT) / 2, 0);
|
||||
int w = STATS_WIDTH;
|
||||
int h = statsHeight;
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(x, y, w, h, backgroundColor);
|
||||
|
||||
|
||||
int horizontalOffset = x + 5;
|
||||
int verticalOffset = y;
|
||||
|
||||
|
||||
float scale = 0.10f;
|
||||
float rotation = 0.0f;
|
||||
int font = 2;
|
||||
|
||||
|
||||
char latencyStatString[512];
|
||||
|
||||
|
||||
float audioInputBufferLatency = 0.0f, inputRingBufferLatency = 0.0f, networkRoundtripLatency = 0.0f, mixerRingBufferLatency = 0.0f, outputRingBufferLatency = 0.0f, audioOutputBufferLatency = 0.0f;
|
||||
|
||||
|
||||
AudioStreamStats downstreamAudioStreamStats = _stats->getMixerDownstreamStats();
|
||||
SharedNodePointer audioMixerNodePointer = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AudioMixer);
|
||||
if (!audioMixerNodePointer.isNull()) {
|
||||
|
@ -78,48 +78,62 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) {
|
|||
audioOutputBufferLatency = _stats->getAudioOutputMsecsUnplayedStats().getWindowAverage();
|
||||
}
|
||||
float totalLatency = audioInputBufferLatency + inputRingBufferLatency + networkRoundtripLatency + mixerRingBufferLatency + outputRingBufferLatency + audioOutputBufferLatency;
|
||||
|
||||
sprintf(latencyStatString, " Audio input buffer: %7.2fms - avg msecs of samples read to the input ring buffer in last 10s", audioInputBufferLatency);
|
||||
|
||||
sprintf(latencyStatString,
|
||||
" Audio input buffer: %7.2fms - avg msecs of samples read to the input ring buffer in last 10s",
|
||||
(double)audioInputBufferLatency);
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
|
||||
|
||||
sprintf(latencyStatString, " Input ring buffer: %7.2fms - avg msecs of samples in input ring buffer in last 10s", inputRingBufferLatency);
|
||||
|
||||
sprintf(latencyStatString,
|
||||
" Input ring buffer: %7.2fms - avg msecs of samples in input ring buffer in last 10s",
|
||||
(double)inputRingBufferLatency);
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
|
||||
|
||||
sprintf(latencyStatString, " Network to mixer: %7.2fms - half of last ping value calculated by the node list", networkRoundtripLatency / 2.0f);
|
||||
|
||||
sprintf(latencyStatString,
|
||||
" Network to mixer: %7.2fms - half of last ping value calculated by the node list",
|
||||
(double)(networkRoundtripLatency / 2.0f));
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
|
||||
|
||||
sprintf(latencyStatString, " AudioMixer ring buffer: %7.2fms - avg msecs of samples in audio mixer's ring buffer in last 10s", mixerRingBufferLatency);
|
||||
|
||||
sprintf(latencyStatString,
|
||||
" AudioMixer ring buffer: %7.2fms - avg msecs of samples in audio mixer's ring buffer in last 10s",
|
||||
(double)mixerRingBufferLatency);
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
|
||||
|
||||
sprintf(latencyStatString, " Network to client: %7.2fms - half of last ping value calculated by the node list", networkRoundtripLatency / 2.0f);
|
||||
|
||||
sprintf(latencyStatString,
|
||||
" Network to client: %7.2fms - half of last ping value calculated by the node list",
|
||||
(double)(networkRoundtripLatency / 2.0f));
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
|
||||
|
||||
sprintf(latencyStatString, " Output ring buffer: %7.2fms - avg msecs of samples in output ring buffer in last 10s", outputRingBufferLatency);
|
||||
|
||||
sprintf(latencyStatString,
|
||||
" Output ring buffer: %7.2fms - avg msecs of samples in output ring buffer in last 10s",
|
||||
(double)outputRingBufferLatency);
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
|
||||
|
||||
sprintf(latencyStatString, " Audio output buffer: %7.2fms - avg msecs of samples in audio output buffer in last 10s", audioOutputBufferLatency);
|
||||
|
||||
sprintf(latencyStatString,
|
||||
" Audio output buffer: %7.2fms - avg msecs of samples in audio output buffer in last 10s",
|
||||
(double)audioOutputBufferLatency);
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
|
||||
|
||||
sprintf(latencyStatString, " TOTAL: %7.2fms\n", totalLatency);
|
||||
|
||||
sprintf(latencyStatString, " TOTAL: %7.2fms\n", (double)totalLatency);
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
|
||||
|
||||
|
||||
|
||||
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE; // blank line
|
||||
|
||||
|
||||
char clientUpstreamMicLabelString[] = "Upstream Mic Audio Packets Sent Gaps (by client):";
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, clientUpstreamMicLabelString, color);
|
||||
|
||||
|
||||
const MovingMinMaxAvg<quint64>& packetSentTimeGaps = _stats->getPacketSentTimeGaps();
|
||||
|
||||
|
||||
char stringBuffer[512];
|
||||
sprintf(stringBuffer, " Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s",
|
||||
formatUsecTime(packetSentTimeGaps.getMin()).toLatin1().data(),
|
||||
|
@ -127,47 +141,47 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) {
|
|||
formatUsecTime(packetSentTimeGaps.getAverage()).toLatin1().data());
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
|
||||
|
||||
|
||||
sprintf(stringBuffer, " Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s",
|
||||
formatUsecTime(packetSentTimeGaps.getWindowMin()).toLatin1().data(),
|
||||
formatUsecTime(packetSentTimeGaps.getWindowMax()).toLatin1().data(),
|
||||
formatUsecTime(packetSentTimeGaps.getWindowAverage()).toLatin1().data());
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
|
||||
|
||||
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE; // blank line
|
||||
|
||||
|
||||
char upstreamMicLabelString[] = "Upstream mic audio stats (received and reported by audio-mixer):";
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamMicLabelString, color);
|
||||
|
||||
|
||||
renderAudioStreamStats(&_stats->getMixerAvatarStreamStats(), horizontalOffset, verticalOffset,
|
||||
scale, rotation, font, color);
|
||||
|
||||
|
||||
|
||||
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE; // blank line
|
||||
|
||||
|
||||
char downstreamLabelString[] = "Downstream mixed audio stats:";
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamLabelString, color);
|
||||
|
||||
|
||||
AudioStreamStats downstreamStats = _stats->getMixerDownstreamStats();
|
||||
renderAudioStreamStats(&downstreamStats, horizontalOffset, verticalOffset,
|
||||
scale, rotation, font, color, true);
|
||||
|
||||
|
||||
|
||||
|
||||
if (_shouldShowInjectedStreams) {
|
||||
|
||||
|
||||
foreach(const AudioStreamStats& injectedStreamAudioStats, _stats->getMixerInjectedStreamStatsMap()) {
|
||||
|
||||
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE; // blank line
|
||||
|
||||
|
||||
char upstreamInjectedLabelString[512];
|
||||
sprintf(upstreamInjectedLabelString, "Upstream injected audio stats: stream ID: %s",
|
||||
injectedStreamAudioStats._streamIdentifier.toString().toLatin1().data());
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamInjectedLabelString, color);
|
||||
|
||||
|
||||
renderAudioStreamStats(&injectedStreamAudioStats, horizontalOffset, verticalOffset, scale, rotation, font, color);
|
||||
}
|
||||
}
|
||||
|
@ -175,22 +189,22 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) {
|
|||
|
||||
void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* streamStats, int horizontalOffset, int& verticalOffset,
|
||||
float scale, float rotation, int font, const float* color, bool isDownstreamStats) {
|
||||
|
||||
|
||||
char stringBuffer[512];
|
||||
|
||||
|
||||
sprintf(stringBuffer, " Packet loss | overall: %5.2f%% (%d lost), last_30s: %5.2f%% (%d lost)",
|
||||
streamStats->_packetStreamStats.getLostRate() * 100.0f,
|
||||
(double)(streamStats->_packetStreamStats.getLostRate() * 100.0f),
|
||||
streamStats->_packetStreamStats._lost,
|
||||
streamStats->_packetStreamWindowStats.getLostRate() * 100.0f,
|
||||
(double)(streamStats->_packetStreamWindowStats.getLostRate() * 100.0f),
|
||||
streamStats->_packetStreamWindowStats._lost);
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
|
||||
|
||||
|
||||
if (isDownstreamStats) {
|
||||
sprintf(stringBuffer, " Ringbuffer frames | desired: %u, avg_available(10s): %u+%d, available: %u+%d",
|
||||
streamStats->_desiredJitterBufferFrames,
|
||||
streamStats->_framesAvailableAverage,
|
||||
(int)(_stats->getAudioInputMsecsReadStats().getWindowAverage() / AudioConstants::NETWORK_FRAME_MSECS),
|
||||
(int)((float)_stats->getAudioInputMsecsReadStats().getWindowAverage() / AudioConstants::NETWORK_FRAME_MSECS),
|
||||
streamStats->_framesAvailable,
|
||||
(int)(_stats->getAudioOutputMsecsUnplayedStats().getCurrentIntervalLastSample()
|
||||
/ AudioConstants::NETWORK_FRAME_MSECS));
|
||||
|
@ -200,10 +214,10 @@ void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* stream
|
|||
streamStats->_framesAvailableAverage,
|
||||
streamStats->_framesAvailable);
|
||||
}
|
||||
|
||||
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
|
||||
|
||||
|
||||
sprintf(stringBuffer, " Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u",
|
||||
streamStats->_starveCount,
|
||||
streamStats->_consecutiveNotMixedCount,
|
||||
|
@ -211,18 +225,18 @@ void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* stream
|
|||
streamStats->_overflowCount);
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
|
||||
|
||||
|
||||
sprintf(stringBuffer, " Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s",
|
||||
formatUsecTime(streamStats->_timeGapMin).toLatin1().data(),
|
||||
formatUsecTime(streamStats->_timeGapMax).toLatin1().data(),
|
||||
formatUsecTime(streamStats->_timeGapAverage).toLatin1().data());
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
|
||||
|
||||
|
||||
sprintf(stringBuffer, " Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s",
|
||||
formatUsecTime(streamStats->_timeGapWindowMin).toLatin1().data(),
|
||||
formatUsecTime(streamStats->_timeGapWindowMax).toLatin1().data(),
|
||||
formatUsecTime(streamStats->_timeGapWindowAverage).toLatin1().data());
|
||||
verticalOffset += STATS_HEIGHT_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
//
|
||||
// AudioToolBox.cpp
|
||||
// interface/src/audio
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-12-16.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <AudioClient.h>
|
||||
#include <GLCanvas.h>
|
||||
#include <PathUtils.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "AudioToolBox.h"
|
||||
|
||||
// Mute icon configration
|
||||
const int MUTE_ICON_SIZE = 24;
|
||||
|
||||
AudioToolBox::AudioToolBox() :
|
||||
_iconPulseTimeReference(usecTimestampNow())
|
||||
{
|
||||
}
|
||||
|
||||
bool AudioToolBox::mousePressEvent(int x, int y) {
|
||||
if (_iconBounds.contains(x, y)) {
|
||||
DependencyManager::get<AudioClient>()->toggleMute();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioToolBox::render(int x, int y, int padding, bool boxed) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
if (!_micTexture) {
|
||||
_micTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic.svg");
|
||||
}
|
||||
if (!_muteTexture) {
|
||||
_muteTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg");
|
||||
}
|
||||
if (_boxTexture) {
|
||||
_boxTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg");
|
||||
}
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
|
||||
if (boxed) {
|
||||
bool isClipping = ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f));
|
||||
const int BOX_LEFT_PADDING = 5;
|
||||
const int BOX_TOP_PADDING = 10;
|
||||
const int BOX_WIDTH = 266;
|
||||
const int BOX_HEIGHT = 44;
|
||||
|
||||
QRect boxBounds = QRect(x - BOX_LEFT_PADDING, y - BOX_TOP_PADDING, BOX_WIDTH, BOX_HEIGHT);
|
||||
glm::vec4 quadColor;
|
||||
|
||||
if (isClipping) {
|
||||
quadColor = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
} else {
|
||||
quadColor = glm::vec4(0.41f, 0.41f, 0.41f, 1.0f);
|
||||
}
|
||||
glm::vec2 topLeft(boxBounds.left(), boxBounds.top());
|
||||
glm::vec2 bottomRight(boxBounds.right(), boxBounds.bottom());
|
||||
static const glm::vec2 texCoordTopLeft(1,1);
|
||||
static const glm::vec2 texCoordBottomRight(0, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_boxTexture));
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
|
||||
}
|
||||
|
||||
float iconColor = 1.0f;
|
||||
|
||||
_iconBounds = QRect(x + padding, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE);
|
||||
if (!audioIO->isMuted()) {
|
||||
glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_micTexture));
|
||||
iconColor = 1.0f;
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_muteTexture));
|
||||
|
||||
// Make muted icon pulsate
|
||||
static const float PULSE_MIN = 0.4f;
|
||||
static const float PULSE_MAX = 1.0f;
|
||||
static const float PULSE_FREQUENCY = 1.0f; // in Hz
|
||||
qint64 now = usecTimestampNow();
|
||||
if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) {
|
||||
// Prevents t from getting too big, which would diminish glm::cos precision
|
||||
_iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND);
|
||||
}
|
||||
float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND;
|
||||
float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f;
|
||||
iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor;
|
||||
}
|
||||
|
||||
glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f);
|
||||
|
||||
glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top());
|
||||
glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom());
|
||||
glm::vec2 texCoordTopLeft(1,1);
|
||||
glm::vec2 texCoordBottomRight(0,0);
|
||||
|
||||
if (_boxQuadID == GeometryCache::UNKNOWN_ID) {
|
||||
_boxQuadID = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
//
|
||||
// AudioToolBox.h
|
||||
// interface/src/audio
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-12-16.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AudioToolBox_h
|
||||
#define hifi_AudioToolBox_h
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <QOpenGLTexture>
|
||||
|
||||
class AudioToolBox : public Dependency {
|
||||
SINGLETON_DEPENDENCY
|
||||
public:
|
||||
void render(int x, int y, int padding, bool boxed);
|
||||
|
||||
bool mousePressEvent(int x, int y);
|
||||
protected:
|
||||
AudioToolBox();
|
||||
private:
|
||||
gpu::TexturePointer _micTexture;
|
||||
gpu::TexturePointer _muteTexture;
|
||||
gpu::TexturePointer _boxTexture;
|
||||
int _boxQuadID = GeometryCache::UNKNOWN_ID;
|
||||
QRect _iconBounds;
|
||||
qint64 _iconPulseTimeReference = 0;
|
||||
};
|
||||
|
||||
#endif // hifi_AudioToolBox_h
|
|
@ -57,7 +57,7 @@ const float CHAT_MESSAGE_SCALE = 0.0015f;
|
|||
const float CHAT_MESSAGE_HEIGHT = 0.1f;
|
||||
const float DISPLAYNAME_FADE_TIME = 0.5f;
|
||||
const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
|
||||
const float DISPLAYNAME_ALPHA = 0.95f;
|
||||
const float DISPLAYNAME_ALPHA = 1.0f;
|
||||
const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
|
||||
|
||||
namespace render {
|
||||
|
@ -280,9 +280,9 @@ enum TextRendererType {
|
|||
};
|
||||
|
||||
static TextRenderer3D* textRenderer(TextRendererType type) {
|
||||
static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 24, -1,
|
||||
static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, -1,
|
||||
false, TextRenderer3D::SHADOW_EFFECT);
|
||||
static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 12);
|
||||
static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY);
|
||||
|
||||
switch(type) {
|
||||
case CHAT:
|
||||
|
@ -323,7 +323,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
|
|||
_referential->update();
|
||||
}
|
||||
|
||||
auto batch = renderArgs->_batch;
|
||||
auto& batch = *renderArgs->_batch;
|
||||
|
||||
if (postLighting &&
|
||||
glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) {
|
||||
|
@ -354,9 +354,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
|
|||
Transform pointerTransform;
|
||||
pointerTransform.setTranslation(position);
|
||||
pointerTransform.setRotation(rotation);
|
||||
batch->setModelTransform(pointerTransform);
|
||||
deferredLighting->bindSimpleProgram(*batch);
|
||||
geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
|
||||
batch.setModelTransform(pointerTransform);
|
||||
deferredLighting->bindSimpleProgram(batch);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,9 +377,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
|
|||
Transform pointerTransform;
|
||||
pointerTransform.setTranslation(position);
|
||||
pointerTransform.setRotation(rotation);
|
||||
batch->setModelTransform(pointerTransform);
|
||||
deferredLighting->bindSimpleProgram(*batch);
|
||||
geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
|
||||
batch.setModelTransform(pointerTransform);
|
||||
deferredLighting->bindSimpleProgram(batch);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -464,8 +464,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
|
|||
}
|
||||
Transform transform;
|
||||
transform.setTranslation(position);
|
||||
batch->setModelTransform(transform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(*batch, LOOK_AT_INDICATOR_RADIUS
|
||||
batch.setModelTransform(transform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS
|
||||
, 15, 15, LOOK_AT_INDICATOR_COLOR);
|
||||
}
|
||||
}
|
||||
|
@ -492,14 +492,14 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
|
|||
Transform transform;
|
||||
transform.setTranslation(_position);
|
||||
transform.setScale(height);
|
||||
batch->setModelTransform(transform);
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
if (_voiceSphereID == GeometryCache::UNKNOWN_ID) {
|
||||
_voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(*batch, sphereRadius, 15, 15,
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch, sphereRadius, 15, 15,
|
||||
glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true,
|
||||
_voiceSphereID);
|
||||
}
|
||||
|
@ -507,14 +507,12 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
|
|||
}
|
||||
|
||||
const float DISPLAYNAME_DISTANCE = 20.0f;
|
||||
setShowDisplayName(renderArgs->_renderMode == RenderArgs::NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE);
|
||||
setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE);
|
||||
|
||||
if (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE || (isMyAvatar() &&
|
||||
Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON)) {
|
||||
return;
|
||||
auto cameraMode = Application::getInstance()->getCamera()->getMode();
|
||||
if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
|
||||
renderDisplayName(batch, *renderArgs->_viewFrustum);
|
||||
}
|
||||
|
||||
renderDisplayName(renderArgs);
|
||||
}
|
||||
|
||||
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
||||
|
@ -654,8 +652,8 @@ float Avatar::getBillboardSize() const {
|
|||
return _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getDisplayNamePosition() {
|
||||
glm::vec3 namePosition;
|
||||
glm::vec3 Avatar::getDisplayNamePosition() const {
|
||||
glm::vec3 namePosition(0.0f);
|
||||
if (getSkeletonModel().getNeckPosition(namePosition)) {
|
||||
namePosition += getBodyUpDirection() * getHeadHeight() * 1.1f;
|
||||
} else {
|
||||
|
@ -665,81 +663,73 @@ glm::vec3 Avatar::getDisplayNamePosition() {
|
|||
return namePosition;
|
||||
}
|
||||
|
||||
float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD) {
|
||||
|
||||
// We need to compute the scale factor such as the text remains with fixed size respect to window coordinates
|
||||
// We project a unit vector and check the difference in screen coordinates, to check which is the
|
||||
// correction scale needed
|
||||
// save the matrices for later scale correction factor
|
||||
// The up vector must be relative to the rotation current rotation matrix:
|
||||
// we set the identity
|
||||
Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize) const {
|
||||
Transform result;
|
||||
// We assume textPosition is whithin the frustum
|
||||
glm::vec3 textPosition = getDisplayNamePosition();
|
||||
|
||||
// Compute viewProjection matrix
|
||||
glm::mat4 projMat, viewMat;
|
||||
Transform view;
|
||||
frustum.evalProjectionMatrix(projMat);
|
||||
frustum.evalViewTransform(view);
|
||||
glm::mat4 viewProj = projMat * view.getInverseMatrix(viewMat);
|
||||
|
||||
// Used to determine correct scale
|
||||
glm::vec3 testPoint0 = textPosition;
|
||||
glm::vec3 testPoint1 = textPosition + (Application::getInstance()->getCamera()->getRotation() * IDENTITY_UP);
|
||||
|
||||
double textWindowHeight;
|
||||
|
||||
glm::vec3 testPoint1 = testPoint0 + glm::normalize(frustum.getUp());
|
||||
// testPoints projections
|
||||
glm::vec4 p0 = viewProj * glm::vec4(testPoint0, 1.0);
|
||||
glm::vec4 p1 = viewProj * glm::vec4(testPoint1, 1.0);
|
||||
|
||||
// TODO REMOVE vvv
|
||||
GLint viewportMatrix[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewportMatrix);
|
||||
glm::dmat4 modelViewMatrix;
|
||||
float windowSizeX = viewportMatrix[2] - viewportMatrix[0];
|
||||
float windowSizeY = viewportMatrix[3] - viewportMatrix[1];
|
||||
// TODO REMOVE ^^^
|
||||
|
||||
const float DESIRED_HIGHT_ON_SCREEN = 20; // In pixels (this is double on retinas)
|
||||
|
||||
// Projected point are between -1.0f and 1.0f, hence 0.5f * windowSizeY
|
||||
double pixelHeight = 0.5f * windowSizeY * glm::abs((p1.y / p1.w) - (p0.y / p0.w)); //
|
||||
// Handles pixel density (especially for macs retina displays)
|
||||
double devicePixelRatio = qApp->getDevicePixelRatio() * qApp->getRenderResolutionScale(); // pixels / unit
|
||||
|
||||
// Compute correct scale to apply
|
||||
float scale = DESIRED_HIGHT_ON_SCREEN / (fontSize * pixelHeight) * devicePixelRatio;
|
||||
|
||||
// Compute pixel alignment offset
|
||||
float clipToPix = 0.5f * windowSizeY / p1.w; // Got from clip to pixel coordinates
|
||||
glm::vec4 screenPos = clipToPix * p1; // in pixels coords
|
||||
glm::vec4 screenOffset = (glm::round(screenPos) - screenPos) / clipToPix; // in clip coords
|
||||
glm::vec3 worldOffset = glm::vec3(screenOffset.x, screenOffset.y, 0.0f) / (float)pixelHeight;
|
||||
|
||||
// Compute orientation
|
||||
glm::vec3 eulerAngles = ::safeEulerAngles(frustum.getOrientation());
|
||||
eulerAngles.z = 0.0f; // Cancel roll
|
||||
glm::quat orientation(eulerAngles); // back to quaternions
|
||||
|
||||
// Set transform (The order IS important)
|
||||
result.setTranslation(textPosition);
|
||||
result.setRotation(orientation); // Always face the screen
|
||||
result.postTranslate(worldOffset); // Pixel alignment
|
||||
result.setScale(scale);
|
||||
return result;
|
||||
|
||||
glm::dmat4 projectionMatrix;
|
||||
Application::getInstance()->getModelViewMatrix(&modelViewMatrix);
|
||||
Application::getInstance()->getProjectionMatrix(&projectionMatrix);
|
||||
|
||||
|
||||
glm::dvec4 p0 = modelViewMatrix * glm::dvec4(testPoint0, 1.0);
|
||||
p0 = projectionMatrix * p0;
|
||||
glm::dvec2 result0 = glm::vec2(windowSizeX * (p0.x / p0.w + 1.0f) * 0.5f, windowSizeY * (p0.y / p0.w + 1.0f) * 0.5f);
|
||||
|
||||
glm::dvec4 p1 = modelViewMatrix * glm::dvec4(testPoint1, 1.0);
|
||||
p1 = projectionMatrix * p1;
|
||||
glm::vec2 result1 = glm::vec2(windowSizeX * (p1.x / p1.w + 1.0f) * 0.5f, windowSizeY * (p1.y / p1.w + 1.0f) * 0.5f);
|
||||
textWindowHeight = abs(result1.y - result0.y);
|
||||
|
||||
// need to scale to compensate for the font resolution due to the device
|
||||
float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() *
|
||||
((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f);
|
||||
if (inHMD) {
|
||||
const float HMDMODE_NAME_SCALE = 0.65f;
|
||||
scaleFactor *= HMDMODE_NAME_SCALE;
|
||||
} else {
|
||||
scaleFactor *= Application::getInstance()->getRenderResolutionScale();
|
||||
}
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
void Avatar::renderDisplayName(RenderArgs* renderArgs) {
|
||||
auto batch = renderArgs->_batch;
|
||||
|
||||
void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) const {
|
||||
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar();
|
||||
|
||||
// If we have nothing to draw, or it's tottaly transparent, return
|
||||
if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
// which viewing mode?
|
||||
bool inHMD = Application::getInstance()->isHMDMode();
|
||||
|
||||
glm::vec3 textPosition = getDisplayNamePosition();
|
||||
|
||||
// we need "always facing camera": we must remove the camera rotation from the stac
|
||||
glm::quat rotation = Application::getInstance()->getCamera()->getRotation();
|
||||
|
||||
// TODO: Fix scaling - at some point this or the text rendering changed in scale.
|
||||
float scaleFactor = calculateDisplayNameScaleFactor(textPosition, inHMD);
|
||||
scaleFactor /= 3.5f;
|
||||
|
||||
Transform textTransform;
|
||||
textTransform.setTranslation(textPosition);
|
||||
textTransform.setRotation(rotation);
|
||||
textTransform.setScale(scaleFactor);
|
||||
auto renderer = textRenderer(DISPLAYNAME);
|
||||
|
||||
// optionally render timing stats for this avatar with the display name
|
||||
QString renderedDisplayName = _displayName;
|
||||
QRect nameDynamicRect = _displayNameBoundingRect;
|
||||
|
||||
if (shouldShowReceiveStats) {
|
||||
float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
||||
|
||||
|
@ -747,42 +737,43 @@ void Avatar::renderDisplayName(RenderArgs* renderArgs) {
|
|||
if (!renderedDisplayName.isEmpty()) {
|
||||
statsFormat.prepend(" - ");
|
||||
}
|
||||
|
||||
QString statsText = statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate());
|
||||
renderedDisplayName += statsText;
|
||||
|
||||
glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(renderedDisplayName);
|
||||
nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y);
|
||||
renderedDisplayName += statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate());
|
||||
}
|
||||
|
||||
int text_x = -nameDynamicRect.width() / 2;
|
||||
int text_y = -nameDynamicRect.height() / 2;
|
||||
|
||||
// draw a gray background
|
||||
int left = text_x;
|
||||
int right = left + nameDynamicRect.width();
|
||||
int bottom = text_y;
|
||||
int top = bottom + nameDynamicRect.height();
|
||||
const int border = 8;
|
||||
bottom -= border;
|
||||
left -= border;
|
||||
top += border;
|
||||
right += border;
|
||||
|
||||
// Compute display name extent/position offset
|
||||
glm::vec2 extent = renderer->computeExtent(renderedDisplayName);
|
||||
QRect nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y);
|
||||
const int text_x = -nameDynamicRect.width() / 2;
|
||||
const int text_y = -nameDynamicRect.height() / 2;
|
||||
|
||||
// Compute background position/size
|
||||
static const float SLIGHTLY_BEHIND = -0.05f;
|
||||
const int border = 0.1f * nameDynamicRect.height();
|
||||
const int left = text_x - border;
|
||||
const int bottom = text_y - border;
|
||||
const int width = nameDynamicRect.width() + 2.0f * border;
|
||||
const int height = nameDynamicRect.height() + 2.0f * border;
|
||||
const int bevelDistance = 0.1f * height;
|
||||
|
||||
// Display name and background colors
|
||||
glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
||||
glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f,
|
||||
_displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA);
|
||||
(_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA);
|
||||
|
||||
// Compute display name transform
|
||||
auto textTransform = calculateDisplayNameTransform(frustum, renderer->getFontSize());
|
||||
|
||||
// Render background slightly behind to avoid z-fighting
|
||||
auto backgroundTransform = textTransform;
|
||||
backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, -0.001f));
|
||||
batch->setModelTransform(backgroundTransform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch);
|
||||
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(*batch, left, bottom, right - left, top - bottom, 3,
|
||||
backgroundColor);
|
||||
backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_BEHIND));
|
||||
batch.setModelTransform(backgroundTransform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
|
||||
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
|
||||
bevelDistance, backgroundColor);
|
||||
// Render actual name
|
||||
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
|
||||
|
||||
batch->setModelTransform(textTransform);
|
||||
textRenderer(DISPLAYNAME)->draw(*batch, text_x, -text_y, nameUTF8.data(), textColor);
|
||||
batch.setModelTransform(textTransform);
|
||||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
|
||||
}
|
||||
|
||||
bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const {
|
||||
|
@ -987,13 +978,6 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::setDisplayName(const QString& displayName) {
|
||||
AvatarData::setDisplayName(displayName);
|
||||
// FIXME is this a sufficient replacement for tightBoundingRect?
|
||||
glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(displayName);
|
||||
_displayNameBoundingRect = QRect(0, 0, (int)extent.x, (int)extent.y);
|
||||
}
|
||||
|
||||
void Avatar::setBillboard(const QByteArray& billboard) {
|
||||
AvatarData::setBillboard(billboard);
|
||||
|
||||
|
@ -1092,7 +1076,12 @@ float Avatar::getSkeletonHeight() const {
|
|||
float Avatar::getHeadHeight() const {
|
||||
Extents extents = getHead()->getFaceModel().getMeshExtents();
|
||||
if (!extents.isEmpty() && extents.isValid()) {
|
||||
return extents.maximum.y - extents.minimum.y;
|
||||
|
||||
// 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 * _scale ) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
extents = _skeletonModel.getMeshExtents();
|
||||
|
@ -1116,7 +1105,7 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
|
|||
}
|
||||
|
||||
// For myAvatar, the alpha update is not done (called in simulate for other avatars)
|
||||
if (DependencyManager::get<AvatarManager>()->getMyAvatar() == this) {
|
||||
if (isMyAvatar()) {
|
||||
if (showDisplayName) {
|
||||
_displayNameAlpha = DISPLAYNAME_ALPHA;
|
||||
} else {
|
||||
|
@ -1129,7 +1118,6 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
|
|||
} else {
|
||||
_displayNameTargetAlpha = 0.0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
|
|
@ -52,6 +52,8 @@ enum DriveKeys {
|
|||
ROT_RIGHT,
|
||||
ROT_UP,
|
||||
ROT_DOWN,
|
||||
BOOM_IN,
|
||||
BOOM_OUT,
|
||||
MAX_DRIVE_KEYS
|
||||
};
|
||||
|
||||
|
@ -96,6 +98,7 @@ public:
|
|||
//getters
|
||||
bool isInitialized() const { return _initialized; }
|
||||
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
|
||||
const SkeletonModel& getSkeletonModel() const { return _skeletonModel; }
|
||||
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
|
||||
glm::vec3 getChestPosition() const;
|
||||
float getScale() const { return _scale; }
|
||||
|
@ -129,7 +132,7 @@ public:
|
|||
/// \return whether or not the plane penetrated
|
||||
bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
|
||||
|
||||
virtual bool isMyAvatar() { return false; }
|
||||
virtual bool isMyAvatar() const { return false; }
|
||||
|
||||
virtual QVector<glm::quat> getJointRotations() const;
|
||||
virtual glm::quat getJointRotation(int index) const;
|
||||
|
@ -139,7 +142,6 @@ public:
|
|||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
virtual void setDisplayName(const QString& displayName);
|
||||
virtual void setBillboard(const QByteArray& billboard);
|
||||
|
||||
void setShowDisplayName(bool showDisplayName);
|
||||
|
@ -230,10 +232,10 @@ protected:
|
|||
float getSkeletonHeight() const;
|
||||
float getHeadHeight() const;
|
||||
float getPelvisFloatingHeight() const;
|
||||
glm::vec3 getDisplayNamePosition();
|
||||
glm::vec3 getDisplayNamePosition() const;
|
||||
|
||||
float calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD);
|
||||
void renderDisplayName(RenderArgs* renderArgs);
|
||||
Transform calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize) const;
|
||||
void renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) const;
|
||||
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f);
|
||||
virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const;
|
||||
virtual void fixupModelsInScene();
|
||||
|
|
|
@ -29,6 +29,12 @@ AvatarActionHold::~AvatarActionHold() {
|
|||
|
||||
void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
if (!tryLockForRead()) {
|
||||
// don't risk hanging the thread running the physics simulation
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 palmPosition;
|
||||
if (_hand == "right") {
|
||||
palmPosition = myAvatar->getRightPalmPosition();
|
||||
|
@ -40,8 +46,11 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
|||
auto offset = rotation * _relativePosition;
|
||||
auto position = palmPosition + offset;
|
||||
rotation *= _relativeRotation;
|
||||
unlock();
|
||||
|
||||
lockForWrite();
|
||||
if (!tryLockForWrite()) {
|
||||
return;
|
||||
}
|
||||
_positionalTarget = position;
|
||||
_rotationalTarget = rotation;
|
||||
unlock();
|
||||
|
|
|
@ -13,8 +13,18 @@
|
|||
|
||||
#include <QScriptEngine>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
#include <GlowEffect.h>
|
||||
#include <PerfStat.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
|
|
@ -77,7 +77,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
|||
const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f;
|
||||
_averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f));
|
||||
|
||||
if (_longTermAverageLoudness == -1.0) {
|
||||
if (_longTermAverageLoudness == -1.0f) {
|
||||
_longTermAverageLoudness = _averageLoudness;
|
||||
} else {
|
||||
_longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f));
|
||||
|
@ -124,18 +124,25 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
|||
|
||||
if (!(_isFaceTrackerConnected || billboard)) {
|
||||
// Update eye saccades
|
||||
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
|
||||
const float AVERAGE_SACCADE_INTERVAL = 4.0f;
|
||||
const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f;
|
||||
const float AVERAGE_SACCADE_INTERVAL = 6.0f;
|
||||
const float MICROSACCADE_MAGNITUDE = 0.002f;
|
||||
const float SACCADE_MAGNITUDE = 0.04f;
|
||||
|
||||
const float MAXIMUM_SACCADE_SPEED = 0.8f;
|
||||
|
||||
if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
|
||||
_saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
|
||||
} else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
|
||||
_saccadeTarget = SACCADE_MAGNITUDE * randVector();
|
||||
}
|
||||
_saccade += (_saccadeTarget - _saccade) * 0.50f;
|
||||
|
||||
|
||||
glm::vec3 saccadeDelta = (_saccadeTarget - _saccade) * 0.5f;
|
||||
float speed = glm::length(saccadeDelta) / deltaTime;
|
||||
if (speed > MAXIMUM_SACCADE_SPEED) {
|
||||
saccadeDelta = saccadeDelta * MAXIMUM_SACCADE_SPEED / speed;
|
||||
}
|
||||
_saccade += saccadeDelta;
|
||||
|
||||
// Detect transition from talking to not; force blink after that and a delay
|
||||
bool forceBlink = false;
|
||||
const float TALKING_LOUDNESS = 100.0f;
|
||||
|
|
|
@ -59,10 +59,10 @@ const float MAX_WALKING_SPEED = 2.5f; // human walking speed
|
|||
const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // keyboard motor gets additive boost below this speed
|
||||
const float MIN_AVATAR_SPEED = 0.05f; // speed is set to zero below this
|
||||
|
||||
// TODO: normalize avatar speed for standard avatar size, then scale all motion logic
|
||||
// TODO: normalize avatar speed for standard avatar size, then scale all motion logic
|
||||
// to properly follow avatar size.
|
||||
float MAX_AVATAR_SPEED = 300.0f;
|
||||
float MAX_KEYBOARD_MOTOR_SPEED = MAX_AVATAR_SPEED;
|
||||
float MAX_KEYBOARD_MOTOR_SPEED = MAX_AVATAR_SPEED;
|
||||
float DEFAULT_KEYBOARD_MOTOR_TIMESCALE = 0.25f;
|
||||
float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;
|
||||
float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
|
||||
|
@ -70,6 +70,10 @@ const int SCRIPTED_MOTOR_CAMERA_FRAME = 0;
|
|||
const int SCRIPTED_MOTOR_AVATAR_FRAME = 1;
|
||||
const int SCRIPTED_MOTOR_WORLD_FRAME = 2;
|
||||
|
||||
const float MyAvatar::ZOOM_MIN = 0.5f;
|
||||
const float MyAvatar::ZOOM_MAX = 10.0f;
|
||||
const float MyAvatar::ZOOM_DEFAULT = 1.5f;
|
||||
|
||||
MyAvatar::MyAvatar() :
|
||||
Avatar(),
|
||||
_turningKeyPressTime(0.0f),
|
||||
|
@ -77,6 +81,7 @@ MyAvatar::MyAvatar() :
|
|||
_wasPushing(false),
|
||||
_isPushing(false),
|
||||
_isBraking(false),
|
||||
_boomLength(ZOOM_DEFAULT),
|
||||
_trapDuration(0.0f),
|
||||
_thrust(0.0f),
|
||||
_keyboardMotorVelocity(0.0f),
|
||||
|
@ -142,7 +147,7 @@ void MyAvatar::update(float deltaTime) {
|
|||
if (_referential) {
|
||||
_referential->update();
|
||||
}
|
||||
|
||||
|
||||
Head* head = getHead();
|
||||
head->relaxLean(deltaTime);
|
||||
updateFromTrackers(deltaTime);
|
||||
|
@ -159,12 +164,12 @@ void MyAvatar::update(float deltaTime) {
|
|||
|
||||
void MyAvatar::simulate(float deltaTime) {
|
||||
PerformanceTimer perfTimer("simulate");
|
||||
|
||||
|
||||
// Play back recording
|
||||
if (_player && _player->isPlaying()) {
|
||||
_player->play();
|
||||
}
|
||||
|
||||
|
||||
if (_scale != _targetScale) {
|
||||
float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
|
||||
setScale(scale);
|
||||
|
@ -175,7 +180,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
updateOrientation(deltaTime);
|
||||
updatePosition(deltaTime);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("hand");
|
||||
// update avatar skeleton and simulate hand and head
|
||||
|
@ -218,12 +223,12 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
head->setScale(_scale);
|
||||
head->simulate(deltaTime, true);
|
||||
}
|
||||
|
||||
|
||||
// Record avatars movements.
|
||||
if (_recorder && _recorder->isRecording()) {
|
||||
_recorder->record();
|
||||
}
|
||||
|
||||
|
||||
// consider updating our billboard
|
||||
maybeUpdateBillboard();
|
||||
}
|
||||
|
@ -231,18 +236,18 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
// Update avatar head rotation with sensor data
|
||||
void MyAvatar::updateFromTrackers(float deltaTime) {
|
||||
glm::vec3 estimatedPosition, estimatedRotation;
|
||||
|
||||
|
||||
bool inHmd = qApp->isHMDMode();
|
||||
|
||||
|
||||
if (isPlaying() && inHmd) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inHmd) {
|
||||
estimatedPosition = qApp->getHeadPosition();
|
||||
estimatedPosition = qApp->getHeadPosition();
|
||||
estimatedPosition.x *= -1.0f;
|
||||
_trackedHeadPosition = estimatedPosition;
|
||||
|
||||
|
||||
const float OCULUS_LEAN_SCALE = 0.05f;
|
||||
estimatedPosition /= OCULUS_LEAN_SCALE;
|
||||
} else {
|
||||
|
@ -253,7 +258,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
|
|||
estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Rotate the body if the head is turned beyond the screen
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) {
|
||||
const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f;
|
||||
|
@ -313,7 +318,7 @@ void MyAvatar::renderDebugBodyPoints() {
|
|||
glm::vec3 headPosition(getHead()->getEyePosition());
|
||||
float torsoToHead = glm::length(headPosition - torsoPosition);
|
||||
glm::vec3 position;
|
||||
qCDebug(interfaceapp, "head-above-torso %.2f, scale = %0.2f", torsoToHead, getScale());
|
||||
qCDebug(interfaceapp, "head-above-torso %.2f, scale = %0.2f", (double)torsoToHead, (double)getScale());
|
||||
|
||||
// Torso Sphere
|
||||
position = torsoPosition;
|
||||
|
@ -429,7 +434,7 @@ void MyAvatar::startRecording() {
|
|||
auto audioClient = DependencyManager::get<AudioClient>();
|
||||
connect(audioClient.data(), &AudioClient::inputReceived, _recorder.data(),
|
||||
&Recorder::recordAudio, Qt::BlockingQueuedConnection);
|
||||
|
||||
|
||||
_recorder->startRecording();
|
||||
}
|
||||
|
||||
|
@ -445,7 +450,7 @@ void MyAvatar::stopRecording() {
|
|||
// stop grabbing audio from the AudioClient
|
||||
auto audioClient = DependencyManager::get<AudioClient>();
|
||||
disconnect(audioClient.data(), 0, _recorder.data(), 0);
|
||||
|
||||
|
||||
_recorder->stopRecording();
|
||||
}
|
||||
}
|
||||
|
@ -477,7 +482,7 @@ void MyAvatar::loadLastRecording() {
|
|||
if (!_player) {
|
||||
_player = PlayerPointer(new Player(this));
|
||||
}
|
||||
|
||||
|
||||
_player->loadRecording(_recorder->getRecording());
|
||||
}
|
||||
|
||||
|
@ -569,7 +574,7 @@ AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) {
|
|||
AnimationDetails result;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "getAnimationDetailsByRole", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(AnimationDetails, result),
|
||||
Q_RETURN_ARG(AnimationDetails, result),
|
||||
Q_ARG(const QString&, role));
|
||||
return result;
|
||||
}
|
||||
|
@ -586,7 +591,7 @@ AnimationDetails MyAvatar::getAnimationDetails(const QString& url) {
|
|||
AnimationDetails result;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(AnimationDetails, result),
|
||||
Q_RETURN_ARG(AnimationDetails, result),
|
||||
Q_ARG(const QString&, url));
|
||||
return result;
|
||||
}
|
||||
|
@ -634,7 +639,7 @@ void MyAvatar::saveData() {
|
|||
settings.setValue("scale", attachment.scale);
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
|
||||
settings.beginWriteArray("animationHandles");
|
||||
for (int i = 0; i < _animationHandles.size(); i++) {
|
||||
settings.setArrayIndex(i);
|
||||
|
@ -651,7 +656,7 @@ void MyAvatar::saveData() {
|
|||
settings.setValue("maskedJoints", pointer->getMaskedJoints());
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
|
||||
settings.setValue("displayName", _displayName);
|
||||
|
||||
settings.endGroup();
|
||||
|
@ -677,7 +682,7 @@ void MyAvatar::loadData() {
|
|||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||
setScale(_scale);
|
||||
|
||||
// The old preferences only stored the face and skeleton URLs, we didn't track if the user wanted to use 1 or 2 urls
|
||||
// The old preferences only stored the face and skeleton URLs, we didn't track if the user wanted to use 1 or 2 urls
|
||||
// for their avatar, So we need to attempt to detect this old case and set our new preferences accordingly. If
|
||||
// the head URL is empty, then we will assume they are using a full url...
|
||||
bool isOldSettings = !(settings.contains("useFullAvatar") || settings.contains("fullAvatarURL"));
|
||||
|
@ -689,7 +694,7 @@ void MyAvatar::loadData() {
|
|||
_headModelName = settings.value("headModelName", DEFAULT_HEAD_MODEL_NAME).toString();
|
||||
_bodyModelName = settings.value("bodyModelName", DEFAULT_BODY_MODEL_NAME).toString();
|
||||
_fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString();
|
||||
|
||||
|
||||
if (isOldSettings) {
|
||||
bool assumeFullAvatar = _headURLFromPreferences.isEmpty();
|
||||
_useFullAvatar = assumeFullAvatar;
|
||||
|
@ -715,14 +720,14 @@ void MyAvatar::loadData() {
|
|||
QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
|
||||
_bodyModelName = bodyFST["name"].toString();
|
||||
}
|
||||
|
||||
|
||||
if (_headURLFromPreferences == DEFAULT_HEAD_MODEL_URL) {
|
||||
_headModelName = DEFAULT_HEAD_MODEL_NAME;
|
||||
} else {
|
||||
QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
|
||||
_headModelName = headFST["name"].toString();
|
||||
}
|
||||
|
||||
|
||||
_fullAvatarModelName = "Default";
|
||||
}
|
||||
}
|
||||
|
@ -732,7 +737,7 @@ void MyAvatar::loadData() {
|
|||
} else {
|
||||
useHeadAndBodyURLs(_headURLFromPreferences, _skeletonURLFromPreferences, _headModelName, _bodyModelName);
|
||||
}
|
||||
|
||||
|
||||
QVector<AttachmentData> attachmentData;
|
||||
int attachmentCount = settings.beginReadArray("attachmentData");
|
||||
for (int i = 0; i < attachmentCount; i++) {
|
||||
|
@ -753,7 +758,7 @@ void MyAvatar::loadData() {
|
|||
}
|
||||
settings.endArray();
|
||||
setAttachmentData(attachmentData);
|
||||
|
||||
|
||||
int animationCount = settings.beginReadArray("animationHandles");
|
||||
while (_animationHandles.size() > animationCount) {
|
||||
_animationHandles.takeLast()->stop();
|
||||
|
@ -776,7 +781,7 @@ void MyAvatar::loadData() {
|
|||
handle->setStartAutomatically(settings.value("startAutomatically", true).toBool());
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
|
||||
setDisplayName(settings.value("displayName").toString());
|
||||
|
||||
settings.endGroup();
|
||||
|
@ -788,7 +793,7 @@ void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
|
|||
settings.beginGroup(_skeletonModel.getURL().toString());
|
||||
settings.beginGroup(attachment.modelURL.toString());
|
||||
settings.setValue("jointName", attachment.jointName);
|
||||
|
||||
|
||||
settings.beginGroup(attachment.jointName);
|
||||
settings.setValue("translation_x", attachment.translation.x);
|
||||
settings.setValue("translation_y", attachment.translation.y);
|
||||
|
@ -798,7 +803,7 @@ void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
|
|||
settings.setValue("rotation_y", eulers.y);
|
||||
settings.setValue("rotation_z", eulers.z);
|
||||
settings.setValue("scale", attachment.scale);
|
||||
|
||||
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
|
@ -810,7 +815,7 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString&
|
|||
settings.beginGroup("savedAttachmentData");
|
||||
settings.beginGroup(_skeletonModel.getURL().toString());
|
||||
settings.beginGroup(modelURL.toString());
|
||||
|
||||
|
||||
AttachmentData attachment;
|
||||
attachment.modelURL = modelURL;
|
||||
if (jointName.isEmpty()) {
|
||||
|
@ -832,18 +837,18 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString&
|
|||
} else {
|
||||
attachment = AttachmentData();
|
||||
}
|
||||
|
||||
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) {
|
||||
qCDebug(interfaceapp) << "Error: ignoring update packet for MyAvatar"
|
||||
<< " packetLength = " << packet.size()
|
||||
<< " packetLength = " << packet.size()
|
||||
<< " offset = " << offset;
|
||||
// this packet is just bad, so we pretend that we unpacked it ALL
|
||||
return packet.size() - offset;
|
||||
|
@ -861,14 +866,14 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
//
|
||||
_lookAtTargetAvatar.reset();
|
||||
_targetAvatarPosition = glm::vec3(0.0f);
|
||||
|
||||
|
||||
glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT;
|
||||
glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition();
|
||||
|
||||
|
||||
float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f;
|
||||
const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f;
|
||||
const float GREATEST_LOOKING_AT_DISTANCE = 10.0f;
|
||||
|
||||
|
||||
int howManyLookingAtMe = 0;
|
||||
foreach (const AvatarSharedPointer& avatarPointer, DependencyManager::get<AvatarManager>()->getAvatarHash()) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.get());
|
||||
|
@ -1000,13 +1005,13 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
|
|||
}
|
||||
|
||||
_useFullAvatar = true;
|
||||
|
||||
|
||||
if (_fullAvatarURLFromPreferences != fullAvatarURL) {
|
||||
_fullAvatarURLFromPreferences = fullAvatarURL;
|
||||
if (modelName.isEmpty()) {
|
||||
QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString());
|
||||
_fullAvatarModelName = fullAvatarFST["name"].toString();
|
||||
} else {
|
||||
} else {
|
||||
_fullAvatarModelName = modelName;
|
||||
}
|
||||
}
|
||||
|
@ -1047,7 +1052,7 @@ void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, cons
|
|||
if (headName.isEmpty()) {
|
||||
QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
|
||||
_headModelName = headFST["name"].toString();
|
||||
} else {
|
||||
} else {
|
||||
_headModelName = headName;
|
||||
}
|
||||
}
|
||||
|
@ -1057,7 +1062,7 @@ void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, cons
|
|||
if (bodyName.isEmpty()) {
|
||||
QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
|
||||
_bodyModelName = bodyFST["name"].toString();
|
||||
} else {
|
||||
} else {
|
||||
_bodyModelName = bodyName;
|
||||
}
|
||||
}
|
||||
|
@ -1088,7 +1093,7 @@ void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData)
|
|||
glm::vec3 MyAvatar::getSkeletonPosition() const {
|
||||
CameraMode mode = Application::getInstance()->getCamera()->getMode();
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
||||
// The avatar is rotated PI about the yAxis, so we have to correct for it
|
||||
// The avatar is rotated PI about the yAxis, so we have to correct for it
|
||||
// to get the skeleton offset contribution in the world-frame.
|
||||
const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::vec3 skeletonOffset = _skeletonOffset;
|
||||
|
@ -1131,9 +1136,9 @@ void MyAvatar::setScriptedMotorVelocity(const glm::vec3& velocity) {
|
|||
}
|
||||
|
||||
void MyAvatar::setScriptedMotorTimescale(float timescale) {
|
||||
// we clamp the timescale on the large side (instead of just the low side) to prevent
|
||||
// we clamp the timescale on the large side (instead of just the low side) to prevent
|
||||
// obnoxiously large values from introducing NaN into avatar's velocity
|
||||
_scriptedMotorTimescale = glm::clamp(timescale, MIN_SCRIPTED_MOTOR_TIMESCALE,
|
||||
_scriptedMotorTimescale = glm::clamp(timescale, MIN_SCRIPTED_MOTOR_TIMESCALE,
|
||||
DEFAULT_SCRIPTED_MOTOR_TIMESCALE);
|
||||
}
|
||||
|
||||
|
@ -1151,14 +1156,14 @@ void MyAvatar::clearScriptableSettings() {
|
|||
clearJointAnimationPriorities();
|
||||
_scriptedMotorVelocity = glm::vec3(0.0f);
|
||||
_scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation,
|
||||
const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (useSaved) {
|
||||
AttachmentData attachment = loadAttachmentData(modelURL, jointName);
|
||||
if (attachment.isValid()) {
|
||||
|
@ -1195,7 +1200,7 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f;
|
|||
|
||||
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const {
|
||||
const Head* head = getHead();
|
||||
return (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE) || (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) ||
|
||||
return (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE) || (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) ||
|
||||
(glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale);
|
||||
}
|
||||
|
||||
|
@ -1245,10 +1250,10 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
|
||||
//Invert yaw and roll when in mirror mode
|
||||
if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) {
|
||||
YAW(euler) *= -1.0;
|
||||
ROLL(euler) *= -1.0;
|
||||
YAW(euler) *= -1.0f;
|
||||
ROLL(euler) *= -1.0f;
|
||||
}
|
||||
|
||||
|
||||
Head* head = getHead();
|
||||
head->setBaseYaw(YAW(euler));
|
||||
head->setBasePitch(PITCH(euler));
|
||||
|
@ -1261,9 +1266,9 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
return localVelocity;
|
||||
}
|
||||
// compute motor efficiency
|
||||
// The timescale of the motor is the approximate time it takes for the motor to
|
||||
// accomplish its intended localVelocity. A short timescale makes the motor strong,
|
||||
// and a long timescale makes it weak. The value of timescale to use depends
|
||||
// The timescale of the motor is the approximate time it takes for the motor to
|
||||
// accomplish its intended localVelocity. A short timescale makes the motor strong,
|
||||
// and a long timescale makes it weak. The value of timescale to use depends
|
||||
// on what the motor is doing:
|
||||
//
|
||||
// (1) braking --> short timescale (aggressive motor assertion)
|
||||
|
@ -1274,8 +1279,8 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
float MIN_KEYBOARD_BRAKE_SPEED = 0.3f;
|
||||
float timescale = MAX_KEYBOARD_MOTOR_TIMESCALE;
|
||||
bool isThrust = (glm::length2(_thrust) > EPSILON);
|
||||
if (_isPushing || isThrust ||
|
||||
(_scriptedMotorTimescale < MAX_KEYBOARD_MOTOR_TIMESCALE &&
|
||||
if (_isPushing || isThrust ||
|
||||
(_scriptedMotorTimescale < MAX_KEYBOARD_MOTOR_TIMESCALE &&
|
||||
_motionBehaviors | AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED)) {
|
||||
// we don't want to brake if something is pushing the avatar around
|
||||
timescale = _keyboardMotorTimescale;
|
||||
|
@ -1292,18 +1297,18 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
|
||||
|
||||
glm::vec3 newLocalVelocity = localVelocity;
|
||||
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
|
||||
(fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
|
||||
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
|
||||
(fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
|
||||
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
|
||||
if (keyboardInput) {
|
||||
// Compute keyboard input
|
||||
glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT;
|
||||
glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT;
|
||||
glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP;
|
||||
|
||||
|
||||
glm::vec3 direction = front + right + up;
|
||||
float directionLength = glm::length(direction);
|
||||
|
||||
|
||||
// Compute motor magnitude
|
||||
if (directionLength > EPSILON) {
|
||||
direction /= directionLength;
|
||||
|
@ -1333,7 +1338,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
motorEfficiency = glm::clamp(deltaTime / WALK_ACCELERATION_TIMESCALE, 0.0f, 1.0f);
|
||||
}
|
||||
_isPushing = true;
|
||||
}
|
||||
}
|
||||
newLocalVelocity = localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity);
|
||||
} else {
|
||||
_keyboardMotorVelocity = glm::vec3(0.0f);
|
||||
|
@ -1347,6 +1352,9 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
_boomLength += _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN];
|
||||
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
||||
|
||||
return newLocalVelocity;
|
||||
}
|
||||
|
@ -1394,13 +1402,13 @@ void MyAvatar::updatePosition(float deltaTime) {
|
|||
_targetVelocity *= MAX_AVATAR_SPEED / speed;
|
||||
speed = MAX_AVATAR_SPEED;
|
||||
}
|
||||
|
||||
|
||||
if (speed > MIN_AVATAR_SPEED && !_characterController.isEnabled()) {
|
||||
// update position ourselves
|
||||
applyPositionDelta(deltaTime * _targetVelocity);
|
||||
measureMotionDerivatives(deltaTime);
|
||||
} // else physics will move avatar later
|
||||
|
||||
|
||||
// update _moving flag based on speed
|
||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||
_moving = speed > MOVING_SPEED_THRESHOLD;
|
||||
|
@ -1417,7 +1425,7 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
|
|||
float xzDistance = sqrt(positionBA.x * positionBA.x + positionBA.z * positionBA.z);
|
||||
if (xzDistance < (radiusA + radiusB)) {
|
||||
float yDistance = fabs(positionBA.y);
|
||||
float halfHeights = 0.5 * (heightA + heightB);
|
||||
float halfHeights = 0.5f * (heightA + heightB);
|
||||
if (yDistance < halfHeights) {
|
||||
// cylinders collide
|
||||
if (xzDistance > 0.0f) {
|
||||
|
@ -1460,60 +1468,60 @@ void MyAvatar::maybeUpdateBillboard() {
|
|||
}
|
||||
gpu::Context context(new gpu::GLBackend());
|
||||
RenderArgs renderArgs(&context);
|
||||
QImage image = Application::getInstance()->renderAvatarBillboard(&renderArgs);
|
||||
QImage image = qApp->renderAvatarBillboard(&renderArgs);
|
||||
_billboard.clear();
|
||||
QBuffer buffer(&_billboard);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
image.save(&buffer, "PNG");
|
||||
_billboardValid = true;
|
||||
|
||||
|
||||
sendBillboardPacket();
|
||||
}
|
||||
|
||||
void MyAvatar::increaseSize() {
|
||||
if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) {
|
||||
_targetScale *= (1.0f + SCALING_RATIO);
|
||||
qCDebug(interfaceapp, "Changed scale to %f", _targetScale);
|
||||
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::decreaseSize() {
|
||||
if (MIN_AVATAR_SCALE < (1.0f - SCALING_RATIO) * _targetScale) {
|
||||
_targetScale *= (1.0f - SCALING_RATIO);
|
||||
qCDebug(interfaceapp, "Changed scale to %f", _targetScale);
|
||||
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::resetSize() {
|
||||
_targetScale = 1.0f;
|
||||
qCDebug(interfaceapp, "Reseted scale to %f", _targetScale);
|
||||
qCDebug(interfaceapp, "Reseted scale to %f", (double)_targetScale);
|
||||
}
|
||||
|
||||
void MyAvatar::goToLocation(const glm::vec3& newPosition,
|
||||
bool hasOrientation, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation) {
|
||||
|
||||
|
||||
qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - moving to " << newPosition.x << ", "
|
||||
<< newPosition.y << ", " << newPosition.z;
|
||||
|
||||
|
||||
glm::vec3 shiftedPosition = newPosition;
|
||||
|
||||
|
||||
if (hasOrientation) {
|
||||
qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - new orientation is "
|
||||
<< newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z << ", " << newOrientation.w;
|
||||
|
||||
|
||||
// orient the user to face the target
|
||||
glm::quat quatOrientation = newOrientation;
|
||||
|
||||
|
||||
if (shouldFaceLocation) {
|
||||
|
||||
|
||||
quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
|
||||
// move the user a couple units away
|
||||
const float DISTANCE_TO_USER = 2.0f;
|
||||
shiftedPosition = newPosition - quatOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
||||
}
|
||||
|
||||
|
||||
setOrientation(quatOrientation);
|
||||
}
|
||||
|
||||
|
@ -1559,7 +1567,6 @@ void MyAvatar::renderLaserPointers() {
|
|||
|
||||
//Gets the tip position for the laser pointer
|
||||
glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
|
||||
const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
|
||||
glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition());
|
||||
|
||||
glm::vec3 position = palm->getPosition();
|
||||
|
@ -1568,7 +1575,8 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
|
|||
|
||||
|
||||
glm::vec3 result;
|
||||
if (applicationOverlay.calculateRayUICollisionPoint(position, direction, result)) {
|
||||
const auto& compositor = qApp->getApplicationCompositor();
|
||||
if (compositor.calculateRayUICollisionPoint(position, direction, result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1581,8 +1589,8 @@ void MyAvatar::clearDriveKeys() {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::relayDriveKeysToCharacterController() {
|
||||
void MyAvatar::relayDriveKeysToCharacterController() {
|
||||
if (_driveKeys[UP] > 0.0f) {
|
||||
_characterController.jump();
|
||||
_characterController.jump();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ public:
|
|||
|
||||
void relayDriveKeysToCharacterController();
|
||||
|
||||
bool isMyAvatar() { return true; }
|
||||
bool isMyAvatar() const { return true; }
|
||||
|
||||
bool isLookingAtLeftEye();
|
||||
|
||||
|
@ -162,6 +162,13 @@ public:
|
|||
const RecorderPointer getRecorder() const { return _recorder; }
|
||||
const PlayerPointer getPlayer() const { return _player; }
|
||||
|
||||
float getBoomLength() const { return _boomLength; }
|
||||
void setBoomLength(float boomLength) { _boomLength = boomLength; }
|
||||
|
||||
static const float ZOOM_MIN;
|
||||
static const float ZOOM_MAX;
|
||||
static const float ZOOM_DEFAULT;
|
||||
|
||||
public slots:
|
||||
void increaseSize();
|
||||
void decreaseSize();
|
||||
|
@ -210,6 +217,8 @@ private:
|
|||
bool _wasPushing;
|
||||
bool _isPushing;
|
||||
bool _isBraking;
|
||||
|
||||
float _boomLength;
|
||||
|
||||
float _trapDuration; // seconds that avatar has been trapped by collisions
|
||||
glm::vec3 _thrust; // impulse accumulator for outside sources
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
//
|
||||
// CameraToolBox.cpp
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by David Rowe on 30 Apr 2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <GLCanvas.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "gpu/GLBackend.h"
|
||||
#include "Application.h"
|
||||
#include "CameraToolBox.h"
|
||||
#include "FaceTracker.h"
|
||||
|
||||
|
||||
CameraToolBox::CameraToolBox() :
|
||||
_iconPulseTimeReference(usecTimestampNow()),
|
||||
_doubleClickTimer(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
CameraToolBox::~CameraToolBox() {
|
||||
if (_doubleClickTimer) {
|
||||
_doubleClickTimer->stop();
|
||||
delete _doubleClickTimer;
|
||||
}
|
||||
}
|
||||
|
||||
bool CameraToolBox::mousePressEvent(int x, int y) {
|
||||
if (_iconBounds.contains(x, y)) {
|
||||
if (!_doubleClickTimer) {
|
||||
// Toggle mute after waiting to check that it's not a double-click.
|
||||
const int DOUBLE_CLICK_WAIT = 200; // ms
|
||||
_doubleClickTimer = new QTimer(this);
|
||||
connect(_doubleClickTimer, SIGNAL(timeout()), this, SLOT(toggleMute()));
|
||||
_doubleClickTimer->setSingleShot(true);
|
||||
_doubleClickTimer->setInterval(DOUBLE_CLICK_WAIT);
|
||||
_doubleClickTimer->start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CameraToolBox::mouseDoublePressEvent(int x, int y) {
|
||||
if (_iconBounds.contains(x, y)) {
|
||||
if (_doubleClickTimer) {
|
||||
_doubleClickTimer->stop();
|
||||
delete _doubleClickTimer;
|
||||
_doubleClickTimer = NULL;
|
||||
}
|
||||
Application::getInstance()->resetSensors();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CameraToolBox::toggleMute() {
|
||||
delete _doubleClickTimer;
|
||||
_doubleClickTimer = NULL;
|
||||
|
||||
FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker();
|
||||
if (faceTracker) {
|
||||
faceTracker->toggleMute();
|
||||
}
|
||||
}
|
||||
|
||||
void CameraToolBox::render(int x, int y, bool boxed) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
if (!_enabledTexture) {
|
||||
_enabledTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face.svg");
|
||||
}
|
||||
if (!_mutedTexture) {
|
||||
_mutedTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg");
|
||||
}
|
||||
|
||||
const int MUTE_ICON_SIZE = 24;
|
||||
_iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE);
|
||||
float iconColor = 1.0f;
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)) {
|
||||
glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_enabledTexture));
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_mutedTexture));
|
||||
|
||||
// Make muted icon pulsate
|
||||
static const float PULSE_MIN = 0.4f;
|
||||
static const float PULSE_MAX = 1.0f;
|
||||
static const float PULSE_FREQUENCY = 1.0f; // in Hz
|
||||
qint64 now = usecTimestampNow();
|
||||
if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) {
|
||||
// Prevents t from getting too big, which would diminish glm::cos precision
|
||||
_iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND);
|
||||
}
|
||||
float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND;
|
||||
float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f;
|
||||
iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor;
|
||||
}
|
||||
|
||||
glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f);
|
||||
|
||||
glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top());
|
||||
glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom());
|
||||
glm::vec2 texCoordTopLeft(1,1);
|
||||
glm::vec2 texCoordBottomRight(0,0);
|
||||
|
||||
if (_boxQuadID == GeometryCache::UNKNOWN_ID) {
|
||||
_boxQuadID = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID);
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
//
|
||||
// CameraToolBox.h
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by David Rowe on 30 Apr 2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_CameraToolBox_h
|
||||
#define hifi_CameraToolBox_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
|
||||
class CameraToolBox : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
void render(int x, int y, bool boxed);
|
||||
bool mousePressEvent(int x, int y);
|
||||
bool mouseDoublePressEvent(int x, int y);
|
||||
|
||||
protected:
|
||||
CameraToolBox();
|
||||
~CameraToolBox();
|
||||
|
||||
private slots:
|
||||
void toggleMute();
|
||||
|
||||
private:
|
||||
gpu::TexturePointer _enabledTexture;
|
||||
gpu::TexturePointer _mutedTexture;
|
||||
int _boxQuadID = GeometryCache::UNKNOWN_ID;
|
||||
QRect _iconBounds;
|
||||
qint64 _iconPulseTimeReference = 0;
|
||||
QTimer* _doubleClickTimer;
|
||||
};
|
||||
|
||||
#endif // hifi_CameraToolBox_h
|
|
@ -415,7 +415,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
float browUp = _coefficients[_browUpCenterIndex];
|
||||
if (isFiltering) {
|
||||
const float BROW_VELOCITY_FILTER_STRENGTH = 0.5f;
|
||||
float velocity = fabs(browUp - _lastBrowUp) / _averageMessageTime;
|
||||
float velocity = fabsf(browUp - _lastBrowUp) / _averageMessageTime;
|
||||
float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
|
||||
_filteredBrowUp = velocityFilter * browUp + (1.0f - velocityFilter) * _filteredBrowUp;
|
||||
_lastBrowUp = browUp;
|
||||
|
@ -438,11 +438,12 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
|
||||
// Velocity filter EyeBlink values
|
||||
const float DDE_EYEBLINK_SCALE = 3.0f;
|
||||
float eyeBlinks[] = { DDE_EYEBLINK_SCALE * _coefficients[_leftBlinkIndex], DDE_EYEBLINK_SCALE * _coefficients[_rightBlinkIndex] };
|
||||
float eyeBlinks[] = { DDE_EYEBLINK_SCALE * _coefficients[_leftBlinkIndex],
|
||||
DDE_EYEBLINK_SCALE * _coefficients[_rightBlinkIndex] };
|
||||
if (isFiltering) {
|
||||
const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
float velocity = fabs(eyeBlinks[i] - _lastEyeBlinks[i]) / _averageMessageTime;
|
||||
float velocity = fabsf(eyeBlinks[i] - _lastEyeBlinks[i]) / _averageMessageTime;
|
||||
float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
|
||||
_filteredEyeBlinks[i] = velocityFilter * eyeBlinks[i] + (1.0f - velocityFilter) * _filteredEyeBlinks[i];
|
||||
_lastEyeBlinks[i] = eyeBlinks[i];
|
||||
|
@ -479,9 +480,9 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
if (_eyeStates[i] == EYE_CLOSING) {
|
||||
// Close eyelid until it's fully closed
|
||||
float closingValue = _lastEyeCoefficients[i] + EYELID_MOVEMENT_RATE * _averageMessageTime;
|
||||
if (closingValue >= 1.0) {
|
||||
if (closingValue >= 1.0f) {
|
||||
_eyeStates[i] = EYE_CLOSED;
|
||||
eyeCoefficients[i] = 1.0;
|
||||
eyeCoefficients[i] = 1.0f;
|
||||
} else {
|
||||
eyeCoefficients[i] = closingValue;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,11 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "Joystick.h"
|
||||
|
||||
const float CONTROLLER_THRESHOLD = 0.25f;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
const float MAX_AXIS = 32768.0f;
|
||||
|
@ -22,10 +25,7 @@ const float MAX_AXIS = 32768.0f;
|
|||
Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) :
|
||||
_sdlGameController(sdlGameController),
|
||||
_sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
|
||||
_instanceId(instanceId),
|
||||
_name(name),
|
||||
_axes(QVector<float>(SDL_JoystickNumAxes(_sdlJoystick))),
|
||||
_buttons(QVector<bool>(SDL_JoystickNumButtons(_sdlJoystick)))
|
||||
_instanceId(instanceId)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -42,24 +42,204 @@ void Joystick::closeJoystick() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Joystick::update() {
|
||||
for (auto axisState : _axisStateMap) {
|
||||
if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) {
|
||||
_axisStateMap[axisState.first] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Joystick::focusOutEvent() {
|
||||
_axisStateMap.clear();
|
||||
_buttonPressedMap.clear();
|
||||
};
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) {
|
||||
if (_axes.size() <= event.axis) {
|
||||
_axes.resize(event.axis + 1);
|
||||
SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis;
|
||||
|
||||
switch (axis) {
|
||||
case SDL_CONTROLLER_AXIS_LEFTX:
|
||||
_axisStateMap[makeInput(LEFT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(LEFT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_LEFTY:
|
||||
_axisStateMap[makeInput(LEFT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(LEFT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTX:
|
||||
_axisStateMap[makeInput(RIGHT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(RIGHT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTY:
|
||||
_axisStateMap[makeInput(RIGHT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
|
||||
_axisStateMap[makeInput(RIGHT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
||||
_axisStateMap[makeInput(RIGHT_SHOULDER).getChannel()] = event.value / MAX_AXIS;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
||||
_axisStateMap[makeInput(LEFT_SHOULDER).getChannel()] = event.value / MAX_AXIS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
float oldValue = _axes[event.axis];
|
||||
float newValue = event.value / MAX_AXIS;
|
||||
_axes[event.axis] = newValue;
|
||||
|
||||
emit axisValueChanged(event.axis, newValue, oldValue);
|
||||
}
|
||||
|
||||
void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
|
||||
bool oldValue = _buttons[event.button];
|
||||
auto input = makeInput((SDL_GameControllerButton) event.button);
|
||||
bool newValue = event.state == SDL_PRESSED;
|
||||
_buttons[event.button] = newValue;
|
||||
emit buttonStateChanged(event.button, newValue, oldValue);
|
||||
if (newValue) {
|
||||
_buttonPressedMap.insert(input.getChannel());
|
||||
} else {
|
||||
_buttonPressedMap.erase(input.getChannel());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void Joystick::registerToUserInputMapper(UserInputMapper& mapper) {
|
||||
// Grab the current free device ID
|
||||
_deviceID = mapper.getFreeDeviceID();
|
||||
|
||||
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name));
|
||||
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
|
||||
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
|
||||
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
|
||||
QVector<UserInputMapper::InputPair> availableInputs;
|
||||
#ifdef HAVE_SDL2
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_A), "Bottom Button"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_B), "Right Button"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_X), "Left Button"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_Y), "Top Button"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), "DPad Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER), "L1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left"));
|
||||
|
||||
#endif
|
||||
return availableInputs;
|
||||
};
|
||||
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
|
||||
mapper.removeAllInputChannelsForDevice(_deviceID);
|
||||
this->assignDefaultInputMapping(mapper);
|
||||
return true;
|
||||
};
|
||||
mapper.registerDevice(_deviceID, proxy);
|
||||
}
|
||||
|
||||
void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {
|
||||
#ifdef HAVE_SDL2
|
||||
const float JOYSTICK_MOVE_SPEED = 1.0f;
|
||||
const float DPAD_MOVE_SPEED = 0.5f;
|
||||
const float JOYSTICK_YAW_SPEED = 0.5f;
|
||||
const float JOYSTICK_PITCH_SPEED = 0.25f;
|
||||
const float BOOM_SPEED = 0.1f;
|
||||
|
||||
// Y axes are flipped (up is negative)
|
||||
// Left Joystick: Movement, strafing
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
|
||||
|
||||
// Right Joystick: Camera orientation
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
|
||||
|
||||
// Dpad movement
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED);
|
||||
|
||||
// Button controls
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_A), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(SDL_CONTROLLER_BUTTON_X), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_B), JOYSTICK_YAW_SPEED);
|
||||
|
||||
// Zoom
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED);
|
||||
|
||||
|
||||
// Hold front right shoulder button for precision controls
|
||||
// Left Joystick: Movement, strafing
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
|
||||
|
||||
// Right Joystick: Camera orientation
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
|
||||
|
||||
// Dpad movement
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
|
||||
// Button controls
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_A), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_B), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
|
||||
// Zoom
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
float Joystick::getButton(int channel) const {
|
||||
if (!_buttonPressedMap.empty()) {
|
||||
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
|
||||
return 1.0f;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float Joystick::getAxis(int channel) const {
|
||||
auto axis = _axisStateMap.find(channel);
|
||||
if (axis != _axisStateMap.end()) {
|
||||
return (*axis).second;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) {
|
||||
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
|
||||
}
|
||||
#endif
|
||||
|
||||
UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) {
|
||||
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#undef main
|
||||
#endif
|
||||
|
||||
#include "ui/UserInputMapper.h"
|
||||
|
||||
class Joystick : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -29,12 +31,40 @@ class Joystick : public QObject {
|
|||
Q_PROPERTY(int instanceId READ getInstanceId)
|
||||
#endif
|
||||
|
||||
Q_PROPERTY(int numAxes READ getNumAxes)
|
||||
Q_PROPERTY(int numButtons READ getNumButtons)
|
||||
public:
|
||||
enum JoystickAxisChannel {
|
||||
LEFT_AXIS_X_POS = 0,
|
||||
LEFT_AXIS_X_NEG,
|
||||
LEFT_AXIS_Y_POS,
|
||||
LEFT_AXIS_Y_NEG,
|
||||
RIGHT_AXIS_X_POS,
|
||||
RIGHT_AXIS_X_NEG,
|
||||
RIGHT_AXIS_Y_POS,
|
||||
RIGHT_AXIS_Y_NEG,
|
||||
RIGHT_SHOULDER,
|
||||
LEFT_SHOULDER,
|
||||
};
|
||||
|
||||
Joystick();
|
||||
~Joystick();
|
||||
|
||||
typedef std::unordered_set<int> ButtonPressedMap;
|
||||
typedef std::map<int, float> AxisStateMap;
|
||||
|
||||
float getButton(int channel) const;
|
||||
float getAxis(int channel) const;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
UserInputMapper::Input makeInput(SDL_GameControllerButton button);
|
||||
#endif
|
||||
UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis);
|
||||
|
||||
void registerToUserInputMapper(UserInputMapper& mapper);
|
||||
void assignDefaultInputMapping(UserInputMapper& mapper);
|
||||
|
||||
void update();
|
||||
void focusOutEvent();
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController);
|
||||
#endif
|
||||
|
@ -51,15 +81,8 @@ public:
|
|||
int getInstanceId() const { return _instanceId; }
|
||||
#endif
|
||||
|
||||
const QVector<float>& getAxes() const { return _axes; }
|
||||
const QVector<bool>& getButtons() const { return _buttons; }
|
||||
int getDeviceID() { return _deviceID; }
|
||||
|
||||
int getNumAxes() const { return _axes.size(); }
|
||||
int getNumButtons() const { return _buttons.size(); }
|
||||
|
||||
signals:
|
||||
void axisValueChanged(int axis, float newValue, float oldValue);
|
||||
void buttonStateChanged(int button, float newValue, float oldValue);
|
||||
private:
|
||||
#ifdef HAVE_SDL2
|
||||
SDL_GameController* _sdlGameController;
|
||||
|
@ -68,8 +91,12 @@ private:
|
|||
#endif
|
||||
|
||||
QString _name;
|
||||
QVector<float> _axes;
|
||||
QVector<bool> _buttons;
|
||||
|
||||
protected:
|
||||
int _deviceID = 0;
|
||||
|
||||
ButtonPressedMap _buttonPressedMap;
|
||||
AxisStateMap _axisStateMap;
|
||||
};
|
||||
|
||||
#endif // hifi_JoystickTracker_h
|
||||
#endif // hifi_Joystick_h
|
||||
|
|
|
@ -160,8 +160,8 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
|
|||
_deviceID = mapper.getFreeDeviceID();
|
||||
|
||||
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Keyboard"));
|
||||
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); };
|
||||
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); };
|
||||
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
|
||||
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
|
||||
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
|
||||
QVector<UserInputMapper::InputPair> availableInputs;
|
||||
for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) {
|
||||
|
@ -170,7 +170,33 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
|
|||
for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) {
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
|
||||
}
|
||||
for (int i = (int) Qt::Key_Left; i <= (int) Qt::Key_Down; i++) {
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
|
||||
}
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Shift), "Shift"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::LeftButton), "Left Mouse Click"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::MiddleButton), "Middle Mouse Click"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::RightButton), "Right Mouse Click"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down"));
|
||||
|
||||
return availableInputs;
|
||||
};
|
||||
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
|
||||
|
@ -189,7 +215,7 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
const float MOUSE_PITCH_SPEED = 0.25f;
|
||||
const float TOUCH_YAW_SPEED = 0.5f;
|
||||
const float TOUCH_PITCH_SPEED = 0.25f;
|
||||
//const float BUTTON_BOOM_SPEED = 0.1f;
|
||||
const float BUTTON_BOOM_SPEED = 0.1f;
|
||||
|
||||
// AWSD keys mapping
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_S), BUTTON_MOVE_SPEED);
|
||||
|
@ -199,8 +225,8 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_C), BUTTON_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_E), BUTTON_MOVE_SPEED);
|
||||
|
||||
// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
|
||||
|
@ -216,8 +242,6 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED);
|
||||
|
||||
// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
|
||||
|
@ -246,8 +270,8 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
|
||||
|
||||
// Wheel move
|
||||
//mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED);
|
||||
//mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED);
|
||||
|
||||
|
|
|
@ -1,73 +1,66 @@
|
|||
//
|
||||
// JoystickScriptingInterface.cpp
|
||||
// SDL2Manager.cpp
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/15/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Created by Sam Gondelman on 6/5/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 <qapplication.h>
|
||||
#include <QtDebug>
|
||||
#include <QScriptValue>
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#undef main
|
||||
#endif
|
||||
|
||||
#include <HFActionEvent.h>
|
||||
#include <HFBackEvent.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Application.h"
|
||||
|
||||
#include "JoystickScriptingInterface.h"
|
||||
#include "SDL2Manager.h"
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
SDL_JoystickID getInstanceId(SDL_GameController* controller) {
|
||||
SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) {
|
||||
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
|
||||
return SDL_JoystickInstanceID(joystick);
|
||||
}
|
||||
#endif
|
||||
|
||||
JoystickScriptingInterface& JoystickScriptingInterface::getInstance() {
|
||||
static JoystickScriptingInterface sharedInstance;
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
JoystickScriptingInterface::JoystickScriptingInterface() :
|
||||
SDL2Manager::SDL2Manager() :
|
||||
#ifdef HAVE_SDL2
|
||||
_openJoysticks(),
|
||||
_openJoysticks(),
|
||||
#endif
|
||||
_isInitialized(false)
|
||||
_isInitialized(false)
|
||||
{
|
||||
#ifdef HAVE_SDL2
|
||||
bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER) == 0);
|
||||
|
||||
if (initSuccess) {
|
||||
int joystickCount = SDL_NumJoysticks();
|
||||
|
||||
|
||||
for (int i = 0; i < joystickCount; i++) {
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(i);
|
||||
|
||||
if (controller) {
|
||||
SDL_JoystickID id = getInstanceId(controller);
|
||||
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
|
||||
_openJoysticks[id] = joystick;
|
||||
if (!_openJoysticks.contains(id)) {
|
||||
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
|
||||
_openJoysticks[id] = joystick;
|
||||
joystick->registerToUserInputMapper(*Application::getUserInputMapper());
|
||||
joystick->assignDefaultInputMapping(*Application::getUserInputMapper());
|
||||
emit joystickAdded(joystick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_isInitialized = true;
|
||||
} else {
|
||||
qDebug() << "Error initializing SDL";
|
||||
qDebug() << "Error initializing SDL2 Manager";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JoystickScriptingInterface::~JoystickScriptingInterface() {
|
||||
SDL2Manager::~SDL2Manager() {
|
||||
#ifdef HAVE_SDL2
|
||||
qDeleteAll(_openJoysticks);
|
||||
|
||||
|
@ -75,34 +68,27 @@ JoystickScriptingInterface::~JoystickScriptingInterface() {
|
|||
#endif
|
||||
}
|
||||
|
||||
const QObjectList JoystickScriptingInterface::getAllJoysticks() const {
|
||||
QObjectList objectList;
|
||||
#ifdef HAVE_SDL2
|
||||
const QList<Joystick*> joystickList = _openJoysticks.values();
|
||||
for (int i = 0; i < joystickList.length(); i++) {
|
||||
objectList << joystickList[i];
|
||||
}
|
||||
#endif
|
||||
return objectList;
|
||||
SDL2Manager* SDL2Manager::getInstance() {
|
||||
static SDL2Manager sharedInstance;
|
||||
return &sharedInstance;
|
||||
}
|
||||
|
||||
Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) {
|
||||
void SDL2Manager::focusOutEvent() {
|
||||
#ifdef HAVE_SDL2
|
||||
QMap<SDL_JoystickID, Joystick*>::iterator iter = _openJoysticks.begin();
|
||||
while (iter != _openJoysticks.end()) {
|
||||
if (iter.value()->getName() == name) {
|
||||
return iter.value();
|
||||
}
|
||||
iter++;
|
||||
for (auto joystick : _openJoysticks) {
|
||||
joystick->focusOutEvent();
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void JoystickScriptingInterface::update() {
|
||||
void SDL2Manager::update() {
|
||||
#ifdef HAVE_SDL2
|
||||
if (_isInitialized) {
|
||||
PerformanceTimer perfTimer("JoystickScriptingInterface::update");
|
||||
for (auto joystick : _openJoysticks) {
|
||||
joystick->update();
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("SDL2Manager::update");
|
||||
SDL_GameControllerUpdate();
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
|
@ -120,16 +106,16 @@ void JoystickScriptingInterface::update() {
|
|||
if (event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK) {
|
||||
// this will either start or stop a global back event
|
||||
QEvent::Type backType = (event.type == SDL_CONTROLLERBUTTONDOWN)
|
||||
? HFBackEvent::startType()
|
||||
: HFBackEvent::endType();
|
||||
? HFBackEvent::startType()
|
||||
: HFBackEvent::endType();
|
||||
HFBackEvent backEvent(backType);
|
||||
|
||||
qApp->sendEvent(qApp, &backEvent);
|
||||
} else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_A) {
|
||||
// this will either start or stop a global action event
|
||||
QEvent::Type actionType = (event.type == SDL_CONTROLLERBUTTONDOWN)
|
||||
? HFActionEvent::startType()
|
||||
: HFActionEvent::endType();
|
||||
? HFActionEvent::startType()
|
||||
: HFActionEvent::endType();
|
||||
|
||||
// global action events fire in the center of the screen
|
||||
Application* app = Application::getInstance();
|
||||
|
@ -141,14 +127,19 @@ void JoystickScriptingInterface::update() {
|
|||
|
||||
} else if (event.type == SDL_CONTROLLERDEVICEADDED) {
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which);
|
||||
|
||||
|
||||
SDL_JoystickID id = getInstanceId(controller);
|
||||
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
|
||||
_openJoysticks[id] = joystick;
|
||||
emit joystickAdded(joystick);
|
||||
if (!_openJoysticks.contains(id)) {
|
||||
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
|
||||
_openJoysticks[id] = joystick;
|
||||
joystick->registerToUserInputMapper(*Application::getUserInputMapper());
|
||||
joystick->assignDefaultInputMapping(*Application::getUserInputMapper());
|
||||
emit joystickAdded(joystick);
|
||||
}
|
||||
} else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
|
||||
Joystick* joystick = _openJoysticks[event.cdevice.which];
|
||||
_openJoysticks.remove(event.cdevice.which);
|
||||
Application::getUserInputMapper()->removeDevice(joystick->getDeviceID());
|
||||
emit joystickRemoved(joystick);
|
||||
}
|
||||
}
|
|
@ -1,77 +1,46 @@
|
|||
//
|
||||
// JoystickScriptingInterface.h
|
||||
// SDL2Manager.h
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/15/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Created by Sam Gondelman on 6/5/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_JoystickScriptingInterface_h
|
||||
#define hifi_JoystickScriptingInterface_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#ifndef hifi__SDL2Manager_h
|
||||
#define hifi__SDL2Manager_h
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
#include "ui/UserInputMapper.h"
|
||||
|
||||
#include "devices/Joystick.h"
|
||||
|
||||
/// Handles joystick input through SDL.
|
||||
class JoystickScriptingInterface : public QObject {
|
||||
class SDL2Manager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
Q_PROPERTY(int AXIS_INVALID READ axisInvalid)
|
||||
Q_PROPERTY(int AXIS_LEFT_X READ axisLeftX)
|
||||
Q_PROPERTY(int AXIS_LEFT_Y READ axisLeftY)
|
||||
Q_PROPERTY(int AXIS_RIGHT_X READ axisRightX)
|
||||
Q_PROPERTY(int AXIS_RIGHT_Y READ axisRightY)
|
||||
Q_PROPERTY(int AXIS_TRIGGER_LEFT READ axisTriggerLeft)
|
||||
Q_PROPERTY(int AXIS_TRIGGER_RIGHT READ axisTriggerRight)
|
||||
Q_PROPERTY(int AXIS_MAX READ axisMax)
|
||||
|
||||
Q_PROPERTY(int BUTTON_INVALID READ buttonInvalid)
|
||||
Q_PROPERTY(int BUTTON_FACE_BOTTOM READ buttonFaceBottom)
|
||||
Q_PROPERTY(int BUTTON_FACE_RIGHT READ buttonFaceRight)
|
||||
Q_PROPERTY(int BUTTON_FACE_LEFT READ buttonFaceLeft)
|
||||
Q_PROPERTY(int BUTTON_FACE_TOP READ buttonFaceTop)
|
||||
Q_PROPERTY(int BUTTON_BACK READ buttonBack)
|
||||
Q_PROPERTY(int BUTTON_GUIDE READ buttonGuide)
|
||||
Q_PROPERTY(int BUTTON_START READ buttonStart)
|
||||
Q_PROPERTY(int BUTTON_LEFT_STICK READ buttonLeftStick)
|
||||
Q_PROPERTY(int BUTTON_RIGHT_STICK READ buttonRightStick)
|
||||
Q_PROPERTY(int BUTTON_LEFT_SHOULDER READ buttonLeftShoulder)
|
||||
Q_PROPERTY(int BUTTON_RIGHT_SHOULDER READ buttonRightShoulder)
|
||||
Q_PROPERTY(int BUTTON_DPAD_UP READ buttonDpadUp)
|
||||
Q_PROPERTY(int BUTTON_DPAD_DOWN READ buttonDpadDown)
|
||||
Q_PROPERTY(int BUTTON_DPAD_LEFT READ buttonDpadLeft)
|
||||
Q_PROPERTY(int BUTTON_DPAD_RIGHT READ buttonDpadRight)
|
||||
Q_PROPERTY(int BUTTON_MAX READ buttonMax)
|
||||
|
||||
Q_PROPERTY(int BUTTON_PRESSED READ buttonPressed)
|
||||
Q_PROPERTY(int BUTTON_RELEASED READ buttonRelease)
|
||||
#endif
|
||||
|
||||
|
||||
public:
|
||||
static JoystickScriptingInterface& getInstance();
|
||||
|
||||
SDL2Manager();
|
||||
~SDL2Manager();
|
||||
|
||||
void focusOutEvent();
|
||||
|
||||
void update();
|
||||
|
||||
public slots:
|
||||
Joystick* joystickWithName(const QString& name);
|
||||
const QObjectList getAllJoysticks() const;
|
||||
|
||||
|
||||
static SDL2Manager* getInstance();
|
||||
|
||||
signals:
|
||||
void joystickAdded(Joystick* joystick);
|
||||
void joystickRemoved(Joystick* joystick);
|
||||
|
||||
|
||||
private:
|
||||
#ifdef HAVE_SDL2
|
||||
SDL_JoystickID getInstanceId(SDL_GameController* controller);
|
||||
|
||||
int axisInvalid() const { return SDL_CONTROLLER_AXIS_INVALID; }
|
||||
int axisLeftX() const { return SDL_CONTROLLER_AXIS_LEFTX; }
|
||||
int axisLeftY() const { return SDL_CONTROLLER_AXIS_LEFTY; }
|
||||
|
@ -80,7 +49,7 @@ private:
|
|||
int axisTriggerLeft() const { return SDL_CONTROLLER_AXIS_TRIGGERLEFT; }
|
||||
int axisTriggerRight() const { return SDL_CONTROLLER_AXIS_TRIGGERRIGHT; }
|
||||
int axisMax() const { return SDL_CONTROLLER_AXIS_MAX; }
|
||||
|
||||
|
||||
int buttonInvalid() const { return SDL_CONTROLLER_BUTTON_INVALID; }
|
||||
int buttonFaceBottom() const { return SDL_CONTROLLER_BUTTON_A; }
|
||||
int buttonFaceRight() const { return SDL_CONTROLLER_BUTTON_B; }
|
||||
|
@ -98,18 +67,15 @@ private:
|
|||
int buttonDpadLeft() const { return SDL_CONTROLLER_BUTTON_DPAD_LEFT; }
|
||||
int buttonDpadRight() const { return SDL_CONTROLLER_BUTTON_DPAD_RIGHT; }
|
||||
int buttonMax() const { return SDL_CONTROLLER_BUTTON_MAX; }
|
||||
|
||||
|
||||
int buttonPressed() const { return SDL_PRESSED; }
|
||||
int buttonRelease() const { return SDL_RELEASED; }
|
||||
#endif
|
||||
|
||||
JoystickScriptingInterface();
|
||||
~JoystickScriptingInterface();
|
||||
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
QMap<SDL_JoystickID, Joystick*> _openJoysticks;
|
||||
#endif
|
||||
bool _isInitialized;
|
||||
};
|
||||
|
||||
#endif // hifi_JoystickScriptingInterface_h
|
||||
#endif // hifi__SDL2Manager_h
|
|
@ -19,6 +19,10 @@
|
|||
#include "UserActivityLogger.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
// These bits aren't used for buttons, so they can be used as masks:
|
||||
const unsigned int LEFT_MASK = 0;
|
||||
const unsigned int RIGHT_MASK = 1U << 1;
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
|
||||
const int CALIBRATION_STATE_IDLE = 0;
|
||||
|
@ -61,29 +65,31 @@ SixenseManager::SixenseManager() :
|
|||
_bumperPressed[1] = false;
|
||||
_oldX[1] = -1;
|
||||
_oldY[1] = -1;
|
||||
_prevPalms[0] = nullptr;
|
||||
_prevPalms[1] = nullptr;
|
||||
}
|
||||
|
||||
SixenseManager::~SixenseManager() {
|
||||
#ifdef HAVE_SIXENSE_
|
||||
|
||||
|
||||
if (_isInitialized) {
|
||||
#ifdef __APPLE__
|
||||
SixenseBaseFunction sixenseExit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseExit");
|
||||
#endif
|
||||
|
||||
|
||||
sixenseExit();
|
||||
}
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
delete _sixenseLibrary;
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void SixenseManager::initialize() {
|
||||
#ifdef HAVE_SIXENSE
|
||||
|
||||
|
||||
if (!_isInitialized) {
|
||||
_lowVelocityFilter = false;
|
||||
_controllersAtBase = true;
|
||||
|
@ -91,22 +97,22 @@ void SixenseManager::initialize() {
|
|||
// By default we assume the _neckBase (in orb frame) is as high above the orb
|
||||
// as the "torso" is below it.
|
||||
_neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z);
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
|
||||
if (!_sixenseLibrary) {
|
||||
|
||||
|
||||
#ifdef SIXENSE_LIB_FILENAME
|
||||
_sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME);
|
||||
#else
|
||||
const QString SIXENSE_LIBRARY_NAME = "libsixense_x64";
|
||||
QString frameworkSixenseLibrary = QCoreApplication::applicationDirPath() + "/../Frameworks/"
|
||||
+ SIXENSE_LIBRARY_NAME;
|
||||
|
||||
|
||||
_sixenseLibrary = new QLibrary(frameworkSixenseLibrary);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
if (_sixenseLibrary->load()){
|
||||
qCDebug(interfaceapp) << "Loaded sixense library for hydra support -" << _sixenseLibrary->fileName();
|
||||
} else {
|
||||
|
@ -114,14 +120,14 @@ void SixenseManager::initialize() {
|
|||
<< "Continuing without hydra support.";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
SixenseBaseFunction sixenseInit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseInit");
|
||||
#endif
|
||||
sixenseInit();
|
||||
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -132,7 +138,7 @@ void SixenseManager::setFilter(bool filter) {
|
|||
#ifdef __APPLE__
|
||||
SixenseTakeIntFunction sixenseSetFilterEnabled = (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseSetFilterEnabled");
|
||||
#endif
|
||||
|
||||
|
||||
if (filter) {
|
||||
sixenseSetFilterEnabled(1);
|
||||
} else {
|
||||
|
@ -147,36 +153,49 @@ void SixenseManager::update(float deltaTime) {
|
|||
#ifdef HAVE_SIXENSE
|
||||
Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
|
||||
if (_isInitialized && _isEnabled) {
|
||||
_buttonPressedMap.clear();
|
||||
#ifdef __APPLE__
|
||||
SixenseBaseFunction sixenseGetNumActiveControllers =
|
||||
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");
|
||||
#endif
|
||||
|
||||
|
||||
if (sixenseGetNumActiveControllers() == 0) {
|
||||
_hydrasConnected = false;
|
||||
if (_deviceID != 0) {
|
||||
Application::getUserInputMapper()->removeDevice(_deviceID);
|
||||
_deviceID = 0;
|
||||
if (_prevPalms[0]) {
|
||||
_prevPalms[0]->setActive(false);
|
||||
}
|
||||
if (_prevPalms[1]) {
|
||||
_prevPalms[1]->setActive(false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
PerformanceTimer perfTimer("sixense");
|
||||
if (!_hydrasConnected) {
|
||||
_hydrasConnected = true;
|
||||
registerToUserInputMapper(*Application::getUserInputMapper());
|
||||
getInstance().assignDefaultInputMapping(*Application::getUserInputMapper());
|
||||
UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
|
||||
}
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
SixenseBaseFunction sixenseGetMaxControllers =
|
||||
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers");
|
||||
#endif
|
||||
|
||||
|
||||
int maxControllers = sixenseGetMaxControllers();
|
||||
|
||||
|
||||
// we only support two controllers
|
||||
sixenseControllerData controllers[2];
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
SixenseTakeIntFunction sixenseIsControllerEnabled =
|
||||
(SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseIsControllerEnabled");
|
||||
|
||||
|
||||
SixenseTakeIntAndSixenseControllerData sixenseGetNewestData =
|
||||
(SixenseTakeIntAndSixenseControllerData) _sixenseLibrary->resolve("sixenseGetNewestData");
|
||||
#endif
|
||||
|
@ -189,15 +208,16 @@ void SixenseManager::update(float deltaTime) {
|
|||
sixenseControllerData* data = controllers + numActiveControllers;
|
||||
++numActiveControllers;
|
||||
sixenseGetNewestData(i, data);
|
||||
|
||||
|
||||
// Set palm position and normal based on Hydra position/orientation
|
||||
|
||||
|
||||
// Either find a palm matching the sixense controller, or make a new one
|
||||
PalmData* palm;
|
||||
bool foundHand = false;
|
||||
for (size_t j = 0; j < hand->getNumPalms(); j++) {
|
||||
if (hand->getPalms()[j].getSixenseID() == data->controller_index) {
|
||||
palm = &(hand->getPalms()[j]);
|
||||
_prevPalms[numActiveControllers - 1] = palm;
|
||||
foundHand = true;
|
||||
}
|
||||
}
|
||||
|
@ -206,47 +226,50 @@ void SixenseManager::update(float deltaTime) {
|
|||
hand->getPalms().push_back(newPalm);
|
||||
palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
|
||||
palm->setSixenseID(data->controller_index);
|
||||
_prevPalms[numActiveControllers - 1] = palm;
|
||||
qCDebug(interfaceapp, "Found new Sixense controller, ID %i", data->controller_index);
|
||||
}
|
||||
|
||||
|
||||
// Disable the hands (and return to default pose) if both controllers are at base station
|
||||
if (foundHand) {
|
||||
palm->setActive(!_controllersAtBase);
|
||||
} else {
|
||||
palm->setActive(false); // if this isn't a Sixsense ID palm, always make it inactive
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Read controller buttons and joystick into the hand
|
||||
palm->setControllerButtons(data->buttons);
|
||||
palm->setTrigger(data->trigger);
|
||||
palm->setJoystick(data->joystick_x, data->joystick_y);
|
||||
|
||||
|
||||
handleButtonEvent(data->buttons, numActiveControllers - 1);
|
||||
handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1);
|
||||
|
||||
// Emulate the mouse so we can use scripts
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput) && !_controllersAtBase) {
|
||||
emulateMouse(palm, numActiveControllers - 1);
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
||||
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
||||
position *= METERS_PER_MILLIMETER;
|
||||
|
||||
|
||||
// Check to see if this hand/controller is on the base
|
||||
const float CONTROLLER_AT_BASE_DISTANCE = 0.075f;
|
||||
if (glm::length(position) < CONTROLLER_AT_BASE_DISTANCE) {
|
||||
numControllersAtBase++;
|
||||
}
|
||||
|
||||
|
||||
// Transform the measured position into body frame.
|
||||
glm::vec3 neck = _neckBase;
|
||||
// Zeroing y component of the "neck" effectively raises the measured position a little bit.
|
||||
neck.y = 0.0f;
|
||||
position = _orbRotation * (position - neck);
|
||||
|
||||
|
||||
// Rotation of Palm
|
||||
glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
|
||||
rotation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)) * _orbRotation * rotation;
|
||||
|
||||
|
||||
// Compute current velocity from position change
|
||||
glm::vec3 rawVelocity;
|
||||
if (deltaTime > 0.0f) {
|
||||
|
@ -255,11 +278,11 @@ void SixenseManager::update(float deltaTime) {
|
|||
rawVelocity = glm::vec3(0.0f);
|
||||
}
|
||||
palm->setRawVelocity(rawVelocity); // meters/sec
|
||||
|
||||
|
||||
// adjustment for hydra controllers fit into hands
|
||||
float sign = (i == 0) ? -1.0f : 1.0f;
|
||||
rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
|
||||
// Angular Velocity of Palm
|
||||
glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation());
|
||||
glm::vec3 angularVelocity(0.0f);
|
||||
|
@ -271,7 +294,7 @@ void SixenseManager::update(float deltaTime) {
|
|||
} else {
|
||||
palm->setRawAngularVelocity(glm::vec3(0.0f));
|
||||
}
|
||||
|
||||
|
||||
if (_lowVelocityFilter) {
|
||||
// Use a velocity sensitive filter to damp small motions and preserve large ones with
|
||||
// no latency.
|
||||
|
@ -284,7 +307,7 @@ void SixenseManager::update(float deltaTime) {
|
|||
palm->setRawPosition(position);
|
||||
palm->setRawRotation(rotation);
|
||||
}
|
||||
|
||||
|
||||
// Store the one fingertip in the palm structure so we can track velocity
|
||||
const float FINGER_LENGTH = 0.3f; // meters
|
||||
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH);
|
||||
|
@ -297,7 +320,7 @@ void SixenseManager::update(float deltaTime) {
|
|||
}
|
||||
palm->setTipPosition(newTipPosition);
|
||||
}
|
||||
|
||||
|
||||
if (numActiveControllers == 2) {
|
||||
updateCalibration(controllers);
|
||||
}
|
||||
|
@ -309,7 +332,7 @@ void SixenseManager::update(float deltaTime) {
|
|||
//Constants for getCursorPixelRangeMultiplier()
|
||||
const float MIN_PIXEL_RANGE_MULT = 0.4f;
|
||||
const float MAX_PIXEL_RANGE_MULT = 2.0f;
|
||||
const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01;
|
||||
const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f;
|
||||
|
||||
//Returns a multiplier to be applied to the cursor range for the controllers
|
||||
float SixenseManager::getCursorPixelRangeMult() const {
|
||||
|
@ -398,7 +421,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
|
|||
_calibrationState = CALIBRATION_STATE_X;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
quint64 now = usecTimestampNow() + LOCK_DURATION;
|
||||
// these are weighted running averages
|
||||
|
@ -408,7 +431,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
|
|||
if (_calibrationState == CALIBRATION_STATE_X) {
|
||||
// compute new sliding average
|
||||
float distance = glm::distance(_averageLeft, _averageRight);
|
||||
if (fabs(distance - _lastDistance) > MAXIMUM_NOISE_LEVEL) {
|
||||
if (fabsf(distance - _lastDistance) > MAXIMUM_NOISE_LEVEL) {
|
||||
// distance is increasing so acquire the data and push the expiry out
|
||||
_reachLeft = _averageLeft;
|
||||
_reachRight = _averageRight;
|
||||
|
@ -427,7 +450,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
|
|||
glm::vec3 torso = 0.5f * (_reachLeft + _reachRight);
|
||||
glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight);
|
||||
float distance = (averagePosition - torso).y;
|
||||
if (fabs(distance) > fabs(_lastDistance) + MAXIMUM_NOISE_LEVEL) {
|
||||
if (fabsf(distance) > fabsf(_lastDistance) + MAXIMUM_NOISE_LEVEL) {
|
||||
// distance is increasing so acquire the data and push the expiry out
|
||||
_reachUp = averagePosition;
|
||||
_lastDistance = distance;
|
||||
|
@ -458,11 +481,11 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
|
|||
_lastDistance = distance;
|
||||
_lockExpiry = now + LOCK_DURATION;
|
||||
} else if (now > _lockExpiry) {
|
||||
if (fabs(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) {
|
||||
if (fabsf(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) {
|
||||
// lock has expired so clamp the data and move on
|
||||
_calibrationState = CALIBRATION_STATE_COMPLETE;
|
||||
qCDebug(interfaceapp, "success: sixense calibration: forward");
|
||||
// TODO: it is theoretically possible to detect that the controllers have been
|
||||
// TODO: it is theoretically possible to detect that the controllers have been
|
||||
// accidentally switched (left hand is holding right controller) and to swap the order.
|
||||
}
|
||||
}
|
||||
|
@ -473,7 +496,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
|
|||
void SixenseManager::emulateMouse(PalmData* palm, int index) {
|
||||
MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
QPoint pos;
|
||||
|
||||
|
||||
Qt::MouseButton bumperButton;
|
||||
Qt::MouseButton triggerButton;
|
||||
|
||||
|
@ -488,14 +511,14 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
|
|||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers) || qApp->isHMDMode()) {
|
||||
pos = qApp->getApplicationOverlay().getPalmClickLocation(palm);
|
||||
pos = qApp->getApplicationCompositor().getPalmClickLocation(palm);
|
||||
} else {
|
||||
// Get directon relative to avatar orientation
|
||||
glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection();
|
||||
|
||||
// Get the angles, scaled between (-0.5,0.5)
|
||||
float xAngle = (atan2(direction.z, direction.x) + M_PI_2);
|
||||
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
||||
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));
|
||||
auto canvasSize = qApp->getCanvasSize();
|
||||
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||
float cursorRange = canvasSize.x * getCursorPixelRangeMult();
|
||||
|
@ -538,11 +561,11 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
|
|||
if (!_bumperPressed[(int)(!index)]) {
|
||||
qApp->mouseMoveEvent(&mouseEvent, deviceID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_oldX[index] = pos.x();
|
||||
_oldY[index] = pos.y();
|
||||
|
||||
|
||||
|
||||
//We need separate coordinates for clicks, since we need to check if
|
||||
//a magnification window was clicked on
|
||||
|
@ -556,7 +579,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
|
|||
if (palm->getControllerButtons() & BUTTON_FWD) {
|
||||
if (!_bumperPressed[index]) {
|
||||
_bumperPressed[index] = true;
|
||||
|
||||
|
||||
QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0);
|
||||
|
||||
qApp->mousePressEvent(&mouseEvent, deviceID);
|
||||
|
@ -589,3 +612,143 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
|
|||
|
||||
#endif // HAVE_SIXENSE
|
||||
|
||||
void SixenseManager::focusOutEvent() {
|
||||
_axisStateMap.clear();
|
||||
_buttonPressedMap.clear();
|
||||
};
|
||||
|
||||
void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) {
|
||||
_axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (stickY > 0.0f) ? stickY : 0.0f;
|
||||
_axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (stickY < 0.0f) ? -stickY : 0.0f;
|
||||
_axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (stickX > 0.0f) ? stickX : 0.0f;
|
||||
_axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (stickX < 0.0f) ? -stickX : 0.0f;
|
||||
_axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = trigger;
|
||||
}
|
||||
|
||||
void SixenseManager::handleButtonEvent(unsigned int buttons, int index) {
|
||||
if (buttons & BUTTON_0) {
|
||||
_buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel());
|
||||
}
|
||||
if (buttons & BUTTON_1) {
|
||||
_buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel());
|
||||
}
|
||||
if (buttons & BUTTON_2) {
|
||||
_buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel());
|
||||
}
|
||||
if (buttons & BUTTON_3) {
|
||||
_buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel());
|
||||
}
|
||||
if (buttons & BUTTON_4) {
|
||||
_buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel());
|
||||
}
|
||||
if (buttons & BUTTON_FWD) {
|
||||
_buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel());
|
||||
}
|
||||
if (buttons & BUTTON_TRIGGER) {
|
||||
_buttonPressedMap.insert(makeInput(BUTTON_TRIGGER, index).getChannel());
|
||||
}
|
||||
}
|
||||
|
||||
void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) {
|
||||
// Grab the current free device ID
|
||||
_deviceID = mapper.getFreeDeviceID();
|
||||
|
||||
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Hydra"));
|
||||
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
|
||||
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
|
||||
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
|
||||
QVector<UserInputMapper::InputPair> availableInputs;
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 0), "Left Start"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 0), "Left Button 1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 0), "Left Trigger Press"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2"));
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Stick Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 1), "Right Trigger Press"));
|
||||
|
||||
return availableInputs;
|
||||
};
|
||||
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
|
||||
mapper.removeAllInputChannelsForDevice(_deviceID);
|
||||
this->assignDefaultInputMapping(mapper);
|
||||
return true;
|
||||
};
|
||||
mapper.registerDevice(_deviceID, proxy);
|
||||
}
|
||||
|
||||
void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
|
||||
const float JOYSTICK_MOVE_SPEED = 1.0f;
|
||||
const float JOYSTICK_YAW_SPEED = 0.5f;
|
||||
const float JOYSTICK_PITCH_SPEED = 0.25f;
|
||||
const float BUTTON_MOVE_SPEED = 1.0f;
|
||||
const float BOOM_SPEED = 0.1f;
|
||||
|
||||
// Left Joystick: Movement, strafing
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED);
|
||||
|
||||
// Right Joystick: Camera orientation
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED);
|
||||
|
||||
// Buttons
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED);
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED);
|
||||
}
|
||||
|
||||
float SixenseManager::getButton(int channel) const {
|
||||
if (!_buttonPressedMap.empty()) {
|
||||
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
|
||||
return 1.0f;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float SixenseManager::getAxis(int channel) const {
|
||||
auto axis = _axisStateMap.find(channel);
|
||||
if (axis != _axisStateMap.end()) {
|
||||
return (*axis).second;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
UserInputMapper::Input SixenseManager::makeInput(unsigned int button, int index) {
|
||||
return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON);
|
||||
}
|
||||
|
||||
UserInputMapper::Input SixenseManager::makeInput(SixenseManager::JoystickAxisChannel axis, int index) {
|
||||
return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_SixenseManager_h
|
||||
|
||||
#include <QObject>
|
||||
#include <unordered_set>
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
#include <glm/glm.hpp>
|
||||
|
@ -25,6 +26,8 @@
|
|||
|
||||
#endif
|
||||
|
||||
#include "ui/UserInputMapper.h"
|
||||
|
||||
class PalmData;
|
||||
|
||||
const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
|
||||
|
@ -33,6 +36,7 @@ const unsigned int BUTTON_2 = 1U << 6;
|
|||
const unsigned int BUTTON_3 = 1U << 3;
|
||||
const unsigned int BUTTON_4 = 1U << 4;
|
||||
const unsigned int BUTTON_FWD = 1U << 7;
|
||||
const unsigned int BUTTON_TRIGGER = 1U << 8;
|
||||
|
||||
// Event type that represents using the controller
|
||||
const unsigned int CONTROLLER_0_EVENT = 1500U;
|
||||
|
@ -45,6 +49,14 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
|
|||
class SixenseManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum JoystickAxisChannel {
|
||||
AXIS_Y_POS = 1U << 0,
|
||||
AXIS_Y_NEG = 1U << 3,
|
||||
AXIS_X_POS = 1U << 4,
|
||||
AXIS_X_NEG = 1U << 5,
|
||||
BACK_TRIGGER = 1U << 6,
|
||||
};
|
||||
|
||||
static SixenseManager& getInstance();
|
||||
|
||||
void initialize();
|
||||
|
@ -60,6 +72,21 @@ public:
|
|||
bool getInvertButtons() const { return _invertButtons; }
|
||||
void setInvertButtons(bool invertSixenseButtons) { _invertButtons = invertSixenseButtons; }
|
||||
|
||||
typedef std::unordered_set<int> ButtonPressedMap;
|
||||
typedef std::map<int, float> AxisStateMap;
|
||||
|
||||
float getButton(int channel) const;
|
||||
float getAxis(int channel) const;
|
||||
|
||||
UserInputMapper::Input makeInput(unsigned int button, int index);
|
||||
UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index);
|
||||
|
||||
void registerToUserInputMapper(UserInputMapper& mapper);
|
||||
void assignDefaultInputMapping(UserInputMapper& mapper);
|
||||
|
||||
void update();
|
||||
void focusOutEvent();
|
||||
|
||||
public slots:
|
||||
void toggleSixense(bool shouldEnable);
|
||||
void setFilter(bool filter);
|
||||
|
@ -69,6 +96,8 @@ private:
|
|||
SixenseManager();
|
||||
~SixenseManager();
|
||||
|
||||
void handleButtonEvent(unsigned int buttons, int index);
|
||||
void handleAxisEvent(float x, float y, float trigger, int index);
|
||||
#ifdef HAVE_SIXENSE
|
||||
void updateCalibration(const sixenseControllerData* controllers);
|
||||
void emulateMouse(PalmData* palm, int index);
|
||||
|
@ -104,12 +133,19 @@ private:
|
|||
bool _bumperPressed[2];
|
||||
int _oldX[2];
|
||||
int _oldY[2];
|
||||
PalmData* _prevPalms[2];
|
||||
|
||||
bool _lowVelocityFilter;
|
||||
bool _controllersAtBase;
|
||||
|
||||
float _reticleMoveSpeed = DEFAULT_SIXENSE_RETICLE_MOVE_SPEED;
|
||||
bool _invertButtons = DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS;
|
||||
|
||||
protected:
|
||||
int _deviceID = 0;
|
||||
|
||||
ButtonPressedMap _buttonPressedMap;
|
||||
AxisStateMap _axisStateMap;
|
||||
};
|
||||
|
||||
#endif // hifi_SixenseManager_h
|
||||
|
|
|
@ -457,6 +457,14 @@ void ControllerScriptingInterface::resetAllDeviceBindings() {
|
|||
Application::getUserInputMapper()->resetAllDeviceBindings();
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::resetDevice(unsigned int device) {
|
||||
Application::getUserInputMapper()->resetDevice(device);
|
||||
}
|
||||
|
||||
int ControllerScriptingInterface::findDevice(QString name) {
|
||||
return Application::getUserInputMapper()->findDevice(name);
|
||||
}
|
||||
|
||||
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
|
||||
AbstractInputController(),
|
||||
_deviceTrackerId(deviceTrackerId),
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
|
||||
void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); }
|
||||
void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mousePressEvent(MouseEvent(*event, deviceID)); }
|
||||
void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); }
|
||||
void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }
|
||||
|
||||
void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
|
||||
|
@ -91,6 +92,8 @@ public slots:
|
|||
Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel);
|
||||
Q_INVOKABLE virtual QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device);
|
||||
Q_INVOKABLE virtual void resetAllDeviceBindings();
|
||||
Q_INVOKABLE virtual void resetDevice(unsigned int device);
|
||||
Q_INVOKABLE virtual int findDevice(QString name);
|
||||
virtual bool isPrimaryButtonPressed() const;
|
||||
virtual glm::vec2 getPrimaryJoystickPosition() const;
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const {
|
|||
|
||||
glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
|
||||
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
|
||||
const auto& compositor = Application::getInstance()->getApplicationCompositor();
|
||||
|
||||
return applicationOverlay.calculateRayUICollisionPoint(position, direction, result);
|
||||
return compositor.calculateRayUICollisionPoint(position, direction, result);
|
||||
}
|
||||
|
||||
QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
|
||||
|
@ -40,7 +40,7 @@ QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* conte
|
|||
glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter);
|
||||
glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction);
|
||||
glm::vec3 eulers = ::safeEulerAngles(rotation);
|
||||
return qScriptValueFromValue<glm::vec2>(engine, Application::getInstance()->getApplicationOverlay()
|
||||
return qScriptValueFromValue<glm::vec2>(engine, Application::getInstance()->getApplicationCompositor()
|
||||
.sphericalToOverlay(glm::vec2(eulers.y, -eulers.x)));
|
||||
}
|
||||
return QScriptValue::NullValue;
|
||||
|
|
|
@ -27,11 +27,11 @@ public:
|
|||
static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
public slots:
|
||||
void toggleMagnifier() { Application::getInstance()->getApplicationOverlay().toggleMagnifier(); };
|
||||
void toggleMagnifier() { Application::getInstance()->getApplicationCompositor().toggleMagnifier(); };
|
||||
|
||||
private:
|
||||
HMDScriptingInterface() {};
|
||||
bool getMagnifier() const { return Application::getInstance()->getApplicationOverlay().hasMagnifier(); };
|
||||
bool getMagnifier() const { return Application::getInstance()->getApplicationCompositor().hasMagnifier(); };
|
||||
bool isHMDMode() const { return Application::getInstance()->isHMDMode(); }
|
||||
|
||||
bool getHUDLookAtPosition3D(glm::vec3& result) const;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
//
|
||||
|
||||
#include "starfield/renderer/Renderer.h"
|
||||
#include "Application.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
|
@ -52,6 +53,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation,
|
|||
matrix[3][1] = 0.0f;
|
||||
matrix[3][2] = 0.0f;
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadMatrixf(glm::value_ptr(qApp->getDisplayViewFrustum()->getProjection()));
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
// extract local z vector
|
||||
vec3 ahead = vec3(matrix[2]);
|
||||
|
||||
|
@ -74,6 +79,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation,
|
|||
floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray));
|
||||
|
||||
this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
}
|
||||
|
||||
// renderer construction
|
||||
|
|
|
@ -22,6 +22,20 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare
|
|||
connect(addressManager.data(), &AddressManager::lookupResultIsOffline, this, &AddressBarDialog::displayAddressOfflineMessage);
|
||||
connect(addressManager.data(), &AddressManager::lookupResultIsNotFound, this, &AddressBarDialog::displayAddressNotFoundMessage);
|
||||
connect(addressManager.data(), &AddressManager::lookupResultsFinished, this, &AddressBarDialog::hide);
|
||||
connect(addressManager.data(), &AddressManager::goBackPossible, this, [this] (bool isPossible) {
|
||||
if (isPossible != _backEnabled) {
|
||||
_backEnabled = isPossible;
|
||||
emit backEnabledChanged();
|
||||
}
|
||||
});
|
||||
connect(addressManager.data(), &AddressManager::goForwardPossible, this, [this] (bool isPossible) {
|
||||
if (isPossible != _forwardEnabled) {
|
||||
_forwardEnabled = isPossible;
|
||||
emit forwardEnabledChanged();
|
||||
}
|
||||
});
|
||||
_backEnabled = !(DependencyManager::get<AddressManager>()->getBackStack().isEmpty());
|
||||
_forwardEnabled = !(DependencyManager::get<AddressManager>()->getForwardStack().isEmpty());
|
||||
}
|
||||
|
||||
void AddressBarDialog::hide() {
|
||||
|
@ -35,6 +49,16 @@ void AddressBarDialog::loadAddress(const QString& address) {
|
|||
}
|
||||
}
|
||||
|
||||
void AddressBarDialog::loadBack() {
|
||||
qDebug() << "Called LoadBack";
|
||||
DependencyManager::get<AddressManager>()->goBack();
|
||||
}
|
||||
|
||||
void AddressBarDialog::loadForward() {
|
||||
qDebug() << "Called LoadForward";
|
||||
DependencyManager::get<AddressManager>()->goForward();
|
||||
}
|
||||
|
||||
void AddressBarDialog::displayAddressOfflineMessage() {
|
||||
OffscreenUi::error("That user or place is currently offline");
|
||||
}
|
||||
|
|
|
@ -18,9 +18,17 @@ class AddressBarDialog : public OffscreenQmlDialog
|
|||
{
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged)
|
||||
Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged)
|
||||
|
||||
public:
|
||||
AddressBarDialog(QQuickItem* parent = nullptr);
|
||||
bool backEnabled() { return _backEnabled; }
|
||||
bool forwardEnabled() { return _forwardEnabled; }
|
||||
|
||||
signals:
|
||||
void backEnabledChanged();
|
||||
void forwardEnabledChanged();
|
||||
|
||||
protected:
|
||||
void displayAddressOfflineMessage();
|
||||
|
@ -28,6 +36,11 @@ protected:
|
|||
void hide();
|
||||
|
||||
Q_INVOKABLE void loadAddress(const QString& address);
|
||||
Q_INVOKABLE void loadBack();
|
||||
Q_INVOKABLE void loadForward();
|
||||
|
||||
bool _backEnabled;
|
||||
bool _forwardEnabled;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
790
interface/src/ui/ApplicationCompositor.cpp
Normal file
790
interface/src/ui/ApplicationCompositor.cpp
Normal file
|
@ -0,0 +1,790 @@
|
|||
//
|
||||
// ApplicationCompositor.cpp
|
||||
// interface/src/ui/overlays
|
||||
//
|
||||
// Created by Benjamin Arnold on 5/27/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include "ApplicationCompositor.h"
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
#include <CursorManager.h>
|
||||
#include <Tooltip.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
|
||||
// Used to animate the magnification windows
|
||||
static const float MAG_SPEED = 0.08f;
|
||||
|
||||
static const quint64 MSECS_TO_USECS = 1000ULL;
|
||||
static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
||||
|
||||
static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
|
||||
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 CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };
|
||||
static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
|
||||
|
||||
static const float CURSOR_PIXEL_SIZE = 32.0f;
|
||||
static const float MOUSE_PITCH_RANGE = 1.0f * PI;
|
||||
static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI;
|
||||
static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE);
|
||||
|
||||
static gpu::BufferPointer _hemiVertices;
|
||||
static gpu::BufferPointer _hemiIndices;
|
||||
static int _hemiIndexCount{ 0 };
|
||||
EntityItemID ApplicationCompositor::_noItemId;
|
||||
static QString _tooltipId;
|
||||
|
||||
// Return a point's cartesian coordinates on a sphere from pitch and yaw
|
||||
glm::vec3 getPoint(float yaw, float pitch) {
|
||||
return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
|
||||
glm::sin(-pitch),
|
||||
glm::cos(-pitch) * (-glm::cos(yaw)));
|
||||
}
|
||||
|
||||
//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should
|
||||
//be multiplied by dir and added to origin to get the location of the collision
|
||||
bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result)
|
||||
{
|
||||
//Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
|
||||
|
||||
//Compute A, B and C coefficients
|
||||
float a = glm::dot(dir, dir);
|
||||
float b = 2 * glm::dot(dir, origin);
|
||||
float c = glm::dot(origin, origin) - (r * r);
|
||||
|
||||
//Find discriminant
|
||||
float disc = b * b - 4 * a * c;
|
||||
|
||||
// if discriminant is negative there are no real roots, so return
|
||||
// false as ray misses sphere
|
||||
if (disc < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute q as described above
|
||||
float distSqrt = sqrtf(disc);
|
||||
float q;
|
||||
if (b < 0) {
|
||||
q = (-b - distSqrt) / 2.0f;
|
||||
} else {
|
||||
q = (-b + distSqrt) / 2.0f;
|
||||
}
|
||||
|
||||
// compute t0 and t1
|
||||
float t0 = q / a;
|
||||
float t1 = c / q;
|
||||
|
||||
// make sure t0 is smaller than t1
|
||||
if (t0 > t1) {
|
||||
// if t0 is bigger than t1 swap them around
|
||||
float temp = t0;
|
||||
t0 = t1;
|
||||
t1 = temp;
|
||||
}
|
||||
|
||||
// if t1 is less than zero, the object is in the ray's negative direction
|
||||
// and consequently the ray misses the sphere
|
||||
if (t1 < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if t0 is less than zero, the intersection point is at t1
|
||||
if (t0 < 0) {
|
||||
*result = t1;
|
||||
return true;
|
||||
} else { // else the intersection point is at t0
|
||||
*result = t0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationCompositor::ApplicationCompositor() {
|
||||
memset(_reticleActive, 0, sizeof(_reticleActive));
|
||||
memset(_magActive, 0, sizeof(_reticleActive));
|
||||
memset(_magSizeMult, 0, sizeof(_magSizeMult));
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
_reticleQuad = geometryCache->allocateID();
|
||||
_magnifierQuad = geometryCache->allocateID();
|
||||
_magnifierBorder = geometryCache->allocateID();
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
if (_hoverItemId != entityItemID) {
|
||||
_hoverItemId = entityItemID;
|
||||
_hoverItemEnterUsecs = usecTimestampNow();
|
||||
auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId);
|
||||
_hoverItemHref = properties.getHref();
|
||||
auto cursor = Cursor::Manager::instance().getCursor();
|
||||
if (!_hoverItemHref.isEmpty()) {
|
||||
cursor->setIcon(Cursor::Icon::LINK);
|
||||
} else {
|
||||
cursor->setIcon(Cursor::Icon::DEFAULT);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
if (_hoverItemId == entityItemID) {
|
||||
_hoverItemId = _noItemId;
|
||||
_hoverItemHref.clear();
|
||||
auto cursor = Cursor::Manager::instance().getCursor();
|
||||
cursor->setIcon(Cursor::Icon::DEFAULT);
|
||||
if (!_tooltipId.isEmpty()) {
|
||||
qDebug() << "Closing tooltip " << _tooltipId;
|
||||
Tooltip::closeTip(_tooltipId);
|
||||
_tooltipId.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ApplicationCompositor::~ApplicationCompositor() {
|
||||
}
|
||||
|
||||
|
||||
void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) {
|
||||
auto& cursorManager = Cursor::Manager::instance();
|
||||
auto cursor = cursorManager.getCursor(cursorIndex);
|
||||
auto iconId = cursor->getIcon();
|
||||
if (!_cursors.count(iconId)) {
|
||||
auto iconPath = cursorManager.getIconImage(cursor->getIcon());
|
||||
_cursors[iconId] = DependencyManager::get<TextureCache>()->
|
||||
getImageTexture(iconPath);
|
||||
}
|
||||
batch.setUniformTexture(0, _cursors[iconId]);
|
||||
}
|
||||
|
||||
// Draws the FBO texture for the screen
|
||||
void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
||||
if (_alpha == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLuint texture = qApp->getApplicationOverlay().getOverlayTexture();
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateTooltips();
|
||||
|
||||
vec2 canvasSize = qApp->getCanvasSize();
|
||||
|
||||
//Handle fading and deactivation/activation of UI
|
||||
gpu::Batch batch;
|
||||
|
||||
renderArgs->_context->syncCache();
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.setViewTransform(Transform());
|
||||
batch.setProjectionTransform(mat4());
|
||||
batch._glBindTexture(GL_TEXTURE_2D, texture);
|
||||
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
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 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize);
|
||||
// Invert the Y axis
|
||||
mousePosition.y *= -1.0f;
|
||||
|
||||
Transform model;
|
||||
model.setTranslation(vec3(mousePosition, 0));
|
||||
vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
|
||||
model.setScale(vec3(mouseSize, 1.0f));
|
||||
batch.setModelTransform(model);
|
||||
bindCursorTexture(batch);
|
||||
vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
|
||||
geometryCache->renderUnitQuad(batch, vec4(1));
|
||||
renderArgs->_context->render(batch);
|
||||
}
|
||||
|
||||
|
||||
vec2 getPolarCoordinates(const PalmData& palm) {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
auto avatarOrientation = myAvatar->getOrientation();
|
||||
auto eyePos = myAvatar->getDefaultEyePosition();
|
||||
glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);
|
||||
// Direction of the tip relative to the eye
|
||||
glm::vec3 tipDirection = tip - eyePos;
|
||||
// orient into avatar space
|
||||
tipDirection = glm::inverse(avatarOrientation) * tipDirection;
|
||||
// Normalize for trig functions
|
||||
tipDirection = glm::normalize(tipDirection);
|
||||
// Convert to polar coordinates
|
||||
glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y));
|
||||
return polar;
|
||||
}
|
||||
|
||||
// Draws the FBO texture for Oculus rift.
|
||||
void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) {
|
||||
if (_alpha == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLuint texture = qApp->getApplicationOverlay().getOverlayTexture();
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateTooltips();
|
||||
|
||||
vec2 canvasSize = qApp->getCanvasSize();
|
||||
_textureAspectRatio = aspect(canvasSize);
|
||||
|
||||
renderArgs->_context->syncCache();
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
gpu::Batch batch;
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
batch._glDisable(GL_DEPTH_TEST);
|
||||
batch._glDisable(GL_CULL_FACE);
|
||||
batch._glBindTexture(GL_TEXTURE_2D, texture);
|
||||
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
batch.setViewTransform(Transform());
|
||||
batch.setProjectionTransform(qApp->getEyeProjection(eye));
|
||||
|
||||
mat4 eyePose = qApp->getEyePose(eye);
|
||||
glm::mat4 overlayXfm = glm::inverse(eyePose);
|
||||
|
||||
#ifdef DEBUG_OVERLAY
|
||||
{
|
||||
batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2)));
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1));
|
||||
}
|
||||
#else
|
||||
{
|
||||
batch.setModelTransform(overlayXfm);
|
||||
drawSphereSection(batch);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Doesn't actually render
|
||||
renderPointers(batch);
|
||||
vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);
|
||||
|
||||
bindCursorTexture(batch);
|
||||
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
//Controller Pointers
|
||||
for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
|
||||
PalmData& palm = myAvatar->getHand()->getPalms()[i];
|
||||
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);
|
||||
}
|
||||
|
||||
renderArgs->_context->render(batch);
|
||||
}
|
||||
|
||||
|
||||
void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
|
||||
cursorPos *= qApp->getCanvasSize();
|
||||
const glm::vec2 projection = screenToSpherical(cursorPos);
|
||||
// The overlay space orientation of the mouse coordinates
|
||||
const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f));
|
||||
// FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE.
|
||||
// Now we need to account for the actual camera position relative to the overlay
|
||||
glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT);
|
||||
|
||||
|
||||
// We need the RAW camera orientation and position, because this is what the overlay is
|
||||
// rendered relative to
|
||||
const glm::vec3 overlayPosition = qApp->getCamera()->getPosition();
|
||||
const glm::quat overlayOrientation = qApp->getCamera()->getRotation();
|
||||
|
||||
// Intersection UI overlay space
|
||||
glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection;
|
||||
glm::vec3 intersectionWithUi = glm::normalize(worldSpaceDirection) * _oculusUIRadius;
|
||||
intersectionWithUi += overlayPosition;
|
||||
|
||||
// Intersection in world space
|
||||
origin = overlayPosition;
|
||||
direction = glm::normalize(intersectionWithUi - origin);
|
||||
}
|
||||
|
||||
//Caculate the click location using one of the sixense controllers. Scale is not applied
|
||||
QPoint ApplicationCompositor::getPalmClickLocation(const PalmData *palm) const {
|
||||
QPoint rv;
|
||||
auto canvasSize = qApp->getCanvasSize();
|
||||
if (qApp->isHMDMode()) {
|
||||
glm::vec2 polar = getPolarCoordinates(*palm);
|
||||
glm::vec2 point = sphericalToScreen(-polar);
|
||||
rv.rx() = point.x;
|
||||
rv.ry() = point.y;
|
||||
} else {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
glm::dmat4 projection;
|
||||
qApp->getProjectionMatrix(&projection);
|
||||
glm::quat invOrientation = glm::inverse(myAvatar->getOrientation());
|
||||
glm::vec3 eyePos = myAvatar->getDefaultEyePosition();
|
||||
glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm);
|
||||
glm::vec3 tipPos = invOrientation * (tip - eyePos);
|
||||
|
||||
glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0));
|
||||
glm::vec3 ndcSpacePos;
|
||||
if (clipSpacePos.w != 0) {
|
||||
ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w;
|
||||
}
|
||||
|
||||
rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x);
|
||||
rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
//Finds the collision point of a world space ray
|
||||
bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation());
|
||||
|
||||
glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition());
|
||||
glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction);
|
||||
|
||||
float t;
|
||||
if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){
|
||||
result = position + direction * t;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//Renders optional pointers
|
||||
void ApplicationCompositor::renderPointers(gpu::Batch& batch) {
|
||||
if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {
|
||||
//If we are in oculus, render reticle later
|
||||
if (_lastMouseMove == 0) {
|
||||
_lastMouseMove = usecTimestampNow();
|
||||
}
|
||||
QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY());
|
||||
|
||||
static const int MAX_IDLE_TIME = 3;
|
||||
if (_reticlePosition[MOUSE] != position) {
|
||||
_lastMouseMove = usecTimestampNow();
|
||||
} else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) {
|
||||
//float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians
|
||||
//OculusManager::getEulerAngles(yaw, pitch, roll);
|
||||
glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll));
|
||||
glm::vec3 result;
|
||||
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
if (calculateRayUICollisionPoint(myAvatar->getEyePosition(),
|
||||
myAvatar->getOrientation() * orientation * IDENTITY_FRONT,
|
||||
result)) {
|
||||
glm::vec3 lookAtDirection = glm::inverse(myAvatar->getOrientation()) * (result - myAvatar->getDefaultEyePosition());
|
||||
glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection));
|
||||
glm::vec2 screenPos = sphericalToScreen(spericalPos);
|
||||
position = QPoint(screenPos.x, screenPos.y);
|
||||
// FIXME
|
||||
//glCanvas->cursor().setPos(glCanvas->mapToGlobal(position));
|
||||
} else {
|
||||
qDebug() << "No collision point";
|
||||
}
|
||||
}
|
||||
|
||||
_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::SixenseMouseInput)) {
|
||||
_lastMouseMove = 0;
|
||||
//only render controller pointer if we aren't already rendering a mouse pointer
|
||||
_reticleActive[MOUSE] = false;
|
||||
_magActive[MOUSE] = false;
|
||||
renderControllerPointers(batch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
|
||||
for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {
|
||||
const int index = palmIndex - 1;
|
||||
|
||||
const PalmData* palmData = NULL;
|
||||
|
||||
if (palmIndex >= handData->getPalms().size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (handData->getPalms()[palmIndex].isActive()) {
|
||||
palmData = &handData->getPalms()[palmIndex];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
int controllerButtons = palmData->getControllerButtons();
|
||||
|
||||
//Check for if we should toggle or drag the magnification window
|
||||
if (controllerButtons & BUTTON_3) {
|
||||
if (isPressed[index] == false) {
|
||||
//We are now dragging the window
|
||||
isPressed[index] = true;
|
||||
//set the pressed time in us
|
||||
pressedTime[index] = usecTimestampNow();
|
||||
stateWhenPressed[index] = _magActive[index];
|
||||
}
|
||||
} else 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;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) {
|
||||
QPoint res = getPalmClickLocation(palmData);
|
||||
mouseX = res.x();
|
||||
mouseY = res.y();
|
||||
} else {
|
||||
// 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 = (atan2(direction.z, direction.x) + M_PI_2);
|
||||
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
||||
|
||||
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||
float cursorRange = canvasSize.x * SixenseManager::getInstance().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(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Renders a small magnification of the currently bound texture at the coordinates
|
||||
void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder) {
|
||||
if (!_magnifier) {
|
||||
return;
|
||||
}
|
||||
auto canvasSize = qApp->getCanvasSize();
|
||||
|
||||
const int widgetWidth = canvasSize.x;
|
||||
const int widgetHeight = canvasSize.y;
|
||||
|
||||
const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f;
|
||||
const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f;
|
||||
// Magnification Texture Coordinates
|
||||
const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth;
|
||||
const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth;
|
||||
const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight;
|
||||
const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight;
|
||||
|
||||
const float newHalfWidth = halfWidth * MAGNIFY_MULT;
|
||||
const float newHalfHeight = halfHeight * MAGNIFY_MULT;
|
||||
//Get yaw / pitch value for the corners
|
||||
const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth,
|
||||
magPos.y - newHalfHeight));
|
||||
const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth,
|
||||
magPos.y + newHalfHeight));
|
||||
|
||||
const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y);
|
||||
const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y);
|
||||
const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y);
|
||||
const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
if (bottomLeft != _previousMagnifierBottomLeft || bottomRight != _previousMagnifierBottomRight
|
||||
|| topLeft != _previousMagnifierTopLeft || topRight != _previousMagnifierTopRight) {
|
||||
QVector<glm::vec3> border;
|
||||
border << topLeft;
|
||||
border << bottomLeft;
|
||||
border << bottomRight;
|
||||
border << topRight;
|
||||
border << topLeft;
|
||||
geometryCache->updateVertices(_magnifierBorder, border, glm::vec4(1.0f, 0.0f, 0.0f, _alpha));
|
||||
|
||||
_previousMagnifierBottomLeft = bottomLeft;
|
||||
_previousMagnifierBottomRight = bottomRight;
|
||||
_previousMagnifierTopLeft = topLeft;
|
||||
_previousMagnifierTopRight = topRight;
|
||||
}
|
||||
|
||||
glPushMatrix(); {
|
||||
if (showBorder) {
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glLineWidth(1.0f);
|
||||
//Outer Line
|
||||
geometryCache->renderVertices(gpu::LINE_STRIP, _magnifierBorder);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
}
|
||||
glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha };
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(bottomLeft, bottomRight, topRight, topLeft,
|
||||
glm::vec2(magnifyULeft, magnifyVBottom),
|
||||
glm::vec2(magnifyURight, magnifyVBottom),
|
||||
glm::vec2(magnifyURight, magnifyVTop),
|
||||
glm::vec2(magnifyULeft, magnifyVTop),
|
||||
magnifierColor, _magnifierQuad);
|
||||
|
||||
} glPopMatrix();
|
||||
}
|
||||
|
||||
void ApplicationCompositor::buildHemiVertices(
|
||||
const float fov, const float aspectRatio, const int slices, const int stacks) {
|
||||
static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
|
||||
if (textureFOV == fov && textureAspectRatio == aspectRatio) {
|
||||
return;
|
||||
}
|
||||
|
||||
textureFOV = fov;
|
||||
textureAspectRatio = aspectRatio;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
_hemiVertices = gpu::BufferPointer(new gpu::Buffer());
|
||||
_hemiIndices = gpu::BufferPointer(new gpu::Buffer());
|
||||
|
||||
|
||||
if (fov >= PI) {
|
||||
qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues";
|
||||
}
|
||||
|
||||
//UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
|
||||
|
||||
vec3 pos;
|
||||
vec2 uv;
|
||||
// Compute vertices positions and texture UV coordinate
|
||||
// Create and write to buffer
|
||||
for (int i = 0; i < stacks; i++) {
|
||||
uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
|
||||
// abs(theta) <= fov / 2.0f
|
||||
float pitch = -fov * (uv.y - 0.5f);
|
||||
for (int j = 0; j < slices; j++) {
|
||||
uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
|
||||
// abs(phi) <= fov * aspectRatio / 2.0f
|
||||
float yaw = -fov * aspectRatio * (uv.x - 0.5f);
|
||||
pos = getPoint(yaw, pitch);
|
||||
static const vec4 color(1);
|
||||
_hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos);
|
||||
_hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv);
|
||||
_hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute number of indices needed
|
||||
static const int VERTEX_PER_TRANGLE = 3;
|
||||
static const int TRIANGLE_PER_RECTANGLE = 2;
|
||||
int numberOfRectangles = (slices - 1) * (stacks - 1);
|
||||
_hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
|
||||
|
||||
// Compute indices order
|
||||
std::vector<GLushort> indices;
|
||||
for (int i = 0; i < stacks - 1; i++) {
|
||||
for (int j = 0; j < slices - 1; j++) {
|
||||
GLushort bottomLeftIndex = i * slices + j;
|
||||
GLushort bottomRightIndex = bottomLeftIndex + 1;
|
||||
GLushort topLeftIndex = bottomLeftIndex + slices;
|
||||
GLushort topRightIndex = topLeftIndex + 1;
|
||||
// FIXME make a z-order curve for better vertex cache locality
|
||||
indices.push_back(topLeftIndex);
|
||||
indices.push_back(bottomLeftIndex);
|
||||
indices.push_back(topRightIndex);
|
||||
|
||||
indices.push_back(topRightIndex);
|
||||
indices.push_back(bottomLeftIndex);
|
||||
indices.push_back(bottomRightIndex);
|
||||
}
|
||||
}
|
||||
_hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]);
|
||||
}
|
||||
|
||||
|
||||
void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) {
|
||||
buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80);
|
||||
static const int VERTEX_DATA_SLOT = 0;
|
||||
static const int TEXTURE_DATA_SLOT = 1;
|
||||
static const int COLOR_DATA_SLOT = 2;
|
||||
gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone
|
||||
streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
||||
streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
|
||||
streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA));
|
||||
batch.setInputFormat(streamFormat);
|
||||
|
||||
static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4);
|
||||
gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
|
||||
gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element);
|
||||
gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
|
||||
batch.setInputBuffer(VERTEX_DATA_SLOT, posView);
|
||||
batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView);
|
||||
batch.setInputBuffer(COLOR_DATA_SLOT, colView);
|
||||
batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount);
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::directionToSpherical(const glm::vec3& direction) {
|
||||
glm::vec2 result;
|
||||
// Compute yaw
|
||||
glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z));
|
||||
result.x = glm::acos(glm::dot(IDENTITY_FRONT, normalProjection));
|
||||
if (glm::dot(IDENTITY_RIGHT, normalProjection) > 0.0f) {
|
||||
result.x = -glm::abs(result.x);
|
||||
} else {
|
||||
result.x = glm::abs(result.x);
|
||||
}
|
||||
// Compute pitch
|
||||
result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec3 ApplicationCompositor::sphericalToDirection(const glm::vec2& sphericalPos) {
|
||||
glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f));
|
||||
return rotation * IDENTITY_FRONT;
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::screenToSpherical(const glm::vec2& screenPos) {
|
||||
auto screenSize = qApp->getCanvasSize();
|
||||
glm::vec2 result;
|
||||
result.x = -(screenPos.x / screenSize.x - 0.5f);
|
||||
result.y = (screenPos.y / screenSize.y - 0.5f);
|
||||
result.x *= MOUSE_YAW_RANGE;
|
||||
result.y *= MOUSE_PITCH_RANGE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::sphericalToScreen(const glm::vec2& sphericalPos) {
|
||||
glm::vec2 result = sphericalPos;
|
||||
result.x *= -1.0;
|
||||
result /= MOUSE_RANGE;
|
||||
result += 0.5f;
|
||||
result *= qApp->getCanvasSize();
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const {
|
||||
glm::vec2 result = sphericalPos;
|
||||
result.x *= -1.0;
|
||||
result /= _textureFov;
|
||||
result.x /= _textureAspectRatio;
|
||||
result += 0.5f;
|
||||
result *= qApp->getCanvasSize();
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos) const {
|
||||
glm::vec2 result = overlayPos;
|
||||
result /= qApp->getCanvasSize();
|
||||
result -= 0.5f;
|
||||
result *= _textureFov;
|
||||
result.x *= _textureAspectRatio;
|
||||
result.x *= -1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::screenToOverlay(const glm::vec2& screenPos) const {
|
||||
return sphericalToOverlay(screenToSpherical(screenPos));
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) const {
|
||||
return sphericalToScreen(overlayToSpherical(overlayPos));
|
||||
}
|
||||
|
||||
void ApplicationCompositor::updateTooltips() {
|
||||
if (_hoverItemId != _noItemId) {
|
||||
quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
|
||||
if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) {
|
||||
// TODO Enable and position the tooltip
|
||||
_hoverItemEnterUsecs = UINT64_MAX;
|
||||
_tooltipId = Tooltip::showTip("URL: " + _hoverItemHref);
|
||||
}
|
||||
}
|
||||
}
|
121
interface/src/ui/ApplicationCompositor.h
Normal file
121
interface/src/ui/ApplicationCompositor.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis Arnold on 2015/06/13
|
||||
// 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_ApplicationCompositor_h
|
||||
#define hifi_ApplicationCompositor_h
|
||||
|
||||
#include <QObject>
|
||||
#include <cstdint>
|
||||
|
||||
#include <EntityItemID.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <gpu/Batch.h>
|
||||
#include <gpu/Texture.h>
|
||||
|
||||
class Camera;
|
||||
class PalmData;
|
||||
class RenderArgs;
|
||||
|
||||
const float MAGNIFY_WIDTH = 220.0f;
|
||||
const float MAGNIFY_HEIGHT = 100.0f;
|
||||
const float MAGNIFY_MULT = 2.0f;
|
||||
|
||||
const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f;
|
||||
|
||||
// Handles the drawing of the overlays to the screen
|
||||
// TODO, move divide up the rendering, displaying and input handling
|
||||
// facilities of this class
|
||||
class ApplicationCompositor : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ApplicationCompositor();
|
||||
~ApplicationCompositor();
|
||||
|
||||
void displayOverlayTexture(RenderArgs* renderArgs);
|
||||
void displayOverlayTextureHmd(RenderArgs* renderArgs, int eye);
|
||||
|
||||
QPoint getPalmClickLocation(const PalmData *palm) const;
|
||||
bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
|
||||
|
||||
bool hasMagnifier() const { return _magnifier; }
|
||||
void toggleMagnifier() { _magnifier = !_magnifier; }
|
||||
|
||||
float getHmdUIAngularSize() const { return _hmdUIAngularSize; }
|
||||
void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; }
|
||||
|
||||
// Converter from one frame of reference to another.
|
||||
// Frame of reference:
|
||||
// Direction: Ray that represents the spherical values
|
||||
// Screen: Position on the screen (x,y)
|
||||
// Spherical: Pitch and yaw that gives the position on the sphere we project on (yaw,pitch)
|
||||
// Overlay: Position on the overlay (x,y)
|
||||
// (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen.
|
||||
// This allows for picking outside of the screen projection in 3D.
|
||||
glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
||||
glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
|
||||
glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const;
|
||||
glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
|
||||
void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
|
||||
GLuint getOverlayTexture() const;
|
||||
|
||||
static glm::vec2 directionToSpherical(const glm::vec3 & direction);
|
||||
static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos);
|
||||
static glm::vec2 screenToSpherical(const glm::vec2 & screenPos);
|
||||
static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos);
|
||||
|
||||
private:
|
||||
void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov);
|
||||
void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
|
||||
void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks);
|
||||
void drawSphereSection(gpu::Batch& batch);
|
||||
void updateTooltips();
|
||||
|
||||
void renderPointers(gpu::Batch& batch);
|
||||
void renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder);
|
||||
void renderControllerPointers(gpu::Batch& batch);
|
||||
void renderPointersOculus(gpu::Batch& batch);
|
||||
|
||||
// Support for hovering and tooltips
|
||||
static EntityItemID _noItemId;
|
||||
EntityItemID _hoverItemId{ _noItemId };
|
||||
QString _hoverItemHref;
|
||||
quint64 _hoverItemEnterUsecs{ 0 };
|
||||
|
||||
float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE;
|
||||
float _textureFov{ glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE) };
|
||||
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];
|
||||
quint64 _lastMouseMove{ 0 };
|
||||
bool _magnifier{ true };
|
||||
|
||||
float _alpha{ 1.0f };
|
||||
float _oculusUIRadius{ 1.0f };
|
||||
|
||||
QMap<uint16_t, gpu::TexturePointer> _cursors;
|
||||
|
||||
int _reticleQuad;
|
||||
int _magnifierQuad;
|
||||
int _magnifierBorder;
|
||||
|
||||
int _previousBorderWidth{ -1 };
|
||||
int _previousBorderHeight{ -1 };
|
||||
|
||||
glm::vec3 _previousMagnifierBottomLeft;
|
||||
glm::vec3 _previousMagnifierBottomRight;
|
||||
glm::vec3 _previousMagnifierTopLeft;
|
||||
glm::vec3 _previousMagnifierTopRight;
|
||||
};
|
||||
|
||||
#endif // hifi_ApplicationCompositor_h
|
File diff suppressed because it is too large
Load diff
|
@ -13,19 +13,11 @@
|
|||
#define hifi_ApplicationOverlay_h
|
||||
|
||||
#include <gpu/Texture.h>
|
||||
class Camera;
|
||||
class Overlays;
|
||||
class QOpenGLFramebufferObject;
|
||||
class QOpenGLTexture;
|
||||
|
||||
class PalmData;
|
||||
|
||||
const float MAGNIFY_WIDTH = 220.0f;
|
||||
const float MAGNIFY_HEIGHT = 100.0f;
|
||||
const float MAGNIFY_MULT = 2.0f;
|
||||
|
||||
const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f;
|
||||
|
||||
// Handles the drawing of the overlays to the screen
|
||||
// TODO, move divide up the rendering, displaying and input handling
|
||||
// facilities of this class
|
||||
|
@ -38,84 +30,24 @@ public:
|
|||
void renderOverlay(RenderArgs* renderArgs);
|
||||
GLuint getOverlayTexture();
|
||||
|
||||
QPoint getPalmClickLocation(const PalmData *palm) const;
|
||||
bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
|
||||
|
||||
bool hasMagnifier() const { return _magnifier; }
|
||||
void toggleMagnifier() { _magnifier = !_magnifier; }
|
||||
|
||||
// FIXME: remove
|
||||
void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov);
|
||||
|
||||
|
||||
// Converter from one frame of reference to another.
|
||||
// Frame of reference:
|
||||
// Direction: Ray that represents the spherical values
|
||||
// Screen: Position on the screen (x,y)
|
||||
// Spherical: Pitch and yaw that gives the position on the sphere we project on (yaw,pitch)
|
||||
// Overlay: Position on the overlay (x,y)
|
||||
// (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen.
|
||||
// This allows for picking outside of the screen projection in 3D.
|
||||
glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
||||
glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
|
||||
glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const;
|
||||
glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
|
||||
void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
|
||||
|
||||
static glm::vec2 directionToSpherical(const glm::vec3 & direction);
|
||||
static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos);
|
||||
static glm::vec2 screenToSpherical(const glm::vec2 & screenPos);
|
||||
static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos);
|
||||
|
||||
private:
|
||||
void renderReticle(glm::quat orientation, float alpha);
|
||||
void renderPointers();;
|
||||
void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder);
|
||||
|
||||
void renderControllerPointers();
|
||||
void renderPointersOculus();
|
||||
|
||||
void renderAudioMeter();
|
||||
void renderCameraToggle();
|
||||
void renderStatsAndLogs();
|
||||
void renderDomainConnectionStatusBorder();
|
||||
void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
|
||||
void renderStatsAndLogs(RenderArgs* renderArgs);
|
||||
void renderDomainConnectionStatusBorder(RenderArgs* renderArgs);
|
||||
void renderRearViewToFbo(RenderArgs* renderArgs);
|
||||
void renderRearView(RenderArgs* renderArgs);
|
||||
void renderQmlUi(RenderArgs* renderArgs);
|
||||
void renderOverlays(RenderArgs* renderArgs);
|
||||
void buildFramebufferObject();
|
||||
|
||||
float _textureFov;
|
||||
float _textureAspectRatio;
|
||||
|
||||
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];
|
||||
quint64 _lastMouseMove;
|
||||
bool _magnifier;
|
||||
float _hmdUIRadius{ 1.0 };
|
||||
float _alpha{ 1.0f };
|
||||
float _trailingAudioLoudness{ 0.0f };
|
||||
GLuint _uiTexture{ 0 };
|
||||
|
||||
|
||||
float _alpha = 1.0f;
|
||||
float _trailingAudioLoudness;
|
||||
QOpenGLFramebufferObject* _framebufferObject{nullptr};
|
||||
QMap<uint16_t, gpu::TexturePointer> _cursors;
|
||||
|
||||
GLuint _newUiTexture{ 0 };
|
||||
|
||||
int _reticleQuad;
|
||||
int _magnifierQuad;
|
||||
int _audioRedQuad;
|
||||
int _audioGreenQuad;
|
||||
int _audioBlueQuad;
|
||||
int _domainStatusBorder;
|
||||
int _magnifierBorder;
|
||||
|
||||
int _previousBorderWidth;
|
||||
int _previousBorderHeight;
|
||||
|
||||
glm::vec3 _previousMagnifierBottomLeft;
|
||||
glm::vec3 _previousMagnifierBottomRight;
|
||||
glm::vec3 _previousMagnifierTopLeft;
|
||||
glm::vec3 _previousMagnifierTopRight;
|
||||
ivec2 _previousBorderSize{ -1 };
|
||||
QOpenGLFramebufferObject* _overlayFramebuffer{ nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_ApplicationOverlay_h
|
||||
|
|
133
interface/src/ui/AvatarInputs.cpp
Normal file
133
interface/src/ui/AvatarInputs.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/06/19
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#include "Application.h"
|
||||
#include "AvatarInputs.h"
|
||||
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#include "Menu.h"
|
||||
#include "devices/FaceTracker.h"
|
||||
|
||||
HIFI_QML_DEF(AvatarInputs)
|
||||
|
||||
|
||||
static AvatarInputs* INSTANCE{ nullptr };
|
||||
static const char SETTINGS_GROUP_NAME[] = "Rear View Tools";
|
||||
static const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel";
|
||||
|
||||
static Setting::Handle<int> rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, 0);
|
||||
|
||||
AvatarInputs* AvatarInputs::getInstance() {
|
||||
if (!INSTANCE) {
|
||||
AvatarInputs::registerType();
|
||||
AvatarInputs::show();
|
||||
Q_ASSERT(INSTANCE);
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) {
|
||||
INSTANCE = this;
|
||||
_mirrorZoomed = rearViewZoomLevel.get() != 0;
|
||||
}
|
||||
|
||||
#define AI_UPDATE(name, src) \
|
||||
{ \
|
||||
auto val = src; \
|
||||
if (_##name != val) { \
|
||||
_##name = val; \
|
||||
emit name##Changed(); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define AI_UPDATE_FLOAT(name, src, epsilon) \
|
||||
{ \
|
||||
float val = src; \
|
||||
if (abs(_##name - val) >= epsilon) { \
|
||||
_##name = val; \
|
||||
emit name##Changed(); \
|
||||
} \
|
||||
}
|
||||
|
||||
void AvatarInputs::update() {
|
||||
if (!Menu::getInstance()) {
|
||||
return;
|
||||
}
|
||||
AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode()
|
||||
&& !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
|
||||
AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
|
||||
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
const float CLIPPING_INDICATOR_TIME = 1.0f;
|
||||
const float AUDIO_METER_AVERAGING = 0.5;
|
||||
const float LOG2 = log(2.0f);
|
||||
const float METER_LOUDNESS_SCALE = 2.8f / 5.0f;
|
||||
const float LOG2_LOUDNESS_FLOOR = 11.0f;
|
||||
float audioLevel = 0.0f;
|
||||
auto audio = DependencyManager::get<AudioClient>();
|
||||
float loudness = audio->getLastInputLoudness() + 1.0f;
|
||||
|
||||
_trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness;
|
||||
|
||||
float log2loudness = logf(_trailingAudioLoudness) / LOG2;
|
||||
|
||||
if (log2loudness <= LOG2_LOUDNESS_FLOOR) {
|
||||
audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE;
|
||||
} else {
|
||||
audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE;
|
||||
}
|
||||
if (audioLevel > 1.0) {
|
||||
audioLevel = 1.0;
|
||||
}
|
||||
AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01);
|
||||
AI_UPDATE(audioClipping, ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f)));
|
||||
AI_UPDATE(audioMuted, audioIO->isMuted());
|
||||
|
||||
//// Make muted icon pulsate
|
||||
//static const float PULSE_MIN = 0.4f;
|
||||
//static const float PULSE_MAX = 1.0f;
|
||||
//static const float PULSE_FREQUENCY = 1.0f; // in Hz
|
||||
//qint64 now = usecTimestampNow();
|
||||
//if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) {
|
||||
// // Prevents t from getting too big, which would diminish glm::cos precision
|
||||
// _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND);
|
||||
//}
|
||||
//float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND;
|
||||
//float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f;
|
||||
//iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor;
|
||||
}
|
||||
|
||||
void AvatarInputs::toggleCameraMute() {
|
||||
FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker();
|
||||
if (faceTracker) {
|
||||
faceTracker->toggleMute();
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarInputs::toggleAudioMute() {
|
||||
DependencyManager::get<AudioClient>()->toggleMute();
|
||||
}
|
||||
|
||||
void AvatarInputs::resetSensors() {
|
||||
qApp->resetSensors();
|
||||
}
|
||||
|
||||
void AvatarInputs::toggleZoom() {
|
||||
_mirrorZoomed = !_mirrorZoomed;
|
||||
rearViewZoomLevel.set(_mirrorZoomed ? 0 : 1);
|
||||
emit mirrorZoomedChanged();
|
||||
}
|
||||
|
||||
void AvatarInputs::closeMirror() {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Mirror);
|
||||
}
|
||||
}
|
59
interface/src/ui/AvatarInputs.h
Normal file
59
interface/src/ui/AvatarInputs.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/06/19
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AvatarInputs_h
|
||||
#define hifi_AvatarInputs_h
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
#define AI_PROPERTY(type, name, initialValue) \
|
||||
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
|
||||
public: \
|
||||
type name() { return _##name; }; \
|
||||
private: \
|
||||
type _##name{ initialValue };
|
||||
|
||||
class AvatarInputs : public QQuickItem {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
|
||||
AI_PROPERTY(bool, cameraEnabled, false)
|
||||
AI_PROPERTY(bool, cameraMuted, false)
|
||||
AI_PROPERTY(bool, audioMuted, false)
|
||||
AI_PROPERTY(bool, audioClipping, false)
|
||||
AI_PROPERTY(float, audioLevel, 0)
|
||||
AI_PROPERTY(bool, mirrorVisible, false)
|
||||
AI_PROPERTY(bool, mirrorZoomed, true)
|
||||
|
||||
public:
|
||||
static AvatarInputs* getInstance();
|
||||
AvatarInputs(QQuickItem* parent = nullptr);
|
||||
void update();
|
||||
|
||||
signals:
|
||||
void cameraEnabledChanged();
|
||||
void cameraMutedChanged();
|
||||
void audioMutedChanged();
|
||||
void audioClippingChanged();
|
||||
void audioLevelChanged();
|
||||
void mirrorVisibleChanged();
|
||||
void mirrorZoomedChanged();
|
||||
|
||||
protected:
|
||||
Q_INVOKABLE void resetSensors();
|
||||
Q_INVOKABLE void toggleCameraMute();
|
||||
Q_INVOKABLE void toggleAudioMute();
|
||||
Q_INVOKABLE void toggleZoom();
|
||||
Q_INVOKABLE void closeMirror();
|
||||
|
||||
private:
|
||||
float _trailingAudioLoudness{ 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarInputs_h
|
|
@ -30,6 +30,7 @@
|
|||
#include "OctreeStatsDialog.h"
|
||||
#include "PreferencesDialog.h"
|
||||
#include "ScriptEditorWindow.h"
|
||||
#include "UpdateDialog.h"
|
||||
|
||||
|
||||
void DialogsManager::toggleAddressBar() {
|
||||
|
@ -49,6 +50,10 @@ void DialogsManager::showLoginDialog() {
|
|||
LoginDialog::show();
|
||||
}
|
||||
|
||||
void DialogsManager::showUpdateDialog() {
|
||||
UpdateDialog::show();
|
||||
}
|
||||
|
||||
void DialogsManager::octreeStatsDetails() {
|
||||
if (!_octreeStatsDialog) {
|
||||
_octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats());
|
||||
|
|
|
@ -33,6 +33,7 @@ class ScriptEditorWindow;
|
|||
class QMessageBox;
|
||||
class AvatarAppearanceDialog;
|
||||
class DomainConnectionDialog;
|
||||
class UpdateDialog;
|
||||
|
||||
class DialogsManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -62,6 +63,9 @@ public slots:
|
|||
void showIRCLink();
|
||||
void changeAvatarAppearance();
|
||||
void showDomainConnectionDialog();
|
||||
|
||||
// Application Update
|
||||
void showUpdateDialog();
|
||||
|
||||
private slots:
|
||||
void toggleToolWindow();
|
||||
|
@ -101,6 +105,7 @@ private:
|
|||
QPointer<ScriptEditorWindow> _scriptEditor;
|
||||
QPointer<AvatarAppearanceDialog> _avatarAppearanceDialog;
|
||||
QPointer<DomainConnectionDialog> _domainConnectionDialog;
|
||||
QPointer<UpdateDialog> _updateDialog;
|
||||
};
|
||||
|
||||
#endif // hifi_DialogsManager_h
|
||||
|
|
|
@ -38,8 +38,17 @@
|
|||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "FlowLayout.h"
|
||||
//! [1]
|
||||
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
|
||||
|
|
|
@ -11,10 +11,19 @@
|
|||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QTextBlock>
|
||||
#include <QtGui>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <PathUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ void PreferencesDialog::loadPreferences() {
|
|||
ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond());
|
||||
|
||||
#if 0
|
||||
ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationOverlay().getHmdUIAngularSize());
|
||||
ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize());
|
||||
#endif
|
||||
|
||||
SixenseManager& sixense = SixenseManager::getInstance();
|
||||
|
@ -241,10 +241,8 @@ void PreferencesDialog::savePreferences() {
|
|||
|
||||
qApp->setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value());
|
||||
|
||||
#if 0
|
||||
qApp->getApplicationOverlay().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
|
||||
#endif
|
||||
|
||||
qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
|
||||
|
||||
SixenseManager& sixense = SixenseManager::getInstance();
|
||||
sixense.setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
|
||||
sixense.setInvertButtons(ui.invertSixenseButtonsCheckBox->isChecked());
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
//
|
||||
// RearMirrorTools.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stojce Slavkovski on 10/23/2013.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include <PathUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "RearMirrorTools.h"
|
||||
#include "Util.h"
|
||||
|
||||
const int ICON_SIZE = 24;
|
||||
const int ICON_PADDING = 5;
|
||||
|
||||
const char SETTINGS_GROUP_NAME[] = "Rear View Tools";
|
||||
const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel";
|
||||
Setting::Handle<int> RearMirrorTools::rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS,
|
||||
ZoomLevel::HEAD);
|
||||
|
||||
RearMirrorTools::RearMirrorTools(QRect& bounds) :
|
||||
_bounds(bounds),
|
||||
_windowed(false),
|
||||
_fullScreen(false)
|
||||
{
|
||||
_closeTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/close.svg");
|
||||
|
||||
_zoomHeadTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/plus.svg");
|
||||
_zoomBodyTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/minus.svg");
|
||||
|
||||
_shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE);
|
||||
_closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
|
||||
_resetIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
|
||||
_bodyZoomIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE);
|
||||
_headZoomIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE);
|
||||
}
|
||||
|
||||
void RearMirrorTools::render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePosition) {
|
||||
if (fullScreen) {
|
||||
_fullScreen = true;
|
||||
displayIcon(QRect(QPoint(), qApp->getDeviceSize()), _shrinkIconRect, _closeTexture);
|
||||
} else {
|
||||
// render rear view tools if mouse is in the bounds
|
||||
_windowed = _bounds.contains(mousePosition);
|
||||
if (_windowed) {
|
||||
displayIcon(_bounds, _closeIconRect, _closeTexture);
|
||||
|
||||
ZoomLevel zoomLevel = (ZoomLevel)rearViewZoomLevel.get();
|
||||
displayIcon(_bounds, _headZoomIconRect, _zoomHeadTexture, zoomLevel == HEAD);
|
||||
displayIcon(_bounds, _bodyZoomIconRect, _zoomBodyTexture, zoomLevel == BODY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RearMirrorTools::mousePressEvent(int x, int y) {
|
||||
if (_windowed) {
|
||||
if (_closeIconRect.contains(x, y)) {
|
||||
_windowed = false;
|
||||
emit closeView();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_headZoomIconRect.contains(x, y)) {
|
||||
rearViewZoomLevel.set(HEAD);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_bodyZoomIconRect.contains(x, y)) {
|
||||
rearViewZoomLevel.set(BODY);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_bounds.contains(x, y)) {
|
||||
_windowed = false;
|
||||
emit restoreView();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_fullScreen) {
|
||||
if (_shrinkIconRect.contains(x, y)) {
|
||||
_fullScreen = false;
|
||||
emit shrinkView();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected) {
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glOrtho(bounds.left(), bounds.right(), bounds.bottom(), bounds.top(), -1.0, 1.0);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glm::vec4 quadColor;
|
||||
if (selected) {
|
||||
quadColor = glm::vec4(.5f, .5f, .5f, 1.0f);
|
||||
} else {
|
||||
quadColor = glm::vec4(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(texture));
|
||||
|
||||
glm::vec2 topLeft(iconBounds.left(), iconBounds.top());
|
||||
glm::vec2 bottomRight(iconBounds.right(), iconBounds.bottom());
|
||||
static const glm::vec2 texCoordTopLeft(0.0f, 1.0f);
|
||||
static const glm::vec2 texCoordBottomRight(1.0f, 0.0f);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
//
|
||||
// RearMirrorTools.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stojce Slavkovski on 10/23/2013.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_RearMirrorTools_h
|
||||
#define hifi_RearMirrorTools_h
|
||||
|
||||
#include <gpu/Texture.h>
|
||||
#include <SettingHandle.h>
|
||||
|
||||
enum ZoomLevel {
|
||||
HEAD = 0,
|
||||
BODY = 1
|
||||
};
|
||||
|
||||
class RearMirrorTools : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RearMirrorTools(QRect& bounds);
|
||||
void render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePos);
|
||||
bool mousePressEvent(int x, int y);
|
||||
|
||||
static Setting::Handle<int> rearViewZoomLevel;
|
||||
|
||||
signals:
|
||||
void closeView();
|
||||
void shrinkView();
|
||||
void resetView();
|
||||
void restoreView();
|
||||
|
||||
private:
|
||||
QRect _bounds;
|
||||
gpu::TexturePointer _closeTexture;
|
||||
gpu::TexturePointer _zoomBodyTexture;
|
||||
gpu::TexturePointer _zoomHeadTexture;
|
||||
|
||||
QRect _closeIconRect;
|
||||
QRect _resetIconRect;
|
||||
QRect _shrinkIconRect;
|
||||
QRect _headZoomIconRect;
|
||||
QRect _bodyZoomIconRect;
|
||||
|
||||
bool _windowed;
|
||||
bool _fullScreen;
|
||||
|
||||
void displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected = false);
|
||||
};
|
||||
|
||||
#endif // hifi_RearMirrorTools_h
|
|
@ -1,17 +1,17 @@
|
|||
//
|
||||
// Stats.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Lucas Crisman on 22/03/14.
|
||||
// Created by Bradley Austin Davis 2015/06/17
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <sstream>
|
||||
#include <gpu/GPUConfig.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "Stats.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <QFontDatabase>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/component_wise.hpp>
|
||||
|
@ -25,147 +25,31 @@
|
|||
#include <LODManager.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "Stats.h"
|
||||
#include "BandwidthRecorder.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "Menu.h"
|
||||
#include "Util.h"
|
||||
#include "SequenceNumberStats.h"
|
||||
|
||||
HIFI_QML_DEF(Stats)
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int STATS_PELS_PER_LINE = 16;
|
||||
const int STATS_PELS_INITIALOFFSET = 12;
|
||||
|
||||
const int STATS_GENERAL_MIN_WIDTH = 165;
|
||||
const int STATS_PING_MIN_WIDTH = 190;
|
||||
const int STATS_GEO_MIN_WIDTH = 240;
|
||||
const int STATS_OCTREE_MIN_WIDTH = 410;
|
||||
static Stats* INSTANCE{ nullptr };
|
||||
|
||||
Stats* Stats::getInstance() {
|
||||
static Stats stats;
|
||||
return &stats;
|
||||
if (!INSTANCE) {
|
||||
Stats::registerType();
|
||||
Stats::show();
|
||||
Q_ASSERT(INSTANCE);
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
Stats::Stats():
|
||||
_expanded(false),
|
||||
_recentMaxPackets(0),
|
||||
_resetRecentMaxPacketsSoon(true),
|
||||
_generalStatsWidth(STATS_GENERAL_MIN_WIDTH),
|
||||
_pingStatsWidth(STATS_PING_MIN_WIDTH),
|
||||
_geoStatsWidth(STATS_GEO_MIN_WIDTH),
|
||||
_octreeStatsWidth(STATS_OCTREE_MIN_WIDTH),
|
||||
_lastHorizontalOffset(0)
|
||||
{
|
||||
auto canvasSize = Application::getInstance()->getCanvasSize();
|
||||
resetWidth(canvasSize.x, 0);
|
||||
}
|
||||
|
||||
void Stats::toggleExpanded() {
|
||||
_expanded = !_expanded;
|
||||
}
|
||||
|
||||
// called on mouse click release
|
||||
// check for clicks over stats in order to expand or contract them
|
||||
void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset) {
|
||||
auto canvasSize = Application::getInstance()->getCanvasSize();
|
||||
|
||||
if (0 != glm::compMax(glm::abs(glm::ivec2(mouseX - mouseDragStartedX, mouseY - mouseDragStartedY)))) {
|
||||
// not worried about dragging on stats
|
||||
return;
|
||||
}
|
||||
|
||||
int statsHeight = 0,
|
||||
statsWidth = 0,
|
||||
statsX = 0,
|
||||
statsY = 0,
|
||||
lines = 0;
|
||||
|
||||
statsX = horizontalOffset;
|
||||
|
||||
// top-left stats click
|
||||
lines = _expanded ? 5 : 3;
|
||||
statsHeight = lines * STATS_PELS_PER_LINE + 10;
|
||||
if (mouseX > statsX && mouseX < statsX + _generalStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
|
||||
toggleExpanded();
|
||||
return;
|
||||
}
|
||||
statsX += _generalStatsWidth;
|
||||
|
||||
// ping stats click
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
||||
lines = _expanded ? 4 : 3;
|
||||
statsHeight = lines * STATS_PELS_PER_LINE + 10;
|
||||
if (mouseX > statsX && mouseX < statsX + _pingStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
|
||||
toggleExpanded();
|
||||
return;
|
||||
}
|
||||
statsX += _pingStatsWidth;
|
||||
}
|
||||
|
||||
// geo stats panel click
|
||||
lines = _expanded ? 4 : 3;
|
||||
statsHeight = lines * STATS_PELS_PER_LINE + 10;
|
||||
if (mouseX > statsX && mouseX < statsX + _geoStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
|
||||
toggleExpanded();
|
||||
return;
|
||||
}
|
||||
statsX += _geoStatsWidth;
|
||||
|
||||
// top-right stats click
|
||||
lines = _expanded ? 11 : 3;
|
||||
statsHeight = lines * STATS_PELS_PER_LINE + 10;
|
||||
statsWidth = canvasSize.x - statsX;
|
||||
if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
|
||||
toggleExpanded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Stats::resetWidth(int width, int horizontalOffset) {
|
||||
auto canvasSize = Application::getInstance()->getCanvasSize();
|
||||
int extraSpace = canvasSize.x - horizontalOffset - 2
|
||||
- STATS_GENERAL_MIN_WIDTH
|
||||
- (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0)
|
||||
- STATS_GEO_MIN_WIDTH
|
||||
- STATS_OCTREE_MIN_WIDTH;
|
||||
|
||||
int panels = 4;
|
||||
|
||||
_generalStatsWidth = STATS_GENERAL_MIN_WIDTH;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
||||
_pingStatsWidth = STATS_PING_MIN_WIDTH;
|
||||
} else {
|
||||
_pingStatsWidth = 0;
|
||||
panels = 3;
|
||||
}
|
||||
_geoStatsWidth = STATS_GEO_MIN_WIDTH;
|
||||
_octreeStatsWidth = STATS_OCTREE_MIN_WIDTH;
|
||||
|
||||
if (extraSpace > panels) {
|
||||
_generalStatsWidth += (int) extraSpace / panels;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
||||
_pingStatsWidth += (int) extraSpace / panels;
|
||||
}
|
||||
_geoStatsWidth += (int) extraSpace / panels;
|
||||
_octreeStatsWidth += canvasSize.x -
|
||||
(_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// translucent background box that makes stats more readable
|
||||
void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int height) {
|
||||
glm::vec4 color(((rgba >> 24) & 0xff) / 255.0f,
|
||||
((rgba >> 16) & 0xff) / 255.0f,
|
||||
((rgba >> 8) & 0xff) / 255.0f,
|
||||
(rgba & 0xff) / 255.0f);
|
||||
|
||||
// FIX ME: is this correct? It seems to work to fix textures bleeding into us...
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(x, y, width, height, color);
|
||||
Stats::Stats(QQuickItem* parent) : QQuickItem(parent) {
|
||||
INSTANCE = this;
|
||||
const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
_monospaceFont = font.family();
|
||||
}
|
||||
|
||||
bool Stats::includeTimingRecord(const QString& name) {
|
||||
|
@ -190,107 +74,240 @@ bool Stats::includeTimingRecord(const QString& name) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// display expanded or contracted stats
|
||||
void Stats::display(
|
||||
const float* color,
|
||||
int horizontalOffset,
|
||||
float fps,
|
||||
int inPacketsPerSecond,
|
||||
int outPacketsPerSecond,
|
||||
int inKbitsPerSecond,
|
||||
int outKbitsPerSecond,
|
||||
int voxelPacketsToProcess)
|
||||
{
|
||||
auto canvasSize = Application::getInstance()->getCanvasSize();
|
||||
|
||||
unsigned int backgroundColor = 0x33333399;
|
||||
int verticalOffset = 0, lines = 0;
|
||||
float scale = 0.10f;
|
||||
float rotation = 0.0f;
|
||||
int font = 2;
|
||||
|
||||
QLocale locale(QLocale::English);
|
||||
std::stringstream octreeStats;
|
||||
|
||||
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
|
||||
|
||||
if (_lastHorizontalOffset != horizontalOffset) {
|
||||
resetWidth(canvasSize.x, horizontalOffset);
|
||||
_lastHorizontalOffset = horizontalOffset;
|
||||
#define STAT_UPDATE(name, src) \
|
||||
{ \
|
||||
auto val = src; \
|
||||
if (_##name != val) { \
|
||||
_##name = val; \
|
||||
emit name##Changed(); \
|
||||
} \
|
||||
}
|
||||
|
||||
glPointSize(1.0f);
|
||||
#define STAT_UPDATE_FLOAT(name, src, epsilon) \
|
||||
{ \
|
||||
float val = src; \
|
||||
if (abs(_##name - val) >= epsilon) { \
|
||||
_##name = val; \
|
||||
emit name##Changed(); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
void Stats::updateStats() {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
if (isVisible()) {
|
||||
setVisible(false);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (!isVisible()) {
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::Stats) && isExpanded();
|
||||
if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) {
|
||||
PerformanceTimer::setActive(shouldDisplayTimingDetail);
|
||||
}
|
||||
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
// we need to take one avatar out so we don't include ourselves
|
||||
int totalAvatars = DependencyManager::get<AvatarManager>()->size() - 1;
|
||||
int totalServers = DependencyManager::get<NodeList>()->size();
|
||||
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
|
||||
STAT_UPDATE(serverCount, nodeList->size());
|
||||
STAT_UPDATE(framerate, (int)qApp->getFps());
|
||||
|
||||
lines = 5;
|
||||
int columnOneWidth = _generalStatsWidth;
|
||||
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
|
||||
STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
|
||||
STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond());
|
||||
STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f);
|
||||
STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f);
|
||||
|
||||
// Second column: ping
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
||||
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
||||
STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1);
|
||||
STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1);
|
||||
|
||||
//// Now handle voxel servers, since there could be more than one, we average their ping times
|
||||
unsigned long totalPingOctree = 0;
|
||||
int octreeServerCount = 0;
|
||||
int pingOctreeMax = 0;
|
||||
int pingVoxel;
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
// TODO: this should also support entities
|
||||
if (node->getType() == NodeType::EntityServer) {
|
||||
totalPingOctree += node->getPingMs();
|
||||
octreeServerCount++;
|
||||
if (pingOctreeMax < node->getPingMs()) {
|
||||
pingOctreeMax = node->getPingMs();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (octreeServerCount) {
|
||||
pingVoxel = totalPingOctree / octreeServerCount;
|
||||
}
|
||||
|
||||
//STAT_UPDATE(entitiesPing, pingVoxel);
|
||||
//if (_expanded) {
|
||||
// QString voxelMaxPing;
|
||||
// if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid.
|
||||
// voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax);
|
||||
// } else {
|
||||
// voxelMaxPing = QString("Voxel max ping: --");
|
||||
// }
|
||||
} else {
|
||||
// -2 causes the QML to hide the ping column
|
||||
STAT_UPDATE(audioPing, -2);
|
||||
}
|
||||
|
||||
|
||||
// Third column, avatar stats
|
||||
MyAvatar* myAvatar = avatarManager->getMyAvatar();
|
||||
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
|
||||
STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f);
|
||||
STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f);
|
||||
if (_expanded) {
|
||||
SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
||||
if (avatarMixer) {
|
||||
STAT_UPDATE(avatarMixerKbps, roundf(
|
||||
bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer) +
|
||||
bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer)));
|
||||
STAT_UPDATE(avatarMixerPps, roundf(
|
||||
bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer) +
|
||||
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer)));
|
||||
} else {
|
||||
STAT_UPDATE(avatarMixerKbps, -1);
|
||||
STAT_UPDATE(avatarMixerPps, -1);
|
||||
}
|
||||
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
if (audioMixerNode) {
|
||||
STAT_UPDATE(audioMixerKbps, roundf(
|
||||
bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
|
||||
bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
|
||||
STAT_UPDATE(audioMixerPps, roundf(
|
||||
bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
|
||||
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
|
||||
} else {
|
||||
STAT_UPDATE(audioMixerKbps, -1);
|
||||
STAT_UPDATE(audioMixerPps, -1);
|
||||
}
|
||||
|
||||
STAT_UPDATE(downloads, ResourceCache::getLoadingRequests().size());
|
||||
STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount());
|
||||
// TODO fix to match original behavior
|
||||
//stringstream downloads;
|
||||
//downloads << "Downloads: ";
|
||||
//foreach(Resource* resource, ) {
|
||||
// downloads << (int)(resource->getProgress() * 100.0f) << "% ";
|
||||
//}
|
||||
//downloads << "(" << << " pending)";
|
||||
} // expanded avatar column
|
||||
|
||||
// Fourth column, octree stats
|
||||
int serverCount = 0;
|
||||
int movingServerCount = 0;
|
||||
unsigned long totalNodes = 0;
|
||||
unsigned long totalInternal = 0;
|
||||
unsigned long totalLeaves = 0;
|
||||
std::stringstream sendingModeStream("");
|
||||
sendingModeStream << "[";
|
||||
NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats();
|
||||
for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) {
|
||||
//const QUuid& uuid = i->first;
|
||||
OctreeSceneStats& stats = i->second;
|
||||
serverCount++;
|
||||
if (_expanded) {
|
||||
if (serverCount > 1) {
|
||||
sendingModeStream << ",";
|
||||
}
|
||||
if (stats.isMoving()) {
|
||||
sendingModeStream << "M";
|
||||
movingServerCount++;
|
||||
} else {
|
||||
sendingModeStream << "S";
|
||||
}
|
||||
}
|
||||
|
||||
// calculate server node totals
|
||||
totalNodes += stats.getTotalElements();
|
||||
if (_expanded) {
|
||||
totalInternal += stats.getTotalInternal();
|
||||
totalLeaves += stats.getTotalLeaves();
|
||||
}
|
||||
}
|
||||
if (_expanded) {
|
||||
if (serverCount == 0) {
|
||||
sendingModeStream << "---";
|
||||
}
|
||||
sendingModeStream << "] " << serverCount << " servers";
|
||||
if (movingServerCount > 0) {
|
||||
sendingModeStream << " <SCENE NOT STABLE>";
|
||||
} else {
|
||||
sendingModeStream << " <SCENE STABLE>";
|
||||
}
|
||||
QString sendingModeResult = sendingModeStream.str().c_str();
|
||||
STAT_UPDATE(sendingMode, sendingModeResult);
|
||||
}
|
||||
|
||||
// Incoming packets
|
||||
QLocale locale(QLocale::English);
|
||||
auto voxelPacketsToProcess = qApp->getOctreePacketProcessor().packetsToProcessCount();
|
||||
if (_expanded) {
|
||||
std::stringstream octreeStats;
|
||||
QString packetsString = locale.toString((int)voxelPacketsToProcess);
|
||||
QString maxString = locale.toString((int)_recentMaxPackets);
|
||||
octreeStats << "Octree Packets to Process: " << qPrintable(packetsString)
|
||||
<< " [Recent Max: " << qPrintable(maxString) << "]";
|
||||
QString str = octreeStats.str().c_str();
|
||||
STAT_UPDATE(packetStats, str);
|
||||
// drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) {
|
||||
_recentMaxPackets = 0;
|
||||
_resetRecentMaxPacketsSoon = false;
|
||||
}
|
||||
if (voxelPacketsToProcess == 0) {
|
||||
_resetRecentMaxPacketsSoon = true;
|
||||
} else if (voxelPacketsToProcess > _recentMaxPackets) {
|
||||
_recentMaxPackets = voxelPacketsToProcess;
|
||||
}
|
||||
|
||||
// Server Octree Elements
|
||||
STAT_UPDATE(serverElements, totalNodes);
|
||||
STAT_UPDATE(localElements, OctreeElement::getNodeCount());
|
||||
|
||||
if (_expanded) {
|
||||
STAT_UPDATE(serverInternal, totalInternal);
|
||||
STAT_UPDATE(serverLeaves, totalLeaves);
|
||||
// Local Voxels
|
||||
STAT_UPDATE(localInternal, OctreeElement::getInternalNodeCount());
|
||||
STAT_UPDATE(localLeaves, OctreeElement::getLeafNodeCount());
|
||||
// LOD Details
|
||||
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
|
||||
}
|
||||
|
||||
bool performanceTimerIsActive = PerformanceTimer::isActive();
|
||||
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
|
||||
if (displayPerf && performanceTimerIsActive) {
|
||||
if (!_timingExpanded) {
|
||||
_timingExpanded = true;
|
||||
emit timingExpandedChanged();
|
||||
}
|
||||
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
|
||||
|
||||
columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide...
|
||||
// we will also include room for 1 line per timing record and a header of 4 lines
|
||||
lines += 4;
|
||||
|
||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
||||
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
||||
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
|
||||
int statsLines = 0;
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (includeTimingRecord(i.key())) {
|
||||
lines++;
|
||||
statsLines++;
|
||||
if (onlyDisplayTopTen && statsLines == 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, (lines + 1) * STATS_PELS_PER_LINE);
|
||||
horizontalOffset += 5;
|
||||
|
||||
int columnOneHorizontalOffset = horizontalOffset;
|
||||
|
||||
QString serverNodes = QString("Servers: %1").arg(totalServers);
|
||||
QString avatarNodes = QString("Avatars: %1").arg(totalAvatars);
|
||||
QString framesPerSecond = QString("Framerate: %1 FPS").arg(fps, 3, 'f', 0);
|
||||
|
||||
verticalOffset = STATS_PELS_INITIALOFFSET; // first one is offset by less than a line
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, serverNodes.toUtf8().constData(), color);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes.toUtf8().constData(), color);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond.toUtf8().constData(), color);
|
||||
|
||||
QString packetsPerSecondString = QString("Packets In/Out: %1/%2").arg(inPacketsPerSecond).arg(outPacketsPerSecond);
|
||||
QString averageMegabitsPerSecond = QString("Mbps In/Out: %1/%2").
|
||||
arg((float)inKbitsPerSecond * 1.0f / 1000.0f).
|
||||
arg((float)outKbitsPerSecond * 1.0f / 1000.0f);
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString.toUtf8().constData(), color);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond.toUtf8().constData(), color);
|
||||
|
||||
|
||||
// TODO: the display of these timing details should all be moved to JavaScript
|
||||
if (displayPerf && performanceTimerIsActive) {
|
||||
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
|
||||
// Timing details...
|
||||
verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns
|
||||
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font,
|
||||
"-------------------------------------------------------- Function "
|
||||
"------------------------------------------------------- --msecs- -calls--", color);
|
||||
|
||||
// First iterate all the records, and for the ones that should be included, insert them into
|
||||
// a new Map sorted by average time...
|
||||
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
|
||||
QMap<float, QString> sortedRecords;
|
||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
||||
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
||||
|
@ -306,316 +323,54 @@ void Stats::display(
|
|||
int linesDisplayed = 0;
|
||||
QMapIterator<float, QString> j(sortedRecords);
|
||||
j.toBack();
|
||||
QString perfLines;
|
||||
while (j.hasPrevious()) {
|
||||
j.previous();
|
||||
QChar noBreakingSpace = QChar::Nbsp;
|
||||
QString functionName = j.value();
|
||||
static const QChar noBreakingSpace = QChar::Nbsp;
|
||||
QString functionName = j.value();
|
||||
const PerformanceTimerRecord& record = allRecords.value(functionName);
|
||||
|
||||
QString perfLine = QString("%1: %2 [%3]").
|
||||
arg(QString(qPrintable(functionName)), 120, noBreakingSpace).
|
||||
perfLines += QString("%1: %2 [%3]\n").
|
||||
arg(QString(qPrintable(functionName)), 90, noBreakingSpace).
|
||||
arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace).
|
||||
arg((int)record.getCount(), 6, 10, noBreakingSpace);
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine.toUtf8().constData(), color);
|
||||
linesDisplayed++;
|
||||
if (onlyDisplayTopTen && linesDisplayed == 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_timingStats = perfLines;
|
||||
emit timingStatsChanged();
|
||||
} else if (_timingExpanded) {
|
||||
_timingExpanded = false;
|
||||
emit timingExpandedChanged();
|
||||
}
|
||||
|
||||
|
||||
verticalOffset = STATS_PELS_INITIALOFFSET;
|
||||
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + 1;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
||||
int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingOctreeMax = -1;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
||||
|
||||
pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : -1;
|
||||
pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : -1;
|
||||
|
||||
// Now handle voxel servers, since there could be more than one, we average their ping times
|
||||
unsigned long totalPingOctree = 0;
|
||||
int octreeServerCount = 0;
|
||||
|
||||
nodeList->eachNode([&totalPingOctree, &pingOctreeMax, &octreeServerCount](const SharedNodePointer& node){
|
||||
// TODO: this should also support entities
|
||||
if (node->getType() == NodeType::EntityServer) {
|
||||
totalPingOctree += node->getPingMs();
|
||||
octreeServerCount++;
|
||||
if (pingOctreeMax < node->getPingMs()) {
|
||||
pingOctreeMax = node->getPingMs();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (octreeServerCount) {
|
||||
pingVoxel = totalPingOctree / octreeServerCount;
|
||||
}
|
||||
|
||||
lines = _expanded ? 4 : 3;
|
||||
|
||||
// only draw our background if column one didn't draw a wide background
|
||||
if (columnOneWidth == _generalStatsWidth) {
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, (lines + 1) * STATS_PELS_PER_LINE);
|
||||
}
|
||||
horizontalOffset += 5;
|
||||
|
||||
|
||||
QString audioPing;
|
||||
if (pingAudio >= 0) {
|
||||
audioPing = QString("Audio ping: %1").arg(pingAudio);
|
||||
} else {
|
||||
audioPing = QString("Audio ping: --");
|
||||
}
|
||||
|
||||
QString avatarPing;
|
||||
if (pingAvatar >= 0) {
|
||||
avatarPing = QString("Avatar ping: %1").arg(pingAvatar);
|
||||
} else {
|
||||
avatarPing = QString("Avatar ping: --");
|
||||
}
|
||||
|
||||
QString voxelAvgPing;
|
||||
if (pingVoxel >= 0) {
|
||||
voxelAvgPing = QString("Entities avg ping: %1").arg(pingVoxel);
|
||||
} else {
|
||||
voxelAvgPing = QString("Entities avg ping: --");
|
||||
}
|
||||
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioPing.toUtf8().constData(), color);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPing.toUtf8().constData(), color);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelAvgPing.toUtf8().constData(), color);
|
||||
|
||||
if (_expanded) {
|
||||
QString voxelMaxPing;
|
||||
if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid.
|
||||
voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax);
|
||||
} else {
|
||||
voxelMaxPing = QString("Voxel max ping: --");
|
||||
}
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing.toUtf8().constData(), color);
|
||||
}
|
||||
|
||||
verticalOffset = STATS_PELS_INITIALOFFSET;
|
||||
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2;
|
||||
}
|
||||
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||
|
||||
lines = _expanded ? 7 : 3;
|
||||
|
||||
if (columnOneWidth == _generalStatsWidth) {
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, (lines + 1) * STATS_PELS_PER_LINE);
|
||||
}
|
||||
horizontalOffset += 5;
|
||||
|
||||
QString avatarPosition = QString("Position: %1, %2, %3").
|
||||
arg(avatarPos.x, -1, 'f', 1).
|
||||
arg(avatarPos.y, -1, 'f', 1).
|
||||
arg(avatarPos.z, -1, 'f', 1);
|
||||
QString avatarVelocity = QString("Velocity: %1").arg(glm::length(myAvatar->getVelocity()), -1, 'f', 1);
|
||||
QString avatarBodyYaw = QString("Yaw: %1").arg(myAvatar->getBodyYaw(), -1, 'f', 1);
|
||||
QString avatarMixerStats;
|
||||
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPosition.toUtf8().constData(), color);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarVelocity.toUtf8().constData(), color);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarBodyYaw.toUtf8().constData(), color);
|
||||
|
||||
if (_expanded) {
|
||||
SharedNodePointer avatarMixer = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AvatarMixer);
|
||||
if (avatarMixer) {
|
||||
avatarMixerStats = QString("Avatar Mixer: %1 kbps, %2 pps").
|
||||
arg(roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
|
||||
bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))).
|
||||
arg(roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
|
||||
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
|
||||
} else {
|
||||
avatarMixerStats = QString("No Avatar Mixer");
|
||||
}
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarMixerStats.toUtf8().constData(), color);
|
||||
|
||||
stringstream downloads;
|
||||
downloads << "Downloads: ";
|
||||
foreach (Resource* resource, ResourceCache::getLoadingRequests()) {
|
||||
downloads << (int)(resource->getProgress() * 100.0f) << "% ";
|
||||
}
|
||||
downloads << "(" << ResourceCache::getPendingRequestCount() << " pending)";
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color);
|
||||
}
|
||||
|
||||
verticalOffset = STATS_PELS_INITIALOFFSET;
|
||||
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3;
|
||||
|
||||
lines = _expanded ? 10 : 3;
|
||||
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset,
|
||||
(lines + 1) * STATS_PELS_PER_LINE);
|
||||
horizontalOffset += 5;
|
||||
|
||||
// Model/Entity render details
|
||||
octreeStats.str("");
|
||||
octreeStats << "Triangles: " << _renderDetails._trianglesRendered
|
||||
<< " / Quads:" << _renderDetails._quadsRendered
|
||||
<< " / Material Switches:" << _renderDetails._materialSwitches;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
|
||||
if (_expanded) {
|
||||
octreeStats.str("");
|
||||
octreeStats << " Mesh Parts Rendered Opaque: " << _renderDetails._opaque._rendered
|
||||
<< " / Translucent:" << _renderDetails._translucent._rendered;
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
|
||||
octreeStats.str("");
|
||||
octreeStats << " Opaque considered: " << _renderDetails._opaque._considered
|
||||
<< " / Out of view:" << _renderDetails._opaque._outOfView
|
||||
<< " / Too small:" << _renderDetails._opaque._tooSmall;
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
|
||||
octreeStats.str("");
|
||||
octreeStats << " Translucent considered: " << _renderDetails._translucent._considered
|
||||
<< " / Out of view:" << _renderDetails._translucent._outOfView
|
||||
<< " / Too small:" << _renderDetails._translucent._tooSmall;
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
// iterate all the current voxel stats, and list their sending modes, and total voxel counts
|
||||
std::stringstream sendingMode("");
|
||||
sendingMode << "Octree Sending Mode: [";
|
||||
int serverCount = 0;
|
||||
int movingServerCount = 0;
|
||||
unsigned long totalNodes = 0;
|
||||
unsigned long totalInternal = 0;
|
||||
unsigned long totalLeaves = 0;
|
||||
NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats();
|
||||
for(NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) {
|
||||
//const QUuid& uuid = i->first;
|
||||
OctreeSceneStats& stats = i->second;
|
||||
serverCount++;
|
||||
if (_expanded) {
|
||||
if (serverCount > 1) {
|
||||
sendingMode << ",";
|
||||
}
|
||||
if (stats.isMoving()) {
|
||||
sendingMode << "M";
|
||||
movingServerCount++;
|
||||
} else {
|
||||
sendingMode << "S";
|
||||
}
|
||||
}
|
||||
|
||||
// calculate server node totals
|
||||
totalNodes += stats.getTotalElements();
|
||||
if (_expanded) {
|
||||
totalInternal += stats.getTotalInternal();
|
||||
totalLeaves += stats.getTotalLeaves();
|
||||
}
|
||||
}
|
||||
if (_expanded) {
|
||||
if (serverCount == 0) {
|
||||
sendingMode << "---";
|
||||
}
|
||||
sendingMode << "] " << serverCount << " servers";
|
||||
if (movingServerCount > 0) {
|
||||
sendingMode << " <SCENE NOT STABLE>";
|
||||
} else {
|
||||
sendingMode << " <SCENE STABLE>";
|
||||
}
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)sendingMode.str().c_str(), color);
|
||||
}
|
||||
|
||||
// Incoming packets
|
||||
if (_expanded) {
|
||||
octreeStats.str("");
|
||||
QString packetsString = locale.toString((int)voxelPacketsToProcess);
|
||||
QString maxString = locale.toString((int)_recentMaxPackets);
|
||||
octreeStats << "Octree Packets to Process: " << qPrintable(packetsString)
|
||||
<< " [Recent Max: " << qPrintable(maxString) << "]";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) {
|
||||
_recentMaxPackets = 0;
|
||||
_resetRecentMaxPacketsSoon = false;
|
||||
}
|
||||
if (voxelPacketsToProcess == 0) {
|
||||
_resetRecentMaxPacketsSoon = true;
|
||||
} else {
|
||||
if (voxelPacketsToProcess > _recentMaxPackets) {
|
||||
_recentMaxPackets = voxelPacketsToProcess;
|
||||
}
|
||||
}
|
||||
|
||||
QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' ');
|
||||
unsigned long localTotal = OctreeElement::getNodeCount();
|
||||
QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' ');
|
||||
|
||||
// Server Octree Elements
|
||||
if (!_expanded) {
|
||||
octreeStats.str("");
|
||||
octreeStats << "Octree Elements Server: " << qPrintable(serversTotalString)
|
||||
<< " Local:" << qPrintable(localTotalString);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
if (_expanded) {
|
||||
octreeStats.str("");
|
||||
octreeStats << "Octree Elements -";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
|
||||
QString serversInternalString = locale.toString((uint)totalInternal);
|
||||
QString serversLeavesString = locale.toString((uint)totalLeaves);
|
||||
|
||||
octreeStats.str("");
|
||||
octreeStats << " Server: " << qPrintable(serversTotalString) <<
|
||||
" Internal: " << qPrintable(serversInternalString) <<
|
||||
" Leaves: " << qPrintable(serversLeavesString);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
|
||||
// Local Voxels
|
||||
unsigned long localInternal = OctreeElement::getInternalNodeCount();
|
||||
unsigned long localLeaves = OctreeElement::getLeafNodeCount();
|
||||
QString localInternalString = locale.toString((uint)localInternal);
|
||||
QString localLeavesString = locale.toString((uint)localLeaves);
|
||||
|
||||
octreeStats.str("");
|
||||
octreeStats << " Local: " << qPrintable(serversTotalString) <<
|
||||
" Internal: " << qPrintable(localInternalString) <<
|
||||
" Leaves: " << qPrintable(localLeavesString) << "";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
// LOD Details
|
||||
octreeStats.str("");
|
||||
QString displayLODDetails = DependencyManager::get<LODManager>()->getLODFeedbackText();
|
||||
octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed());
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
void Stats::setRenderDetails(const RenderDetails& details) {
|
||||
STAT_UPDATE(triangles, details._trianglesRendered);
|
||||
STAT_UPDATE(quads, details._quadsRendered);
|
||||
STAT_UPDATE(materialSwitches, details._materialSwitches);
|
||||
if (_expanded) {
|
||||
STAT_UPDATE(meshOpaque, details._opaque._rendered);
|
||||
STAT_UPDATE(meshTranslucent, details._opaque._rendered);
|
||||
STAT_UPDATE(opaqueConsidered, details._opaque._considered);
|
||||
STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView);
|
||||
STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall);
|
||||
STAT_UPDATE(translucentConsidered, details._translucent._considered);
|
||||
STAT_UPDATE(translucentOutOfView, details._translucent._outOfView);
|
||||
STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// display expanded or contracted stats
|
||||
void Stats::display(
|
||||
int voxelPacketsToProcess)
|
||||
{
|
||||
// iterate all the current voxel stats, and list their sending modes, and total voxel counts
|
||||
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
//
|
||||
// Stats.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Lucas Crisman on 22/03/14.
|
||||
// Created by Bradley Austin Davis 2015/06/17
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
@ -13,47 +10,139 @@
|
|||
#define hifi_Stats_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QQuickItem>
|
||||
#include <QVector3D>
|
||||
|
||||
#include <OffscreenUi.h>
|
||||
#include <RenderArgs.h>
|
||||
|
||||
class Stats: public QObject {
|
||||
#define STATS_PROPERTY(type, name, initialValue) \
|
||||
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
|
||||
public: \
|
||||
type name() { return _##name; }; \
|
||||
private: \
|
||||
type _##name{ initialValue };
|
||||
|
||||
|
||||
class Stats : public QQuickItem {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged)
|
||||
Q_PROPERTY(bool timingExpanded READ isTimingExpanded NOTIFY timingExpandedChanged)
|
||||
Q_PROPERTY(QString monospaceFont READ monospaceFont)
|
||||
|
||||
STATS_PROPERTY(int, serverCount, 0)
|
||||
STATS_PROPERTY(int, framerate, 0)
|
||||
STATS_PROPERTY(int, avatarCount, 0)
|
||||
STATS_PROPERTY(int, packetInCount, 0)
|
||||
STATS_PROPERTY(int, packetOutCount, 0)
|
||||
STATS_PROPERTY(float, mbpsIn, 0)
|
||||
STATS_PROPERTY(float, mbpsOut, 0)
|
||||
STATS_PROPERTY(int, audioPing, 0)
|
||||
STATS_PROPERTY(int, avatarPing, 0)
|
||||
STATS_PROPERTY(int, entitiesPing, 0)
|
||||
STATS_PROPERTY(QVector3D, position, QVector3D(0, 0, 0) )
|
||||
STATS_PROPERTY(float, velocity, 0)
|
||||
STATS_PROPERTY(float, yaw, 0)
|
||||
STATS_PROPERTY(int, avatarMixerKbps, 0)
|
||||
STATS_PROPERTY(int, avatarMixerPps, 0)
|
||||
STATS_PROPERTY(int, audioMixerKbps, 0)
|
||||
STATS_PROPERTY(int, audioMixerPps, 0)
|
||||
STATS_PROPERTY(int, downloads, 0)
|
||||
STATS_PROPERTY(int, downloadsPending, 0)
|
||||
STATS_PROPERTY(int, triangles, 0)
|
||||
STATS_PROPERTY(int, quads, 0)
|
||||
STATS_PROPERTY(int, materialSwitches, 0)
|
||||
STATS_PROPERTY(int, meshOpaque, 0)
|
||||
STATS_PROPERTY(int, meshTranslucent, 0)
|
||||
STATS_PROPERTY(int, opaqueConsidered, 0)
|
||||
STATS_PROPERTY(int, opaqueOutOfView, 0)
|
||||
STATS_PROPERTY(int, opaqueTooSmall, 0)
|
||||
STATS_PROPERTY(int, translucentConsidered, 0)
|
||||
STATS_PROPERTY(int, translucentOutOfView, 0)
|
||||
STATS_PROPERTY(int, translucentTooSmall, 0)
|
||||
STATS_PROPERTY(QString, sendingMode, QString())
|
||||
STATS_PROPERTY(QString, packetStats, QString())
|
||||
STATS_PROPERTY(QString, lodStatus, QString())
|
||||
STATS_PROPERTY(QString, timingStats, QString())
|
||||
STATS_PROPERTY(int, serverElements, 0)
|
||||
STATS_PROPERTY(int, serverInternal, 0)
|
||||
STATS_PROPERTY(int, serverLeaves, 0)
|
||||
STATS_PROPERTY(int, localElements, 0)
|
||||
STATS_PROPERTY(int, localInternal, 0)
|
||||
STATS_PROPERTY(int, localLeaves, 0)
|
||||
|
||||
public:
|
||||
static Stats* getInstance();
|
||||
|
||||
Stats();
|
||||
|
||||
static void drawBackground(unsigned int rgba, int x, int y, int width, int height);
|
||||
|
||||
void toggleExpanded();
|
||||
bool isExpanded() { return _expanded; }
|
||||
|
||||
void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset);
|
||||
void resetWidth(int width, int horizontalOffset);
|
||||
void display(const float* color, int horizontalOffset, float fps, int inPacketsPerSecond, int outPacketsPerSecond,
|
||||
int inKbitsPerSecond, int outKbitsPerSecond, int voxelPacketsToProcess);
|
||||
Stats(QQuickItem* parent = nullptr);
|
||||
bool includeTimingRecord(const QString& name);
|
||||
|
||||
void setRenderDetails(const RenderDetails& details) { _renderDetails = details; }
|
||||
|
||||
void setRenderDetails(const RenderDetails& details);
|
||||
const QString& monospaceFont() {
|
||||
return _monospaceFont;
|
||||
}
|
||||
void updateStats();
|
||||
|
||||
bool isExpanded() { return _expanded; }
|
||||
bool isTimingExpanded() { return _timingExpanded; }
|
||||
|
||||
void setExpanded(bool expanded) {
|
||||
if (_expanded != expanded) {
|
||||
_expanded = expanded;
|
||||
emit expandedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
signals:
|
||||
void expandedChanged();
|
||||
void timingExpandedChanged();
|
||||
void serverCountChanged();
|
||||
void framerateChanged();
|
||||
void avatarCountChanged();
|
||||
void packetInCountChanged();
|
||||
void packetOutCountChanged();
|
||||
void mbpsInChanged();
|
||||
void mbpsOutChanged();
|
||||
void audioPingChanged();
|
||||
void avatarPingChanged();
|
||||
void entitiesPingChanged();
|
||||
void positionChanged();
|
||||
void velocityChanged();
|
||||
void yawChanged();
|
||||
void avatarMixerKbpsChanged();
|
||||
void avatarMixerPpsChanged();
|
||||
void audioMixerKbpsChanged();
|
||||
void audioMixerPpsChanged();
|
||||
void downloadsChanged();
|
||||
void downloadsPendingChanged();
|
||||
void trianglesChanged();
|
||||
void quadsChanged();
|
||||
void materialSwitchesChanged();
|
||||
void meshOpaqueChanged();
|
||||
void meshTranslucentChanged();
|
||||
void opaqueConsideredChanged();
|
||||
void opaqueOutOfViewChanged();
|
||||
void opaqueTooSmallChanged();
|
||||
void translucentConsideredChanged();
|
||||
void translucentOutOfViewChanged();
|
||||
void translucentTooSmallChanged();
|
||||
void sendingModeChanged();
|
||||
void packetStatsChanged();
|
||||
void lodStatusChanged();
|
||||
void serverElementsChanged();
|
||||
void serverInternalChanged();
|
||||
void serverLeavesChanged();
|
||||
void localElementsChanged();
|
||||
void localInternalChanged();
|
||||
void localLeavesChanged();
|
||||
void timingStatsChanged();
|
||||
|
||||
private:
|
||||
static Stats* _sharedInstance;
|
||||
|
||||
bool _expanded;
|
||||
|
||||
int _recentMaxPackets; // recent max incoming voxel packets to process
|
||||
bool _resetRecentMaxPacketsSoon;
|
||||
|
||||
int _generalStatsWidth;
|
||||
int _bandwidthStatsWidth;
|
||||
int _pingStatsWidth;
|
||||
int _geoStatsWidth;
|
||||
int _octreeStatsWidth;
|
||||
|
||||
int _lastHorizontalOffset;
|
||||
|
||||
RenderDetails _renderDetails;
|
||||
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
|
||||
bool _resetRecentMaxPacketsSoon{ true };
|
||||
bool _expanded{ false };
|
||||
bool _timingExpanded{ false };
|
||||
QString _monospaceFont;
|
||||
};
|
||||
|
||||
#endif // hifi_Stats_h
|
||||
|
|
|
@ -2,54 +2,50 @@
|
|||
// UpdateDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Created by Leonardo Murillo on 6/3/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "ui_updateDialog.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "UpdateDialog.h"
|
||||
|
||||
#include <AutoUpdater.h>
|
||||
|
||||
UpdateDialog::UpdateDialog(QWidget *parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL) :
|
||||
QDialog(parent),
|
||||
_latestVersion(latestVersion),
|
||||
_downloadUrl(downloadURL)
|
||||
#include "DependencyManager.h"
|
||||
|
||||
HIFI_QML_DEF(UpdateDialog)
|
||||
|
||||
UpdateDialog::UpdateDialog(QQuickItem* parent) :
|
||||
OffscreenQmlDialog(parent)
|
||||
{
|
||||
Ui::Dialog dialogUI;
|
||||
dialogUI.setupUi(this);
|
||||
|
||||
QString updateRequired = QString("You are currently running build %1, the latest build released is %2."
|
||||
"\n\nPlease download and install the most recent release to access the latest features and bug fixes.")
|
||||
.arg(Application::getInstance()->applicationVersion(), latestVersion);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
QPushButton* downloadButton = findChild<QPushButton*>("downloadButton");
|
||||
QPushButton* skipButton = findChild<QPushButton*>("skipButton");
|
||||
QPushButton* closeButton = findChild<QPushButton*>("closeButton");
|
||||
QLabel* updateContent = findChild<QLabel*>("updateContent");
|
||||
|
||||
updateContent->setText(updateRequired);
|
||||
|
||||
connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload()));
|
||||
connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip()));
|
||||
connect(closeButton, SIGNAL(released()), this, SLOT(close()));
|
||||
|
||||
QMetaObject::invokeMethod(this, "show", Qt::QueuedConnection);
|
||||
auto applicationUpdater = DependencyManager::get<AutoUpdater>();
|
||||
int currentVersion = QCoreApplication::applicationVersion().toInt();
|
||||
int latestVersion = applicationUpdater.data()->getBuildData().lastKey();
|
||||
int versionsBehind = latestVersion - currentVersion;
|
||||
_updateAvailableDetails = "v" + QString::number(latestVersion) + " released on " + applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"];
|
||||
_updateAvailableDetails += "\nYou are " + QString::number(versionsBehind) + " versions behind";
|
||||
_releaseNotes = applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"];
|
||||
}
|
||||
|
||||
void UpdateDialog::handleDownload() {
|
||||
QDesktopServices::openUrl(_downloadUrl);
|
||||
Application::getInstance()->quit();
|
||||
const QString& UpdateDialog::updateAvailableDetails() const {
|
||||
return _updateAvailableDetails;
|
||||
}
|
||||
|
||||
void UpdateDialog::handleSkip() {
|
||||
Application::getInstance()->skipVersion(_latestVersion);
|
||||
this->close();
|
||||
const QString& UpdateDialog::releaseNotes() const {
|
||||
return _releaseNotes;
|
||||
}
|
||||
|
||||
void UpdateDialog::closeDialog() {
|
||||
hide();
|
||||
}
|
||||
|
||||
void UpdateDialog::hide() {
|
||||
((QQuickItem*)parent())->setEnabled(false);
|
||||
}
|
||||
|
||||
void UpdateDialog::triggerUpgrade() {
|
||||
auto applicationUpdater = DependencyManager::get<AutoUpdater>();
|
||||
applicationUpdater.data()->performAutoUpdate(applicationUpdater.data()->getBuildData().lastKey());
|
||||
}
|
|
@ -2,30 +2,42 @@
|
|||
// UpdateDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Created by Leonardo Murillo on 6/3/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_UpdateDialog_h
|
||||
#define hifi_UpdateDialog_h
|
||||
|
||||
#include <QWidget>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
class UpdateDialog : public QDialog {
|
||||
#include <OffscreenQmlDialog.h>
|
||||
|
||||
class UpdateDialog : public OffscreenQmlDialog {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
|
||||
Q_PROPERTY(QString updateAvailableDetails READ updateAvailableDetails)
|
||||
Q_PROPERTY(QString releaseNotes READ releaseNotes)
|
||||
|
||||
public:
|
||||
UpdateDialog(QWidget* parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL);
|
||||
UpdateDialog(QQuickItem* parent = nullptr);
|
||||
const QString& updateAvailableDetails() const;
|
||||
const QString& releaseNotes() const;
|
||||
|
||||
private:
|
||||
QString _latestVersion;
|
||||
QUrl _downloadUrl;
|
||||
|
||||
private slots:
|
||||
void handleDownload();
|
||||
void handleSkip();
|
||||
QString _updateAvailableDetails;
|
||||
QString _releaseNotes;
|
||||
|
||||
protected:
|
||||
void hide();
|
||||
Q_INVOKABLE void triggerUpgrade();
|
||||
Q_INVOKABLE void closeDialog();
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_UpdateDialog_h
|
||||
|
|
|
@ -41,6 +41,22 @@ void UserInputMapper::resetAllDeviceBindings() {
|
|||
}
|
||||
}
|
||||
|
||||
void UserInputMapper::resetDevice(uint16 deviceID) {
|
||||
auto device = _registeredDevices.find(deviceID);
|
||||
if (device != _registeredDevices.end()) {
|
||||
device->second->resetDeviceBindings();
|
||||
}
|
||||
}
|
||||
|
||||
int UserInputMapper::findDevice(QString name) {
|
||||
for (auto device : _registeredDevices) {
|
||||
if (device.second->_name.split(" (")[0] == name) {
|
||||
return device.first;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) {
|
||||
return addInputChannel(action, input, Input(), scale);
|
||||
}
|
||||
|
@ -106,6 +122,11 @@ void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) {
|
|||
}
|
||||
}
|
||||
|
||||
void UserInputMapper::removeDevice(int device) {
|
||||
removeAllInputChannelsForDevice((uint16) device);
|
||||
_registeredDevices.erase(device);
|
||||
}
|
||||
|
||||
int UserInputMapper::getInputChannels(InputChannels& channels) const {
|
||||
for (auto& channel : _actionToInputsMap) {
|
||||
channels.push_back(channel.second);
|
||||
|
|
|
@ -116,6 +116,8 @@ public:
|
|||
QString getDeviceName(uint16 deviceID) { return _registeredDevices[deviceID]->_name; }
|
||||
QVector<InputPair> getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); }
|
||||
void resetAllDeviceBindings();
|
||||
void resetDevice(uint16 deviceID);
|
||||
int findDevice(QString name);
|
||||
|
||||
// Actions are the output channels of the Mapper, that's what the InputChannel map to
|
||||
// For now the Actions are hardcoded, this is bad, but we will fix that in the near future
|
||||
|
@ -189,6 +191,7 @@ public:
|
|||
bool removeInputChannel(InputChannel channel);
|
||||
void removeAllInputChannels();
|
||||
void removeAllInputChannelsForDevice(uint16 device);
|
||||
void removeDevice(int device);
|
||||
//Grab all the input channels currently in use, return the number
|
||||
int getInputChannels(InputChannels& channels) const;
|
||||
QVector<InputChannel> getAllInputsForDevice(uint16 device);
|
||||
|
|
|
@ -43,72 +43,90 @@ void BillboardOverlay::render(RenderArgs* args) {
|
|||
return;
|
||||
}
|
||||
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_GREATER, 0.5f);
|
||||
glm::quat rotation;
|
||||
if (_isFacingAvatar) {
|
||||
// rotate about vertical to face the camera
|
||||
rotation = Application::getInstance()->getCamera()->getRotation();
|
||||
rotation *= glm::angleAxis(glm::pi<float>(), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
rotation *= getRotation();
|
||||
} else {
|
||||
rotation = getRotation();
|
||||
}
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDisable(GL_LIGHTING);
|
||||
float imageWidth = _texture->getWidth();
|
||||
float imageHeight = _texture->getHeight();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _texture->getID());
|
||||
QRect fromImage;
|
||||
if (_fromImage.isNull()) {
|
||||
fromImage.setX(0);
|
||||
fromImage.setY(0);
|
||||
fromImage.setWidth(imageWidth);
|
||||
fromImage.setHeight(imageHeight);
|
||||
} else {
|
||||
float scaleX = imageWidth / _texture->getOriginalWidth();
|
||||
float scaleY = imageHeight / _texture->getOriginalHeight();
|
||||
|
||||
glPushMatrix(); {
|
||||
glTranslatef(_position.x, _position.y, _position.z);
|
||||
glm::quat rotation;
|
||||
if (_isFacingAvatar) {
|
||||
// rotate about vertical to face the camera
|
||||
rotation = Application::getInstance()->getCamera()->getRotation();
|
||||
rotation *= glm::angleAxis(glm::pi<float>(), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
rotation *= getRotation();
|
||||
} else {
|
||||
rotation = getRotation();
|
||||
}
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glScalef(_scale, _scale, _scale);
|
||||
fromImage.setX(scaleX * _fromImage.x());
|
||||
fromImage.setY(scaleY * _fromImage.y());
|
||||
fromImage.setWidth(scaleX * _fromImage.width());
|
||||
fromImage.setHeight(scaleY * _fromImage.height());
|
||||
}
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
xColor color = getColor();
|
||||
float alpha = getAlpha();
|
||||
float maxSize = glm::max(fromImage.width(), fromImage.height());
|
||||
float x = fromImage.width() / (2.0f * maxSize);
|
||||
float y = -fromImage.height() / (2.0f * maxSize);
|
||||
|
||||
float imageWidth = _texture->getWidth();
|
||||
float imageHeight = _texture->getHeight();
|
||||
glm::vec2 topLeft(-x, -y);
|
||||
glm::vec2 bottomRight(x, y);
|
||||
glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight);
|
||||
glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth,
|
||||
(fromImage.y() + fromImage.height()) / imageHeight);
|
||||
|
||||
QRect fromImage;
|
||||
if (_fromImage.isNull()) {
|
||||
fromImage.setX(0);
|
||||
fromImage.setY(0);
|
||||
fromImage.setWidth(imageWidth);
|
||||
fromImage.setHeight(imageHeight);
|
||||
} else {
|
||||
float scaleX = imageWidth / _texture->getOriginalWidth();
|
||||
float scaleY = imageHeight / _texture->getOriginalHeight();
|
||||
const float MAX_COLOR = 255.0f;
|
||||
xColor color = getColor();
|
||||
float alpha = getAlpha();
|
||||
|
||||
fromImage.setX(scaleX * _fromImage.x());
|
||||
fromImage.setY(scaleY * _fromImage.y());
|
||||
fromImage.setWidth(scaleX * _fromImage.width());
|
||||
fromImage.setHeight(scaleY * _fromImage.height());
|
||||
}
|
||||
auto batch = args->_batch;
|
||||
|
||||
float maxSize = glm::max(fromImage.width(), fromImage.height());
|
||||
float x = fromImage.width() / (2.0f * maxSize);
|
||||
float y = -fromImage.height() / (2.0f * maxSize);
|
||||
if (batch) {
|
||||
Transform transform;
|
||||
transform.setTranslation(_position);
|
||||
transform.setRotation(rotation);
|
||||
transform.setScale(_scale);
|
||||
|
||||
glm::vec2 topLeft(-x, -y);
|
||||
glm::vec2 bottomRight(x, y);
|
||||
glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight);
|
||||
glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth,
|
||||
(fromImage.y() + fromImage.height()) / imageHeight);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
batch->setModelTransform(transform);
|
||||
batch->setUniformTexture(0, _texture->getGPUTexture());
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
|
||||
|
||||
batch->setUniformTexture(0, args->_whiteTexture); // restore default white color after me
|
||||
} else {
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_GREATER, 0.5f);
|
||||
|
||||
} glPopMatrix();
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_LIGHTING);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glBindTexture(GL_TEXTURE_2D, _texture->getID());
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glPushMatrix(); {
|
||||
glTranslatef(_position.x, _position.y, _position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glScalef(_scale, _scale, _scale);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
|
||||
|
||||
} glPopMatrix();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_LIGHTING);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void BillboardOverlay::setProperties(const QScriptValue &properties) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// include this before QGLWidget, which includes an earlier version of OpenGL
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <GlowEffect.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -75,20 +76,20 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
float alpha = getAlpha();
|
||||
|
||||
if (alpha == 0.0) {
|
||||
if (alpha == 0.0f) {
|
||||
return; // do nothing if our alpha is 0, we're not visible
|
||||
}
|
||||
|
||||
|
||||
// Create the circle in the coordinates origin
|
||||
float outerRadius = getOuterRadius();
|
||||
float innerRadius = getInnerRadius(); // only used in solid case
|
||||
float startAt = getStartAt();
|
||||
float endAt = getEndAt();
|
||||
|
||||
|
||||
bool geometryChanged = (startAt != _lastStartAt || endAt != _lastEndAt ||
|
||||
innerRadius != _lastInnerRadius || outerRadius != _lastOuterRadius);
|
||||
|
||||
|
||||
|
||||
const float FULL_CIRCLE = 360.0f;
|
||||
const float SLICES = 180.0f; // The amount of segment to create the circle
|
||||
const float SLICE_ANGLE = FULL_CIRCLE / SLICES;
|
||||
|
@ -101,195 +102,178 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
bool colorChanged = colorX.red != _lastColor.red || colorX.green != _lastColor.green || colorX.blue != _lastColor.blue;
|
||||
_lastColor = colorX;
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(getCenter());
|
||||
transform.setRotation(getRotation());
|
||||
transform.setScale(glm::vec3(getDimensions(), 0.01f));
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 center = getCenter();
|
||||
glm::vec2 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
glScalef(dimensions.x / 2.0f, dimensions.y / 2.0f, 1.0f);
|
||||
|
||||
glLineWidth(_lineWidth);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
auto& batch = *args->_batch;
|
||||
batch._glLineWidth(_lineWidth);
|
||||
batch.setModelTransform(transform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false);
|
||||
|
||||
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
|
||||
// we just draw a line...
|
||||
if (getIsSolid()) {
|
||||
if (_quadVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
_quadVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
if (geometryChanged || colorChanged) {
|
||||
|
||||
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
|
||||
// we just draw a line...
|
||||
if (getIsSolid()) {
|
||||
if (_quadVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
_quadVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
QVector<glm::vec2> points;
|
||||
|
||||
float angle = startAt;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
glm::vec2 firstInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
||||
glm::vec2 firstOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
|
||||
points << firstInnerPoint << firstOuterPoint;
|
||||
|
||||
while (angle < endAt) {
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 thisInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
||||
glm::vec2 thisOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
|
||||
if (geometryChanged || colorChanged) {
|
||||
|
||||
QVector<glm::vec2> points;
|
||||
|
||||
float angle = startAt;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
glm::vec2 firstInnerPoint(cos(angleInRadians) * innerRadius, sin(angleInRadians) * innerRadius);
|
||||
glm::vec2 firstOuterPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius);
|
||||
|
||||
points << firstInnerPoint << firstOuterPoint;
|
||||
|
||||
while (angle < endAt) {
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 thisInnerPoint(cos(angleInRadians) * innerRadius, sin(angleInRadians) * innerRadius);
|
||||
glm::vec2 thisOuterPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius);
|
||||
|
||||
points << thisOuterPoint << thisInnerPoint;
|
||||
points << thisOuterPoint << thisInnerPoint;
|
||||
|
||||
angle += SLICE_ANGLE;
|
||||
}
|
||||
angle += SLICE_ANGLE;
|
||||
}
|
||||
|
||||
// get the last slice portion....
|
||||
angle = endAt;
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 lastInnerPoint(cos(angleInRadians) * innerRadius, sin(angleInRadians) * innerRadius);
|
||||
glm::vec2 lastOuterPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius);
|
||||
// get the last slice portion....
|
||||
angle = endAt;
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 lastInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
||||
glm::vec2 lastOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
|
||||
points << lastOuterPoint << lastInnerPoint;
|
||||
|
||||
geometryCache->updateVertices(_quadVerticesID, points, color);
|
||||
}
|
||||
points << lastOuterPoint << lastInnerPoint;
|
||||
|
||||
geometryCache->updateVertices(_quadVerticesID, points, color);
|
||||
}
|
||||
|
||||
geometryCache->renderVertices(batch, gpu::QUAD_STRIP, _quadVerticesID);
|
||||
|
||||
} else {
|
||||
if (_lineVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
_lineVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
if (geometryChanged || colorChanged) {
|
||||
QVector<glm::vec2> points;
|
||||
|
||||
float angle = startAt;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
glm::vec2 firstPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
points << firstPoint;
|
||||
|
||||
while (angle < endAt) {
|
||||
angle += SLICE_ANGLE;
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 thisPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
points << thisPoint;
|
||||
|
||||
geometryCache->renderVertices(gpu::QUAD_STRIP, _quadVerticesID);
|
||||
|
||||
} else {
|
||||
if (_lineVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
_lineVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
if (geometryChanged || colorChanged) {
|
||||
QVector<glm::vec2> points;
|
||||
|
||||
float angle = startAt;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
glm::vec2 firstPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius);
|
||||
points << firstPoint;
|
||||
|
||||
while (angle < endAt) {
|
||||
angle += SLICE_ANGLE;
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 thisPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius);
|
||||
points << thisPoint;
|
||||
|
||||
if (getIsDashedLine()) {
|
||||
angle += SLICE_ANGLE / 2.0f; // short gap
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 dashStartPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius);
|
||||
points << dashStartPoint;
|
||||
}
|
||||
}
|
||||
|
||||
// get the last slice portion....
|
||||
angle = endAt;
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 lastPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius);
|
||||
points << lastPoint;
|
||||
|
||||
geometryCache->updateVertices(_lineVerticesID, points, color);
|
||||
}
|
||||
|
||||
if (getIsDashedLine()) {
|
||||
geometryCache->renderVertices(gpu::LINES, _lineVerticesID);
|
||||
} else {
|
||||
geometryCache->renderVertices(gpu::LINE_STRIP, _lineVerticesID);
|
||||
angle += SLICE_ANGLE / 2.0f; // short gap
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 dashStartPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
points << dashStartPoint;
|
||||
}
|
||||
}
|
||||
|
||||
// draw our tick marks
|
||||
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
|
||||
// we just draw a line...
|
||||
if (getHasTickMarks()) {
|
||||
|
||||
if (_majorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
_majorTicksVerticesID = geometryCache->allocateID();
|
||||
// get the last slice portion....
|
||||
angle = endAt;
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 lastPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
points << lastPoint;
|
||||
|
||||
geometryCache->updateVertices(_lineVerticesID, points, color);
|
||||
}
|
||||
|
||||
if (getIsDashedLine()) {
|
||||
geometryCache->renderVertices(batch, gpu::LINES, _lineVerticesID);
|
||||
} else {
|
||||
geometryCache->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID);
|
||||
}
|
||||
}
|
||||
|
||||
// draw our tick marks
|
||||
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
|
||||
// we just draw a line...
|
||||
if (getHasTickMarks()) {
|
||||
|
||||
if (_majorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
_majorTicksVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
if (_minorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
_minorTicksVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
if (geometryChanged) {
|
||||
QVector<glm::vec2> majorPoints;
|
||||
QVector<glm::vec2> minorPoints;
|
||||
|
||||
// draw our major tick marks
|
||||
if (getMajorTickMarksAngle() > 0.0f && getMajorTickMarksLength() != 0.0f) {
|
||||
|
||||
float tickMarkAngle = getMajorTickMarksAngle();
|
||||
float angle = startAt - fmodf(startAt, tickMarkAngle) + tickMarkAngle;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
float tickMarkLength = getMajorTickMarksLength();
|
||||
float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
|
||||
float endRadius = startRadius + tickMarkLength;
|
||||
|
||||
while (angle <= endAt) {
|
||||
angleInRadians = glm::radians(angle);
|
||||
|
||||
glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
|
||||
glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius);
|
||||
|
||||
majorPoints << thisPointA << thisPointB;
|
||||
|
||||
angle += tickMarkAngle;
|
||||
}
|
||||
if (_minorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
_minorTicksVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
if (geometryChanged) {
|
||||
QVector<glm::vec2> majorPoints;
|
||||
QVector<glm::vec2> minorPoints;
|
||||
|
||||
// draw our major tick marks
|
||||
if (getMajorTickMarksAngle() > 0.0f && getMajorTickMarksLength() != 0.0f) {
|
||||
|
||||
float tickMarkAngle = getMajorTickMarksAngle();
|
||||
float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
float tickMarkLength = getMajorTickMarksLength();
|
||||
float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
|
||||
float endRadius = startRadius + tickMarkLength;
|
||||
|
||||
while (angle <= endAt) {
|
||||
angleInRadians = glm::radians(angle);
|
||||
|
||||
glm::vec2 thisPointA(cos(angleInRadians) * startRadius, sin(angleInRadians) * startRadius);
|
||||
glm::vec2 thisPointB(cos(angleInRadians) * endRadius, sin(angleInRadians) * endRadius);
|
||||
|
||||
majorPoints << thisPointA << thisPointB;
|
||||
|
||||
angle += tickMarkAngle;
|
||||
}
|
||||
}
|
||||
|
||||
// draw our minor tick marks
|
||||
if (getMinorTickMarksAngle() > 0.0f && getMinorTickMarksLength() != 0.0f) {
|
||||
|
||||
float tickMarkAngle = getMinorTickMarksAngle();
|
||||
float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
float tickMarkLength = getMinorTickMarksLength();
|
||||
float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
|
||||
float endRadius = startRadius + tickMarkLength;
|
||||
|
||||
while (angle <= endAt) {
|
||||
angleInRadians = glm::radians(angle);
|
||||
|
||||
glm::vec2 thisPointA(cos(angleInRadians) * startRadius, sin(angleInRadians) * startRadius);
|
||||
glm::vec2 thisPointB(cos(angleInRadians) * endRadius, sin(angleInRadians) * endRadius);
|
||||
|
||||
minorPoints << thisPointA << thisPointB;
|
||||
|
||||
angle += tickMarkAngle;
|
||||
}
|
||||
}
|
||||
|
||||
xColor majorColorX = getMajorTickMarksColor();
|
||||
glm::vec4 majorColor(majorColorX.red / MAX_COLOR, majorColorX.green / MAX_COLOR, majorColorX.blue / MAX_COLOR, alpha);
|
||||
|
||||
geometryCache->updateVertices(_majorTicksVerticesID, majorPoints, majorColor);
|
||||
|
||||
xColor minorColorX = getMinorTickMarksColor();
|
||||
glm::vec4 minorColor(minorColorX.red / MAX_COLOR, minorColorX.green / MAX_COLOR, minorColorX.blue / MAX_COLOR, alpha);
|
||||
|
||||
geometryCache->updateVertices(_minorTicksVerticesID, minorPoints, minorColor);
|
||||
}
|
||||
|
||||
geometryCache->renderVertices(gpu::LINES, _majorTicksVerticesID);
|
||||
|
||||
geometryCache->renderVertices(gpu::LINES, _minorTicksVerticesID);
|
||||
}
|
||||
|
||||
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
// draw our minor tick marks
|
||||
if (getMinorTickMarksAngle() > 0.0f && getMinorTickMarksLength() != 0.0f) {
|
||||
|
||||
float tickMarkAngle = getMinorTickMarksAngle();
|
||||
float angle = startAt - fmodf(startAt, tickMarkAngle) + tickMarkAngle;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
float tickMarkLength = getMinorTickMarksLength();
|
||||
float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
|
||||
float endRadius = startRadius + tickMarkLength;
|
||||
|
||||
while (angle <= endAt) {
|
||||
angleInRadians = glm::radians(angle);
|
||||
|
||||
glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
|
||||
glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius);
|
||||
|
||||
minorPoints << thisPointA << thisPointB;
|
||||
|
||||
angle += tickMarkAngle;
|
||||
}
|
||||
}
|
||||
|
||||
xColor majorColorX = getMajorTickMarksColor();
|
||||
glm::vec4 majorColor(majorColorX.red / MAX_COLOR, majorColorX.green / MAX_COLOR, majorColorX.blue / MAX_COLOR, alpha);
|
||||
|
||||
geometryCache->updateVertices(_majorTicksVerticesID, majorPoints, majorColor);
|
||||
|
||||
xColor minorColorX = getMinorTickMarksColor();
|
||||
glm::vec4 minorColor(minorColorX.red / MAX_COLOR, minorColorX.green / MAX_COLOR, minorColorX.blue / MAX_COLOR, alpha);
|
||||
|
||||
geometryCache->updateVertices(_minorTicksVerticesID, minorPoints, minorColor);
|
||||
}
|
||||
|
||||
geometryCache->renderVertices(batch, gpu::LINES, _majorTicksVerticesID);
|
||||
|
||||
geometryCache->renderVertices(batch, gpu::LINES, _minorTicksVerticesID);
|
||||
}
|
||||
|
||||
if (geometryChanged) {
|
||||
_lastStartAt = startAt;
|
||||
|
@ -297,15 +281,11 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
_lastInnerRadius = innerRadius;
|
||||
_lastOuterRadius = outerRadius;
|
||||
}
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
}
|
||||
|
||||
void Circle3DOverlay::setProperties(const QScriptValue &properties) {
|
||||
Planar3DOverlay::setProperties(properties);
|
||||
|
||||
|
||||
QScriptValue startAt = properties.property("startAt");
|
||||
if (startAt.isValid()) {
|
||||
setStartAt(startAt.toVariant().toFloat());
|
||||
|
@ -415,7 +395,7 @@ QScriptValue Circle3DOverlay::getProperty(const QString& property) {
|
|||
}
|
||||
|
||||
|
||||
bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin,
|
||||
bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance, BoxFace& face) {
|
||||
|
||||
bool intersects = Planar3DOverlay::findRayIntersection(origin, direction, distance, face);
|
||||
|
|
|
@ -35,13 +35,6 @@ void Cube3DOverlay::render(RenderArgs* args) {
|
|||
return; // do nothing if we're not visible
|
||||
}
|
||||
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
float alpha = getAlpha();
|
||||
xColor color = getColor();
|
||||
const float MAX_COLOR = 255.0f;
|
||||
|
@ -49,91 +42,161 @@ void Cube3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
//glDisable(GL_LIGHTING);
|
||||
|
||||
// TODO: handle registration point??
|
||||
// TODO: handle registration point??
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 center = getCenter();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
if (_isSolid) {
|
||||
if (_borderSize > 0) {
|
||||
// Draw a cube at a larger size behind the main cube, creating
|
||||
// a border effect.
|
||||
// Disable writing to the depth mask so that the "border" cube will not
|
||||
// occlude the main cube. This means the border could be covered by
|
||||
// overlays that are further back and drawn later, but this is good
|
||||
// enough for the use-case.
|
||||
glDepthMask(GL_FALSE);
|
||||
glPushMatrix();
|
||||
glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize);
|
||||
|
||||
if (_drawOnHUD) {
|
||||
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
|
||||
}
|
||||
auto batch = args->_batch;
|
||||
|
||||
glPopMatrix();
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
if (batch) {
|
||||
Transform transform;
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(rotation);
|
||||
if (_isSolid) {
|
||||
// if (_borderSize > 0) {
|
||||
// // Draw a cube at a larger size behind the main cube, creating
|
||||
// // a border effect.
|
||||
// // Disable writing to the depth mask so that the "border" cube will not
|
||||
// // occlude the main cube. This means the border could be covered by
|
||||
// // overlays that are further back and drawn later, but this is good
|
||||
// // enough for the use-case.
|
||||
// transform.setScale(dimensions * _borderSize);
|
||||
// batch->setModelTransform(transform);
|
||||
// DependencyManager::get<GeometryCache>()->renderSolidCube(*batch, 1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
|
||||
// }
|
||||
|
||||
transform.setScale(dimensions);
|
||||
batch->setModelTransform(transform);
|
||||
DependencyManager::get<GeometryCache>()->renderSolidCube(*batch, 1.0f, cubeColor);
|
||||
} else {
|
||||
|
||||
if (getIsDashedLine()) {
|
||||
transform.setScale(1.0f);
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
glm::vec3 halfDimensions = dimensions / 2.0f;
|
||||
glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z);
|
||||
glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z);
|
||||
glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z);
|
||||
glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z);
|
||||
|
||||
glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z);
|
||||
glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z);
|
||||
glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z);
|
||||
glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
geometryCache->renderDashedLine(*batch, bottomLeftNear, bottomRightNear, cubeColor);
|
||||
geometryCache->renderDashedLine(*batch, bottomRightNear, bottomRightFar, cubeColor);
|
||||
geometryCache->renderDashedLine(*batch, bottomRightFar, bottomLeftFar, cubeColor);
|
||||
geometryCache->renderDashedLine(*batch, bottomLeftFar, bottomLeftNear, cubeColor);
|
||||
|
||||
geometryCache->renderDashedLine(*batch, topLeftNear, topRightNear, cubeColor);
|
||||
geometryCache->renderDashedLine(*batch, topRightNear, topRightFar, cubeColor);
|
||||
geometryCache->renderDashedLine(*batch, topRightFar, topLeftFar, cubeColor);
|
||||
geometryCache->renderDashedLine(*batch, topLeftFar, topLeftNear, cubeColor);
|
||||
|
||||
geometryCache->renderDashedLine(*batch, bottomLeftNear, topLeftNear, cubeColor);
|
||||
geometryCache->renderDashedLine(*batch, bottomRightNear, topRightNear, cubeColor);
|
||||
geometryCache->renderDashedLine(*batch, bottomLeftFar, topLeftFar, cubeColor);
|
||||
geometryCache->renderDashedLine(*batch, bottomRightFar, topRightFar, cubeColor);
|
||||
|
||||
glPushMatrix();
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
if (_drawOnHUD) {
|
||||
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, cubeColor);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, cubeColor);
|
||||
}
|
||||
glPopMatrix();
|
||||
} else {
|
||||
glLineWidth(_lineWidth);
|
||||
|
||||
if (getIsDashedLine()) {
|
||||
glm::vec3 halfDimensions = dimensions / 2.0f;
|
||||
glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z);
|
||||
glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z);
|
||||
glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z);
|
||||
glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z);
|
||||
|
||||
glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z);
|
||||
glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z);
|
||||
glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z);
|
||||
glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
geometryCache->renderDashedLine(bottomLeftNear, bottomRightNear, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomRightNear, bottomRightFar, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomRightFar, bottomLeftFar, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomLeftFar, bottomLeftNear, cubeColor);
|
||||
|
||||
geometryCache->renderDashedLine(topLeftNear, topRightNear, cubeColor);
|
||||
geometryCache->renderDashedLine(topRightNear, topRightFar, cubeColor);
|
||||
geometryCache->renderDashedLine(topRightFar, topLeftFar, cubeColor);
|
||||
geometryCache->renderDashedLine(topLeftFar, topLeftNear, cubeColor);
|
||||
|
||||
geometryCache->renderDashedLine(bottomLeftNear, topLeftNear, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomRightNear, topRightNear, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomLeftFar, topLeftFar, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomRightFar, topRightFar, cubeColor);
|
||||
|
||||
} else {
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
DependencyManager::get<GeometryCache>()->renderWireCube(1.0f, cubeColor);
|
||||
}
|
||||
transform.setScale(dimensions);
|
||||
batch->setModelTransform(transform);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(*batch, 1.0f, cubeColor);
|
||||
}
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
}
|
||||
} else {
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
if (_isSolid) {
|
||||
if (_borderSize > 0) {
|
||||
// Draw a cube at a larger size behind the main cube, creating
|
||||
// a border effect.
|
||||
// Disable writing to the depth mask so that the "border" cube will not
|
||||
// occlude the main cube. This means the border could be covered by
|
||||
// overlays that are further back and drawn later, but this is good
|
||||
// enough for the use-case.
|
||||
glDepthMask(GL_FALSE);
|
||||
glPushMatrix();
|
||||
glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize);
|
||||
|
||||
if (_drawOnHUD) {
|
||||
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
if (_drawOnHUD) {
|
||||
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, cubeColor);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, cubeColor);
|
||||
}
|
||||
glPopMatrix();
|
||||
} else {
|
||||
glLineWidth(_lineWidth);
|
||||
|
||||
if (getIsDashedLine()) {
|
||||
glm::vec3 halfDimensions = dimensions / 2.0f;
|
||||
glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z);
|
||||
glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z);
|
||||
glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z);
|
||||
glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z);
|
||||
|
||||
glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z);
|
||||
glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z);
|
||||
glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z);
|
||||
glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
geometryCache->renderDashedLine(bottomLeftNear, bottomRightNear, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomRightNear, bottomRightFar, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomRightFar, bottomLeftFar, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomLeftFar, bottomLeftNear, cubeColor);
|
||||
|
||||
geometryCache->renderDashedLine(topLeftNear, topRightNear, cubeColor);
|
||||
geometryCache->renderDashedLine(topRightNear, topRightFar, cubeColor);
|
||||
geometryCache->renderDashedLine(topRightFar, topLeftFar, cubeColor);
|
||||
geometryCache->renderDashedLine(topLeftFar, topLeftNear, cubeColor);
|
||||
|
||||
geometryCache->renderDashedLine(bottomLeftNear, topLeftNear, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomRightNear, topRightNear, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomLeftFar, topLeftFar, cubeColor);
|
||||
geometryCache->renderDashedLine(bottomRightFar, topRightFar, cubeColor);
|
||||
|
||||
} else {
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
DependencyManager::get<GeometryCache>()->renderWireCube(1.0f, cubeColor);
|
||||
}
|
||||
}
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,87 +36,128 @@ void Grid3DOverlay::render(RenderArgs* args) {
|
|||
return; // do nothing if we're not visible
|
||||
}
|
||||
|
||||
if (!_gridProgram.isLinked()) {
|
||||
if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) {
|
||||
qDebug() << "Failed to compile: " + _gridProgram.log();
|
||||
return;
|
||||
}
|
||||
if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) {
|
||||
qDebug() << "Failed to compile: " + _gridProgram.log();
|
||||
return;
|
||||
}
|
||||
if (!_gridProgram.link()) {
|
||||
qDebug() << "Failed to link: " + _gridProgram.log();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Render code largely taken from MetavoxelEditor::render()
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glPushMatrix();
|
||||
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
glLineWidth(1.5f);
|
||||
|
||||
// center the grid around the camera position on the plane
|
||||
glm::vec3 rotated = glm::inverse(rotation) * Application::getInstance()->getCamera()->getPosition();
|
||||
float spacing = _minorGridWidth;
|
||||
|
||||
float alpha = getAlpha();
|
||||
xColor color = getColor();
|
||||
glm::vec3 position = getPosition();
|
||||
|
||||
const int MINOR_GRID_DIVISIONS = 200;
|
||||
const int MAJOR_GRID_DIVISIONS = 100;
|
||||
const float MAX_COLOR = 255.0f;
|
||||
|
||||
// center the grid around the camera position on the plane
|
||||
glm::vec3 rotated = glm::inverse(_rotation) * Application::getInstance()->getCamera()->getPosition();
|
||||
|
||||
float spacing = _minorGridWidth;
|
||||
|
||||
float alpha = getAlpha();
|
||||
xColor color = getColor();
|
||||
glm::vec4 gridColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
||||
_gridProgram.bind();
|
||||
auto batch = args->_batch;
|
||||
|
||||
// Minor grid
|
||||
glPushMatrix();
|
||||
{
|
||||
glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z);
|
||||
if (batch) {
|
||||
Transform transform;
|
||||
transform.setRotation(_rotation);
|
||||
|
||||
float scale = MINOR_GRID_DIVISIONS * spacing;
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor);
|
||||
// Minor grid
|
||||
{
|
||||
batch->_glLineWidth(1.0f);
|
||||
auto position = glm::vec3(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2),
|
||||
_position.z);
|
||||
float scale = MINOR_GRID_DIVISIONS * spacing;
|
||||
|
||||
transform.setTranslation(position);
|
||||
transform.setScale(scale);
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(*batch, MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
|
||||
// Major grid
|
||||
{
|
||||
batch->_glLineWidth(4.0f);
|
||||
spacing *= _majorGridEvery;
|
||||
auto position = glm::vec3(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2),
|
||||
_position.z);
|
||||
float scale = MAJOR_GRID_DIVISIONS * spacing;
|
||||
|
||||
transform.setTranslation(position);
|
||||
transform.setScale(scale);
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(*batch, MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
} else {
|
||||
if (!_gridProgram.isLinked()) {
|
||||
if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) {
|
||||
qDebug() << "Failed to compile: " + _gridProgram.log();
|
||||
return;
|
||||
}
|
||||
if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) {
|
||||
qDebug() << "Failed to compile: " + _gridProgram.log();
|
||||
return;
|
||||
}
|
||||
if (!_gridProgram.link()) {
|
||||
qDebug() << "Failed to link: " + _gridProgram.log();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Render code largely taken from MetavoxelEditor::render()
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glPushMatrix();
|
||||
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
glLineWidth(1.5f);
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
|
||||
_gridProgram.bind();
|
||||
|
||||
// Minor grid
|
||||
glPushMatrix();
|
||||
{
|
||||
glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z);
|
||||
|
||||
float scale = MINOR_GRID_DIVISIONS * spacing;
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
// Major grid
|
||||
glPushMatrix();
|
||||
{
|
||||
glLineWidth(4.0f);
|
||||
spacing *= _majorGridEvery;
|
||||
glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z);
|
||||
|
||||
float scale = MAJOR_GRID_DIVISIONS * spacing;
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
_gridProgram.release();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
// Major grid
|
||||
glPushMatrix();
|
||||
{
|
||||
glLineWidth(4.0f);
|
||||
spacing *= _majorGridEvery;
|
||||
glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z);
|
||||
|
||||
float scale = MAJOR_GRID_DIVISIONS * spacing;
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
_gridProgram.release();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
void Grid3DOverlay::setProperties(const QScriptValue& properties) {
|
||||
|
|
|
@ -32,46 +32,72 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
|
|||
Line3DOverlay::~Line3DOverlay() {
|
||||
}
|
||||
|
||||
AABox Line3DOverlay::getBounds() const {
|
||||
auto start = _position + _start;
|
||||
auto end = _position + _end;
|
||||
|
||||
auto min = glm::min(start, end);
|
||||
auto max = glm::max(start, end);
|
||||
|
||||
return AABox(min, max - min);
|
||||
}
|
||||
|
||||
void Line3DOverlay::render(RenderArgs* args) {
|
||||
if (!_visible) {
|
||||
return; // do nothing if we're not visible
|
||||
}
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glLineWidth(_lineWidth);
|
||||
|
||||
float alpha = getAlpha();
|
||||
xColor color = getColor();
|
||||
const float MAX_COLOR = 255.0f;
|
||||
glm::vec4 colorv4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::quat rotation = getRotation();
|
||||
auto batch = args->_batch;
|
||||
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
if (batch) {
|
||||
Transform transform;
|
||||
transform.setTranslation(_position);
|
||||
transform.setRotation(_rotation);
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
if (getIsDashedLine()) {
|
||||
// TODO: add support for color to renderDashedLine()
|
||||
DependencyManager::get<GeometryCache>()->renderDashedLine(_position, _end, colorv4, _geometryCacheID);
|
||||
if (getIsDashedLine()) {
|
||||
// TODO: add support for color to renderDashedLine()
|
||||
DependencyManager::get<GeometryCache>()->renderDashedLine(*batch, _position, _end, colorv4, _geometryCacheID);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderLine(*batch, _start, _end, colorv4, _geometryCacheID);
|
||||
}
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderLine(_start, _end, colorv4, _geometryCacheID);
|
||||
}
|
||||
glEnable(GL_LIGHTING);
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
glPushMatrix();
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
glDisable(GL_LIGHTING);
|
||||
glLineWidth(_lineWidth);
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
if (getIsDashedLine()) {
|
||||
// TODO: add support for color to renderDashedLine()
|
||||
DependencyManager::get<GeometryCache>()->renderDashedLine(_position, _end, colorv4, _geometryCacheID);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderLine(_start, _end, colorv4, _geometryCacheID);
|
||||
}
|
||||
glEnable(GL_LIGHTING);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
Line3DOverlay(const Line3DOverlay* line3DOverlay);
|
||||
~Line3DOverlay();
|
||||
virtual void render(RenderArgs* args);
|
||||
virtual AABox getBounds() const override;
|
||||
|
||||
// getters
|
||||
const glm::vec3& getStart() const { return _start; }
|
||||
|
|
|
@ -258,7 +258,7 @@ void Overlays::deleteOverlay(unsigned int id) {
|
|||
unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
||||
glm::vec2 pointCopy = point;
|
||||
if (qApp->isHMDMode()) {
|
||||
pointCopy = qApp->getApplicationOverlay().screenToOverlay(point);
|
||||
pointCopy = qApp->getApplicationCompositor().screenToOverlay(point);
|
||||
}
|
||||
|
||||
QReadLocker lock(&_lock);
|
||||
|
|
|
@ -66,8 +66,8 @@ namespace render {
|
|||
}
|
||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
|
||||
if (args) {
|
||||
glPushMatrix();
|
||||
if (overlay->getAnchor() == Overlay::MY_AVATAR) {
|
||||
glPushMatrix();
|
||||
MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
glm::quat myAvatarRotation = avatar->getOrientation();
|
||||
glm::vec3 myAvatarPosition = avatar->getPosition();
|
||||
|
@ -78,9 +78,11 @@ namespace render {
|
|||
glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z);
|
||||
glRotatef(angle, axis.x, axis.y, axis.z);
|
||||
glScalef(myAvatarScale, myAvatarScale, myAvatarScale);
|
||||
overlay->render(args);
|
||||
glPopMatrix();
|
||||
} else {
|
||||
overlay->render(args);
|
||||
}
|
||||
overlay->render(args);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,74 +41,116 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
|
|||
const float MAX_COLOR = 255.0f;
|
||||
glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 center = getCenter();
|
||||
glm::vec2 dimensions = getDimensions();
|
||||
glm::vec2 halfDimensions = dimensions * 0.5f;
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
auto batch = args->_batch;
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
//glScalef(dimensions.x, dimensions.y, 1.0f);
|
||||
if (batch) {
|
||||
Transform transform;
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(rotation);
|
||||
|
||||
glLineWidth(_lineWidth);
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
if (getIsSolid()) {
|
||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(*batch, topLeft, bottomRight, rectangleColor);
|
||||
} else {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (getIsDashedLine()) {
|
||||
glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
|
||||
// for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line...
|
||||
if (getIsSolid()) {
|
||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, rectangleColor);
|
||||
geometryCache->renderDashedLine(*batch, point1, point2, rectangleColor);
|
||||
geometryCache->renderDashedLine(*batch, point2, point3, rectangleColor);
|
||||
geometryCache->renderDashedLine(*batch, point3, point4, rectangleColor);
|
||||
geometryCache->renderDashedLine(*batch, point4, point1, rectangleColor);
|
||||
} else {
|
||||
if (getIsDashedLine()) {
|
||||
if (halfDimensions != _previousHalfDimensions) {
|
||||
QVector<glm::vec3> border;
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
geometryCache->updateVertices(_geometryCacheID, border, rectangleColor);
|
||||
|
||||
glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
|
||||
geometryCache->renderDashedLine(point1, point2, rectangleColor);
|
||||
geometryCache->renderDashedLine(point2, point3, rectangleColor);
|
||||
geometryCache->renderDashedLine(point3, point4, rectangleColor);
|
||||
geometryCache->renderDashedLine(point4, point1, rectangleColor);
|
||||
|
||||
} else {
|
||||
|
||||
if (halfDimensions != _previousHalfDimensions) {
|
||||
QVector<glm::vec3> border;
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
geometryCache->updateVertices(_geometryCacheID, border, rectangleColor);
|
||||
|
||||
_previousHalfDimensions = halfDimensions;
|
||||
|
||||
}
|
||||
geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID);
|
||||
_previousHalfDimensions = halfDimensions;
|
||||
}
|
||||
geometryCache->renderVertices(*batch, gpu::LINE_STRIP, _geometryCacheID);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
//glScalef(dimensions.x, dimensions.y, 1.0f);
|
||||
|
||||
glLineWidth(_lineWidth);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
// for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line...
|
||||
if (getIsSolid()) {
|
||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, rectangleColor);
|
||||
} else {
|
||||
if (getIsDashedLine()) {
|
||||
|
||||
glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
|
||||
geometryCache->renderDashedLine(point1, point2, rectangleColor);
|
||||
geometryCache->renderDashedLine(point2, point3, rectangleColor);
|
||||
geometryCache->renderDashedLine(point3, point4, rectangleColor);
|
||||
geometryCache->renderDashedLine(point4, point1, rectangleColor);
|
||||
|
||||
} else {
|
||||
|
||||
if (halfDimensions != _previousHalfDimensions) {
|
||||
QVector<glm::vec3> border;
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f);
|
||||
border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f);
|
||||
geometryCache->updateVertices(_geometryCacheID, border, rectangleColor);
|
||||
|
||||
_previousHalfDimensions = halfDimensions;
|
||||
|
||||
}
|
||||
geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID);
|
||||
}
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,33 +39,45 @@ void Sphere3DOverlay::render(RenderArgs* args) {
|
|||
const float MAX_COLOR = 255.0f;
|
||||
glm::vec4 sphereColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 center = getCenter();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
auto batch = args->_batch;
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
if (batch) {
|
||||
Transform transform;
|
||||
transform.setTranslation(_position);
|
||||
transform.setRotation(_rotation);
|
||||
transform.setScale(_dimensions);
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(*batch, 1.0f, SLICES, SLICES, sphereColor, _isSolid);
|
||||
} else {
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 center = getCenter();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
float glowLevel = getGlowLevel();
|
||||
Glower* glower = NULL;
|
||||
if (glowLevel > 0.0f) {
|
||||
glower = new Glower(glowLevel);
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid);
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid);
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,132 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>750</width>
|
||||
<height>213</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Update Required</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(255, 255, 255);</string>
|
||||
</property>
|
||||
<widget class="QLabel" name="updateContent">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>20</y>
|
||||
<width>641</width>
|
||||
<height>111</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
<pointsize>-1</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Arial;
|
||||
font-size: 20px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
<y>160</y>
|
||||
<width>374</width>
|
||||
<height>42</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="downloadButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #b7b7b7;
|
||||
width: 120px;
|
||||
height: 40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Download</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="skipButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #b7b7b7;
|
||||
width: 120px;
|
||||
height: 40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Skip Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #b7b7b7;
|
||||
width: 120px;
|
||||
height: 40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -33,11 +33,20 @@
|
|||
#include <QtMultimedia/QAudioInput>
|
||||
#include <QtMultimedia/QAudioOutput>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <gverb/gverb.h>
|
||||
#include <gverb/gverbdsp.h>
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <soxr.h>
|
||||
|
||||
#include <NodeList.h>
|
||||
|
@ -1011,7 +1020,10 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) {
|
|||
localOutput->moveToThread(injector->getLocalBuffer()->thread());
|
||||
|
||||
// have it be stopped when that local buffer is about to close
|
||||
connect(injector->getLocalBuffer(), &AudioInjectorLocalBuffer::bufferEmpty, localOutput, &QAudioOutput::stop);
|
||||
connect(localOutput, &QAudioOutput::stateChanged, this, &AudioClient::audioStateChanged);
|
||||
connect(this, &AudioClient::audioFinished, localOutput, &QAudioOutput::stop);
|
||||
connect(this, &AudioClient::audioFinished, injector, &AudioInjector::stop);
|
||||
|
||||
connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop);
|
||||
|
||||
qCDebug(audioclient) << "Starting QAudioOutput for local injector" << localOutput;
|
||||
|
@ -1329,3 +1341,9 @@ void AudioClient::saveSettings() {
|
|||
windowSecondsForDesiredReduction.set(_receivedAudioStream.getWindowSecondsForDesiredReduction());
|
||||
repetitionWithFade.set(_receivedAudioStream.getRepetitionWithFade());
|
||||
}
|
||||
|
||||
void AudioClient::audioStateChanged(QAudio::State state) {
|
||||
if (state == QAudio::IdleState) {
|
||||
emit audioFinished();
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue