mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 10:43:45 +02:00
Merge branch 'master' of http://github.com/highfidelity/hifi into locallights
This commit is contained in:
commit
a807e41ff4
104 changed files with 1440 additions and 971 deletions
|
@ -1,5 +1,7 @@
|
||||||
# this guide is specific to Ubuntu 16.04.
|
## This guide is specific to Ubuntu 16.04.
|
||||||
# deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
|
Deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
|
||||||
|
|
||||||
|
```
|
||||||
sudo su -
|
sudo su -
|
||||||
apt-get -y update
|
apt-get -y update
|
||||||
apt-get install -y software-properties-common
|
apt-get install -y software-properties-common
|
||||||
|
@ -8,20 +10,27 @@ add-apt-repository "deb http://debian.highfidelity.com stable main"
|
||||||
apt-get -y update
|
apt-get -y update
|
||||||
apt-get install -y hifi-domain-server
|
apt-get install -y hifi-domain-server
|
||||||
apt-get install -y hifi-assignment-client
|
apt-get install -y hifi-assignment-client
|
||||||
|
```
|
||||||
|
|
||||||
# When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to:
|
When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to:
|
||||||
|
```
|
||||||
apt-get install -y hifi-dev-domain-server
|
apt-get install -y hifi-dev-domain-server
|
||||||
apt-get install -y hifi-dev-assignment-client
|
apt-get install -y hifi-dev-assignment-client
|
||||||
|
```
|
||||||
|
|
||||||
# domain server and assignment clients should already be running. The processes are controlled via:
|
Domain server and assignment clients should already be running. The processes are controlled via:
|
||||||
|
```
|
||||||
systemctl start hifi-domain-server
|
systemctl start hifi-domain-server
|
||||||
systemctl stop hifi-domain-server
|
systemctl stop hifi-domain-server
|
||||||
|
```
|
||||||
|
|
||||||
# Once the machine is setup and processes are running you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (further customizations can be done via http://IPAddress:40100).
|
Once the machine is setup and processes are running, you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (Further customizations can be done via http://IPAddress:40100).
|
||||||
|
|
||||||
# The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time.
|
The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time.
|
||||||
# As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly).
|
As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly).
|
||||||
To do this you can modify /etc/crontab by adding the following lines
|
To do this you can modify /etc/crontab by adding the following lines
|
||||||
|
```
|
||||||
0 */1 * * * root apt-get update
|
0 */1 * * * root apt-get update
|
||||||
1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server
|
1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server
|
||||||
2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client
|
2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client
|
||||||
|
```
|
||||||
|
|
|
@ -343,7 +343,6 @@ void Agent::scriptRequestFinished() {
|
||||||
|
|
||||||
void Agent::executeScript() {
|
void Agent::executeScript() {
|
||||||
_scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload);
|
_scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload);
|
||||||
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
|
|
||||||
|
|
||||||
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine);
|
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine);
|
||||||
|
|
||||||
|
|
21
interface/resources/icons/tablet-icons/EmoteAppIcon.svg
Normal file
21
interface/resources/icons/tablet-icons/EmoteAppIcon.svg
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 100 100.8" style="enable-background:new 0 0 100 100.8;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<path class="st0" d="M26.7,83.9c7.3,1.2,14.8,1.8,22.1,1.8c0.4,0,0.8,0,1.2,0c7.8-0.1,15.6-0.8,23.4-2.2l0,0
|
||||||
|
c5.7-1.1,11.3-6.6,12.5-12.3C87.3,64.2,88,57,88,50s-0.7-14.2-2.1-21.2c-1.2-5.6-6.8-11.1-12.5-12.2c-7.7-1.4-15.6-2.2-23.4-2.2
|
||||||
|
c-7.7-0.1-15.6,0.5-23.4,1.8c-5.7,1-11.4,6.5-12.6,12.3c-1.4,7.2-2.1,14.4-2.1,21.6s0.7,14.4,2.1,21.7
|
||||||
|
C15.3,77.4,20.9,82.9,26.7,83.9z M20.9,29.8c0.6-2.9,4-6.3,6.9-6.8c7-1.1,14-1.7,21-1.7c0.4,0,0.8,0,1.2,0
|
||||||
|
c7.4,0.1,14.8,0.8,22.1,2.1c2.9,0.6,6.4,3.9,6.9,6.7c1.3,6.6,1.9,13.3,1.9,19.9c0,6.6-0.6,13.3-1.9,19.8c-0.6,2.8-4,6.2-6.9,6.8
|
||||||
|
c-7.3,1.3-14.8,2.1-22.1,2.1c-7.4,0.1-14.8-0.5-22.1-1.7c-2.9-0.5-6.3-3.9-6.9-6.7c-1.3-6.7-2-13.5-2-20.3
|
||||||
|
C19,43.3,19.6,36.4,20.9,29.8z"/>
|
||||||
|
<path class="st0" d="M32.3,61.4c-0.5,1.3-0.1,2.8,0.9,3.8c0.3,0.3,7.2,6.6,15.9,6.6c0.8,0,1.7-0.1,2.6-0.2
|
||||||
|
c9.8-1.5,15.5-11.1,15.8-11.5c0.7-1.2,0.6-2.8-0.2-3.9c-0.9-1.1-2.3-1.6-3.7-1.3c-9.2,2.5-18.6,3.9-28.1,4.2
|
||||||
|
C34,59.1,32.8,60,32.3,61.4z"/>
|
||||||
|
<circle class="st0" cx="36.5" cy="42.8" r="9"/>
|
||||||
|
<path class="st0" d="M61.4,44.1h6.1c1.9,0,3.3-1.5,3.3-3.3c0-1.9-1.5-3.3-3.3-3.3h-6.1c-1.9,0-3.3,1.5-3.3,3.3
|
||||||
|
C58.1,42.7,59.6,44.1,61.4,44.1z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
BIN
interface/resources/images/preview-privacy.png
Normal file
BIN
interface/resources/images/preview-privacy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
|
@ -102,7 +102,7 @@ Column {
|
||||||
'include_actions=' + actions,
|
'include_actions=' + actions,
|
||||||
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
||||||
'require_online=true',
|
'require_online=true',
|
||||||
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
|
'protocol=' + encodeURIComponent(Window.protocolSignature()),
|
||||||
'page=' + pageNumber
|
'page=' + pageNumber
|
||||||
];
|
];
|
||||||
var url = metaverseBase + 'user_stories?' + options.join('&');
|
var url = metaverseBase + 'user_stories?' + options.join('&');
|
||||||
|
|
|
@ -392,7 +392,6 @@ Item {
|
||||||
width: 118;
|
width: 118;
|
||||||
height: paintedHeight;
|
height: paintedHeight;
|
||||||
wrapMode: Text.WordWrap;
|
wrapMode: Text.WordWrap;
|
||||||
font.bold: true;
|
|
||||||
// Alignment
|
// Alignment
|
||||||
horizontalAlignment: Text.AlignRight;
|
horizontalAlignment: Text.AlignRight;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import "."
|
||||||
Overlay {
|
Overlay {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
Image {
|
AnimatedImage {
|
||||||
id: image
|
id: image
|
||||||
property bool scaleFix: true
|
property bool scaleFix: true
|
||||||
property real xStart: 0
|
property real xStart: 0
|
||||||
|
|
|
@ -209,6 +209,7 @@
|
||||||
#include "commerce/QmlCommerce.h"
|
#include "commerce/QmlCommerce.h"
|
||||||
|
|
||||||
#include "webbrowser/WebBrowserSuggestionsEngine.h"
|
#include "webbrowser/WebBrowserSuggestionsEngine.h"
|
||||||
|
#include <DesktopPreviewProvider.h>
|
||||||
|
|
||||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||||
// FIXME seems to be broken.
|
// FIXME seems to be broken.
|
||||||
|
@ -573,8 +574,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reportAndQuit("--protocolVersion", [&](FILE* fp) {
|
reportAndQuit("--protocolVersion", [&](FILE* fp) {
|
||||||
DependencyManager::set<AddressManager>();
|
auto version = protocolVersionsSignatureBase64();
|
||||||
auto version = DependencyManager::get<AddressManager>()->protocolVersion();
|
|
||||||
fputs(version.toLatin1().data(), fp);
|
fputs(version.toLatin1().data(), fp);
|
||||||
});
|
});
|
||||||
reportAndQuit("--version", [&](FILE* fp) {
|
reportAndQuit("--version", [&](FILE* fp) {
|
||||||
|
@ -632,6 +632,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
||||||
DependencyManager::set<PointerScriptingInterface>();
|
DependencyManager::set<PointerScriptingInterface>();
|
||||||
DependencyManager::set<PickScriptingInterface>();
|
DependencyManager::set<PickScriptingInterface>();
|
||||||
DependencyManager::set<Cursor::Manager>();
|
DependencyManager::set<Cursor::Manager>();
|
||||||
|
DependencyManager::set<DesktopPreviewProvider>();
|
||||||
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
|
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
|
||||||
DependencyManager::set<StatTracker>();
|
DependencyManager::set<StatTracker>();
|
||||||
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
|
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
|
||||||
|
@ -2093,6 +2094,11 @@ void Application::cleanupBeforeQuit() {
|
||||||
DependencyManager::destroy<AudioInjectorManager>();
|
DependencyManager::destroy<AudioInjectorManager>();
|
||||||
DependencyManager::destroy<AudioScriptingInterface>();
|
DependencyManager::destroy<AudioScriptingInterface>();
|
||||||
|
|
||||||
|
// The PointerManager must be destroyed before the PickManager because when a Pointer is deleted,
|
||||||
|
// it accesses the PickManager to delete its associated Pick
|
||||||
|
DependencyManager::destroy<PointerManager>();
|
||||||
|
DependencyManager::destroy<PickManager>();
|
||||||
|
|
||||||
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
|
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4351,8 +4357,9 @@ void Application::updateLOD(float deltaTime) const {
|
||||||
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
|
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
|
||||||
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
|
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
|
||||||
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
|
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
|
||||||
float maxRenderTime = glm::max(gpuTime, glm::max(presentTime, engineRunTime));
|
auto lodManager = DependencyManager::get<LODManager>();
|
||||||
DependencyManager::get<LODManager>()->autoAdjustLOD(maxRenderTime, deltaTime);
|
lodManager->setRenderTimes(presentTime, engineRunTime, gpuTime);
|
||||||
|
lodManager->autoAdjustLOD(deltaTime);
|
||||||
} else {
|
} else {
|
||||||
DependencyManager::get<LODManager>()->resetLODAdjust();
|
DependencyManager::get<LODManager>()->resetLODAdjust();
|
||||||
}
|
}
|
||||||
|
@ -5758,6 +5765,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
||||||
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
|
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
|
||||||
|
|
||||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||||
|
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());
|
||||||
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
||||||
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
||||||
scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get<Snapshot>().data());
|
scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get<Snapshot>().data());
|
||||||
|
|
|
@ -26,43 +26,50 @@ Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DO
|
||||||
LODManager::LODManager() {
|
LODManager::LODManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float LODManager::getLODDecreaseFPS() {
|
float LODManager::getLODDecreaseFPS() const {
|
||||||
if (qApp->isHMDMode()) {
|
if (qApp->isHMDMode()) {
|
||||||
return getHMDLODDecreaseFPS();
|
return getHMDLODDecreaseFPS();
|
||||||
}
|
}
|
||||||
return getDesktopLODDecreaseFPS();
|
return getDesktopLODDecreaseFPS();
|
||||||
}
|
}
|
||||||
|
|
||||||
float LODManager::getLODIncreaseFPS() {
|
float LODManager::getLODIncreaseFPS() const {
|
||||||
if (qApp->isHMDMode()) {
|
if (qApp->isHMDMode()) {
|
||||||
return getHMDLODIncreaseFPS();
|
return getHMDLODIncreaseFPS();
|
||||||
}
|
}
|
||||||
return getDesktopLODIncreaseFPS();
|
return getDesktopLODIncreaseFPS();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use a "time-weighted running average" of the renderTime and compare it against min/max thresholds
|
// We use a "time-weighted running average" of the maxRenderTime and compare it against min/max thresholds
|
||||||
// to determine if we should adjust the level of detail (LOD).
|
// to determine if we should adjust the level of detail (LOD).
|
||||||
//
|
//
|
||||||
// A time-weighted running average has a timescale which determines how fast the average tracks the measured
|
// A time-weighted running average has a timescale which determines how fast the average tracks the measured
|
||||||
// value in real-time. Given a step-function in the mesured value, and assuming measurements happen
|
// value in real-time. Given a step-function in the mesured value, and assuming measurements happen
|
||||||
// faster than the runningAverage is computed, the error between the value and its runningAverage will be
|
// faster than the runningAverage is computed, the error between the value and its runningAverage will be
|
||||||
// reduced by 1/e every timescale of real-time that passes.
|
// reduced by 1/e every timescale of real-time that passes.
|
||||||
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.1f; // sec
|
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.08f; // sec
|
||||||
//
|
//
|
||||||
// Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its
|
// Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its
|
||||||
// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage to settle
|
// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage to settle
|
||||||
// to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few
|
// to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few
|
||||||
// multiples of the running average timescale:
|
// multiples of the running average timescale:
|
||||||
const uint64_t LOD_AUTO_ADJUST_PERIOD = 5 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
|
const uint64_t LOD_AUTO_ADJUST_PERIOD = 4 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
|
||||||
|
|
||||||
const float LOD_AUTO_ADJUST_DECREMENT_FACTOR = 0.8f;
|
const float LOD_AUTO_ADJUST_DECREMENT_FACTOR = 0.8f;
|
||||||
const float LOD_AUTO_ADJUST_INCREMENT_FACTOR = 1.2f;
|
const float LOD_AUTO_ADJUST_INCREMENT_FACTOR = 1.2f;
|
||||||
|
|
||||||
void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
void LODManager::setRenderTimes(float presentTime, float engineRunTime, float gpuTime) {
|
||||||
// compute time-weighted running average renderTime
|
_presentTime = presentTime;
|
||||||
|
_engineRunTime = engineRunTime;
|
||||||
|
_gpuTime = gpuTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LODManager::autoAdjustLOD(float realTimeDelta) {
|
||||||
|
float maxRenderTime = glm::max(glm::max(_presentTime, _engineRunTime), _gpuTime);
|
||||||
|
// compute time-weighted running average maxRenderTime
|
||||||
// Note: we MUST clamp the blend to 1.0 for stability
|
// Note: we MUST clamp the blend to 1.0 for stability
|
||||||
float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
|
float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
|
||||||
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * renderTime; // msec
|
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxRenderTime; // msec
|
||||||
if (!_automaticLODAdjust) {
|
if (!_automaticLODAdjust) {
|
||||||
// early exit
|
// early exit
|
||||||
return;
|
return;
|
||||||
|
@ -84,6 +91,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
||||||
<< "targetFPS =" << getLODDecreaseFPS()
|
<< "targetFPS =" << getLODDecreaseFPS()
|
||||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||||
emit LODDecreased();
|
emit LODDecreased();
|
||||||
|
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||||
|
// to provide an FPS just above the decrease threshold. It will drift close to its
|
||||||
|
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
|
||||||
|
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODDecreaseFPS() + 1.0f);
|
||||||
}
|
}
|
||||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||||
}
|
}
|
||||||
|
@ -105,6 +116,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
||||||
<< "targetFPS =" << getLODDecreaseFPS()
|
<< "targetFPS =" << getLODDecreaseFPS()
|
||||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||||
emit LODIncreased();
|
emit LODIncreased();
|
||||||
|
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||||
|
// to provide an FPS just below the increase threshold. It will drift close to its
|
||||||
|
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
|
||||||
|
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODIncreaseFPS() - 1.0f);
|
||||||
}
|
}
|
||||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||||
}
|
}
|
||||||
|
@ -119,11 +134,6 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
||||||
if (lodToolsDialog) {
|
if (lodToolsDialog) {
|
||||||
lodToolsDialog->reloadSliders();
|
lodToolsDialog->reloadSliders();
|
||||||
}
|
}
|
||||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
|
||||||
// to be at middle of target zone. It will drift close to its true value within
|
|
||||||
// about three few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
|
|
||||||
float expectedFPS = 0.5f * (getLODIncreaseFPS() + getLODDecreaseFPS());
|
|
||||||
_avgRenderTime = MSECS_PER_SECOND / expectedFPS;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +141,18 @@ void LODManager::resetLODAdjust() {
|
||||||
_decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD;
|
_decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float LODManager::getLODLevel() const {
|
||||||
|
// simpleLOD is a linearized and normalized number that represents how much LOD is being applied.
|
||||||
|
// It ranges from:
|
||||||
|
// 1.0 = normal (max) level of detail
|
||||||
|
// 0.0 = min level of detail
|
||||||
|
// In other words: as LOD "drops" the value of simpleLOD will also "drop", and it cannot go lower than 0.0.
|
||||||
|
const float LOG_MIN_LOD_RATIO = logf(ADJUST_LOD_MIN_SIZE_SCALE / ADJUST_LOD_MAX_SIZE_SCALE);
|
||||||
|
float power = logf(_octreeSizeScale / ADJUST_LOD_MAX_SIZE_SCALE);
|
||||||
|
float simpleLOD = (LOG_MIN_LOD_RATIO - power) / LOG_MIN_LOD_RATIO;
|
||||||
|
return simpleLOD;
|
||||||
|
}
|
||||||
|
|
||||||
const float MIN_DECREASE_FPS = 0.5f;
|
const float MIN_DECREASE_FPS = 0.5f;
|
||||||
|
|
||||||
void LODManager::setDesktopLODDecreaseFPS(float fps) {
|
void LODManager::setDesktopLODDecreaseFPS(float fps) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ class AABox;
|
||||||
class LODManager : public QObject, public Dependency {
|
class LODManager : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
|
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
|
||||||
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
|
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
|
||||||
|
@ -49,34 +49,56 @@ public:
|
||||||
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
|
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
|
||||||
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
|
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
|
||||||
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
|
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
|
||||||
|
|
||||||
// User Tweakable LOD Items
|
// User Tweakable LOD Items
|
||||||
Q_INVOKABLE QString getLODFeedbackText();
|
Q_INVOKABLE QString getLODFeedbackText();
|
||||||
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
|
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
|
||||||
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
|
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
|
||||||
|
|
||||||
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
||||||
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||||
|
|
||||||
Q_INVOKABLE float getLODDecreaseFPS();
|
Q_INVOKABLE float getLODDecreaseFPS() const;
|
||||||
Q_INVOKABLE float getLODIncreaseFPS();
|
Q_INVOKABLE float getLODIncreaseFPS() const;
|
||||||
|
|
||||||
|
Q_PROPERTY(float presentTime READ getPresentTime)
|
||||||
|
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
|
||||||
|
Q_PROPERTY(float gpuTime READ getGPUTime)
|
||||||
|
Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
|
||||||
|
Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
|
||||||
|
Q_PROPERTY(float lodLevel READ getLODLevel)
|
||||||
|
|
||||||
|
Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
|
||||||
|
Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
|
||||||
|
|
||||||
|
float getPresentTime() const { return _presentTime; }
|
||||||
|
float getEngineRunTime() const { return _engineRunTime; }
|
||||||
|
float getGPUTime() const { return _gpuTime; }
|
||||||
|
|
||||||
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
|
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
|
||||||
void autoAdjustLOD(float renderTime, float realTimeDelta);
|
void setRenderTimes(float presentTime, float engineRunTime, float gpuTime);
|
||||||
|
void autoAdjustLOD(float realTimeDelta);
|
||||||
|
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
void resetLODAdjust();
|
void resetLODAdjust();
|
||||||
|
|
||||||
|
float getAverageRenderTime() const { return _avgRenderTime; };
|
||||||
|
float getMaxTheoreticalFPS() const { return (float)MSECS_PER_SECOND / _avgRenderTime; };
|
||||||
|
float getLODLevel() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void LODIncreased();
|
void LODIncreased();
|
||||||
void LODDecreased();
|
void LODDecreased();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LODManager();
|
LODManager();
|
||||||
|
|
||||||
bool _automaticLODAdjust = true;
|
bool _automaticLODAdjust = true;
|
||||||
float _avgRenderTime { 0.0f };
|
float _presentTime { 0.0f }; // msec
|
||||||
|
float _engineRunTime { 0.0f }; // msec
|
||||||
|
float _gpuTime { 0.0f }; // msec
|
||||||
|
float _avgRenderTime { 0.0f }; // msec
|
||||||
float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME };
|
float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME };
|
||||||
float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };
|
float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "Ledger.h"
|
#include "Ledger.h"
|
||||||
#include "CommerceLogging.h"
|
#include "CommerceLogging.h"
|
||||||
#include <NetworkingConstants.h>
|
#include <NetworkingConstants.h>
|
||||||
|
#include <AddressManager.h>
|
||||||
|
|
||||||
// inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}}
|
// inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}}
|
||||||
// balance answers {status: 'success', data: {balance: integer}}
|
// balance answers {status: 'success', data: {balance: integer}}
|
||||||
|
@ -122,24 +123,33 @@ QString hfcString(const QJsonValue& sentValue, const QJsonValue& receivedValue)
|
||||||
int sent = sentValue.toInt();
|
int sent = sentValue.toInt();
|
||||||
int received = receivedValue.toInt();
|
int received = receivedValue.toInt();
|
||||||
if (sent <= 0 && received <= 0) {
|
if (sent <= 0 && received <= 0) {
|
||||||
return QString("-");
|
return QString("0 HFC");
|
||||||
}
|
}
|
||||||
QString result;
|
QString result;
|
||||||
if (sent > 0) {
|
if (sent > 0) {
|
||||||
result += QString("<font color='#B70A37'>-%1 HFC</font>").arg(sent);
|
result += QString("<font color='#B70A37'><b>-%1 HFC</b></font>").arg(sent);
|
||||||
if (received > 0) {
|
if (received > 0) {
|
||||||
result += QString("<br>");
|
result += QString("<br>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (received > 0) {
|
if (received > 0) {
|
||||||
result += QString("<font color='#3AA38F'>%1 HFC</font>").arg(received);
|
result += QString("<font color='#3AA38F'><b>%1 HFC</b></font>").arg(received);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
static const QString USER_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/users/";
|
static const QString USER_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/users/";
|
||||||
QString userLink(const QString& username) {
|
static const QString PLACE_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/places/";
|
||||||
|
static const QStringList KNOWN_USERS(QStringList() << "highfidelity" << "marketplace");
|
||||||
|
QString userLink(const QString& username, const QString& placename) {
|
||||||
if (username.isEmpty()) {
|
if (username.isEmpty()) {
|
||||||
return QString("someone");
|
if (placename.isEmpty()) {
|
||||||
|
return QString("someone");
|
||||||
|
} else {
|
||||||
|
return QString("someone <a href=\"%1%2\">nearby</a>").arg(PLACE_PAGE_BASE_URL, placename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (KNOWN_USERS.contains(username)) {
|
||||||
|
return username;
|
||||||
}
|
}
|
||||||
return QString("<a href=\"%1%2\">%2</a>").arg(USER_PAGE_BASE_URL, username);
|
return QString("<a href=\"%1%2\">%2</a>").arg(USER_PAGE_BASE_URL, username);
|
||||||
}
|
}
|
||||||
|
@ -153,13 +163,13 @@ QString transactionString(const QJsonObject& valueObject) {
|
||||||
QDateTime createdAt(QDateTime::fromSecsSinceEpoch(dateInteger, Qt::UTC));
|
QDateTime createdAt(QDateTime::fromSecsSinceEpoch(dateInteger, Qt::UTC));
|
||||||
QString result;
|
QString result;
|
||||||
|
|
||||||
if (sentCerts <= 0 && receivedCerts <= 0) {
|
if (sentCerts <= 0 && receivedCerts <= 0 && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) {
|
||||||
// this is an hfc transfer.
|
// this is an hfc transfer.
|
||||||
if (sent > 0) {
|
if (sent > 0) {
|
||||||
QString recipient = userLink(valueObject["recipient_name"].toString());
|
QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString());
|
||||||
result += QString("Money sent to %1").arg(recipient);
|
result += QString("Money sent to %1").arg(recipient);
|
||||||
} else {
|
} else {
|
||||||
QString sender = userLink(valueObject["sender_name"].toString());
|
QString sender = userLink(valueObject["sender_name"].toString(), valueObject["place_name"].toString());
|
||||||
result += QString("Money from %1").arg(sender);
|
result += QString("Money from %1").arg(sender);
|
||||||
}
|
}
|
||||||
if (!message.isEmpty()) {
|
if (!message.isEmpty()) {
|
||||||
|
@ -168,8 +178,8 @@ QString transactionString(const QJsonObject& valueObject) {
|
||||||
} else {
|
} else {
|
||||||
result += valueObject["message"].toString();
|
result += valueObject["message"].toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// no matter what we append a smaller date to the bottom of this...
|
// no matter what we append a smaller date to the bottom of this...
|
||||||
|
|
||||||
result += QString("<br><font size='-2' color='#1080B8'>%1").arg(createdAt.toLocalTime().toString(Qt::DefaultLocaleShortDate));
|
result += QString("<br><font size='-2' color='#1080B8'>%1").arg(createdAt.toLocalTime().toString(Qt::DefaultLocaleShortDate));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -310,6 +320,7 @@ void Ledger::transferHfcToNode(const QString& hfc_key, const QString& nodeID, co
|
||||||
transaction["node_id"] = nodeID;
|
transaction["node_id"] = nodeID;
|
||||||
transaction["quantity"] = amount;
|
transaction["quantity"] = amount;
|
||||||
transaction["message"] = optionalMessage;
|
transaction["message"] = optionalMessage;
|
||||||
|
transaction["place_name"] = DependencyManager::get<AddressManager>()->getPlaceName();
|
||||||
QJsonDocument transactionDoc{ transaction };
|
QJsonDocument transactionDoc{ transaction };
|
||||||
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||||
signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_node", "transferHfcToNodeSuccess", "transferHfcToNodeFailure");
|
signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_node", "transferHfcToNodeSuccess", "transferHfcToNodeFailure");
|
||||||
|
|
|
@ -175,4 +175,8 @@ QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const
|
||||||
result = pickResult->toVariantMap();
|
result = pickResult->toVariantMap();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PointerScriptingInterface::setDoesHover(unsigned int uid, bool hover) const {
|
||||||
|
DependencyManager::get<PointerManager>()->setDoesHover(uid, hover);
|
||||||
|
}
|
||||||
|
|
|
@ -202,6 +202,14 @@ public:
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); }
|
Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); }
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Sets whether or not a pointer should generate hover events.
|
||||||
|
* @function Pointers.setDoesHover
|
||||||
|
* @param {boolean} uid - The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||||
|
* @param {boolean} hover - If <code>true</code> then the pointer generates hover events, otherwise it does not.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void setDoesHover(unsigned int uid, bool hove) const;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Check if a Pointer is associated with the left hand.
|
* Check if a Pointer is associated with the left hand.
|
||||||
* @function Pointers.isLeftHand
|
* @function Pointers.isLeftHand
|
||||||
|
|
|
@ -277,7 +277,8 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString&
|
||||||
if (!result.isEmpty()) {
|
if (!result.isEmpty()) {
|
||||||
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
|
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
|
||||||
}
|
}
|
||||||
emit openFileChanged(result);
|
emit browseChanged(result);
|
||||||
|
emit openFileChanged(result); // Deprecated signal; to be removed in due course.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,6 +391,10 @@ QString WindowScriptingInterface::checkVersion() {
|
||||||
return QCoreApplication::applicationVersion();
|
return QCoreApplication::applicationVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString WindowScriptingInterface::protocolSignature() {
|
||||||
|
return protocolVersionsSignatureBase64();
|
||||||
|
}
|
||||||
|
|
||||||
int WindowScriptingInterface::getInnerWidth() {
|
int WindowScriptingInterface::getInnerWidth() {
|
||||||
return qApp->getDeviceSize().x;
|
return qApp->getDeviceSize().x;
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,18 +197,19 @@ public slots:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Prompt the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A
|
* Prompt the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A
|
||||||
* {@link Window.openFileChanged|openFileChanged} signal is emitted when a file is chosen; no signal is emitted if the user
|
* {@link Window.browseChanged|browseChanged} signal is emitted when a file is chosen; no signal is emitted if the user
|
||||||
* cancels the dialog.
|
* cancels the dialog.
|
||||||
|
* @deprecated A deprecated {@link Window.openFileChanged|openFileChanged} signal is also emitted when a file is chosen.
|
||||||
* @function Window.browseAsync
|
* @function Window.browseAsync
|
||||||
* @param {string} title="" - The title to display at the top of the dialog.
|
* @param {string} title="" - The title to display at the top of the dialog.
|
||||||
* @param {string} directory="" - The initial directory to start browsing at.
|
* @param {string} directory="" - The initial directory to start browsing at.
|
||||||
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
|
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
|
||||||
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
|
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
|
||||||
* @example <caption>Ask the user to choose an image file without waiting for the answer.</caption>
|
* @example <caption>Ask the user to choose an image file without waiting for the answer.</caption>
|
||||||
* function onOpenFileChanged(filename) {
|
* function onBrowseChanged(filename) {
|
||||||
* print("File: " + filename);
|
* print("File: " + filename);
|
||||||
* }
|
* }
|
||||||
* Window.openFileChanged.connect(onOpenFileChanged);
|
* Window.browseChanged.connect(onBrowseChanged);
|
||||||
*
|
*
|
||||||
* Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)");
|
* Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)");
|
||||||
* print("Script continues without waiting");
|
* print("Script continues without waiting");
|
||||||
|
@ -305,6 +306,13 @@ public slots:
|
||||||
*/
|
*/
|
||||||
QString checkVersion();
|
QString checkVersion();
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Get the signature for Interface's protocol version.
|
||||||
|
* @function Window.protocolSignature
|
||||||
|
* @returns {string} A string uniquely identifying the version of the metaverse protocol that Interface is using.
|
||||||
|
*/
|
||||||
|
QString protocolSignature();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Copies text to the operating system's clipboard.
|
* Copies text to the operating system's clipboard.
|
||||||
* @function Window.copyToClipboard
|
* @function Window.copyToClipboard
|
||||||
|
@ -652,9 +660,18 @@ signals:
|
||||||
*/
|
*/
|
||||||
void saveFileChanged(QString filename);
|
void saveFileChanged(QString filename);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
|
||||||
|
* @function Window.browseChanged
|
||||||
|
* @param {string} filename - The path and name of the file the user chose in the dialog.
|
||||||
|
* @returns {Signal}
|
||||||
|
*/
|
||||||
|
void browseChanged(QString filename);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
|
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
|
||||||
* @function Window.openFileChanged
|
* @function Window.openFileChanged
|
||||||
|
* @deprecated This signal is being replaced with {@link Window.browseChanged|browseChanged} and will be removed.
|
||||||
* @param {string} filename - The path and name of the file the user chose in the dialog.
|
* @param {string} filename - The path and name of the file the user chose in the dialog.
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -386,8 +386,6 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -154,8 +154,6 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -132,8 +132,6 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -208,8 +208,6 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -275,8 +275,6 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -306,8 +306,6 @@ vectorType ModelOverlay::mapJoints(mapFunction<itemType> function) const {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -31,8 +31,7 @@ Overlay::Overlay() :
|
||||||
_alphaPulse(0.0f),
|
_alphaPulse(0.0f),
|
||||||
_colorPulse(0.0f),
|
_colorPulse(0.0f),
|
||||||
_color(DEFAULT_OVERLAY_COLOR),
|
_color(DEFAULT_OVERLAY_COLOR),
|
||||||
_visible(true),
|
_visible(true)
|
||||||
_anchor(NO_ANCHOR)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,8 +48,7 @@ Overlay::Overlay(const Overlay* overlay) :
|
||||||
_alphaPulse(overlay->_alphaPulse),
|
_alphaPulse(overlay->_alphaPulse),
|
||||||
_colorPulse(overlay->_colorPulse),
|
_colorPulse(overlay->_colorPulse),
|
||||||
_color(overlay->_color),
|
_color(overlay->_color),
|
||||||
_visible(overlay->_visible),
|
_visible(overlay->_visible)
|
||||||
_anchor(overlay->_anchor)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,13 +90,6 @@ void Overlay::setProperties(const QVariantMap& properties) {
|
||||||
bool visible = properties["visible"].toBool();
|
bool visible = properties["visible"].toBool();
|
||||||
setVisible(visible);
|
setVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties["anchor"].isValid()) {
|
|
||||||
QString property = properties["anchor"].toString();
|
|
||||||
if (property == "MyAvatar") {
|
|
||||||
setAnchor(MY_AVATAR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSDoc for copying to @typedefs of overlay types that inherit Overlay.
|
// JSDoc for copying to @typedefs of overlay types that inherit Overlay.
|
||||||
|
@ -119,8 +110,6 @@ void Overlay::setProperties(const QVariantMap& properties) {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*/
|
*/
|
||||||
QVariant Overlay::getProperty(const QString& property) {
|
QVariant Overlay::getProperty(const QString& property) {
|
||||||
if (property == "type") {
|
if (property == "type") {
|
||||||
|
@ -150,9 +139,6 @@ QVariant Overlay::getProperty(const QString& property) {
|
||||||
if (property == "visible") {
|
if (property == "visible") {
|
||||||
return _visible;
|
return _visible;
|
||||||
}
|
}
|
||||||
if (property == "anchor") {
|
|
||||||
return _anchor == MY_AVATAR ? "MyAvatar" : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,6 @@ class Overlay : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Anchor {
|
|
||||||
NO_ANCHOR,
|
|
||||||
MY_AVATAR
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::shared_ptr<Overlay> Pointer;
|
typedef std::shared_ptr<Overlay> Pointer;
|
||||||
typedef render::Payload<Overlay> Payload;
|
typedef render::Payload<Overlay> Payload;
|
||||||
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
|
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
|
||||||
|
@ -63,7 +58,6 @@ public:
|
||||||
virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; };
|
virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; };
|
||||||
xColor getColor();
|
xColor getColor();
|
||||||
float getAlpha();
|
float getAlpha();
|
||||||
Anchor getAnchor() const { return _anchor; }
|
|
||||||
|
|
||||||
float getPulseMax() const { return _pulseMax; }
|
float getPulseMax() const { return _pulseMax; }
|
||||||
float getPulseMin() const { return _pulseMin; }
|
float getPulseMin() const { return _pulseMin; }
|
||||||
|
@ -78,7 +72,6 @@ public:
|
||||||
void setDrawHUDLayer(bool drawHUDLayer);
|
void setDrawHUDLayer(bool drawHUDLayer);
|
||||||
void setColor(const xColor& color) { _color = color; }
|
void setColor(const xColor& color) { _color = color; }
|
||||||
void setAlpha(float alpha) { _alpha = alpha; }
|
void setAlpha(float alpha) { _alpha = alpha; }
|
||||||
void setAnchor(Anchor anchor) { _anchor = anchor; }
|
|
||||||
|
|
||||||
void setPulseMax(float value) { _pulseMax = value; }
|
void setPulseMax(float value) { _pulseMax = value; }
|
||||||
void setPulseMin(float value) { _pulseMin = value; }
|
void setPulseMin(float value) { _pulseMin = value; }
|
||||||
|
@ -118,7 +111,6 @@ protected:
|
||||||
|
|
||||||
xColor _color;
|
xColor _color;
|
||||||
bool _visible; // should the overlay be drawn at all
|
bool _visible; // should the overlay be drawn at all
|
||||||
Anchor _anchor;
|
|
||||||
|
|
||||||
unsigned int _stackOrder { 0 };
|
unsigned int _stackOrder { 0 };
|
||||||
|
|
||||||
|
|
|
@ -1,190 +0,0 @@
|
||||||
//
|
|
||||||
// OverlayPanel.cpp
|
|
||||||
// interface/src/ui/overlays
|
|
||||||
//
|
|
||||||
// Created by Zander Otavka on 7/2/15.
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "OverlayPanel.h"
|
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
|
||||||
|
|
||||||
#include <QVariant>
|
|
||||||
#include <RegisteredMetaTypes.h>
|
|
||||||
#include <DependencyManager.h>
|
|
||||||
#include <EntityScriptingInterface.h>
|
|
||||||
|
|
||||||
#include "avatar/AvatarManager.h"
|
|
||||||
#include "avatar/MyAvatar.h"
|
|
||||||
#include "Base3DOverlay.h"
|
|
||||||
|
|
||||||
PropertyBinding::PropertyBinding(QString avatar, QUuid entity) :
|
|
||||||
avatar(avatar),
|
|
||||||
entity(entity)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant propertyBindingToVariant(const PropertyBinding& value) {
|
|
||||||
QVariantMap obj;
|
|
||||||
|
|
||||||
if (value.avatar == "MyAvatar") {
|
|
||||||
obj["avatar"] = "MyAvatar";
|
|
||||||
} else if (!value.entity.isNull()) {
|
|
||||||
obj["entity"] = value.entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
void propertyBindingFromVariant(const QVariant& objectVar, PropertyBinding& value) {
|
|
||||||
auto object = objectVar.toMap();
|
|
||||||
auto avatar = object["avatar"];
|
|
||||||
auto entity = object["entity"];
|
|
||||||
|
|
||||||
if (avatar.isValid() && !avatar.isNull()) {
|
|
||||||
value.avatar = avatar.toString();
|
|
||||||
} else if (entity.isValid() && !entity.isNull()) {
|
|
||||||
value.entity = entity.toUuid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void OverlayPanel::addChild(OverlayID childId) {
|
|
||||||
if (!_children.contains(childId)) {
|
|
||||||
_children.append(childId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OverlayPanel::removeChild(OverlayID childId) {
|
|
||||||
if (_children.contains(childId)) {
|
|
||||||
_children.removeOne(childId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant OverlayPanel::getProperty(const QString &property) {
|
|
||||||
if (property == "anchorPosition") {
|
|
||||||
return vec3toVariant(getAnchorPosition());
|
|
||||||
}
|
|
||||||
if (property == "anchorPositionBinding") {
|
|
||||||
return propertyBindingToVariant(PropertyBinding(_anchorPositionBindMyAvatar ?
|
|
||||||
"MyAvatar" : "",
|
|
||||||
_anchorPositionBindEntity));
|
|
||||||
}
|
|
||||||
if (property == "anchorRotation") {
|
|
||||||
return quatToVariant(getAnchorRotation());
|
|
||||||
}
|
|
||||||
if (property == "anchorRotationBinding") {
|
|
||||||
return propertyBindingToVariant(PropertyBinding(_anchorRotationBindMyAvatar ?
|
|
||||||
"MyAvatar" : "",
|
|
||||||
_anchorRotationBindEntity));
|
|
||||||
}
|
|
||||||
if (property == "anchorScale") {
|
|
||||||
return vec3toVariant(getAnchorScale());
|
|
||||||
}
|
|
||||||
if (property == "visible") {
|
|
||||||
return getVisible();
|
|
||||||
}
|
|
||||||
if (property == "children") {
|
|
||||||
QVariantList array;
|
|
||||||
for (int i = 0; i < _children.length(); i++) {
|
|
||||||
array.append(OverlayIDtoScriptValue(nullptr, _children[i]).toVariant());
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto value = Billboardable::getProperty(property);
|
|
||||||
if (value.isValid()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return PanelAttachable::getProperty(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OverlayPanel::setProperties(const QVariantMap& properties) {
|
|
||||||
PanelAttachable::setProperties(properties);
|
|
||||||
Billboardable::setProperties(properties);
|
|
||||||
|
|
||||||
auto anchorPosition = properties["anchorPosition"];
|
|
||||||
if (anchorPosition.isValid()) {
|
|
||||||
setAnchorPosition(vec3FromVariant(anchorPosition));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto anchorPositionBinding = properties["anchorPositionBinding"];
|
|
||||||
if (anchorPositionBinding.isValid()) {
|
|
||||||
PropertyBinding binding = {};
|
|
||||||
propertyBindingFromVariant(anchorPositionBinding, binding);
|
|
||||||
_anchorPositionBindMyAvatar = binding.avatar == "MyAvatar";
|
|
||||||
_anchorPositionBindEntity = binding.entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto anchorRotation = properties["anchorRotation"];
|
|
||||||
if (anchorRotation.isValid()) {
|
|
||||||
setAnchorRotation(quatFromVariant(anchorRotation));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto anchorRotationBinding = properties["anchorRotationBinding"];
|
|
||||||
if (anchorRotationBinding.isValid()) {
|
|
||||||
PropertyBinding binding = {};
|
|
||||||
propertyBindingFromVariant(anchorPositionBinding, binding);
|
|
||||||
_anchorRotationBindMyAvatar = binding.avatar == "MyAvatar";
|
|
||||||
_anchorRotationBindEntity = binding.entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto anchorScale = properties["anchorScale"];
|
|
||||||
if (anchorScale.isValid()) {
|
|
||||||
setAnchorScale(vec3FromVariant(anchorScale));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto visible = properties["visible"];
|
|
||||||
if (visible.isValid()) {
|
|
||||||
setVisible(visible.toBool());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OverlayPanel::applyTransformTo(Transform& transform, bool force) {
|
|
||||||
if (force || usecTimestampNow() > _transformExpiry) {
|
|
||||||
PanelAttachable::applyTransformTo(transform, true);
|
|
||||||
if (!getParentPanel()) {
|
|
||||||
if (_anchorPositionBindMyAvatar) {
|
|
||||||
transform.setTranslation(DependencyManager::get<AvatarManager>()->getMyAvatar()
|
|
||||||
->getPosition());
|
|
||||||
} else if (!_anchorPositionBindEntity.isNull()) {
|
|
||||||
EntityTreePointer entityTree = DependencyManager::get<EntityScriptingInterface>()->getEntityTree();
|
|
||||||
entityTree->withReadLock([&] {
|
|
||||||
EntityItemPointer foundEntity = entityTree->findEntityByID(_anchorPositionBindEntity);
|
|
||||||
if (foundEntity) {
|
|
||||||
transform.setTranslation(foundEntity->getPosition());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
transform.setTranslation(getAnchorPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_anchorRotationBindMyAvatar) {
|
|
||||||
transform.setRotation(DependencyManager::get<AvatarManager>()->getMyAvatar()
|
|
||||||
->getOrientation());
|
|
||||||
} else if (!_anchorRotationBindEntity.isNull()) {
|
|
||||||
EntityTreePointer entityTree = DependencyManager::get<EntityScriptingInterface>()->getEntityTree();
|
|
||||||
entityTree->withReadLock([&] {
|
|
||||||
EntityItemPointer foundEntity = entityTree->findEntityByID(_anchorRotationBindEntity);
|
|
||||||
if (foundEntity) {
|
|
||||||
transform.setRotation(foundEntity->getRotation());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
transform.setRotation(getAnchorRotation());
|
|
||||||
}
|
|
||||||
|
|
||||||
transform.setScale(getAnchorScale());
|
|
||||||
|
|
||||||
transform.postTranslate(getOffsetPosition());
|
|
||||||
transform.postRotate(getOffsetRotation());
|
|
||||||
transform.postScale(getOffsetScale());
|
|
||||||
}
|
|
||||||
pointTransformAtCamera(transform, getOffsetRotation());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,86 +0,0 @@
|
||||||
//
|
|
||||||
// OverlayPanel.h
|
|
||||||
// interface/src/ui/overlays
|
|
||||||
//
|
|
||||||
// Created by Zander Otavka on 7/2/15.
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_OverlayPanel_h
|
|
||||||
#define hifi_OverlayPanel_h
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
#include <QUuid>
|
|
||||||
|
|
||||||
#include "PanelAttachable.h"
|
|
||||||
#include "Billboardable.h"
|
|
||||||
#include "Overlay.h"
|
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
|
||||||
class PropertyBinding {
|
|
||||||
public:
|
|
||||||
PropertyBinding() {}
|
|
||||||
PropertyBinding(QString avatar, QUuid entity);
|
|
||||||
QString avatar;
|
|
||||||
QUuid entity;
|
|
||||||
};
|
|
||||||
|
|
||||||
QVariant propertyBindingToVariant(const PropertyBinding& value);
|
|
||||||
void propertyBindingFromVariant(const QVariant& object, PropertyBinding& value);
|
|
||||||
|
|
||||||
|
|
||||||
class OverlayPanel : public QObject, public PanelAttachable, public Billboardable {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
typedef std::shared_ptr<OverlayPanel> Pointer;
|
|
||||||
|
|
||||||
void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; }
|
|
||||||
|
|
||||||
// getters
|
|
||||||
glm::vec3 getAnchorPosition() const { return _anchorTransform.getTranslation(); }
|
|
||||||
glm::quat getAnchorRotation() const { return _anchorTransform.getRotation(); }
|
|
||||||
glm::vec3 getAnchorScale() const { return _anchorTransform.getScale(); }
|
|
||||||
bool getVisible() const { return _visible; }
|
|
||||||
|
|
||||||
// setters
|
|
||||||
void setAnchorPosition(const glm::vec3& position) { _anchorTransform.setTranslation(position); }
|
|
||||||
void setAnchorRotation(const glm::quat& rotation) { _anchorTransform.setRotation(rotation); }
|
|
||||||
void setAnchorScale(float scale) { _anchorTransform.setScale(scale); }
|
|
||||||
void setAnchorScale(const glm::vec3& scale) { _anchorTransform.setScale(scale); }
|
|
||||||
void setVisible(bool visible) { _visible = visible; }
|
|
||||||
|
|
||||||
const QList<OverlayID>& getChildren() { return _children; }
|
|
||||||
void addChild(OverlayID childId);
|
|
||||||
void removeChild(OverlayID childId);
|
|
||||||
OverlayID popLastChild() { return _children.takeLast(); }
|
|
||||||
|
|
||||||
void setProperties(const QVariantMap& properties);
|
|
||||||
QVariant getProperty(const QString& property);
|
|
||||||
|
|
||||||
virtual void applyTransformTo(Transform& transform, bool force = false) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Transform _anchorTransform;
|
|
||||||
|
|
||||||
bool _anchorPositionBindMyAvatar = false;
|
|
||||||
QUuid _anchorPositionBindEntity;
|
|
||||||
|
|
||||||
bool _anchorRotationBindMyAvatar = false;
|
|
||||||
QUuid _anchorRotationBindEntity;
|
|
||||||
|
|
||||||
bool _visible = true;
|
|
||||||
QList<OverlayID> _children;
|
|
||||||
|
|
||||||
QScriptEngine* _scriptEngine;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // hifi_OverlayPanel_h
|
|
|
@ -68,16 +68,10 @@ void Overlays::cleanupAllOverlays() {
|
||||||
foreach(Overlay::Pointer overlay, overlaysWorld) {
|
foreach(Overlay::Pointer overlay, overlaysWorld) {
|
||||||
_overlaysToDelete.push_back(overlay);
|
_overlaysToDelete.push_back(overlay);
|
||||||
}
|
}
|
||||||
#if OVERLAY_PANELS
|
|
||||||
_panels.clear();
|
|
||||||
#endif
|
|
||||||
cleanupOverlaysToDelete();
|
cleanupOverlaysToDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlays::init() {
|
void Overlays::init() {
|
||||||
#if OVERLAY_PANELS
|
|
||||||
_scriptEngine = new QScriptEngine();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlays::update(float deltatime) {
|
void Overlays::update(float deltatime) {
|
||||||
|
@ -300,12 +294,6 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
|
||||||
|
|
||||||
if (thisOverlay) {
|
if (thisOverlay) {
|
||||||
OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone(), [](Overlay* ptr) { ptr->deleteLater(); }));
|
OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone(), [](Overlay* ptr) { ptr->deleteLater(); }));
|
||||||
#if OVERLAY_PANELS
|
|
||||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(thisOverlay);
|
|
||||||
if (attachable && attachable->getParentPanel()) {
|
|
||||||
attachable->getParentPanel()->addChild(cloneId);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return cloneId;
|
return cloneId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,15 +369,6 @@ void Overlays::deleteOverlay(OverlayID id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
|
||||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlayToDelete);
|
|
||||||
if (attachable && attachable->getParentPanel()) {
|
|
||||||
attachable->getParentPanel()->removeChild(id);
|
|
||||||
attachable->setParentPanel(nullptr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
_overlaysToDelete.push_back(overlayToDelete);
|
_overlaysToDelete.push_back(overlayToDelete);
|
||||||
emit overlayDeleted(id);
|
emit overlayDeleted(id);
|
||||||
}
|
}
|
||||||
|
@ -424,49 +403,6 @@ QObject* Overlays::getOverlayObject(OverlayID id) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
|
||||||
OverlayID Overlays::getParentPanel(OverlayID childId) const {
|
|
||||||
Overlay::Pointer overlay = getOverlay(childId);
|
|
||||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
|
|
||||||
if (attachable) {
|
|
||||||
return _panels.key(attachable->getParentPanel());
|
|
||||||
} else if (_panels.contains(childId)) {
|
|
||||||
return _panels.key(getPanel(childId)->getParentPanel());
|
|
||||||
}
|
|
||||||
return UNKNOWN_OVERLAY_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Overlays::setParentPanel(OverlayID childId, OverlayID panelId) {
|
|
||||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(getOverlay(childId));
|
|
||||||
if (attachable) {
|
|
||||||
if (_panels.contains(panelId)) {
|
|
||||||
auto panel = getPanel(panelId);
|
|
||||||
panel->addChild(childId);
|
|
||||||
attachable->setParentPanel(panel);
|
|
||||||
} else {
|
|
||||||
auto panel = attachable->getParentPanel();
|
|
||||||
if (panel) {
|
|
||||||
panel->removeChild(childId);
|
|
||||||
attachable->setParentPanel(nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (_panels.contains(childId)) {
|
|
||||||
OverlayPanel::Pointer child = getPanel(childId);
|
|
||||||
if (_panels.contains(panelId)) {
|
|
||||||
auto panel = getPanel(panelId);
|
|
||||||
panel->addChild(childId);
|
|
||||||
child->setParentPanel(panel);
|
|
||||||
} else {
|
|
||||||
auto panel = child->getParentPanel();
|
|
||||||
if (panel) {
|
|
||||||
panel->removeChild(childId);
|
|
||||||
child->setParentPanel(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
||||||
if (!_enabled) {
|
if (!_enabled) {
|
||||||
return UNKNOWN_OVERLAY_ID;
|
return UNKNOWN_OVERLAY_ID;
|
||||||
|
@ -717,62 +653,6 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) {
|
||||||
return QSizeF(0.0f, 0.0f);
|
return QSizeF(0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
|
||||||
OverlayID Overlays::addPanel(OverlayPanel::Pointer panel) {
|
|
||||||
QWriteLocker lock(&_lock);
|
|
||||||
|
|
||||||
OverlayID thisID = QUuid::createUuid();
|
|
||||||
_panels[thisID] = panel;
|
|
||||||
|
|
||||||
return thisID;
|
|
||||||
}
|
|
||||||
|
|
||||||
OverlayID Overlays::addPanel(const QVariant& properties) {
|
|
||||||
OverlayPanel::Pointer panel = std::make_shared<OverlayPanel>();
|
|
||||||
panel->init(_scriptEngine);
|
|
||||||
panel->setProperties(properties.toMap());
|
|
||||||
return addPanel(panel);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Overlays::editPanel(OverlayID panelId, const QVariant& properties) {
|
|
||||||
if (_panels.contains(panelId)) {
|
|
||||||
_panels[panelId]->setProperties(properties.toMap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OverlayPropertyResult Overlays::getPanelProperty(OverlayID panelId, const QString& property) {
|
|
||||||
OverlayPropertyResult result;
|
|
||||||
if (_panels.contains(panelId)) {
|
|
||||||
OverlayPanel::Pointer thisPanel = getPanel(panelId);
|
|
||||||
QReadLocker lock(&_lock);
|
|
||||||
result.value = thisPanel->getProperty(property);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Overlays::deletePanel(OverlayID panelId) {
|
|
||||||
OverlayPanel::Pointer panelToDelete;
|
|
||||||
|
|
||||||
{
|
|
||||||
QWriteLocker lock(&_lock);
|
|
||||||
if (_panels.contains(panelId)) {
|
|
||||||
panelToDelete = _panels.take(panelId);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!panelToDelete->getChildren().isEmpty()) {
|
|
||||||
OverlayID childId = panelToDelete->popLastChild();
|
|
||||||
deleteOverlay(childId);
|
|
||||||
deletePanel(childId);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit panelDeleted(panelId);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool Overlays::isAddedOverlay(OverlayID id) {
|
bool Overlays::isAddedOverlay(OverlayID id) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
bool result;
|
bool result;
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#include "Overlay.h"
|
#include "Overlay.h"
|
||||||
|
|
||||||
#include "PanelAttachable.h"
|
#include "PanelAttachable.h"
|
||||||
#include "OverlayPanel.h"
|
|
||||||
|
|
||||||
class PickRay;
|
class PickRay;
|
||||||
|
|
||||||
|
@ -93,9 +92,6 @@ public:
|
||||||
void enable();
|
void enable();
|
||||||
|
|
||||||
Overlay::Pointer getOverlay(OverlayID id) const;
|
Overlay::Pointer getOverlay(OverlayID id) const;
|
||||||
#if OVERLAY_PANELS
|
|
||||||
OverlayPanel::Pointer getPanel(OverlayID id) const { return _panels[id]; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// adds an overlay that's already been created
|
/// adds an overlay that's already been created
|
||||||
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
|
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
|
||||||
|
@ -468,30 +464,6 @@ public slots:
|
||||||
*/
|
*/
|
||||||
bool isAddedOverlay(OverlayID id);
|
bool isAddedOverlay(OverlayID id);
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
|
||||||
OverlayID getParentPanel(OverlayID childId) const;
|
|
||||||
void setParentPanel(OverlayID childId, OverlayID panelId);
|
|
||||||
|
|
||||||
/// adds a panel that has already been created
|
|
||||||
OverlayID addPanel(OverlayPanel::Pointer panel);
|
|
||||||
|
|
||||||
/// creates and adds a panel based on a set of properties
|
|
||||||
OverlayID addPanel(const QVariant& properties);
|
|
||||||
|
|
||||||
/// edit the properties of a panel
|
|
||||||
void editPanel(OverlayID panelId, const QVariant& properties);
|
|
||||||
|
|
||||||
/// get a property of a panel
|
|
||||||
OverlayPropertyResult getPanelProperty(OverlayID panelId, const QString& property);
|
|
||||||
|
|
||||||
/// deletes a panel and all child overlays
|
|
||||||
void deletePanel(OverlayID panelId);
|
|
||||||
|
|
||||||
/// return true if there is a panel with that id else false
|
|
||||||
bool isAddedPanel(OverlayID id) { return _panels.contains(id); }
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Generate a mouse press event on an overlay.
|
* Generate a mouse press event on an overlay.
|
||||||
* @function Overlays.sendMousePressOnOverlay
|
* @function Overlays.sendMousePressOnOverlay
|
||||||
|
@ -612,10 +584,6 @@ signals:
|
||||||
*/
|
*/
|
||||||
void overlayDeleted(OverlayID id);
|
void overlayDeleted(OverlayID id);
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
|
||||||
void panelDeleted(OverlayID id);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Triggered when a mouse press event occurs on an overlay. Only occurs for 3D overlays (unless you use
|
* Triggered when a mouse press event occurs on an overlay. Only occurs for 3D overlays (unless you use
|
||||||
* {@link Overlays.sendMousePressOnOverlay|sendMousePressOnOverlay} for a 2D overlay).
|
* {@link Overlays.sendMousePressOnOverlay|sendMousePressOnOverlay} for a 2D overlay).
|
||||||
|
@ -732,15 +700,9 @@ private:
|
||||||
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
|
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
|
||||||
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
|
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
|
||||||
QMap<OverlayID, OverlayPanel::Pointer> _panels;
|
|
||||||
#endif
|
|
||||||
QList<Overlay::Pointer> _overlaysToDelete;
|
QList<Overlay::Pointer> _overlaysToDelete;
|
||||||
unsigned int _stackOrder { 1 };
|
unsigned int _stackOrder { 1 };
|
||||||
|
|
||||||
#if OVERLAY_PANELS
|
|
||||||
QScriptEngine* _scriptEngine;
|
|
||||||
#endif
|
|
||||||
bool _enabled = true;
|
bool _enabled = true;
|
||||||
|
|
||||||
PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
|
PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
|
||||||
|
|
|
@ -65,23 +65,7 @@ namespace render {
|
||||||
}
|
}
|
||||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
|
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
|
||||||
if (args) {
|
if (args) {
|
||||||
if (overlay->getAnchor() == Overlay::MY_AVATAR) {
|
overlay->render(args);
|
||||||
auto batch = args->_batch;
|
|
||||||
auto avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
|
||||||
glm::quat myAvatarRotation = avatar->getWorldOrientation();
|
|
||||||
glm::vec3 myAvatarPosition = avatar->getWorldPosition();
|
|
||||||
float angle = glm::degrees(glm::angle(myAvatarRotation));
|
|
||||||
glm::vec3 axis = glm::axis(myAvatarRotation);
|
|
||||||
float myAvatarScale = avatar->getModelScale();
|
|
||||||
Transform transform = Transform();
|
|
||||||
transform.setTranslation(myAvatarPosition);
|
|
||||||
transform.setRotation(glm::angleAxis(angle, axis));
|
|
||||||
transform.setScale(myAvatarScale);
|
|
||||||
batch->setModelTransform(transform);
|
|
||||||
overlay->render(args);
|
|
||||||
} else {
|
|
||||||
overlay->render(args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay) {
|
template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay) {
|
||||||
|
|
|
@ -13,18 +13,8 @@
|
||||||
|
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
|
|
||||||
#include "OverlayPanel.h"
|
|
||||||
|
|
||||||
bool PanelAttachable::getParentVisible() const {
|
bool PanelAttachable::getParentVisible() const {
|
||||||
#if OVERLAY_PANELS
|
|
||||||
if (getParentPanel()) {
|
|
||||||
return getParentPanel()->getVisible() && getParentPanel()->getParentVisible();
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return true;
|
return true;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSDoc for copying to @typedefs of overlay types that inherit PanelAttachable.
|
// JSDoc for copying to @typedefs of overlay types that inherit PanelAttachable.
|
||||||
|
@ -67,15 +57,6 @@ bool PanelAttachable::applyTransformTo(Transform& transform, bool force) {
|
||||||
if (force || usecTimestampNow() > _transformExpiry) {
|
if (force || usecTimestampNow() > _transformExpiry) {
|
||||||
const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz
|
const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz
|
||||||
_transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD;
|
_transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD;
|
||||||
#if OVERLAY_PANELS
|
|
||||||
if (getParentPanel()) {
|
|
||||||
getParentPanel()->applyTransformTo(transform, true);
|
|
||||||
transform.postTranslate(getOffsetPosition());
|
|
||||||
transform.postRotate(getOffsetRotation());
|
|
||||||
transform.postScale(getOffsetScale());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,6 @@
|
||||||
#ifndef hifi_PanelAttachable_h
|
#ifndef hifi_PanelAttachable_h
|
||||||
#define hifi_PanelAttachable_h
|
#define hifi_PanelAttachable_h
|
||||||
|
|
||||||
#define OVERLAY_PANELS 0
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
@ -44,18 +42,12 @@ class OverlayPanel;
|
||||||
class PanelAttachable {
|
class PanelAttachable {
|
||||||
public:
|
public:
|
||||||
// getters
|
// getters
|
||||||
#if OVERLAY_PANELS
|
|
||||||
std::shared_ptr<OverlayPanel> getParentPanel() const { return _parentPanel; }
|
|
||||||
#endif
|
|
||||||
glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); }
|
glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); }
|
||||||
glm::quat getOffsetRotation() const { return _offset.getRotation(); }
|
glm::quat getOffsetRotation() const { return _offset.getRotation(); }
|
||||||
glm::vec3 getOffsetScale() const { return _offset.getScale(); }
|
glm::vec3 getOffsetScale() const { return _offset.getScale(); }
|
||||||
bool getParentVisible() const;
|
bool getParentVisible() const;
|
||||||
|
|
||||||
// setters
|
// setters
|
||||||
#if OVERLAY_PANELS
|
|
||||||
void setParentPanel(std::shared_ptr<OverlayPanel> panel) { _parentPanel = panel; }
|
|
||||||
#endif
|
|
||||||
void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); }
|
void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); }
|
||||||
void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); }
|
void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); }
|
||||||
void setOffsetScale(float scale) { _offset.setScale(scale); }
|
void setOffsetScale(float scale) { _offset.setScale(scale); }
|
||||||
|
@ -71,9 +63,6 @@ protected:
|
||||||
quint64 _transformExpiry = 0;
|
quint64 _transformExpiry = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if OVERLAY_PANELS
|
|
||||||
std::shared_ptr<OverlayPanel> _parentPanel = nullptr;
|
|
||||||
#endif
|
|
||||||
Transform _offset;
|
Transform _offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -127,8 +127,6 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -128,8 +128,6 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -47,8 +47,6 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -224,8 +224,6 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -484,8 +484,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||||
* used.)
|
* used.)
|
||||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
|
||||||
* rotating as you move your avatar.
|
|
||||||
*
|
*
|
||||||
* @property {string} name="" - A friendly name for the overlay.
|
* @property {string} name="" - A friendly name for the overlay.
|
||||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||||
|
|
|
@ -328,13 +328,15 @@ void Avatar::updateAvatarEntities() {
|
||||||
AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs();
|
AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs();
|
||||||
if (!recentlyDettachedAvatarEntities.empty()) {
|
if (!recentlyDettachedAvatarEntities.empty()) {
|
||||||
// only lock this thread when absolutely necessary
|
// only lock this thread when absolutely necessary
|
||||||
|
AvatarEntityMap avatarEntityData;
|
||||||
_avatarEntitiesLock.withReadLock([&] {
|
_avatarEntitiesLock.withReadLock([&] {
|
||||||
foreach (auto entityID, recentlyDettachedAvatarEntities) {
|
avatarEntityData = _avatarEntityData;
|
||||||
if (!_avatarEntityData.contains(entityID)) {
|
|
||||||
entityTree->deleteEntity(entityID, true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
foreach (auto entityID, recentlyDettachedAvatarEntities) {
|
||||||
|
if (!avatarEntityData.contains(entityID)) {
|
||||||
|
entityTree->deleteEntity(entityID, true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove stale data hashes
|
// remove stale data hashes
|
||||||
foreach (auto entityID, recentlyDettachedAvatarEntities) {
|
foreach (auto entityID, recentlyDettachedAvatarEntities) {
|
||||||
|
@ -789,10 +791,19 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void Avatar::simulateAttachments(float deltaTime) {
|
void Avatar::simulateAttachments(float deltaTime) {
|
||||||
|
assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size());
|
||||||
PerformanceTimer perfTimer("attachments");
|
PerformanceTimer perfTimer("attachments");
|
||||||
for (int i = 0; i < (int)_attachmentModels.size(); i++) {
|
for (int i = 0; i < (int)_attachmentModels.size(); i++) {
|
||||||
const AttachmentData& attachment = _attachmentData.at(i);
|
const AttachmentData& attachment = _attachmentData.at(i);
|
||||||
auto& model = _attachmentModels.at(i);
|
auto& model = _attachmentModels.at(i);
|
||||||
|
bool texturesLoaded = _attachmentModelsTexturesLoaded.at(i);
|
||||||
|
|
||||||
|
// Watch for texture loading
|
||||||
|
if (!texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
|
||||||
|
_attachmentModelsTexturesLoaded[i] = true;
|
||||||
|
model->updateRenderItems();
|
||||||
|
}
|
||||||
|
|
||||||
int jointIndex = getJointIndex(attachment.jointName);
|
int jointIndex = getJointIndex(attachment.jointName);
|
||||||
glm::vec3 jointPosition;
|
glm::vec3 jointPosition;
|
||||||
glm::quat jointRotation;
|
glm::quat jointRotation;
|
||||||
|
@ -1317,6 +1328,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||||
while ((int)_attachmentModels.size() > attachmentData.size()) {
|
while ((int)_attachmentModels.size() > attachmentData.size()) {
|
||||||
auto attachmentModel = _attachmentModels.back();
|
auto attachmentModel = _attachmentModels.back();
|
||||||
_attachmentModels.pop_back();
|
_attachmentModels.pop_back();
|
||||||
|
_attachmentModelsTexturesLoaded.pop_back();
|
||||||
_attachmentsToRemove.push_back(attachmentModel);
|
_attachmentsToRemove.push_back(attachmentModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1324,11 +1336,16 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||||
if (i == (int)_attachmentModels.size()) {
|
if (i == (int)_attachmentModels.size()) {
|
||||||
// if number of attachments has been increased, we need to allocate a new model
|
// if number of attachments has been increased, we need to allocate a new model
|
||||||
_attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()));
|
_attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()));
|
||||||
}
|
_attachmentModelsTexturesLoaded.push_back(false);
|
||||||
else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) {
|
} else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) {
|
||||||
// if the attachment has changed type, we need to re-allocate a new one.
|
// if the attachment has changed type, we need to re-allocate a new one.
|
||||||
_attachmentsToRemove.push_back(_attachmentModels[i]);
|
_attachmentsToRemove.push_back(_attachmentModels[i]);
|
||||||
_attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar());
|
_attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar());
|
||||||
|
_attachmentModelsTexturesLoaded[i] = false;
|
||||||
|
}
|
||||||
|
// If the model URL has changd, we need to wait for the textures to load
|
||||||
|
if (_attachmentModels[i]->getURL() != attachmentData[i].modelURL) {
|
||||||
|
_attachmentModelsTexturesLoaded[i] = false;
|
||||||
}
|
}
|
||||||
_attachmentModels[i]->setURL(attachmentData[i].modelURL);
|
_attachmentModels[i]->setURL(attachmentData[i].modelURL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,6 +306,7 @@ protected:
|
||||||
|
|
||||||
glm::vec3 _skeletonOffset;
|
glm::vec3 _skeletonOffset;
|
||||||
std::vector<std::shared_ptr<Model>> _attachmentModels;
|
std::vector<std::shared_ptr<Model>> _attachmentModels;
|
||||||
|
std::vector<bool> _attachmentModelsTexturesLoaded;
|
||||||
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
||||||
std::vector<std::shared_ptr<Model>> _attachmentsToDelete;
|
std::vector<std::shared_ptr<Model>> _attachmentsToDelete;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "../Logging.h"
|
#include "../Logging.h"
|
||||||
#include "../CompositorHelper.h"
|
#include "../CompositorHelper.h"
|
||||||
|
|
||||||
|
#include "DesktopPreviewProvider.h"
|
||||||
#include "render-utils/hmd_ui_vert.h"
|
#include "render-utils/hmd_ui_vert.h"
|
||||||
#include "render-utils/hmd_ui_frag.h"
|
#include "render-utils/hmd_ui_frag.h"
|
||||||
|
|
||||||
|
@ -254,17 +255,9 @@ void HmdDisplayPlugin::internalPresent() {
|
||||||
swapBuffers();
|
swapBuffers();
|
||||||
|
|
||||||
} else if (_clearPreviewFlag) {
|
} else if (_clearPreviewFlag) {
|
||||||
QImage image;
|
|
||||||
if (_vsyncEnabled) {
|
|
||||||
image = QImage(PathUtils::resourcesPath() + "images/preview.png");
|
|
||||||
} else {
|
|
||||||
image = QImage(PathUtils::resourcesPath() + "images/preview-disabled.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
image = image.mirrored();
|
QImage image = DesktopPreviewProvider::getInstance()->getPreviewDisabledImage(_vsyncEnabled);
|
||||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
_previewTexture = gpu::Texture::createStrict(
|
||||||
if (!_previewTexture) {
|
|
||||||
_previewTexture = gpu::Texture::createStrict(
|
|
||||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||||
image.width(), image.height(),
|
image.width(), image.height(),
|
||||||
gpu::Texture::MAX_NUM_MIPS,
|
gpu::Texture::MAX_NUM_MIPS,
|
||||||
|
@ -274,7 +267,6 @@ void HmdDisplayPlugin::internalPresent() {
|
||||||
_previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
_previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||||
_previewTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
_previewTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||||
_previewTexture->setAutoGenerateMips(true);
|
_previewTexture->setAutoGenerateMips(true);
|
||||||
}
|
|
||||||
|
|
||||||
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
|
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
|
||||||
|
|
||||||
|
|
|
@ -1394,8 +1394,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
||||||
// That is where _currentFrame and _lastAnimated were updated.
|
// That is where _currentFrame and _lastAnimated were updated.
|
||||||
if (_animating) {
|
if (_animating) {
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
|
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
|
||||||
|
|
||||||
if (!jointsMapped()) {
|
if (!jointsMapped()) {
|
||||||
mapJoints(entity, model->getJointNames());
|
mapJoints(entity, model->getJointNames());
|
||||||
|
//else the joint have been mapped before but we have a new animation to load
|
||||||
|
} else if (_animation && (_animation->getURL().toString() != entity->getAnimationURL())) {
|
||||||
|
_animation = DependencyManager::get<AnimationCache>()->getAnimation(entity->getAnimationURL());
|
||||||
|
_jointMappingCompleted = false;
|
||||||
|
mapJoints(entity, model->getJointNames());
|
||||||
}
|
}
|
||||||
if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) {
|
if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) {
|
||||||
animate(entity);
|
animate(entity);
|
||||||
|
|
|
@ -2086,6 +2086,10 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
||||||
|
|
||||||
EntityDynamicPointer action = _objectActions[actionID];
|
EntityDynamicPointer action = _objectActions[actionID];
|
||||||
auto removedActionType = action->getType();
|
auto removedActionType = action->getType();
|
||||||
|
action->setOwnerEntity(nullptr);
|
||||||
|
action->setIsMine(false);
|
||||||
|
_objectActions.remove(actionID);
|
||||||
|
|
||||||
if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) {
|
if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) {
|
||||||
_dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING;
|
_dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING;
|
||||||
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
||||||
|
@ -2101,9 +2105,6 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
||||||
// because they should have been set correctly when the action was added
|
// because they should have been set correctly when the action was added
|
||||||
// and/or when children were linked
|
// and/or when children were linked
|
||||||
}
|
}
|
||||||
action->setOwnerEntity(nullptr);
|
|
||||||
action->setIsMine(false);
|
|
||||||
_objectActions.remove(actionID);
|
|
||||||
|
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
action->removeFromSimulation(simulation);
|
action->removeFromSimulation(simulation);
|
||||||
|
|
|
@ -368,6 +368,7 @@ public:
|
||||||
void* getPhysicsInfo() const { return _physicsInfo; }
|
void* getPhysicsInfo() const { return _physicsInfo; }
|
||||||
|
|
||||||
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
||||||
|
|
||||||
EntityTreeElementPointer getElement() const { return _element; }
|
EntityTreeElementPointer getElement() const { return _element; }
|
||||||
EntityTreePointer getTree() const;
|
EntityTreePointer getTree() const;
|
||||||
virtual SpatialParentTree* getParentTree() const override;
|
virtual SpatialParentTree* getParentTree() const override;
|
||||||
|
|
|
@ -722,7 +722,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionless, bool, setCollisionless);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionless, bool, setCollisionless);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ignoreForCollisions, bool, setCollisionless, getCollisionless); // legacy support
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ignoreForCollisions, bool, setCollisionless, getCollisionless); // legacy support
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint8_t, setCollisionMask);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint8_t, setCollisionMask);
|
||||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(collidesWith, CollisionMask);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(collidesWith, CollisionMask);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(collisionsWillMove, bool, setDynamic, getDynamic); // legacy support
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(collisionsWillMove, bool, setDynamic, getDynamic); // legacy support
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(dynamic, bool, setDynamic);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(dynamic, bool, setDynamic);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(isSpotlight, bool, setIsSpotlight);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(isSpotlight, bool, setIsSpotlight);
|
||||||
|
@ -737,7 +737,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(lineHeight, float, setLineHeight);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(lineHeight, float, setLineHeight);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, xColor, setTextColor);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, xColor, setTextColor);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, xColor, setBackgroundColor);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, xColor, setBackgroundColor);
|
||||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(shapeType, ShapeType);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(shapeType, ShapeType);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, quint32, setMaxParticles);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, quint32, setMaxParticles);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(lifespan, float, setLifespan);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(lifespan, float, setLifespan);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(isEmitting, bool, setIsEmitting);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(isEmitting, bool, setIsEmitting);
|
||||||
|
@ -775,10 +775,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL);
|
||||||
|
|
||||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(hazeMode, HazeMode);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(hazeMode, HazeMode);
|
||||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(keyLightMode, KeyLightMode);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode);
|
||||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(ambientLightMode, AmbientLightMode);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode);
|
||||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(skyboxMode, SkyboxMode);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode);
|
||||||
|
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize);
|
||||||
|
|
|
@ -367,7 +367,7 @@ inline xColor xColor_convertFromScriptValue(const QScriptValue& v, bool& isValid
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(P, S) \
|
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(P, S) \
|
||||||
QScriptValue P = object.property(#P); \
|
QScriptValue P = object.property(#P); \
|
||||||
if (P.isValid()) { \
|
if (P.isValid()) { \
|
||||||
QString newValue = P.toVariant().toString(); \
|
QString newValue = P.toVariant().toString(); \
|
||||||
|
|
|
@ -19,15 +19,6 @@
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QImageReader>
|
#include <QImageReader>
|
||||||
|
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
|
||||||
#define CPU_MIPMAPS 0
|
|
||||||
#else
|
|
||||||
#define CPU_MIPMAPS 1
|
|
||||||
#include <nvtt/nvtt.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include <Finally.h>
|
#include <Finally.h>
|
||||||
#include <Profile.h>
|
#include <Profile.h>
|
||||||
#include <StatTracker.h>
|
#include <StatTracker.h>
|
||||||
|
@ -37,6 +28,12 @@
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
#define CPU_MIPMAPS 0
|
||||||
|
#else
|
||||||
|
#define CPU_MIPMAPS 1
|
||||||
|
#include <nvtt/nvtt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static const glm::uvec2 SPARSE_PAGE_SIZE(128);
|
static const glm::uvec2 SPARSE_PAGE_SIZE(128);
|
||||||
static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
|
static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
|
||||||
|
@ -51,25 +48,21 @@ static std::atomic<bool> compressNormalTextures { false };
|
||||||
static std::atomic<bool> compressGrayscaleTextures { false };
|
static std::atomic<bool> compressGrayscaleTextures { false };
|
||||||
static std::atomic<bool> compressCubeTextures { false };
|
static std::atomic<bool> compressCubeTextures { false };
|
||||||
|
|
||||||
bool needsSparseRectification(const glm::uvec2& size) {
|
uint rectifyDimension(const uint& dimension) {
|
||||||
// Don't attempt to rectify small textures (textures less than the sparse page size in any dimension)
|
if (dimension < SPARSE_PAGE_SIZE.x) {
|
||||||
if (glm::any(glm::lessThan(size, SPARSE_PAGE_SIZE))) {
|
uint newSize = SPARSE_PAGE_SIZE.x;
|
||||||
return false;
|
while (dimension <= newSize / 2) {
|
||||||
|
newSize /= 2;
|
||||||
|
}
|
||||||
|
return newSize;
|
||||||
|
} else {
|
||||||
|
uint pages = (dimension / SPARSE_PAGE_SIZE.x) + (dimension % SPARSE_PAGE_SIZE.x == 0 ? 0 : 1);
|
||||||
|
return pages * SPARSE_PAGE_SIZE.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't rectify textures that are already an exact multiple of sparse page size
|
|
||||||
if (glm::uvec2(0) == (size % SPARSE_PAGE_SIZE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texture is not sparse compatible, but is bigger than the sparse page size in both dimensions, rectify!
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::uvec2 rectifyToSparseSize(const glm::uvec2& size) {
|
glm::uvec2 rectifySize(const glm::uvec2& size) {
|
||||||
glm::uvec2 pages = ((size / SPARSE_PAGE_SIZE) + glm::clamp(size % SPARSE_PAGE_SIZE, glm::uvec2(0), glm::uvec2(1)));
|
return { rectifyDimension(size.x), rectifyDimension(size.y) };
|
||||||
glm::uvec2 result = pages * SPARSE_PAGE_SIZE;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -329,9 +322,12 @@ QImage processSourceImage(QImage&& srcImage, bool cubemap) {
|
||||||
++DECIMATED_TEXTURE_COUNT;
|
++DECIMATED_TEXTURE_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cubemap && needsSparseRectification(targetSize)) {
|
if (!cubemap) {
|
||||||
++RECTIFIED_TEXTURE_COUNT;
|
auto rectifiedSize = rectifySize(targetSize);
|
||||||
targetSize = rectifyToSparseSize(targetSize);
|
if (rectifiedSize != targetSize) {
|
||||||
|
++RECTIFIED_TEXTURE_COUNT;
|
||||||
|
targetSize = rectifiedSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEV_DECIMATE_TEXTURES && glm::all(glm::greaterThanEqual(targetSize / SPARSE_PAGE_SIZE, glm::uvec2(2)))) {
|
if (DEV_DECIMATE_TEXTURES && glm::all(glm::greaterThanEqual(targetSize / SPARSE_PAGE_SIZE, glm::uvec2(2)))) {
|
||||||
|
|
|
@ -73,6 +73,7 @@ public:
|
||||||
* Get Interface's protocol version.
|
* Get Interface's protocol version.
|
||||||
* @function location.protocolVersion
|
* @function location.protocolVersion
|
||||||
* @returns {string} A string uniquely identifying the version of the metaverse protocol that Interface is using.
|
* @returns {string} A string uniquely identifying the version of the metaverse protocol that Interface is using.
|
||||||
|
* @deprecated This function is deprecated and will be removed. Use {@link Window.protocolSignature} instead.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE QString protocolVersion();
|
Q_INVOKABLE QString protocolVersion();
|
||||||
|
|
||||||
|
|
|
@ -63,13 +63,6 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
|
||||||
_packetStatTimer(),
|
_packetStatTimer(),
|
||||||
_permissions(NodePermissions())
|
_permissions(NodePermissions())
|
||||||
{
|
{
|
||||||
static bool firstCall = true;
|
|
||||||
if (firstCall) {
|
|
||||||
NodeType::init();
|
|
||||||
|
|
||||||
firstCall = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
qRegisterMetaType<ConnectionStep>("ConnectionStep");
|
qRegisterMetaType<ConnectionStep>("ConnectionStep");
|
||||||
auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get();
|
auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get();
|
||||||
_nodeSocket.bind(QHostAddress::AnyIPv4, port);
|
_nodeSocket.bind(QHostAddress::AnyIPv4, port);
|
||||||
|
|
|
@ -29,28 +29,25 @@ int NodePtrMetaTypeId = qRegisterMetaType<Node*>("Node*");
|
||||||
int sharedPtrNodeMetaTypeId = qRegisterMetaType<QSharedPointer<Node>>("QSharedPointer<Node>");
|
int sharedPtrNodeMetaTypeId = qRegisterMetaType<QSharedPointer<Node>>("QSharedPointer<Node>");
|
||||||
int sharedNodePtrMetaTypeId = qRegisterMetaType<SharedNodePointer>("SharedNodePointer");
|
int sharedNodePtrMetaTypeId = qRegisterMetaType<SharedNodePointer>("SharedNodePointer");
|
||||||
|
|
||||||
void NodeType::init() {
|
static const QHash<NodeType_t, QString> TYPE_NAME_HASH {
|
||||||
QHash<NodeType_t, QString>& TypeNameHash = Node::getTypeNameHash();
|
{ NodeType::DomainServer, "Domain Server" },
|
||||||
|
{ NodeType::EntityServer, "Entity Server" },
|
||||||
TypeNameHash.insert(NodeType::DomainServer, "Domain Server");
|
{ NodeType::Agent, "Agent" },
|
||||||
TypeNameHash.insert(NodeType::EntityServer, "Entity Server");
|
{ NodeType::AudioMixer, "Audio Mixer" },
|
||||||
TypeNameHash.insert(NodeType::Agent, "Agent");
|
{ NodeType::AvatarMixer, "Avatar Mixer" },
|
||||||
TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer");
|
{ NodeType::MessagesMixer, "Messages Mixer" },
|
||||||
TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer");
|
{ NodeType::AssetServer, "Asset Server" },
|
||||||
TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer");
|
{ NodeType::EntityScriptServer, "Entity Script Server" },
|
||||||
TypeNameHash.insert(NodeType::AssetServer, "Asset Server");
|
{ NodeType::UpstreamAudioMixer, "Upstream Audio Mixer" },
|
||||||
TypeNameHash.insert(NodeType::EntityScriptServer, "Entity Script Server");
|
{ NodeType::UpstreamAvatarMixer, "Upstream Avatar Mixer" },
|
||||||
TypeNameHash.insert(NodeType::UpstreamAudioMixer, "Upstream Audio Mixer");
|
{ NodeType::DownstreamAudioMixer, "Downstream Audio Mixer" },
|
||||||
TypeNameHash.insert(NodeType::UpstreamAvatarMixer, "Upstream Avatar Mixer");
|
{ NodeType::DownstreamAvatarMixer, "Downstream Avatar Mixer" },
|
||||||
TypeNameHash.insert(NodeType::DownstreamAudioMixer, "Downstream Audio Mixer");
|
{ NodeType::Unassigned, "Unassigned" }
|
||||||
TypeNameHash.insert(NodeType::DownstreamAvatarMixer, "Downstream Avatar Mixer");
|
};
|
||||||
TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
|
const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
|
||||||
QHash<NodeType_t, QString>& TypeNameHash = Node::getTypeNameHash();
|
const auto matchedTypeName = TYPE_NAME_HASH.find(nodeType);
|
||||||
QHash<NodeType_t, QString>::iterator matchedTypeName = TypeNameHash.find(nodeType);
|
return matchedTypeName != TYPE_NAME_HASH.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
|
||||||
return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NodeType::isUpstream(NodeType_t nodeType) {
|
bool NodeType::isUpstream(NodeType_t nodeType) {
|
||||||
|
@ -84,8 +81,7 @@ NodeType_t NodeType::downstreamType(NodeType_t primaryType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeType_t NodeType::fromString(QString type) {
|
NodeType_t NodeType::fromString(QString type) {
|
||||||
QHash<NodeType_t, QString>& TypeNameHash = Node::getTypeNameHash();
|
return TYPE_NAME_HASH.key(type, NodeType::Unassigned);
|
||||||
return TypeNameHash.key(type, NodeType::Unassigned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -89,11 +89,6 @@ public:
|
||||||
|
|
||||||
bool isIgnoreRadiusEnabled() const { return _ignoreRadiusEnabled; }
|
bool isIgnoreRadiusEnabled() const { return _ignoreRadiusEnabled; }
|
||||||
|
|
||||||
static QHash<NodeType_t, QString>& getTypeNameHash() {
|
|
||||||
static QHash<NodeType_t, QString> TypeNameHash;
|
|
||||||
return TypeNameHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// privatize copy and assignment operator to disallow Node copying
|
// privatize copy and assignment operator to disallow Node copying
|
||||||
Node(const Node &otherNode);
|
Node(const Node &otherNode);
|
||||||
|
|
|
@ -31,8 +31,6 @@ namespace NodeType {
|
||||||
const NodeType_t DownstreamAvatarMixer = 'w';
|
const NodeType_t DownstreamAvatarMixer = 'w';
|
||||||
const NodeType_t Unassigned = 1;
|
const NodeType_t Unassigned = 1;
|
||||||
|
|
||||||
void init();
|
|
||||||
|
|
||||||
const QString& getNodeTypeName(NodeType_t nodeType);
|
const QString& getNodeTypeName(NodeType_t nodeType);
|
||||||
bool isUpstream(NodeType_t nodeType);
|
bool isUpstream(NodeType_t nodeType);
|
||||||
bool isDownstream(NodeType_t nodeType);
|
bool isDownstream(NodeType_t nodeType);
|
||||||
|
|
|
@ -256,25 +256,32 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
assert(_entity);
|
assert(_entity);
|
||||||
assert(entityTreeIsLocked());
|
assert(entityTreeIsLocked());
|
||||||
measureBodyAcceleration();
|
measureBodyAcceleration();
|
||||||
bool positionSuccess;
|
|
||||||
_entity->setWorldPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false);
|
// If transform or velocities are flagged as dirty it means a network or scripted change
|
||||||
if (!positionSuccess) {
|
// occured between the beginning and end of the stepSimulation() and we DON'T want to apply
|
||||||
static QString repeatedMessage =
|
// these physics simulation results.
|
||||||
LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform "
|
uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES);
|
||||||
"setPosition failed.*");
|
if (!flags) {
|
||||||
qCDebug(physics) << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID();
|
// flags are clear
|
||||||
|
_entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation()));
|
||||||
|
_entity->setWorldVelocity(getBodyLinearVelocity());
|
||||||
|
_entity->setWorldAngularVelocity(getBodyAngularVelocity());
|
||||||
|
_entity->setLastSimulated(usecTimestampNow());
|
||||||
|
} else {
|
||||||
|
// only set properties NOT flagged
|
||||||
|
if (!(flags & Simulation::DIRTY_TRANSFORM)) {
|
||||||
|
_entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation()));
|
||||||
|
}
|
||||||
|
if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) {
|
||||||
|
_entity->setWorldVelocity(getBodyLinearVelocity());
|
||||||
|
}
|
||||||
|
if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) {
|
||||||
|
_entity->setWorldAngularVelocity(getBodyAngularVelocity());
|
||||||
|
}
|
||||||
|
if (flags != (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
|
||||||
|
_entity->setLastSimulated(usecTimestampNow());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bool orientationSuccess;
|
|
||||||
_entity->setWorldOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false);
|
|
||||||
if (!orientationSuccess) {
|
|
||||||
static QString repeatedMessage =
|
|
||||||
LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform "
|
|
||||||
"setOrientation failed.*");
|
|
||||||
qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID();
|
|
||||||
}
|
|
||||||
_entity->setVelocity(getBodyLinearVelocity());
|
|
||||||
_entity->setAngularVelocity(getBodyAngularVelocity());
|
|
||||||
_entity->setLastSimulated(usecTimestampNow());
|
|
||||||
|
|
||||||
if (_entity->getSimulatorID().isNull()) {
|
if (_entity->getSimulatorID().isNull()) {
|
||||||
_loopsWithoutOwner++;
|
_loopsWithoutOwner++;
|
||||||
|
@ -530,9 +537,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
|
|
||||||
if (!_body->isActive()) {
|
if (!_body->isActive()) {
|
||||||
// make sure all derivatives are zero
|
// make sure all derivatives are zero
|
||||||
_entity->setVelocity(Vectors::ZERO);
|
zeroCleanObjectVelocities();
|
||||||
_entity->setAngularVelocity(Vectors::ZERO);
|
|
||||||
_entity->setAcceleration(Vectors::ZERO);
|
|
||||||
_numInactiveUpdates++;
|
_numInactiveUpdates++;
|
||||||
} else {
|
} else {
|
||||||
glm::vec3 gravity = _entity->getGravity();
|
glm::vec3 gravity = _entity->getGravity();
|
||||||
|
@ -559,9 +564,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
if (movingSlowly) {
|
if (movingSlowly) {
|
||||||
// velocities might not be zero, but we'll fake them as such, which will hopefully help convince
|
// velocities might not be zero, but we'll fake them as such, which will hopefully help convince
|
||||||
// other simulating observers to deactivate their own copies
|
// other simulating observers to deactivate their own copies
|
||||||
glm::vec3 zero(0.0f);
|
zeroCleanObjectVelocities();
|
||||||
_entity->setVelocity(zero);
|
|
||||||
_entity->setAngularVelocity(zero);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_numInactiveUpdates = 0;
|
_numInactiveUpdates = 0;
|
||||||
|
@ -818,3 +821,22 @@ bool EntityMotionState::shouldBeLocallyOwned() const {
|
||||||
void EntityMotionState::upgradeOutgoingPriority(uint8_t priority) {
|
void EntityMotionState::upgradeOutgoingPriority(uint8_t priority) {
|
||||||
_outgoingPriority = glm::max<uint8_t>(_outgoingPriority, priority);
|
_outgoingPriority = glm::max<uint8_t>(_outgoingPriority, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityMotionState::zeroCleanObjectVelocities() const {
|
||||||
|
// If transform or velocities are flagged as dirty it means a network or scripted change
|
||||||
|
// occured between the beginning and end of the stepSimulation() and we DON'T want to apply
|
||||||
|
// these physics simulation results.
|
||||||
|
uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES);
|
||||||
|
if (!flags) {
|
||||||
|
_entity->setWorldVelocity(glm::vec3(0.0f));
|
||||||
|
_entity->setWorldAngularVelocity(glm::vec3(0.0f));
|
||||||
|
} else {
|
||||||
|
if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) {
|
||||||
|
_entity->setWorldVelocity(glm::vec3(0.0f));
|
||||||
|
}
|
||||||
|
if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) {
|
||||||
|
_entity->setWorldAngularVelocity(glm::vec3(0.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_entity->setAcceleration(glm::vec3(0.0f));
|
||||||
|
}
|
||||||
|
|
|
@ -87,6 +87,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
// changes _outgoingPriority only if priority is larger
|
// changes _outgoingPriority only if priority is larger
|
||||||
void upgradeOutgoingPriority(uint8_t priority);
|
void upgradeOutgoingPriority(uint8_t priority);
|
||||||
|
void zeroCleanObjectVelocities() const;
|
||||||
|
|
||||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
||||||
bool entityTreeIsLocked() const;
|
bool entityTreeIsLocked() const;
|
||||||
|
|
|
@ -64,6 +64,12 @@ bool Pointer::isMouse() const {
|
||||||
return DependencyManager::get<PickManager>()->isMouse(_pickUID);
|
return DependencyManager::get<PickManager>()->isMouse(_pickUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Pointer::setDoesHover(bool doesHover) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_hover = doesHover;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Pointer::update(unsigned int pointerID) {
|
void Pointer::update(unsigned int pointerID) {
|
||||||
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
|
@ -95,7 +101,8 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hover events
|
// Hover events
|
||||||
bool doHover = shouldHover(pickResult);
|
bool doHover = _hover && shouldHover(pickResult);
|
||||||
|
|
||||||
Pointer::PickedObject hoveredObject = getHoveredObject(pickResult);
|
Pointer::PickedObject hoveredObject = getHoveredObject(pickResult);
|
||||||
PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult);
|
PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult);
|
||||||
hoveredEvent.setType(PointerEvent::Move);
|
hoveredEvent.setType(PointerEvent::Move);
|
||||||
|
@ -104,7 +111,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
||||||
hoveredEvent.setMoveOnHoverLeave(moveOnHoverLeave);
|
hoveredEvent.setMoveOnHoverLeave(moveOnHoverLeave);
|
||||||
|
|
||||||
// if shouldHover && !_prevDoHover, only send hoverBegin
|
// if shouldHover && !_prevDoHover, only send hoverBegin
|
||||||
if (_enabled && _hover && doHover && !_prevDoHover) {
|
if (_enabled && doHover && !_prevDoHover) {
|
||||||
if (hoveredObject.type == ENTITY) {
|
if (hoveredObject.type == ENTITY) {
|
||||||
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
|
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
|
||||||
} else if (hoveredObject.type == OVERLAY) {
|
} else if (hoveredObject.type == OVERLAY) {
|
||||||
|
@ -112,7 +119,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
||||||
} else if (hoveredObject.type == HUD) {
|
} else if (hoveredObject.type == HUD) {
|
||||||
emit pointerManager->hoverBeginHUD(hoveredEvent);
|
emit pointerManager->hoverBeginHUD(hoveredEvent);
|
||||||
}
|
}
|
||||||
} else if (_enabled && _hover && doHover) {
|
} else if (_enabled && doHover) {
|
||||||
if (hoveredObject.type == OVERLAY) {
|
if (hoveredObject.type == OVERLAY) {
|
||||||
if (_prevHoveredObject.type == OVERLAY) {
|
if (_prevHoveredObject.type == OVERLAY) {
|
||||||
if (hoveredObject.objectID == _prevHoveredObject.objectID) {
|
if (hoveredObject.objectID == _prevHoveredObject.objectID) {
|
||||||
|
@ -229,7 +236,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we disable the pointer or disable hovering, send hoverEnd events after triggerEnd
|
// if we disable the pointer or disable hovering, send hoverEnd events after triggerEnd
|
||||||
if (_hover && ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover))) {
|
if ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover)) {
|
||||||
if (_prevHoveredObject.type == ENTITY) {
|
if (_prevHoveredObject.type == ENTITY) {
|
||||||
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
|
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
|
||||||
} else if (_prevHoveredObject.type == OVERLAY) {
|
} else if (_prevHoveredObject.type == OVERLAY) {
|
||||||
|
|
|
@ -62,6 +62,8 @@ public:
|
||||||
virtual void setLength(float length) {}
|
virtual void setLength(float length) {}
|
||||||
virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) {}
|
virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) {}
|
||||||
|
|
||||||
|
virtual void setDoesHover(bool hover);
|
||||||
|
|
||||||
void update(unsigned int pointerID);
|
void update(unsigned int pointerID);
|
||||||
virtual void updateVisuals(const PickResultPointer& pickResult) = 0;
|
virtual void updateVisuals(const PickResultPointer& pickResult) = 0;
|
||||||
void generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult);
|
void generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult);
|
||||||
|
@ -101,7 +103,6 @@ private:
|
||||||
std::unordered_map<std::string, PickedObject> _triggeredObjects;
|
std::unordered_map<std::string, PickedObject> _triggeredObjects;
|
||||||
|
|
||||||
PointerEvent::Button chooseButton(const std::string& button);
|
PointerEvent::Button chooseButton(const std::string& button);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Pick_h
|
#endif // hifi_Pick_h
|
||||||
|
|
|
@ -122,6 +122,13 @@ void PointerManager::setLockEndUUID(unsigned int uid, const QUuid& objectID, boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PointerManager::setDoesHover(unsigned int uid, bool hover) const {
|
||||||
|
auto pointer = find(uid);
|
||||||
|
if (pointer) {
|
||||||
|
pointer->setDoesHover(hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool PointerManager::isLeftHand(unsigned int uid) {
|
bool PointerManager::isLeftHand(unsigned int uid) {
|
||||||
auto pointer = find(uid);
|
auto pointer = find(uid);
|
||||||
if (pointer) {
|
if (pointer) {
|
||||||
|
|
|
@ -37,6 +37,7 @@ public:
|
||||||
|
|
||||||
void setLength(unsigned int uid, float length) const;
|
void setLength(unsigned int uid, float length) const;
|
||||||
void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const;
|
void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const;
|
||||||
|
void setDoesHover(unsigned int uid, bool hover) const;
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
|
|
|
@ -186,7 +186,7 @@ float specularDistribution(SurfaceData surface) {
|
||||||
// Add geometric factors G1(n,l) and G1(n,v)
|
// Add geometric factors G1(n,l) and G1(n,v)
|
||||||
float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl);
|
float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl);
|
||||||
denom *= surface.smithInvG1NdotV * smithInvG1NdotL;
|
denom *= surface.smithInvG1NdotV * smithInvG1NdotL;
|
||||||
// Don't divide by PI as it will be done later
|
// Don't divide by PI as this is part of the light normalization factor
|
||||||
float power = surface.roughness4 / denom;
|
float power = surface.roughness4 / denom;
|
||||||
return power;
|
return power;
|
||||||
}
|
}
|
||||||
|
@ -202,12 +202,11 @@ vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) {
|
||||||
vec3 specular = fresnelColor * power * angleAttenuation;
|
vec3 specular = fresnelColor * power * angleAttenuation;
|
||||||
float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x);
|
float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x);
|
||||||
|
|
||||||
diffuse /= 3.1415926;
|
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
|
||||||
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
|
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
|
||||||
// has a multiplier of PI, says Naty Hoffman.
|
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
|
||||||
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
||||||
// page 23 paragraph "Punctual light sources")
|
// page 23 paragraph "Punctual light sources")
|
||||||
|
|
||||||
return vec4(specular, diffuse);
|
return vec4(specular, diffuse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,9 +221,9 @@ vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) {
|
||||||
vec3 specular = vec3(fresnelScalar) * power * angleAttenuation;
|
vec3 specular = vec3(fresnelScalar) * power * angleAttenuation;
|
||||||
float diffuse = angleAttenuation * (1.0 - fresnelScalar);
|
float diffuse = angleAttenuation * (1.0 - fresnelScalar);
|
||||||
|
|
||||||
diffuse /= 3.1415926;
|
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
|
||||||
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
|
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
|
||||||
// has a multiplier of PI, says Naty Hoffman.
|
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
|
||||||
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
||||||
// page 23 paragraph "Punctual light sources")
|
// page 23 paragraph "Punctual light sources")
|
||||||
return vec4(specular, diffuse);
|
return vec4(specular, diffuse);
|
||||||
|
@ -239,8 +238,9 @@ vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) {
|
||||||
float power = specularDistribution(surface);
|
float power = specularDistribution(surface);
|
||||||
vec3 specular = fresnelColor * power * angleAttenuation;
|
vec3 specular = fresnelColor * power * angleAttenuation;
|
||||||
|
|
||||||
// Specular isn't divided by PI because an infinitesimal volume light source
|
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
|
||||||
// has a multiplier of PI, says Naty Hoffman.
|
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
|
||||||
|
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
|
||||||
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
||||||
// page 23 paragraph "Punctual light sources")
|
// page 23 paragraph "Punctual light sources")
|
||||||
return vec4(specular, 0.f);
|
return vec4(specular, 0.f);
|
||||||
|
|
|
@ -253,6 +253,7 @@ glm::vec2 getFacingDir2D(const glm::mat4& m);
|
||||||
|
|
||||||
inline bool isNaN(const glm::vec3& value) { return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); }
|
inline bool isNaN(const glm::vec3& value) { return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); }
|
||||||
inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z); }
|
inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z); }
|
||||||
|
inline bool isNaN(const glm::mat3& value) { return isNaN(value * glm::vec3(1.0f)); }
|
||||||
|
|
||||||
glm::mat4 orthoInverse(const glm::mat4& m);
|
glm::mat4 orthoInverse(const glm::mat4& m);
|
||||||
|
|
||||||
|
|
|
@ -464,6 +464,36 @@ glm::vec3 SpatiallyNestable::localToWorldDimensions(const glm::vec3& dimensions,
|
||||||
return dimensions;
|
return dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpatiallyNestable::setWorldTransform(const glm::vec3& position, const glm::quat& orientation) {
|
||||||
|
// guard against introducing NaN into the transform
|
||||||
|
if (isNaN(orientation) || isNaN(position)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
bool success = true;
|
||||||
|
Transform parentTransform = getParentTransform(success);
|
||||||
|
_transformLock.withWriteLock([&] {
|
||||||
|
Transform myWorldTransform;
|
||||||
|
Transform::mult(myWorldTransform, parentTransform, _transform);
|
||||||
|
if (myWorldTransform.getRotation() != orientation) {
|
||||||
|
changed = true;
|
||||||
|
myWorldTransform.setRotation(orientation);
|
||||||
|
}
|
||||||
|
if (myWorldTransform.getTranslation() != position) {
|
||||||
|
changed = true;
|
||||||
|
myWorldTransform.setTranslation(position);
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
Transform::inverseMult(_transform, parentTransform, myWorldTransform);
|
||||||
|
_translationChanged = usecTimestampNow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (success && changed) {
|
||||||
|
locationChanged(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec3 SpatiallyNestable::getWorldPosition(bool& success) const {
|
glm::vec3 SpatiallyNestable::getWorldPosition(bool& success) const {
|
||||||
return getTransform(success).getTranslation();
|
return getTransform(success).getTranslation();
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,7 @@ public:
|
||||||
|
|
||||||
virtual Transform getParentTransform(bool& success, int depth = 0) const;
|
virtual Transform getParentTransform(bool& success, int depth = 0) const;
|
||||||
|
|
||||||
|
void setWorldTransform(const glm::vec3& position, const glm::quat& orientation);
|
||||||
virtual glm::vec3 getWorldPosition(bool& success) const;
|
virtual glm::vec3 getWorldPosition(bool& success) const;
|
||||||
virtual glm::vec3 getWorldPosition() const;
|
virtual glm::vec3 getWorldPosition() const;
|
||||||
virtual void setWorldPosition(const glm::vec3& position, bool& success, bool tellPhysics = true);
|
virtual void setWorldPosition(const glm::vec3& position, bool& success, bool tellPhysics = true);
|
||||||
|
|
46
libraries/ui/src/DesktopPreviewProvider.cpp
Normal file
46
libraries/ui/src/DesktopPreviewProvider.cpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#include "DesktopPreviewProvider.h"
|
||||||
|
#include <PathUtils.h>
|
||||||
|
#include <QMetaEnum>
|
||||||
|
#include <QtPlugin>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
DesktopPreviewProvider::DesktopPreviewProvider() {
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const char* DesktopPreviewProvider::imagePaths[];
|
||||||
|
|
||||||
|
QSharedPointer<DesktopPreviewProvider> DesktopPreviewProvider::getInstance() {
|
||||||
|
static QSharedPointer<DesktopPreviewProvider> instance = DependencyManager::get<DesktopPreviewProvider>();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage DesktopPreviewProvider::getPreviewDisabledImage(bool vsyncEnabled) const {
|
||||||
|
|
||||||
|
auto imageIndex = vsyncEnabled ? VSYNC : m_previewDisabledReason;
|
||||||
|
assert(imageIndex >= 0 && imageIndex <= VSYNC);
|
||||||
|
|
||||||
|
return !m_previewDisabled[imageIndex].isNull() ? m_previewDisabled[imageIndex] : loadPreviewImage(m_previewDisabled[imageIndex], PathUtils::resourcesPath() + imagePaths[imageIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopPreviewProvider::setPreviewDisabledReason(PreviewDisabledReasons reason) {
|
||||||
|
if (reason == VSYNC) {
|
||||||
|
qDebug() << "Preview disabled reason can't be forced to " << QMetaEnum::fromType<DesktopPreviewProvider::PreviewDisabledReasons>().valueToKey(reason);
|
||||||
|
return; // Not settable via this interface, as VSYNC is controlled by HMD plugin..
|
||||||
|
}
|
||||||
|
|
||||||
|
m_previewDisabledReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopPreviewProvider::setPreviewDisabledReason(const QString& reasonString) {
|
||||||
|
PreviewDisabledReasons reason = USER;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
reason = (PreviewDisabledReasons) QMetaEnum::fromType<DesktopPreviewProvider::PreviewDisabledReasons>().keyToValue(reasonString.toLatin1().data(), &ok);
|
||||||
|
if (ok) {
|
||||||
|
setPreviewDisabledReason(reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage& DesktopPreviewProvider::loadPreviewImage(QImage& image, const QString& path) const {
|
||||||
|
return image = QImage(path).mirrored().convertToFormat(QImage::Format_RGBA8888);
|
||||||
|
}
|
47
libraries/ui/src/DesktopPreviewProvider.h
Normal file
47
libraries/ui/src/DesktopPreviewProvider.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// Created by Alexander Ivash on 2018/01/08
|
||||||
|
// Copyright 2018 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 <DependencyManager.h>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
class DesktopPreviewProvider : public QObject, public Dependency {
|
||||||
|
SINGLETON_DEPENDENCY
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
DesktopPreviewProvider();
|
||||||
|
DesktopPreviewProvider(const DesktopPreviewProvider& other) = delete;
|
||||||
|
|
||||||
|
constexpr static const char* imagePaths[] = {
|
||||||
|
"images/preview-disabled.png", // USER
|
||||||
|
"images/preview-privacy.png", // SECURE_SCREEN
|
||||||
|
"images/preview.png", // VSYNC
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum PreviewDisabledReasons {
|
||||||
|
USER = 0,
|
||||||
|
SECURE_SCREEN,
|
||||||
|
VSYNC // Not settable via this interface, as VSYNC is controlled by HMD plugin..
|
||||||
|
};
|
||||||
|
Q_ENUM(PreviewDisabledReasons)
|
||||||
|
|
||||||
|
static QSharedPointer<DesktopPreviewProvider> getInstance();
|
||||||
|
|
||||||
|
QImage getPreviewDisabledImage(bool vsyncEnabled) const;
|
||||||
|
void setPreviewDisabledReason(PreviewDisabledReasons reason);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setPreviewDisabledReason(const QString& reason);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage& loadPreviewImage(QImage& image, const QString& path) const;
|
||||||
|
|
||||||
|
PreviewDisabledReasons m_previewDisabledReason = { USER };
|
||||||
|
|
||||||
|
mutable QImage m_previewDisabled[3];
|
||||||
|
};
|
|
@ -31,6 +31,8 @@
|
||||||
const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";
|
const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";
|
||||||
const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
||||||
const QString TabletScriptingInterface::QML = "hifi/tablet/TabletRoot.qml";
|
const QString TabletScriptingInterface::QML = "hifi/tablet/TabletRoot.qml";
|
||||||
|
const QString BUTTON_SORT_ORDER_KEY = "sortOrder";
|
||||||
|
const int DEFAULT_BUTTON_SORT_ORDER = 100;
|
||||||
|
|
||||||
static QString getUsername() {
|
static QString getUsername() {
|
||||||
QString username = "Unknown user";
|
QString username = "Unknown user";
|
||||||
|
@ -74,11 +76,21 @@ QVariant TabletButtonListModel::data(const QModelIndex& index, int role) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
TabletButtonProxy* TabletButtonListModel::addButton(const QVariant& properties) {
|
TabletButtonProxy* TabletButtonListModel::addButton(const QVariant& properties) {
|
||||||
auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap()));
|
QVariantMap newProperties = properties.toMap();
|
||||||
|
if (newProperties.find(BUTTON_SORT_ORDER_KEY) == newProperties.end()) {
|
||||||
|
newProperties[BUTTON_SORT_ORDER_KEY] = DEFAULT_BUTTON_SORT_ORDER;
|
||||||
|
}
|
||||||
|
int index = computeNewButtonIndex(newProperties);
|
||||||
|
auto button = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(newProperties));
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
_buttons.push_back(tabletButtonProxy);
|
int numButtons = (int)_buttons.size();
|
||||||
|
if (index < numButtons) {
|
||||||
|
_buttons.insert(_buttons.begin() + index, button);
|
||||||
|
} else {
|
||||||
|
_buttons.push_back(button);
|
||||||
|
}
|
||||||
endResetModel();
|
endResetModel();
|
||||||
return tabletButtonProxy.data();
|
return button.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabletButtonListModel::removeButton(TabletButtonProxy* button) {
|
void TabletButtonListModel::removeButton(TabletButtonProxy* button) {
|
||||||
|
@ -92,6 +104,20 @@ void TabletButtonListModel::removeButton(TabletButtonProxy* button) {
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TabletButtonListModel::computeNewButtonIndex(const QVariantMap& newButtonProperties) {
|
||||||
|
int numButtons = (int)_buttons.size();
|
||||||
|
int newButtonSortOrder = newButtonProperties[BUTTON_SORT_ORDER_KEY].toInt();
|
||||||
|
if (newButtonSortOrder == DEFAULT_BUTTON_SORT_ORDER) return numButtons;
|
||||||
|
for (int i = 0; i < numButtons; i++) {
|
||||||
|
QVariantMap tabletButtonProperties = _buttons[i]->getProperties();
|
||||||
|
int tabletButtonSortOrder = tabletButtonProperties[BUTTON_SORT_ORDER_KEY].toInt();
|
||||||
|
if (newButtonSortOrder <= tabletButtonSortOrder) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numButtons;
|
||||||
|
}
|
||||||
|
|
||||||
TabletButtonsProxyModel::TabletButtonsProxyModel(QObject *parent)
|
TabletButtonsProxyModel::TabletButtonsProxyModel(QObject *parent)
|
||||||
: QSortFilterProxyModel(parent) {
|
: QSortFilterProxyModel(parent) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,7 @@ protected:
|
||||||
friend class TabletProxy;
|
friend class TabletProxy;
|
||||||
TabletButtonProxy* addButton(const QVariant& properties);
|
TabletButtonProxy* addButton(const QVariant& properties);
|
||||||
void removeButton(TabletButtonProxy* button);
|
void removeButton(TabletButtonProxy* button);
|
||||||
|
int computeNewButtonIndex(const QVariantMap& newButtonProperties);
|
||||||
using List = std::list<QSharedPointer<TabletButtonProxy>>;
|
using List = std::list<QSharedPointer<TabletButtonProxy>>;
|
||||||
static QHash<int, QByteArray> _roles;
|
static QHash<int, QByteArray> _roles;
|
||||||
static Qt::ItemFlags _flags;
|
static Qt::ItemFlags _flags;
|
||||||
|
|
|
@ -22,8 +22,7 @@
|
||||||
|
|
||||||
var button = tablet.addButton({
|
var button = tablet.addButton({
|
||||||
icon: Script.resolvePath("dynamicsTests.svg"),
|
icon: Script.resolvePath("dynamicsTests.svg"),
|
||||||
text: "Dynamics",
|
text: "Dynamics"
|
||||||
sortOrder: 15
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,7 @@
|
||||||
var button = tablet.addButton({
|
var button = tablet.addButton({
|
||||||
text: TABLET_BUTTON_NAME,
|
text: TABLET_BUTTON_NAME,
|
||||||
icon: ICON_URL,
|
icon: ICON_URL,
|
||||||
activeIcon: ACTIVE_ICON_URL,
|
activeIcon: ACTIVE_ICON_URL
|
||||||
sortOrder: 1
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var hasEventBridge = false;
|
var hasEventBridge = false;
|
||||||
|
|
76
scripts/developer/utilities/render/lod.js
Normal file
76
scripts/developer/utilities/render/lod.js
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
//
|
||||||
|
// lodi.js
|
||||||
|
// tablet-engine app
|
||||||
|
//
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var TABLET_BUTTON_NAME = "LOD";
|
||||||
|
var QMLAPP_URL = Script.resolvePath("./lod.qml");
|
||||||
|
var ICON_URL = Script.resolvePath("../../../system/assets/images/lod-i.svg");
|
||||||
|
var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/lod-a.svg");
|
||||||
|
|
||||||
|
var onScreen = false;
|
||||||
|
|
||||||
|
function onClicked() {
|
||||||
|
if (onScreen) {
|
||||||
|
tablet.gotoHomeScreen();
|
||||||
|
} else {
|
||||||
|
tablet.loadQMLSource(QMLAPP_URL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
|
var button = tablet.addButton({
|
||||||
|
text: TABLET_BUTTON_NAME,
|
||||||
|
icon: ICON_URL,
|
||||||
|
activeIcon: ACTIVE_ICON_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
var hasEventBridge = false;
|
||||||
|
|
||||||
|
function wireEventBridge(on) {
|
||||||
|
if (!tablet) {
|
||||||
|
print("Warning in wireEventBridge(): 'tablet' undefined!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (on) {
|
||||||
|
if (!hasEventBridge) {
|
||||||
|
tablet.fromQml.connect(fromQml);
|
||||||
|
hasEventBridge = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (hasEventBridge) {
|
||||||
|
tablet.fromQml.disconnect(fromQml);
|
||||||
|
hasEventBridge = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScreenChanged(type, url) {
|
||||||
|
onScreen = (url === QMLAPP_URL);
|
||||||
|
button.editProperties({isActive: onScreen});
|
||||||
|
wireEventBridge(onScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromQml(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
button.clicked.connect(onClicked);
|
||||||
|
tablet.screenChanged.connect(onScreenChanged);
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function () {
|
||||||
|
if (onScreen) {
|
||||||
|
tablet.gotoHomeScreen();
|
||||||
|
}
|
||||||
|
button.clicked.disconnect(onClicked);
|
||||||
|
tablet.screenChanged.disconnect(onScreenChanged);
|
||||||
|
tablet.removeButton(button);
|
||||||
|
});
|
||||||
|
}());
|
92
scripts/developer/utilities/render/lod.qml
Normal file
92
scripts/developer/utilities/render/lod.qml
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
//
|
||||||
|
// lod.qml
|
||||||
|
// scripts/developer/utilities/render
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows on 2018.01.10
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import "../lib/plotperf"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: lodIU
|
||||||
|
anchors.fill:parent
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: stats
|
||||||
|
spacing: 8
|
||||||
|
anchors.fill:parent
|
||||||
|
|
||||||
|
function evalEvenHeight() {
|
||||||
|
// Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ?
|
||||||
|
return (height - spacing * (children.length - 1)) / children.length
|
||||||
|
}
|
||||||
|
|
||||||
|
PlotPerf {
|
||||||
|
title: "Load Indicators"
|
||||||
|
height: parent.evalEvenHeight()
|
||||||
|
object: LODManager
|
||||||
|
valueScale: 1
|
||||||
|
valueUnit: "ms"
|
||||||
|
plots: [
|
||||||
|
{
|
||||||
|
prop: "presentTime",
|
||||||
|
label: "present",
|
||||||
|
color: "#FFFF00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "engineRunTime",
|
||||||
|
label: "engineRun",
|
||||||
|
color: "#FF00FF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "gpuTime",
|
||||||
|
label: "gpu",
|
||||||
|
color: "#00FFFF"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
PlotPerf {
|
||||||
|
title: "FPS"
|
||||||
|
height: parent.evalEvenHeight()
|
||||||
|
object: LODManager
|
||||||
|
valueScale: 1
|
||||||
|
valueUnit: "Hz"
|
||||||
|
plots: [
|
||||||
|
{
|
||||||
|
prop: "lodIncreaseFPS",
|
||||||
|
label: "LOD++",
|
||||||
|
color: "#66FF66"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "fps",
|
||||||
|
label: "FPS",
|
||||||
|
color: "#FFFFFF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "lodDecreaseFPS",
|
||||||
|
label: "LOD--",
|
||||||
|
color: "#FF6666"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
PlotPerf {
|
||||||
|
title: "LOD"
|
||||||
|
height: parent.evalEvenHeight()
|
||||||
|
object: LODManager
|
||||||
|
valueScale: 0.1
|
||||||
|
valueUnit: ""
|
||||||
|
plots: [
|
||||||
|
{
|
||||||
|
prop: "lodLevel",
|
||||||
|
label: "LOD",
|
||||||
|
color: "#9999FF"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,8 +31,7 @@
|
||||||
var button = tablet.addButton({
|
var button = tablet.addButton({
|
||||||
text: TABLET_BUTTON_NAME,
|
text: TABLET_BUTTON_NAME,
|
||||||
icon: ICON_URL,
|
icon: ICON_URL,
|
||||||
activeIcon: ACTIVE_ICON_URL,
|
activeIcon: ACTIVE_ICON_URL
|
||||||
sortOrder: 1
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var hasEventBridge = false;
|
var hasEventBridge = false;
|
||||||
|
|
46
scripts/system/assets/images/lod-a.svg
Normal file
46
scripts/system/assets/images/lod-a.svg
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 21.1.0, 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="0 0 50 50"
|
||||||
|
style="enable-background:new 0 0 50 50;"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="lod-a.svg"><metadata
|
||||||
|
id="metadata4241"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs4239" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2880"
|
||||||
|
inkscape:window-height="1724"
|
||||||
|
id="namedview4237"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="14.66"
|
||||||
|
inkscape:cx="22.9339"
|
||||||
|
inkscape:cy="22.463149"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="33"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="Layer_1" /><path
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:44.7959404px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.17382812 0.10351562 L 0.17382812 26.466797 L 12.335938 26.466797 C 12.295785 25.916449 12.273438 25.352399 12.273438 24.773438 C 12.273438 24.325519 12.298233 23.895635 12.322266 23.464844 L 5.6464844 23.464844 L 5.6464844 0.10351562 L 0.17382812 0.10351562 z M 12.322266 23.464844 L 16.9375 23.464844 C 17.131223 20.269693 18.048402 17.696004 19.695312 15.748047 C 21.57624 13.54041 24.107908 12.4375 27.291016 12.4375 C 30.474123 12.4375 32.99176 13.54041 34.84375 15.748047 C 36.522116 17.733209 37.438284 20.371891 37.607422 23.652344 C 39.352093 23.851854 40.904899 24.187106 42.263672 24.662109 C 42.242096 19.950324 40.88586 16.190967 38.1875 13.386719 C 35.46739 10.546406 31.834178 9.125 27.291016 9.125 C 22.733385 9.125 19.088094 10.546406 16.353516 13.386719 C 13.889087 15.947857 12.553913 19.312575 12.322266 23.464844 z M 42.263672 24.662109 C 42.263846 24.700089 42.267578 24.735334 42.267578 24.773438 C 42.267578 25.968111 42.179749 27.101932 42.007812 28.175781 C 42.345297 28.39193 42.664811 28.621521 42.951172 28.876953 C 44.826172 30.525965 45.763672 33.130435 45.763672 36.689453 C 45.763672 40.272198 44.826172 42.893812 42.951172 44.554688 C 41.089193 46.215563 38.146485 47.046875 34.123047 47.046875 L 29.357422 47.046875 L 29.357422 40.285156 C 28.688843 40.354889 28.004565 40.402344 27.291016 40.402344 C 26.644531 40.402344 26.021914 40.365471 25.412109 40.308594 L 25.412109 50 L 33.517578 50 C 39.142578 50 43.283203 48.926571 45.939453 46.779297 C 48.595703 44.632023 49.923828 41.268723 49.923828 36.689453 C 49.923828 32.13391 48.602214 28.787755 45.958984 26.652344 C 44.948178 25.831197 43.714341 25.169238 42.263672 24.662109 z M 25.412109 40.308594 L 25.412109 36.939453 C 23.099 36.57794 21.188155 35.53144 19.695312 33.779297 C 18.116681 31.9121 17.214144 29.470287 16.970703 26.466797 L 12.335938 26.466797 C 12.626268 30.446204 13.963889 33.678709 16.353516 36.162109 C 18.700203 38.587982 21.722877 39.964492 25.412109 40.308594 z M 16.970703 26.466797 L 25.34375 26.466797 L 25.34375 23.464844 L 16.9375 23.464844 C 16.911675 23.890786 16.896484 24.325331 16.896484 24.773438 C 16.896484 25.358829 16.926317 25.91918 16.970703 26.466797 z M 25.412109 36.939453 C 26.013434 37.033433 26.634257 37.091797 27.291016 37.091797 C 28.01543 37.091797 28.701951 37.02645 29.357422 36.912109 L 29.357422 26.386719 L 34.123047 26.386719 C 35.374898 26.386719 36.510129 26.475933 37.552734 26.636719 C 37.606917 26.036054 37.644531 25.420213 37.644531 24.773438 C 37.644531 24.389533 37.626377 24.01998 37.607422 23.652344 C 36.34394 23.507859 34.983448 23.431641 33.517578 23.431641 L 25.412109 23.431641 L 25.412109 36.939453 z M 37.552734 26.636719 C 37.288908 29.561476 36.3922 31.947799 34.84375 33.779297 C 33.413238 35.484518 31.582121 36.524034 29.357422 36.912109 L 29.357422 40.285156 C 32.945677 39.910903 35.894611 38.544976 38.1875 36.162109 C 40.223734 34.035893 41.495873 31.373159 42.007812 28.175781 C 40.833664 27.423776 39.345701 26.913222 37.552734 26.636719 z "
|
||||||
|
id="text4243" /></svg>
|
After Width: | Height: | Size: 4.9 KiB |
46
scripts/system/assets/images/lod-i.svg
Normal file
46
scripts/system/assets/images/lod-i.svg
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 21.1.0, 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="0 0 50 50"
|
||||||
|
style="enable-background:new 0 0 50 50;"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="lod-i.svg"><metadata
|
||||||
|
id="metadata4241"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs4239" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2880"
|
||||||
|
inkscape:window-height="1724"
|
||||||
|
id="namedview4237"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="14.66"
|
||||||
|
inkscape:cx="22.9339"
|
||||||
|
inkscape:cy="22.463149"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="33"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="Layer_1" /><path
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:44.7959404px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.17382812 0.10351562 L 0.17382812 26.466797 L 12.335938 26.466797 C 12.295785 25.916449 12.273438 25.352399 12.273438 24.773438 C 12.273438 24.325519 12.298233 23.895635 12.322266 23.464844 L 5.6464844 23.464844 L 5.6464844 0.10351562 L 0.17382812 0.10351562 z M 12.322266 23.464844 L 16.9375 23.464844 C 17.131223 20.269693 18.048402 17.696004 19.695312 15.748047 C 21.57624 13.54041 24.107908 12.4375 27.291016 12.4375 C 30.474123 12.4375 32.99176 13.54041 34.84375 15.748047 C 36.522116 17.733209 37.438284 20.371891 37.607422 23.652344 C 39.352093 23.851854 40.904899 24.187106 42.263672 24.662109 C 42.242096 19.950324 40.88586 16.190967 38.1875 13.386719 C 35.46739 10.546406 31.834178 9.125 27.291016 9.125 C 22.733385 9.125 19.088094 10.546406 16.353516 13.386719 C 13.889087 15.947857 12.553913 19.312575 12.322266 23.464844 z M 42.263672 24.662109 C 42.263846 24.700089 42.267578 24.735334 42.267578 24.773438 C 42.267578 25.968111 42.179749 27.101932 42.007812 28.175781 C 42.345297 28.39193 42.664811 28.621521 42.951172 28.876953 C 44.826172 30.525965 45.763672 33.130435 45.763672 36.689453 C 45.763672 40.272198 44.826172 42.893812 42.951172 44.554688 C 41.089193 46.215563 38.146485 47.046875 34.123047 47.046875 L 29.357422 47.046875 L 29.357422 40.285156 C 28.688843 40.354889 28.004565 40.402344 27.291016 40.402344 C 26.644531 40.402344 26.021914 40.365471 25.412109 40.308594 L 25.412109 50 L 33.517578 50 C 39.142578 50 43.283203 48.926571 45.939453 46.779297 C 48.595703 44.632023 49.923828 41.268723 49.923828 36.689453 C 49.923828 32.13391 48.602214 28.787755 45.958984 26.652344 C 44.948178 25.831197 43.714341 25.169238 42.263672 24.662109 z M 25.412109 40.308594 L 25.412109 36.939453 C 23.099 36.57794 21.188155 35.53144 19.695312 33.779297 C 18.116681 31.9121 17.214144 29.470287 16.970703 26.466797 L 12.335938 26.466797 C 12.626268 30.446204 13.963889 33.678709 16.353516 36.162109 C 18.700203 38.587982 21.722877 39.964492 25.412109 40.308594 z M 16.970703 26.466797 L 25.34375 26.466797 L 25.34375 23.464844 L 16.9375 23.464844 C 16.911675 23.890786 16.896484 24.325331 16.896484 24.773438 C 16.896484 25.358829 16.926317 25.91918 16.970703 26.466797 z M 25.412109 36.939453 C 26.013434 37.033433 26.634257 37.091797 27.291016 37.091797 C 28.01543 37.091797 28.701951 37.02645 29.357422 36.912109 L 29.357422 26.386719 L 34.123047 26.386719 C 35.374898 26.386719 36.510129 26.475933 37.552734 26.636719 C 37.606917 26.036054 37.644531 25.420213 37.644531 24.773438 C 37.644531 24.389533 37.626377 24.01998 37.607422 23.652344 C 36.34394 23.507859 34.983448 23.431641 33.517578 23.431641 L 25.412109 23.431641 L 25.412109 36.939453 z M 37.552734 26.636719 C 37.288908 29.561476 36.3922 31.947799 34.84375 33.779297 C 33.413238 35.484518 31.582121 36.524034 29.357422 36.912109 L 29.357422 40.285156 C 32.945677 39.910903 35.894611 38.544976 38.1875 36.162109 C 40.223734 34.035893 41.495873 31.373159 42.007812 28.175781 C 40.833664 27.423776 39.345701 26.913222 37.552734 26.636719 z "
|
||||||
|
id="text4243" /></svg>
|
After Width: | Height: | Size: 4.9 KiB |
|
@ -16,10 +16,10 @@
|
||||||
var button;
|
var button;
|
||||||
// Used for animating and disappearing the bubble
|
// Used for animating and disappearing the bubble
|
||||||
var bubbleOverlayTimestamp;
|
var bubbleOverlayTimestamp;
|
||||||
|
// Used for rate limiting the bubble sound
|
||||||
|
var lastBubbleSoundTimestamp = 0;
|
||||||
// Used for flashing the HUD button upon activation
|
// Used for flashing the HUD button upon activation
|
||||||
var bubbleButtonFlashState = false;
|
var bubbleButtonFlashState = false;
|
||||||
// Used for flashing the HUD button upon activation
|
|
||||||
var bubbleButtonTimestamp;
|
|
||||||
// Affects bubble height
|
// Affects bubble height
|
||||||
var BUBBLE_HEIGHT_SCALE = 0.15;
|
var BUBBLE_HEIGHT_SCALE = 0.15;
|
||||||
// The bubble model itself
|
// The bubble model itself
|
||||||
|
@ -36,9 +36,11 @@
|
||||||
var bubbleActivateSound = SoundCache.getSound(Script.resolvePath("assets/sounds/bubble.wav"));
|
var bubbleActivateSound = SoundCache.getSound(Script.resolvePath("assets/sounds/bubble.wav"));
|
||||||
// Is the update() function connected?
|
// Is the update() function connected?
|
||||||
var updateConnected = false;
|
var updateConnected = false;
|
||||||
|
var bubbleFlashTimer = false;
|
||||||
|
|
||||||
var BUBBLE_VISIBLE_DURATION_MS = 3000;
|
var BUBBLE_VISIBLE_DURATION_MS = 3000;
|
||||||
var BUBBLE_RAISE_ANIMATION_DURATION_MS = 750;
|
var BUBBLE_RAISE_ANIMATION_DURATION_MS = 750;
|
||||||
|
var BUBBLE_SOUND_RATE_LIMIT_MS = 15000;
|
||||||
|
|
||||||
// Hides the bubble model overlay and resets the button flash state
|
// Hides the bubble model overlay and resets the button flash state
|
||||||
function hideOverlays() {
|
function hideOverlays() {
|
||||||
|
@ -50,11 +52,15 @@
|
||||||
|
|
||||||
// Make the bubble overlay visible, set its position, and play the sound
|
// Make the bubble overlay visible, set its position, and play the sound
|
||||||
function createOverlays() {
|
function createOverlays() {
|
||||||
Audio.playSound(bubbleActivateSound, {
|
var nowTimestamp = Date.now();
|
||||||
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
|
if (nowTimestamp - lastBubbleSoundTimestamp >= BUBBLE_SOUND_RATE_LIMIT_MS) {
|
||||||
localOnly: true,
|
Audio.playSound(bubbleActivateSound, {
|
||||||
volume: 0.2
|
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
|
||||||
});
|
localOnly: true,
|
||||||
|
volume: 0.2
|
||||||
|
});
|
||||||
|
lastBubbleSoundTimestamp = nowTimestamp;
|
||||||
|
}
|
||||||
hideOverlays();
|
hideOverlays();
|
||||||
if (updateConnected === true) {
|
if (updateConnected === true) {
|
||||||
updateConnected = false;
|
updateConnected = false;
|
||||||
|
@ -80,10 +86,17 @@
|
||||||
},
|
},
|
||||||
visible: true
|
visible: true
|
||||||
});
|
});
|
||||||
bubbleOverlayTimestamp = Date.now();
|
bubbleOverlayTimestamp = nowTimestamp;
|
||||||
bubbleButtonTimestamp = bubbleOverlayTimestamp;
|
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
updateConnected = true;
|
updateConnected = true;
|
||||||
|
|
||||||
|
// Flash button
|
||||||
|
if (!bubbleFlashTimer) {
|
||||||
|
bubbleFlashTimer = Script.setInterval(function () {
|
||||||
|
writeButtonProperties(bubbleButtonFlashState);
|
||||||
|
bubbleButtonFlashState = !bubbleButtonFlashState;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called from the C++ scripting interface to show the bubble overlay
|
// Called from the C++ scripting interface to show the bubble overlay
|
||||||
|
@ -103,12 +116,6 @@
|
||||||
var delay = (timestamp - bubbleOverlayTimestamp);
|
var delay = (timestamp - bubbleOverlayTimestamp);
|
||||||
var overlayAlpha = 1.0 - (delay / BUBBLE_VISIBLE_DURATION_MS);
|
var overlayAlpha = 1.0 - (delay / BUBBLE_VISIBLE_DURATION_MS);
|
||||||
if (overlayAlpha > 0) {
|
if (overlayAlpha > 0) {
|
||||||
// Flash button
|
|
||||||
if ((timestamp - bubbleButtonTimestamp) >= BUBBLE_VISIBLE_DURATION_MS) {
|
|
||||||
writeButtonProperties(bubbleButtonFlashState);
|
|
||||||
bubbleButtonTimestamp = timestamp;
|
|
||||||
bubbleButtonFlashState = !bubbleButtonFlashState;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delay < BUBBLE_RAISE_ANIMATION_DURATION_MS) {
|
if (delay < BUBBLE_RAISE_ANIMATION_DURATION_MS) {
|
||||||
Overlays.editOverlay(bubbleOverlay, {
|
Overlays.editOverlay(bubbleOverlay, {
|
||||||
|
@ -157,8 +164,11 @@
|
||||||
Script.update.disconnect(update);
|
Script.update.disconnect(update);
|
||||||
updateConnected = false;
|
updateConnected = false;
|
||||||
}
|
}
|
||||||
var bubbleActive = Users.getIgnoreRadiusEnabled();
|
if (bubbleFlashTimer) {
|
||||||
writeButtonProperties(bubbleActive);
|
Script.clearTimeout(bubbleFlashTimer);
|
||||||
|
bubbleFlashTimer = false;
|
||||||
|
}
|
||||||
|
writeButtonProperties(Users.getIgnoreRadiusEnabled());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +176,10 @@
|
||||||
// NOTE: the c++ calls this with just the first param -- we added a second
|
// NOTE: the c++ calls this with just the first param -- we added a second
|
||||||
// just for not logging the initial state of the bubble when we startup.
|
// just for not logging the initial state of the bubble when we startup.
|
||||||
function onBubbleToggled(enabled, doNotLog) {
|
function onBubbleToggled(enabled, doNotLog) {
|
||||||
|
if (bubbleFlashTimer) {
|
||||||
|
Script.clearTimeout(bubbleFlashTimer);
|
||||||
|
bubbleFlashTimer = false;
|
||||||
|
}
|
||||||
writeButtonProperties(enabled);
|
writeButtonProperties(enabled);
|
||||||
if (doNotLog !== true) {
|
if (doNotLog !== true) {
|
||||||
UserActivityLogger.bubbleToggled(enabled);
|
UserActivityLogger.bubbleToggled(enabled);
|
||||||
|
@ -200,6 +214,10 @@
|
||||||
// Cleanup the tablet button and overlays when script is stopped
|
// Cleanup the tablet button and overlays when script is stopped
|
||||||
Script.scriptEnding.connect(function () {
|
Script.scriptEnding.connect(function () {
|
||||||
button.clicked.disconnect(Users.toggleIgnoreRadius);
|
button.clicked.disconnect(Users.toggleIgnoreRadius);
|
||||||
|
if (bubbleFlashTimer) {
|
||||||
|
Script.clearTimeout(bubbleFlashTimer);
|
||||||
|
bubbleFlashTimer = false;
|
||||||
|
}
|
||||||
if (tablet) {
|
if (tablet) {
|
||||||
tablet.removeButton(button);
|
tablet.removeButton(button);
|
||||||
}
|
}
|
||||||
|
|
|
@ -945,8 +945,7 @@
|
||||||
tabletButton = tablet.addButton({
|
tabletButton = tablet.addButton({
|
||||||
icon: tabletButtonIcon,
|
icon: tabletButtonIcon,
|
||||||
activeIcon: tabletButtonActiveIcon,
|
activeIcon: tabletButtonActiveIcon,
|
||||||
text: tabletButtonName,
|
text: tabletButtonName
|
||||||
sortOrder: 0
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Messages.subscribe(channelName);
|
Messages.subscribe(channelName);
|
||||||
|
|
|
@ -562,9 +562,11 @@
|
||||||
break;
|
break;
|
||||||
case 'disableHmdPreview':
|
case 'disableHmdPreview':
|
||||||
isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview");
|
isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview");
|
||||||
|
DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN");
|
||||||
Menu.setIsOptionChecked("Disable Preview", true);
|
Menu.setIsOptionChecked("Disable Preview", true);
|
||||||
break;
|
break;
|
||||||
case 'maybeEnableHmdPreview':
|
case 'maybeEnableHmdPreview':
|
||||||
|
DesktopPreviewProvider.setPreviewDisabledReason("USER");
|
||||||
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
|
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
|
||||||
break;
|
break;
|
||||||
case 'passphraseReset':
|
case 'passphraseReset':
|
||||||
|
@ -635,7 +637,11 @@
|
||||||
// -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string
|
// -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string
|
||||||
// value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML.
|
// value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML.
|
||||||
function onTabletScreenChanged(type, url) {
|
function onTabletScreenChanged(type, url) {
|
||||||
onWalletScreen = (type === "QML" && url === WALLET_QML_SOURCE);
|
var onWalletScreenNow = (type === "QML" && url === WALLET_QML_SOURCE);
|
||||||
|
if (!onWalletScreenNow && onWalletScreen) {
|
||||||
|
DesktopPreviewProvider.setPreviewDisabledReason("USER");
|
||||||
|
}
|
||||||
|
onWalletScreen = onWalletScreenNow;
|
||||||
wireEventBridge(onWalletScreen);
|
wireEventBridge(onWalletScreen);
|
||||||
// Change button to active when window is first openend, false otherwise.
|
// Change button to active when window is first openend, false otherwise.
|
||||||
if (button) {
|
if (button) {
|
||||||
|
|
|
@ -44,7 +44,12 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
this.highVarianceCount = 0;
|
this.highVarianceCount = 0;
|
||||||
this.veryhighVarianceCount = 0;
|
this.veryhighVarianceCount = 0;
|
||||||
this.tabletID = null;
|
this.tabletID = null;
|
||||||
|
this.TABLET_UI_UUIDS = [];
|
||||||
this.blacklist = [];
|
this.blacklist = [];
|
||||||
|
this.leftPointerDoesHover = true;
|
||||||
|
this.leftPointerDoesHoverChanged = false;
|
||||||
|
this.rightPointerDoesHover = true;
|
||||||
|
this.rightPointerDoesHoverChanged = false;
|
||||||
this.pointerManager = new PointerManager();
|
this.pointerManager = new PointerManager();
|
||||||
|
|
||||||
// a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are
|
// a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are
|
||||||
|
@ -122,6 +127,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
return getControllerWorldLocation(Controller.Standard.RightHand, true);
|
return getControllerWorldLocation(Controller.Standard.RightHand, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.isTabletID = function (uuid) {
|
||||||
|
return _this.TABLET_UI_UUIDS.indexOf(uuid) !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
this.updateTimings = function () {
|
this.updateTimings = function () {
|
||||||
_this.intervalCount++;
|
_this.intervalCount++;
|
||||||
var thisInterval = Date.now();
|
var thisInterval = Date.now();
|
||||||
|
@ -148,11 +157,35 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
this.setIgnorePointerItems = function() {
|
this.setIgnorePointerItems = function() {
|
||||||
if (HMD.tabletID !== this.tabletID) {
|
if (HMD.tabletID !== this.tabletID) {
|
||||||
this.tabletID = HMD.tabletID;
|
this.tabletID = HMD.tabletID;
|
||||||
|
this.TABLET_UI_UUIDS = [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID];
|
||||||
Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist);
|
Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist);
|
||||||
Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist);
|
Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.updateDoesHover = function(handLaser, doesHover) {
|
||||||
|
if (handLaser.doesHover !== undefined) {
|
||||||
|
if (handLaser.hand === LEFT_HAND && _this.leftPointerDoesHover !== doesHover) {
|
||||||
|
_this.leftPointerDoesHover = doesHover;
|
||||||
|
_this.leftPointerDoesHoverChanged = true;
|
||||||
|
} else if (handLaser.hand === RIGHT_HAND && _this.rightPointerDoesHover !== doesHover) {
|
||||||
|
_this.rightPointerDoesHover = doesHover;
|
||||||
|
_this.rightPointerDoesHoverChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateHovering = function () {
|
||||||
|
if (_this.leftPointerDoesHoverChanged) {
|
||||||
|
Pointers.setDoesHover(_this.leftPointer, _this.leftPointerDoesHover);
|
||||||
|
_this.leftPointerDoesHoverChanged = false;
|
||||||
|
}
|
||||||
|
if (_this.rightPointerDoesHoverChanged) {
|
||||||
|
Pointers.setDoesHover(_this.rightPointer, _this.rightPointerDoesHover);
|
||||||
|
_this.rightPointerDoesHoverChanged = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.update = function () {
|
this.update = function () {
|
||||||
try {
|
try {
|
||||||
_this.updateInternal();
|
_this.updateInternal();
|
||||||
|
@ -324,6 +357,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
_this.runningPluginNames[orderedPluginName] = true;
|
_this.runningPluginNames[orderedPluginName] = true;
|
||||||
_this.markSlots(candidatePlugin, orderedPluginName);
|
_this.markSlots(candidatePlugin, orderedPluginName);
|
||||||
_this.pointerManager.makePointerVisible(candidatePlugin.parameters.handLaser);
|
_this.pointerManager.makePointerVisible(candidatePlugin.parameters.handLaser);
|
||||||
|
_this.updateDoesHover(candidatePlugin.parameters.handLaser,
|
||||||
|
candidatePlugin.parameters.handLaser.doesHover);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
print("controllerDispatcher running " + orderedPluginName);
|
print("controllerDispatcher running " + orderedPluginName);
|
||||||
}
|
}
|
||||||
|
@ -354,12 +389,15 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
Script.beginProfileRange("dispatch.run." + runningPluginName);
|
Script.beginProfileRange("dispatch.run." + runningPluginName);
|
||||||
}
|
}
|
||||||
var runningness = plugin.run(controllerData, deltaTime);
|
var runningness = plugin.run(controllerData, deltaTime);
|
||||||
if (!runningness.active) {
|
if (runningness.active) {
|
||||||
|
_this.updateDoesHover(plugin.parameters.handLaser, plugin.parameters.handLaser.doesHover);
|
||||||
|
} else {
|
||||||
// plugin is finished running, for now. remove it from the list
|
// plugin is finished running, for now. remove it from the list
|
||||||
// of running plugins and mark its activity-slots as "not in use"
|
// of running plugins and mark its activity-slots as "not in use"
|
||||||
delete _this.runningPluginNames[runningPluginName];
|
delete _this.runningPluginNames[runningPluginName];
|
||||||
_this.markSlots(plugin, false);
|
_this.markSlots(plugin, false);
|
||||||
_this.pointerManager.makePointerInvisible(plugin.parameters.handLaser);
|
_this.pointerManager.makePointerInvisible(plugin.parameters.handLaser);
|
||||||
|
_this.updateDoesHover(plugin.parameters.handLaser, true);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
print("controllerDispatcher stopping " + runningPluginName);
|
print("controllerDispatcher stopping " + runningPluginName);
|
||||||
}
|
}
|
||||||
|
@ -372,6 +410,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_this.pointerManager.updatePointersRenderState(controllerData.triggerClicks, controllerData.triggerValues);
|
_this.pointerManager.updatePointersRenderState(controllerData.triggerClicks, controllerData.triggerValues);
|
||||||
|
_this.updateHovering();
|
||||||
|
|
||||||
if (PROFILE) {
|
if (PROFILE) {
|
||||||
Script.endProfileRange("dispatch.run");
|
Script.endProfileRange("dispatch.run");
|
||||||
}
|
}
|
||||||
|
|
|
@ -370,20 +370,23 @@ Script.include("/~/system/libraries/Xform.js");
|
||||||
};
|
};
|
||||||
|
|
||||||
this.isReady = function (controllerData) {
|
this.isReady = function (controllerData) {
|
||||||
if (this.notPointingAtEntity(controllerData)) {
|
if (HMD.active) {
|
||||||
return makeRunningValues(false, [], []);
|
if (this.notPointingAtEntity(controllerData)) {
|
||||||
}
|
return makeRunningValues(false, [], []);
|
||||||
|
}
|
||||||
|
|
||||||
this.distanceHolding = false;
|
this.distanceHolding = false;
|
||||||
this.distanceRotating = false;
|
this.distanceRotating = false;
|
||||||
|
|
||||||
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
|
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
|
||||||
this.prepareDistanceRotatingData(controllerData);
|
this.prepareDistanceRotatingData(controllerData);
|
||||||
return makeRunningValues(true, [], []);
|
return makeRunningValues(true, [], []);
|
||||||
} else {
|
} else {
|
||||||
this.destroyContextOverlay();
|
this.destroyContextOverlay();
|
||||||
return makeRunningValues(false, [], []);
|
return makeRunningValues(false, [], []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return makeRunningValues(false, [], []);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.run = function (controllerData) {
|
this.run = function (controllerData) {
|
||||||
|
|
|
@ -89,7 +89,7 @@
|
||||||
|
|
||||||
this.isReady = function (controllerData) {
|
this.isReady = function (controllerData) {
|
||||||
var otherModuleRunning = this.getOtherModule().running;
|
var otherModuleRunning = this.getOtherModule().running;
|
||||||
if (!otherModuleRunning) {
|
if (!otherModuleRunning && HMD.active) {
|
||||||
if (this.processLaser(controllerData)) {
|
if (this.processLaser(controllerData)) {
|
||||||
this.running = true;
|
this.running = true;
|
||||||
return ControllerDispatcherUtils.makeRunningValues(true, [], []);
|
return ControllerDispatcherUtils.makeRunningValues(true, [], []);
|
||||||
|
|
|
@ -87,41 +87,89 @@ Script.include("/~/system/libraries/controllers.js");
|
||||||
return MyAvatar.getDominantHand() === "right" ? 1 : 0;
|
return MyAvatar.getDominantHand() === "right" ? 1 : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dominantHandOverride = false;
|
this.letOtherHandRunFirst = function (controllerData, pointingAt) {
|
||||||
|
// If both hands are ready to run, let the other hand run first if it is the dominant hand so that it gets the
|
||||||
|
// highlight.
|
||||||
|
var isOtherTriggerPressed = controllerData.triggerValues[this.otherHand] > TRIGGER_OFF_VALUE;
|
||||||
|
var isLetOtherHandRunFirst = !this.getOtherModule().running
|
||||||
|
&& this.getDominantHand() === this.otherHand
|
||||||
|
&& (this.parameters.handLaser.allwaysOn || isOtherTriggerPressed);
|
||||||
|
if (isLetOtherHandRunFirst) {
|
||||||
|
var otherHandPointingAt = controllerData.rayPicks[this.otherHand].objectID;
|
||||||
|
if (this.isTabletID(otherHandPointingAt)) {
|
||||||
|
otherHandPointingAt = HMD.tabletID;
|
||||||
|
}
|
||||||
|
isLetOtherHandRunFirst = pointingAt === otherHandPointingAt;
|
||||||
|
}
|
||||||
|
return isLetOtherHandRunFirst;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.hoverItem = null;
|
||||||
|
|
||||||
|
this.isTabletID = function (uuid) {
|
||||||
|
return [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID].indexOf(uuid) !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
this.isReady = function(controllerData) {
|
this.isReady = function(controllerData) {
|
||||||
var otherModuleRunning = this.getOtherModule().running;
|
if (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)) {
|
||||||
otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand.
|
|
||||||
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE
|
|
||||||
&& controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE;
|
|
||||||
if ((!otherModuleRunning || isTriggerPressed)
|
|
||||||
&& (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData))) {
|
|
||||||
this.updateAllwaysOn();
|
this.updateAllwaysOn();
|
||||||
if (isTriggerPressed) {
|
|
||||||
this.dominantHandOverride = true; // Override dominant hand.
|
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
|
||||||
this.getOtherModule().dominantHandOverride = false;
|
|
||||||
}
|
|
||||||
if (this.parameters.handLaser.allwaysOn || isTriggerPressed) {
|
if (this.parameters.handLaser.allwaysOn || isTriggerPressed) {
|
||||||
return makeRunningValues(true, [], []);
|
var pointingAt = controllerData.rayPicks[this.hand].objectID;
|
||||||
|
if (this.isTabletID(pointingAt)) {
|
||||||
|
pointingAt = HMD.tabletID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.letOtherHandRunFirst(controllerData, pointingAt)) {
|
||||||
|
|
||||||
|
if (pointingAt !== this.getOtherModule().hoverItem) {
|
||||||
|
this.parameters.handLaser.doesHover = true;
|
||||||
|
this.hoverItem = pointingAt;
|
||||||
|
} else {
|
||||||
|
this.parameters.handLaser.doesHover = false;
|
||||||
|
this.hoverItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeRunningValues(true, [], []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.parameters.handLaser.doesHover = false;
|
||||||
|
this.hoverItem = null;
|
||||||
|
|
||||||
return makeRunningValues(false, [], []);
|
return makeRunningValues(false, [], []);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.run = function(controllerData, deltaTime) {
|
this.run = function(controllerData, deltaTime) {
|
||||||
var otherModuleRunning = this.getOtherModule().running;
|
|
||||||
otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand.
|
|
||||||
otherModuleRunning = otherModuleRunning || this.getOtherModule().dominantHandOverride; // Override dominant hand.
|
|
||||||
var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData);
|
var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData);
|
||||||
if (!otherModuleRunning && !grabModuleNeedsToRun && (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE
|
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
|
||||||
|| this.parameters.handLaser.allwaysOn
|
if (!grabModuleNeedsToRun && (isTriggerPressed || this.parameters.handLaser.allwaysOn
|
||||||
&& (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)))) {
|
&& (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)))) {
|
||||||
this.running = true;
|
this.running = true;
|
||||||
|
|
||||||
|
var pointingAt = controllerData.rayPicks[this.hand].objectID;
|
||||||
|
if (this.isTabletID(pointingAt)) {
|
||||||
|
pointingAt = HMD.tabletID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointingAt !== this.getOtherModule().hoverItem || isTriggerPressed) {
|
||||||
|
this.parameters.handLaser.doesHover = true;
|
||||||
|
this.hoverItem = pointingAt;
|
||||||
|
} else {
|
||||||
|
this.parameters.handLaser.doesHover = false;
|
||||||
|
this.hoverItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
return makeRunningValues(true, [], []);
|
return makeRunningValues(true, [], []);
|
||||||
}
|
}
|
||||||
this.deleteContextOverlay();
|
this.deleteContextOverlay();
|
||||||
this.running = false;
|
this.running = false;
|
||||||
this.dominantHandOverride = false;
|
|
||||||
|
this.parameters.handLaser.doesHover = false;
|
||||||
|
this.hoverItem = null;
|
||||||
|
|
||||||
return makeRunningValues(false, [], []);
|
return makeRunningValues(false, [], []);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,7 +448,7 @@ var toolBar = (function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
addButton("importEntitiesButton", "assets-01.svg", function() {
|
addButton("importEntitiesButton", "assets-01.svg", function() {
|
||||||
Window.openFileChanged.connect(onFileOpenChanged);
|
Window.browseChanged.connect(onFileOpenChanged);
|
||||||
Window.browseAsync("Select Model to Import", "", "*.json");
|
Window.browseAsync("Select Model to Import", "", "*.json");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1497,7 +1497,7 @@ function onFileOpenChanged(filename) {
|
||||||
// disconnect the event, otherwise the requests will stack up
|
// disconnect the event, otherwise the requests will stack up
|
||||||
try {
|
try {
|
||||||
// Not all calls to onFileOpenChanged() connect an event.
|
// Not all calls to onFileOpenChanged() connect an event.
|
||||||
Window.openFileChanged.disconnect(onFileOpenChanged);
|
Window.browseChanged.disconnect(onFileOpenChanged);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore.
|
// Ignore.
|
||||||
}
|
}
|
||||||
|
@ -1549,7 +1549,7 @@ function handeMenuEvent(menuItem) {
|
||||||
}
|
}
|
||||||
} else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") {
|
} else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") {
|
||||||
if (menuItem === "Import Entities") {
|
if (menuItem === "Import Entities") {
|
||||||
Window.openFileChanged.connect(onFileOpenChanged);
|
Window.browseChanged.connect(onFileOpenChanged);
|
||||||
Window.browseAsync("Select Model to Import", "", "*.json");
|
Window.browseAsync("Select Model to Import", "", "*.json");
|
||||||
} else {
|
} else {
|
||||||
Window.promptTextChanged.connect(onPromptTextChanged);
|
Window.promptTextChanged.connect(onPromptTextChanged);
|
||||||
|
|
|
@ -43,7 +43,7 @@ var activeTimer = false; // used to cancel active timer if a user plays an amima
|
||||||
var activeEmote = false; // to keep track of the currently playing emote
|
var activeEmote = false; // to keep track of the currently playing emote
|
||||||
|
|
||||||
button = tablet.addButton({
|
button = tablet.addButton({
|
||||||
//icon: "icons/tablet-icons/emote.svg", // TODO - we need graphics for this
|
icon: "icons/tablet-icons/EmoteAppIcon.svg",
|
||||||
text: EMOTE_LABEL,
|
text: EMOTE_LABEL,
|
||||||
sortOrder: EMOTE_APP_SORT_ORDER
|
sortOrder: EMOTE_APP_SORT_ORDER
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,8 +37,7 @@
|
||||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
button = tablet.addButton({
|
button = tablet.addButton({
|
||||||
icon: "icons/tablet-icons/goto-i.svg",
|
icon: "icons/tablet-icons/goto-i.svg",
|
||||||
text: buttonName,
|
text: buttonName
|
||||||
sortOrder: 8
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,7 @@ if (Settings.getValue("HUDUIEnabled")) {
|
||||||
button = tablet.addButton({
|
button = tablet.addButton({
|
||||||
icon: "icons/tablet-icons/goto-i.svg",
|
icon: "icons/tablet-icons/goto-i.svg",
|
||||||
activeIcon: "icons/tablet-icons/goto-a.svg",
|
activeIcon: "icons/tablet-icons/goto-a.svg",
|
||||||
text: buttonName,
|
text: buttonName
|
||||||
sortOrder: 8
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,11 +44,11 @@
|
||||||
input[type=button] {
|
input[type=button] {
|
||||||
font-family: 'Raleway';
|
font-family: 'Raleway';
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 13px;
|
font-size: 20px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
height: 28px;
|
height: 105px;
|
||||||
min-width: 120px;
|
min-width: 190px;
|
||||||
padding: 0px 18px;
|
padding: 0px 18px;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
@ -98,14 +98,14 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p>Click an emotion to Emote:<p>
|
<p>Click an emotion to Emote:<p>
|
||||||
<p><input type="button" class="emote-button white" value="Crying"></p>
|
<p><input type="button" class="emote-button white" value="Crying">
|
||||||
<p><input type="button" class="emote-button white" value="Surprised"></p>
|
<input type="button" class="emote-button white" value="Surprised"></p>
|
||||||
<p><input type="button" class="emote-button white" value="Dancing"></p>
|
<p><input type="button" class="emote-button white" value="Dancing">
|
||||||
<p><input type="button" class="emote-button white" value="Cheering"></p>
|
<input type="button" class="emote-button white" value="Cheering"></p>
|
||||||
<p><input type="button" class="emote-button white" value="Waving"></p>
|
<p><input type="button" class="emote-button white" value="Waving">
|
||||||
<p><input type="button" class="emote-button white" value="Fall"></p>
|
<input type="button" class="emote-button white" value="Fall"></p>
|
||||||
<p><input type="button" class="emote-button white" value="Pointing"></p>
|
<p><input type="button" class="emote-button white" value="Pointing">
|
||||||
<p><input type="button" class="emote-button white" value="Clapping"></p>
|
<input type="button" class="emote-button white" value="Clapping"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||||
|
|
|
@ -539,8 +539,14 @@
|
||||||
return startingUp;
|
return startingUp;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDomainConnectionRefused(reason) {
|
function onDomainConnectionRefused(reason, reasonCode) {
|
||||||
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
// the "login error" reason means that the DS couldn't decrypt the username signature
|
||||||
|
// since this eventually resolves itself for good actors we don't need to show a notification for it
|
||||||
|
var LOGIN_ERROR_REASON_CODE = 2;
|
||||||
|
|
||||||
|
if (reasonCode != LOGIN_ERROR_REASON_CODE) {
|
||||||
|
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEditError(msg) {
|
function onEditError(msg) {
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
var button = tablet.addButton({
|
var button = tablet.addButton({
|
||||||
icon: Script.resolvePath("assets/images/run.svg"),
|
icon: Script.resolvePath("assets/images/run.svg"),
|
||||||
text: "RUN",
|
text: "RUN"
|
||||||
sortOrder: 15
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function onClicked() {
|
function onClicked() {
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
'include_actions=' + actions,
|
'include_actions=' + actions,
|
||||||
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
||||||
'require_online=true',
|
'require_online=true',
|
||||||
'protocol=' + encodeURIComponent(location.protocolVersion()),
|
'protocol=' + encodeURIComponent(Window.protocolSignature()),
|
||||||
'per_page=' + count
|
'per_page=' + count
|
||||||
];
|
];
|
||||||
var url = Account.metaverseServerURL + '/api/v1/user_stories?' + options.join('&');
|
var url = Account.metaverseServerURL + '/api/v1/user_stories?' + options.join('&');
|
||||||
|
|
|
@ -37,8 +37,7 @@
|
||||||
var button = tablet.addButton({
|
var button = tablet.addButton({
|
||||||
icon: "icons/tablet-icons/users-i.svg",
|
icon: "icons/tablet-icons/users-i.svg",
|
||||||
activeIcon: "icons/tablet-icons/users-a.svg",
|
activeIcon: "icons/tablet-icons/users-a.svg",
|
||||||
text: "USERS",
|
text: "USERS"
|
||||||
sortOrder: 11
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var onUsersScreen = false;
|
var onUsersScreen = false;
|
||||||
|
|
|
@ -163,46 +163,31 @@ class MyTestWindow : public TestWindow {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern bool needsSparseRectification(const uvec2& size);
|
extern uvec2 rectifySize(const uvec2& size);
|
||||||
extern uvec2 rectifyToSparseSize(const uvec2& size);
|
|
||||||
|
|
||||||
void testSparseRectify() {
|
void testSparseRectify() {
|
||||||
std::vector<std::pair<uvec2, bool>> NEEDS_SPARSE_TESTS {{
|
std::vector<std::pair<uvec2, uvec2>> SPARSE_SIZE_TESTS {
|
||||||
// Already sparse
|
// Already sparse
|
||||||
{ {1024, 1024 }, false },
|
{ {1024, 1024 }, { 1024, 1024 } },
|
||||||
{ { 128, 128 }, false },
|
{ { 128, 128 }, { 128, 128 } },
|
||||||
// Too small in one dimension
|
// Too small in one dimension
|
||||||
{ { 127, 127 }, false },
|
{ { 127, 127 }, { 128, 128 } },
|
||||||
{ { 1, 1 }, false },
|
{ { 1, 1 }, { 1, 1 } },
|
||||||
{ { 1000, 1 }, false },
|
{ { 1000, 1 }, { 1024, 1 } },
|
||||||
{ { 1024, 1 }, false },
|
{ { 1024, 1 }, { 1024, 1 } },
|
||||||
{ { 100, 100 }, false },
|
{ { 100, 100 }, { 128, 128 } },
|
||||||
// needs rectification
|
{ { 57, 510 }, { 64, 512 } },
|
||||||
{ { 1000, 1000 }, true },
|
|
||||||
{ { 1024, 1000 }, true },
|
|
||||||
} };
|
|
||||||
|
|
||||||
for (const auto& test : NEEDS_SPARSE_TESTS) {
|
|
||||||
const auto& size = test.first;
|
|
||||||
const auto& expected = test.second;
|
|
||||||
auto result = needsSparseRectification(size);
|
|
||||||
Q_ASSERT(expected == result);
|
|
||||||
result = needsSparseRectification(uvec2(size.y, size.x));
|
|
||||||
Q_ASSERT(expected == result);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<uvec2, uvec2>> SPARSE_SIZE_TESTS { {
|
|
||||||
// needs rectification
|
// needs rectification
|
||||||
{ { 1000, 1000 }, { 1024, 1024 } },
|
{ { 1000, 1000 }, { 1024, 1024 } },
|
||||||
{ { 1024, 1000 }, { 1024, 1024 } },
|
{ { 1024, 1000 }, { 1024, 1024 } },
|
||||||
} };
|
};
|
||||||
|
|
||||||
for (const auto& test : SPARSE_SIZE_TESTS) {
|
for (const auto& test : SPARSE_SIZE_TESTS) {
|
||||||
const auto& size = test.first;
|
const auto& size = test.first;
|
||||||
const auto& expected = test.second;
|
const auto& expected = test.second;
|
||||||
auto result = rectifyToSparseSize(size);
|
auto result = rectifySize(size);
|
||||||
Q_ASSERT(expected == result);
|
Q_ASSERT(expected == result);
|
||||||
result = rectifyToSparseSize(uvec2(size.y, size.x));
|
result = rectifySize(uvec2(size.y, size.x));
|
||||||
Q_ASSERT(expected == uvec2(result.y, result.x));
|
Q_ASSERT(expected == uvec2(result.y, result.x));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +1,41 @@
|
||||||
set(TARGET_NAME auto-tester)
|
set (TARGET_NAME auto-tester)
|
||||||
project(${TARGET_NAME})
|
project(${TARGET_NAME})
|
||||||
|
|
||||||
# Automatically run UIC and MOC. This replaces the older WRAP macros
|
# Automatically run UIC and MOC. This replaces the older WRAP macros
|
||||||
SET(CMAKE_AUTOUIC ON)
|
SET (CMAKE_AUTOUIC ON)
|
||||||
SET(CMAKE_AUTOMOC ON)
|
SET (CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
setup_hifi_project(Core Widgets)
|
setup_hifi_project (Core Widgets)
|
||||||
link_hifi_libraries()
|
link_hifi_libraries ()
|
||||||
|
|
||||||
# FIX: Qt was built with -reduce-relocations
|
# FIX: Qt was built with -reduce-relocations
|
||||||
if (Qt5_POSITION_INDEPENDENT_CODE)
|
if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
SET (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Qt includes
|
# Qt includes
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
include_directories(${Qt5Core_INCLUDE_DIRS})
|
include_directories (${Qt5Core_INCLUDE_DIRS})
|
||||||
include_directories(${Qt5Widgets_INCLUDE_DIRS})
|
include_directories (${Qt5Widgets_INCLUDE_DIRS})
|
||||||
|
|
||||||
set(QT_LIBRARIES Qt5::Core Qt5::Widgets)
|
set (QT_LIBRARIES Qt5::Core Qt5::Widgets)
|
||||||
|
|
||||||
# Find all sources files
|
|
||||||
file (GLOB_RECURSE SOURCES src/*.cpp)
|
|
||||||
file (GLOB_RECURSE HEADERS src/*.h)
|
|
||||||
file (GLOB_RECURSE UIS src/ui/*.ui)
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
# Do not show Console
|
# Do not show Console
|
||||||
set_property(TARGET auto-tester PROPERTY WIN32_EXECUTABLE true)
|
set_property (TARGET auto-tester PROPERTY WIN32_EXECUTABLE true)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(PROJECT_NAME ${SOURCES} ${HEADERS} ${UIS})
|
target_zlib()
|
||||||
|
add_dependency_external_projects (quazip)
|
||||||
|
find_package (QuaZip REQUIRED)
|
||||||
|
target_include_directories( ${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES})
|
||||||
|
|
||||||
target_link_libraries(PROJECT_NAME ${QT_LIBRARIES})
|
package_libraries_for_deployment()
|
||||||
|
|
||||||
# Copy required dll's.
|
|
||||||
add_custom_command(TARGET auto-tester POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:auto-tester>
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:auto-tester>
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:auto-tester>
|
|
||||||
)
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
add_paths_to_fixup_libs (${QUAZIP_DLL_PATH})
|
||||||
|
|
||||||
find_program(WINDEPLOYQT_COMMAND windeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH)
|
find_program(WINDEPLOYQT_COMMAND windeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH)
|
||||||
|
|
||||||
if (NOT WINDEPLOYQT_COMMAND)
|
if (NOT WINDEPLOYQT_COMMAND)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
#include "ImageComparer.h"
|
#include "ImageComparer.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
@ -26,11 +27,6 @@ double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) co
|
||||||
const double c1 = pow((K1 * L), 2);
|
const double c1 = pow((K1 * L), 2);
|
||||||
const double c2 = pow((K2 * L), 2);
|
const double c2 = pow((K2 * L), 2);
|
||||||
|
|
||||||
// Coefficients for luminosity calculation
|
|
||||||
const double R_Y = 0.212655f;
|
|
||||||
const double G_Y = 0.715158f;
|
|
||||||
const double B_Y = 0.072187f;
|
|
||||||
|
|
||||||
// First go over all full 8x8 blocks
|
// First go over all full 8x8 blocks
|
||||||
// This is done in 3 loops
|
// This is done in 3 loops
|
||||||
// 1) Read the pixels into a linear array (an optimization)
|
// 1) Read the pixels into a linear array (an optimization)
|
||||||
|
@ -116,4 +112,4 @@ double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) co
|
||||||
}
|
}
|
||||||
|
|
||||||
return ssim / windowCounter;
|
return ssim / windowCounter;
|
||||||
};
|
};
|
|
@ -13,18 +13,62 @@
|
||||||
#include <QtCore/QTextStream>
|
#include <QtCore/QTextStream>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
|
|
||||||
|
#include <quazip5/quazip.h>
|
||||||
|
#include <quazip5/JlCompress.h>
|
||||||
|
|
||||||
Test::Test() {
|
Test::Test() {
|
||||||
snapshotFilenameFormat = QRegularExpression("hifi-snap-by-.+-on-\\d\\d\\d\\d-\\d\\d-\\d\\d_\\d\\d-\\d\\d-\\d\\d.jpg");
|
snapshotFilenameFormat = QRegularExpression("hifi-snap-by-.*-on-\\d\\d\\d\\d-\\d\\d-\\d\\d_\\d\\d-\\d\\d-\\d\\d.jpg");
|
||||||
|
|
||||||
expectedImageFilenameFormat = QRegularExpression("ExpectedImage_\\d+.jpg");
|
expectedImageFilenameFormat = QRegularExpression("ExpectedImage_\\d+.jpg");
|
||||||
|
|
||||||
mismatchWindow.setModal(true);
|
mismatchWindow.setModal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Test::compareImageLists(QStringList expectedImages, QStringList resultImages) {
|
bool Test::createTestResultsFolderPathIfNeeded(QString directory) {
|
||||||
|
// The test results folder is located in the root of the tests (i.e. for recursive test evaluation)
|
||||||
|
if (testResultsFolderPath == "") {
|
||||||
|
testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER;
|
||||||
|
QDir testResultsFolder(testResultsFolderPath);
|
||||||
|
|
||||||
|
if (testResultsFolder.exists()) {
|
||||||
|
testResultsFolder.removeRecursively();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new test results folder
|
||||||
|
return QDir().mkdir(testResultsFolderPath);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Test::zipAndDeleteTestResultsFolder() {
|
||||||
|
QString zippedResultsFileName { testResultsFolderPath + ".zip" };
|
||||||
|
QFileInfo fileInfo(zippedResultsFileName);
|
||||||
|
if (!fileInfo.exists()) {
|
||||||
|
QFile::remove(zippedResultsFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir testResultsFolder(testResultsFolderPath);
|
||||||
|
if (!testResultsFolder.isEmpty()) {
|
||||||
|
JlCompress::compressDir(testResultsFolderPath + ".zip", testResultsFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
testResultsFolder.removeRecursively();
|
||||||
|
|
||||||
|
//In all cases, for the next evaluation
|
||||||
|
testResultsFolderPath = "";
|
||||||
|
index = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Test::compareImageLists(QStringList expectedImages, QStringList resultImages, QString testDirectory, bool interactiveMode, QProgressBar* progressBar) {
|
||||||
|
progressBar->setMinimum(0);
|
||||||
|
progressBar->setMaximum(expectedImages.length() - 1);
|
||||||
|
progressBar->setValue(0);
|
||||||
|
progressBar->setVisible(true);
|
||||||
|
|
||||||
// Loop over both lists and compare each pair of images
|
// Loop over both lists and compare each pair of images
|
||||||
// Quit loop if user has aborted due to a failed test.
|
// Quit loop if user has aborted due to a failed test.
|
||||||
const double THRESHOLD{ 0.999 };
|
const double THRESHOLD { 0.999 };
|
||||||
bool success{ true };
|
bool success{ true };
|
||||||
bool keepOn{ true };
|
bool keepOn{ true };
|
||||||
for (int i = 0; keepOn && i < expectedImages.length(); ++i) {
|
for (int i = 0; keepOn && i < expectedImages.length(); ++i) {
|
||||||
|
@ -45,42 +89,107 @@ bool Test::compareImageLists(QStringList expectedImages, QStringList resultImage
|
||||||
}
|
}
|
||||||
|
|
||||||
if (similarityIndex < THRESHOLD) {
|
if (similarityIndex < THRESHOLD) {
|
||||||
mismatchWindow.setTestFailure(TestFailure{
|
TestFailure testFailure = TestFailure{
|
||||||
(float)similarityIndex,
|
(float)similarityIndex,
|
||||||
expectedImages[i].left(expectedImages[i].lastIndexOf("/") + 1), // path to the test (including trailing /)
|
expectedImages[i].left(expectedImages[i].lastIndexOf("/") + 1), // path to the test (including trailing /)
|
||||||
QFileInfo(expectedImages[i].toStdString().c_str()).fileName(), // filename of expected image
|
QFileInfo(expectedImages[i].toStdString().c_str()).fileName(), // filename of expected image
|
||||||
QFileInfo(resultImages[i].toStdString().c_str()).fileName() // filename of result image
|
QFileInfo(resultImages[i].toStdString().c_str()).fileName() // filename of result image
|
||||||
});
|
};
|
||||||
|
|
||||||
mismatchWindow.exec();
|
mismatchWindow.setTestFailure(testFailure);
|
||||||
|
|
||||||
switch (mismatchWindow.getUserResponse()) {
|
if (!interactiveMode) {
|
||||||
case USER_RESPONSE_PASS:
|
appendTestResultsToFile(testResultsFolderPath, testFailure, mismatchWindow.getComparisonImage());
|
||||||
break;
|
success = false;
|
||||||
case USE_RESPONSE_FAIL:
|
} else {
|
||||||
success = false;
|
mismatchWindow.exec();
|
||||||
break;
|
|
||||||
case USER_RESPONSE_ABORT:
|
switch (mismatchWindow.getUserResponse()) {
|
||||||
keepOn = false;
|
case USER_RESPONSE_PASS:
|
||||||
success = false;
|
break;
|
||||||
break;
|
case USE_RESPONSE_FAIL:
|
||||||
default:
|
appendTestResultsToFile(testResultsFolderPath, testFailure, mismatchWindow.getComparisonImage());
|
||||||
assert(false);
|
success = false;
|
||||||
break;
|
break;
|
||||||
|
case USER_RESPONSE_ABORT:
|
||||||
|
keepOn = false;
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
progressBar->setValue(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
progressBar->setVisible(false);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Test::evaluateTests() {
|
void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
|
||||||
|
if (!QDir().exists(testResultsFolderPath)) {
|
||||||
|
messageBox.critical(0, "Internal error", "Folder " + testResultsFolderPath + " not found");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString failureFolderPath { testResultsFolderPath + "/" + "Failure_" + QString::number(index) };
|
||||||
|
if (!QDir().mkdir(failureFolderPath)) {
|
||||||
|
messageBox.critical(0, "Internal error", "Failed to create folder " + failureFolderPath);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
|
||||||
|
QFile descriptionFile(failureFolderPath + "/" + TEST_RESULTS_FILENAME);
|
||||||
|
if (!descriptionFile.open(QIODevice::ReadWrite)) {
|
||||||
|
messageBox.critical(0, "Internal error", "Failed to create file " + TEST_RESULTS_FILENAME);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create text file describing the failure
|
||||||
|
QTextStream stream(&descriptionFile);
|
||||||
|
stream << "Test failed in folder " << testFailure._pathname.left(testFailure._pathname.length() - 1) << endl; // remove trailing '/'
|
||||||
|
stream << "Expected image was " << testFailure._expectedImageFilename << endl;
|
||||||
|
stream << "Actual image was " << testFailure._actualImageFilename << endl;
|
||||||
|
stream << "Similarity index was " << testFailure._error << endl;
|
||||||
|
|
||||||
|
descriptionFile.close();
|
||||||
|
|
||||||
|
// Copy expected and actual images, and save the difference image
|
||||||
|
QString sourceFile;
|
||||||
|
QString destinationFile;
|
||||||
|
|
||||||
|
sourceFile = testFailure._pathname + testFailure._expectedImageFilename;
|
||||||
|
destinationFile = failureFolderPath + "/" + "Expected Image.jpg";
|
||||||
|
if (!QFile::copy(sourceFile, destinationFile)) {
|
||||||
|
messageBox.critical(0, "Internal error", "Failed to copy " + sourceFile + " to " + destinationFile);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceFile = testFailure._pathname + testFailure._actualImageFilename;
|
||||||
|
destinationFile = failureFolderPath + "/" + "Actual Image.jpg";
|
||||||
|
if (!QFile::copy(sourceFile, destinationFile)) {
|
||||||
|
messageBox.critical(0, "Internal error", "Failed to copy " + sourceFile + " to " + destinationFile);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
comparisonImage.save(failureFolderPath + "/" + "Difference Image.jpg");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Test::evaluateTests(bool interactiveMode, QProgressBar* progressBar) {
|
||||||
// Get list of JPEG images in folder, sorted by name
|
// Get list of JPEG images in folder, sorted by name
|
||||||
QString pathToImageDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
QString pathToImageDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
||||||
if (pathToImageDirectory == "") {
|
if (pathToImageDirectory == "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Leave if test results folder could not be created
|
||||||
|
if (!createTestResultsFolderPathIfNeeded(pathToImageDirectory)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QStringList sortedImageFilenames = createListOfAllJPEGimagesInDirectory(pathToImageDirectory);
|
QStringList sortedImageFilenames = createListOfAllJPEGimagesInDirectory(pathToImageDirectory);
|
||||||
|
|
||||||
// Separate images into two lists. The first is the expected images, the second is the test results
|
// Separate images into two lists. The first is the expected images, the second is the test results
|
||||||
|
@ -107,36 +216,57 @@ void Test::evaluateTests() {
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = compareImageLists(expectedImages, resultImages);
|
bool success = compareImageLists(expectedImages, resultImages, pathToImageDirectory, interactiveMode, progressBar);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
messageBox.information(0, "Success", "All images are as expected");
|
messageBox.information(0, "Success", "All images are as expected");
|
||||||
} else {
|
} else {
|
||||||
messageBox.information(0, "Failure", "One or more images are not as expected");
|
messageBox.information(0, "Failure", "One or more images are not as expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zipAndDeleteTestResultsFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Test::isAValidDirectory(QString pathname) {
|
||||||
|
// Only process directories
|
||||||
|
QDir dir(pathname);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore '.', '..' directories
|
||||||
|
if (pathname[pathname.length() - 1] == '.') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Two criteria are used to decide if a folder contains valid test results.
|
// Two criteria are used to decide if a folder contains valid test results.
|
||||||
// 1) a 'test'js' file exists in the folder
|
// 1) a 'test'js' file exists in the folder
|
||||||
// 2) the folder has the same number of actual and expected images
|
// 2) the folder has the same number of actual and expected images
|
||||||
void Test::evaluateTestsRecursively() {
|
void Test::evaluateTestsRecursively(bool interactiveMode, QProgressBar* progressBar) {
|
||||||
// Select folder to start recursing from
|
// Select folder to start recursing from
|
||||||
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", ".", QFileDialog::ShowDirsOnly);
|
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", ".", QFileDialog::ShowDirsOnly);
|
||||||
if (topLevelDirectory == "") {
|
if (topLevelDirectory == "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Leave if test results folder could not be created
|
||||||
|
if (!createTestResultsFolderPathIfNeeded(topLevelDirectory)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool success{ true };
|
bool success{ true };
|
||||||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
QString directory = it.next();
|
QString directory = it.next();
|
||||||
if (directory[directory.length() - 1] == '.') {
|
|
||||||
// ignore '.', '..' directories
|
if (!isAValidDirectory(directory)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
const QString testPathname{ directory + "/" + TEST_FILENAME };
|
||||||
const QString testPathname{ directory + "/" + testFilename };
|
|
||||||
QFileInfo fileInfo(testPathname);
|
QFileInfo fileInfo(testPathname);
|
||||||
if (!fileInfo.exists()) {
|
if (!fileInfo.exists()) {
|
||||||
// Folder does not contain 'test.js'
|
// Folder does not contain 'test.js'
|
||||||
|
@ -164,7 +294,7 @@ void Test::evaluateTestsRecursively() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set success to false if any test has failed
|
// Set success to false if any test has failed
|
||||||
success &= compareImageLists(expectedImages, resultImages);
|
success &= compareImageLists(expectedImages, resultImages, directory, interactiveMode, progressBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
|
@ -172,6 +302,8 @@ void Test::evaluateTestsRecursively() {
|
||||||
} else {
|
} else {
|
||||||
messageBox.information(0, "Failure", "One or more images are not as expected");
|
messageBox.information(0, "Failure", "One or more images are not as expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zipAndDeleteTestResultsFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Test::importTest(QTextStream& textStream, const QString& testPathname, int testNumber) {
|
void Test::importTest(QTextStream& textStream, const QString& testPathname, int testNumber) {
|
||||||
|
@ -191,7 +323,8 @@ void Test::createRecursiveScript() {
|
||||||
if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
messageBox.critical(0,
|
messageBox.critical(0,
|
||||||
"Internal Error",
|
"Internal Error",
|
||||||
"Failed to create \"allTests.js\" in directory \"" + topLevelDirectory + "\"");
|
"Failed to create \"allTests.js\" in directory \"" + topLevelDirectory + "\""
|
||||||
|
);
|
||||||
|
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
@ -206,7 +339,7 @@ void Test::createRecursiveScript() {
|
||||||
QVector<QString> testPathnames;
|
QVector<QString> testPathnames;
|
||||||
|
|
||||||
// First test if top-level folder has a test.js file
|
// First test if top-level folder has a test.js file
|
||||||
const QString testPathname{ topLevelDirectory + "/" + testFilename };
|
const QString testPathname{ topLevelDirectory + "/" + TEST_FILENAME };
|
||||||
QFileInfo fileInfo(testPathname);
|
QFileInfo fileInfo(testPathname);
|
||||||
if (fileInfo.exists()) {
|
if (fileInfo.exists()) {
|
||||||
// Current folder contains a test
|
// Current folder contains a test
|
||||||
|
@ -219,12 +352,14 @@ void Test::createRecursiveScript() {
|
||||||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
QString directory = it.next();
|
QString directory = it.next();
|
||||||
if (directory[directory.length() - 1] == '.') {
|
|
||||||
// ignore '.', '..' directories
|
// Only process directories
|
||||||
|
QDir dir(directory);
|
||||||
|
if (!isAValidDirectory(directory)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString testPathname{ directory + "/" + testFilename };
|
const QString testPathname{ directory + "/" + TEST_FILENAME };
|
||||||
QFileInfo fileInfo(testPathname);
|
QFileInfo fileInfo(testPathname);
|
||||||
if (fileInfo.exists()) {
|
if (fileInfo.exists()) {
|
||||||
// Current folder contains a test
|
// Current folder contains a test
|
||||||
|
@ -264,7 +399,7 @@ void Test::createRecursiveScript() {
|
||||||
// The script produced will look as follows:
|
// The script produced will look as follows:
|
||||||
// if (test1HasNotStarted) {
|
// if (test1HasNotStarted) {
|
||||||
// test1HasNotStarted = false;
|
// test1HasNotStarted = false;
|
||||||
// test1.test();
|
// test1.test("auto");
|
||||||
// print("******started test 1******");
|
// print("******started test 1******");
|
||||||
// }
|
// }
|
||||||
// |
|
// |
|
||||||
|
@ -287,7 +422,7 @@ void Test::createRecursiveScript() {
|
||||||
textStream << tab << tab << "if (test" << i - 1 << ".complete && test" << i << "HasNotStarted) {" << endl;
|
textStream << tab << tab << "if (test" << i - 1 << ".complete && test" << i << "HasNotStarted) {" << endl;
|
||||||
}
|
}
|
||||||
textStream << tab << tab << tab << "test" << i << "HasNotStarted = false;" << endl;
|
textStream << tab << tab << tab << "test" << i << "HasNotStarted = false;" << endl;
|
||||||
textStream << tab << tab << tab << "test" << i << "." << testFunction << "();" << endl;
|
textStream << tab << tab << tab << "test" << i << "." << testFunction << "(\"auto\");" << endl;
|
||||||
textStream << tab << tab << tab << "print(\"******started test " << i << "******\");" << endl;
|
textStream << tab << tab << tab << "print(\"******started test " << i << "******\");" << endl;
|
||||||
|
|
||||||
textStream << tab << tab << "}" << endl << endl;
|
textStream << tab << tab << "}" << endl << endl;
|
||||||
|
@ -366,6 +501,41 @@ void Test::createTest() {
|
||||||
messageBox.information(0, "Success", "Test images have been created");
|
messageBox.information(0, "Success", "Test images have been created");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Test::deleteOldSnapshots() {
|
||||||
|
// Select folder to start recursing from
|
||||||
|
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select root folder for snapshot deletion", ".", QFileDialog::ShowDirsOnly);
|
||||||
|
if (topLevelDirectory == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse over folders
|
||||||
|
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
QString directory = it.next();
|
||||||
|
|
||||||
|
// Only process directories
|
||||||
|
QDir dir(directory);
|
||||||
|
if (!isAValidDirectory(directory)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList sortedImageFilenames = createListOfAllJPEGimagesInDirectory(directory);
|
||||||
|
|
||||||
|
// Delete any file that is a snapshot (NOT the Expected Images)
|
||||||
|
QStringList expectedImages;
|
||||||
|
QStringList resultImages;
|
||||||
|
foreach(QString currentFilename, sortedImageFilenames) {
|
||||||
|
QString fullCurrentFilename = directory + "/" + currentFilename;
|
||||||
|
if (isInSnapshotFilenameFormat(currentFilename)) {
|
||||||
|
if (!QFile::remove(fullCurrentFilename)) {
|
||||||
|
messageBox.critical(0, "Error", "Could not delete existing file: " + currentFilename + "\nSnapshot deletion aborted");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QStringList Test::createListOfAllJPEGimagesInDirectory(QString pathToImageDirectory) {
|
QStringList Test::createListOfAllJPEGimagesInDirectory(QString pathToImageDirectory) {
|
||||||
imageDirectory = QDir(pathToImageDirectory);
|
imageDirectory = QDir(pathToImageDirectory);
|
||||||
QStringList nameFilters;
|
QStringList nameFilters;
|
||||||
|
@ -374,6 +544,7 @@ QStringList Test::createListOfAllJPEGimagesInDirectory(QString pathToImageDirect
|
||||||
return imageDirectory.entryList(nameFilters, QDir::Files, QDir::Name);
|
return imageDirectory.entryList(nameFilters, QDir::Files, QDir::Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use regular expressions to check if files are in specific format
|
||||||
bool Test::isInSnapshotFilenameFormat(QString filename) {
|
bool Test::isInSnapshotFilenameFormat(QString filename) {
|
||||||
return (snapshotFilenameFormat.match(filename).hasMatch());
|
return (snapshotFilenameFormat.match(filename).hasMatch());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//
|
//
|
||||||
// Test.h
|
// Test.h
|
||||||
// zone/ambientLightInheritence
|
|
||||||
//
|
//
|
||||||
// Created by Nissim Hadar on 2 Nov 2017.
|
// Created by Nissim Hadar on 2 Nov 2017.
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
@ -15,6 +14,7 @@
|
||||||
#include <QtWidgets/QFileDialog>
|
#include <QtWidgets/QFileDialog>
|
||||||
#include <QtWidgets/QMessageBox>
|
#include <QtWidgets/QMessageBox>
|
||||||
#include <QtCore/QRegularExpression>
|
#include <QtCore/QRegularExpression>
|
||||||
|
#include <QProgressBar>
|
||||||
|
|
||||||
#include "ImageComparer.h"
|
#include "ImageComparer.h"
|
||||||
#include "ui/MismatchWindow.h"
|
#include "ui/MismatchWindow.h"
|
||||||
|
@ -23,10 +23,13 @@ class Test {
|
||||||
public:
|
public:
|
||||||
Test();
|
Test();
|
||||||
|
|
||||||
void evaluateTests();
|
void evaluateTests(bool interactiveMode, QProgressBar* progressBar);
|
||||||
void evaluateTestsRecursively();
|
void evaluateTestsRecursively(bool interactiveMode, QProgressBar* progressBar);
|
||||||
void createRecursiveScript();
|
void createRecursiveScript();
|
||||||
void createTest();
|
void createTest();
|
||||||
|
void deleteOldSnapshots();
|
||||||
|
|
||||||
|
bool compareImageLists(QStringList expectedImages, QStringList resultImages, QString testDirectory, bool interactiveMode, QProgressBar* progressBar);
|
||||||
|
|
||||||
QStringList createListOfAllJPEGimagesInDirectory(QString pathToImageDirectory);
|
QStringList createListOfAllJPEGimagesInDirectory(QString pathToImageDirectory);
|
||||||
|
|
||||||
|
@ -35,8 +38,17 @@ public:
|
||||||
|
|
||||||
void importTest(QTextStream& textStream, const QString& testPathname, int testNumber);
|
void importTest(QTextStream& textStream, const QString& testPathname, int testNumber);
|
||||||
|
|
||||||
|
void appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage);
|
||||||
|
|
||||||
|
bool createTestResultsFolderPathIfNeeded(QString directory);
|
||||||
|
void zipAndDeleteTestResultsFolder();
|
||||||
|
|
||||||
|
bool isAValidDirectory(QString pathname);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QString testFilename{ "test.js" };
|
const QString TEST_FILENAME { "test.js" };
|
||||||
|
const QString TEST_RESULTS_FOLDER { "TestResults" };
|
||||||
|
const QString TEST_RESULTS_FILENAME { "TestResults.txt" };
|
||||||
|
|
||||||
QMessageBox messageBox;
|
QMessageBox messageBox;
|
||||||
|
|
||||||
|
@ -49,7 +61,9 @@ private:
|
||||||
|
|
||||||
ImageComparer imageComparer;
|
ImageComparer imageComparer;
|
||||||
|
|
||||||
bool compareImageLists(QStringList expectedImages, QStringList resultImages);
|
|
||||||
|
QString testResultsFolderPath { "" };
|
||||||
|
int index { 1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_test_h
|
#endif // hifi_test_h
|
|
@ -34,4 +34,9 @@ enum UserResponse {
|
||||||
USER_RESPONSE_ABORT
|
USER_RESPONSE_ABORT
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_common_h
|
// Coefficients for luminosity calculation
|
||||||
|
const double R_Y = 0.212655f;
|
||||||
|
const double G_Y = 0.715158f;
|
||||||
|
const double B_Y = 0.072187f;
|
||||||
|
|
||||||
|
#endif // hifi_common_h
|
|
@ -17,4 +17,4 @@ int main(int argc, char *argv[]) {
|
||||||
autoTester.show();
|
autoTester.show();
|
||||||
|
|
||||||
return application.exec();
|
return application.exec();
|
||||||
}
|
}
|
|
@ -12,14 +12,18 @@
|
||||||
|
|
||||||
AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
|
AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
|
||||||
ui.setupUi(this);
|
ui.setupUi(this);
|
||||||
|
|
||||||
|
ui.checkBoxInteractiveMode->setChecked(true);
|
||||||
|
|
||||||
|
ui.progressBar->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTester::on_evaluateTestsButton_clicked() {
|
void AutoTester::on_evaluateTestsButton_clicked() {
|
||||||
test.evaluateTests();
|
test.evaluateTests(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTester::on_evaluateTestsRecursivelyButton_clicked() {
|
void AutoTester::on_evaluateTestsRecursivelyButton_clicked() {
|
||||||
test.evaluateTestsRecursively();
|
test.evaluateTestsRecursively(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTester::on_createRecursiveScriptButton_clicked() {
|
void AutoTester::on_createRecursiveScriptButton_clicked() {
|
||||||
|
@ -30,6 +34,10 @@ void AutoTester::on_createTestButton_clicked() {
|
||||||
test.createTest();
|
test.createTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AutoTester::on_deleteOldSnapshotsButton_clicked() {
|
||||||
|
test.deleteOldSnapshots();
|
||||||
|
}
|
||||||
|
|
||||||
void AutoTester::on_closeButton_clicked() {
|
void AutoTester::on_closeButton_clicked() {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
//
|
//
|
||||||
// AutoTester.h
|
// AutoTester.h
|
||||||
// zone/ambientLightInheritence
|
|
||||||
//
|
//
|
||||||
// Created by Nissim Hadar on 2 Nov 2017.
|
// Created by Nissim Hadar on 2 Nov 2017.
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
@ -22,10 +21,11 @@ public:
|
||||||
AutoTester(QWidget *parent = Q_NULLPTR);
|
AutoTester(QWidget *parent = Q_NULLPTR);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_evaluateTestsButton_clicked();
|
void on_evaluateTestsButton_clicked();
|
||||||
void on_evaluateTestsRecursivelyButton_clicked();
|
void on_evaluateTestsRecursivelyButton_clicked();
|
||||||
void on_createRecursiveScriptButton_clicked();
|
void on_createRecursiveScriptButton_clicked();
|
||||||
void on_createTestButton_clicked();
|
void on_createTestButton_clicked();
|
||||||
|
void on_deleteOldSnapshotsButton_clicked();
|
||||||
void on_closeButton_clicked();
|
void on_closeButton_clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>286</width>
|
<width>607</width>
|
||||||
<height>470</height>
|
<height>395</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -17,9 +17,9 @@
|
||||||
<widget class="QPushButton" name="closeButton">
|
<widget class="QPushButton" name="closeButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>60</x>
|
<x>190</x>
|
||||||
<y>360</y>
|
<y>300</y>
|
||||||
<width>160</width>
|
<width>220</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -30,9 +30,9 @@
|
||||||
<widget class="QPushButton" name="createTestButton">
|
<widget class="QPushButton" name="createTestButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>60</x>
|
<x>360</x>
|
||||||
<y>270</y>
|
<y>130</y>
|
||||||
<width>160</width>
|
<width>220</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -43,9 +43,9 @@
|
||||||
<widget class="QPushButton" name="evaluateTestsButton">
|
<widget class="QPushButton" name="evaluateTestsButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>60</x>
|
<x>20</x>
|
||||||
<y>20</y>
|
<y>75</y>
|
||||||
<width>160</width>
|
<width>220</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -56,9 +56,9 @@
|
||||||
<widget class="QPushButton" name="createRecursiveScriptButton">
|
<widget class="QPushButton" name="createRecursiveScriptButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>60</x>
|
<x>360</x>
|
||||||
<y>210</y>
|
<y>75</y>
|
||||||
<width>160</width>
|
<width>220</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -69,9 +69,9 @@
|
||||||
<widget class="QPushButton" name="evaluateTestsRecursivelyButton">
|
<widget class="QPushButton" name="evaluateTestsRecursivelyButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>60</x>
|
<x>20</x>
|
||||||
<y>75</y>
|
<y>130</y>
|
||||||
<width>160</width>
|
<width>220</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -79,13 +79,55 @@
|
||||||
<string>Evaluate Tests Recursively</string>
|
<string>Evaluate Tests Recursively</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QCheckBox" name="checkBoxInteractiveMode">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>23</x>
|
||||||
|
<y>40</y>
|
||||||
|
<width>131</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>If unchecked, will not show results during evaluation</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Interactive Mode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QProgressBar" name="progressBar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>20</x>
|
||||||
|
<y>190</y>
|
||||||
|
<width>255</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>24</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="deleteOldSnapshotsButton">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>360</x>
|
||||||
|
<y>240</y>
|
||||||
|
<width>220</width>
|
||||||
|
<height>40</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete Old Snapshots</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenuBar" name="menuBar">
|
<widget class="QMenuBar" name="menuBar">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>286</width>
|
<width>607</width>
|
||||||
<height>21</height>
|
<height>21</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -103,4 +145,4 @@
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
|
@ -11,11 +11,48 @@
|
||||||
|
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
MismatchWindow::MismatchWindow(QWidget *parent) : QDialog(parent) {
|
MismatchWindow::MismatchWindow(QWidget *parent) : QDialog(parent) {
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
|
||||||
expectedImage->setScaledContents(true);
|
expectedImage->setScaledContents(true);
|
||||||
resultImage->setScaledContents(true);
|
resultImage->setScaledContents(true);
|
||||||
|
diffImage->setScaledContents(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap MismatchWindow::computeDiffPixmap(QImage expectedImage, QImage resultImage) {
|
||||||
|
// This is an optimization, as QImage.setPixel() is embarrassingly slow
|
||||||
|
unsigned char* buffer = new unsigned char[expectedImage.height() * expectedImage.width() * 3];
|
||||||
|
|
||||||
|
// loop over each pixel
|
||||||
|
for (int y = 0; y < expectedImage.height(); ++y) {
|
||||||
|
for (int x = 0; x < expectedImage.width(); ++x) {
|
||||||
|
QRgb pixelP = expectedImage.pixel(QPoint(x, y));
|
||||||
|
QRgb pixelQ = resultImage.pixel(QPoint(x, y));
|
||||||
|
|
||||||
|
// Convert to luminance
|
||||||
|
double p = R_Y * qRed(pixelP) + G_Y * qGreen(pixelP) + B_Y * qBlue(pixelP);
|
||||||
|
double q = R_Y * qRed(pixelQ) + G_Y * qGreen(pixelQ) + B_Y * qBlue(pixelQ);
|
||||||
|
|
||||||
|
// The intensity value is modified to increase the brightness of the displayed image
|
||||||
|
double absoluteDifference = fabs(p - q) / 255.0;
|
||||||
|
double modifiedDifference = sqrt(absoluteDifference);
|
||||||
|
|
||||||
|
int difference = (int)(modifiedDifference * 255.0);
|
||||||
|
|
||||||
|
buffer[3 * (x + y * expectedImage.width()) + 0] = difference;
|
||||||
|
buffer[3 * (x + y * expectedImage.width()) + 1] = difference;
|
||||||
|
buffer[3 * (x + y * expectedImage.width()) + 2] = difference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage diffImage(buffer, expectedImage.width(), expectedImage.height(), QImage::Format_RGB888);
|
||||||
|
QPixmap resultPixmap = QPixmap::fromImage(diffImage);
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
return resultPixmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MismatchWindow::setTestFailure(TestFailure testFailure) {
|
void MismatchWindow::setTestFailure(TestFailure testFailure) {
|
||||||
|
@ -24,10 +61,19 @@ void MismatchWindow::setTestFailure(TestFailure testFailure) {
|
||||||
imagePath->setText("Path to test: " + testFailure._pathname);
|
imagePath->setText("Path to test: " + testFailure._pathname);
|
||||||
|
|
||||||
expectedFilename->setText(testFailure._expectedImageFilename);
|
expectedFilename->setText(testFailure._expectedImageFilename);
|
||||||
expectedImage->setPixmap(QPixmap(testFailure._pathname + testFailure._expectedImageFilename));
|
|
||||||
|
|
||||||
resultFilename->setText(testFailure._actualImageFilename);
|
resultFilename->setText(testFailure._actualImageFilename);
|
||||||
resultImage->setPixmap(QPixmap(testFailure._pathname + testFailure._actualImageFilename));
|
|
||||||
|
QPixmap expectedPixmap = QPixmap(testFailure._pathname + testFailure._expectedImageFilename);
|
||||||
|
QPixmap actualPixmap = QPixmap(testFailure._pathname + testFailure._actualImageFilename);
|
||||||
|
|
||||||
|
diffPixmap = computeDiffPixmap(
|
||||||
|
QImage(testFailure._pathname + testFailure._expectedImageFilename),
|
||||||
|
QImage(testFailure._pathname + testFailure._actualImageFilename)
|
||||||
|
);
|
||||||
|
|
||||||
|
expectedImage->setPixmap(expectedPixmap);
|
||||||
|
resultImage->setPixmap(actualPixmap);
|
||||||
|
diffImage->setPixmap(diffPixmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MismatchWindow::on_passTestButton_clicked() {
|
void MismatchWindow::on_passTestButton_clicked() {
|
||||||
|
@ -44,3 +90,7 @@ void MismatchWindow::on_abortTestsButton_clicked() {
|
||||||
_userResponse = USER_RESPONSE_ABORT;
|
_userResponse = USER_RESPONSE_ABORT;
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPixmap MismatchWindow::getComparisonImage() {
|
||||||
|
return diffPixmap;
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ public:
|
||||||
|
|
||||||
UserResponse getUserResponse() { return _userResponse; }
|
UserResponse getUserResponse() { return _userResponse; }
|
||||||
|
|
||||||
|
QPixmap computeDiffPixmap(QImage expectedImage, QImage resultImage);
|
||||||
|
QPixmap getComparisonImage();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_passTestButton_clicked();
|
void on_passTestButton_clicked();
|
||||||
void on_failTestButton_clicked();
|
void on_failTestButton_clicked();
|
||||||
|
@ -32,7 +35,9 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UserResponse _userResponse{ USER_RESPONSE_INVALID };
|
UserResponse _userResponse{ USER_RESPONSE_INVALID };
|
||||||
|
|
||||||
|
QPixmap diffPixmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_MismatchWindow_h
|
#endif // hifi_MismatchWindow_h
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1585</width>
|
<width>1782</width>
|
||||||
<height>694</height>
|
<height>942</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -16,10 +16,10 @@
|
||||||
<widget class="QLabel" name="expectedImage">
|
<widget class="QLabel" name="expectedImage">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>20</x>
|
<x>10</x>
|
||||||
<y>170</y>
|
<y>25</y>
|
||||||
<width>720</width>
|
<width>800</width>
|
||||||
<height>362</height>
|
<height>450</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -29,28 +29,41 @@
|
||||||
<widget class="QLabel" name="resultImage">
|
<widget class="QLabel" name="resultImage">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>760</x>
|
<x>900</x>
|
||||||
<y>170</y>
|
<y>25</y>
|
||||||
<width>720</width>
|
<width>800</width>
|
||||||
<height>362</height>
|
<height>450</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>result image</string>
|
<string>result image</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QLabel" name="diffImage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>540</x>
|
||||||
|
<y>480</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>450</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>diff image</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
<widget class="QLabel" name="resultFilename">
|
<widget class="QLabel" name="resultFilename">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>760</x>
|
<x>60</x>
|
||||||
<y>90</y>
|
<y>660</y>
|
||||||
<width>800</width>
|
<width>480</width>
|
||||||
<height>28</height>
|
<height>28</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<pointsize>16</pointsize>
|
<pointsize>12</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -60,15 +73,15 @@
|
||||||
<widget class="QLabel" name="expectedFilename">
|
<widget class="QLabel" name="expectedFilename">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>40</x>
|
<x>60</x>
|
||||||
<y>90</y>
|
<y>630</y>
|
||||||
<width>700</width>
|
<width>480</width>
|
||||||
<height>28</height>
|
<height>28</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<pointsize>16</pointsize>
|
<pointsize>12</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -78,15 +91,15 @@
|
||||||
<widget class="QLabel" name="imagePath">
|
<widget class="QLabel" name="imagePath">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>40</x>
|
<x>20</x>
|
||||||
<y>30</y>
|
<y>600</y>
|
||||||
<width>1200</width>
|
<width>1200</width>
|
||||||
<height>28</height>
|
<height>28</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<pointsize>16</pointsize>
|
<pointsize>12</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -97,7 +110,7 @@
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>30</x>
|
<x>30</x>
|
||||||
<y>600</y>
|
<y>790</y>
|
||||||
<width>75</width>
|
<width>75</width>
|
||||||
<height>23</height>
|
<height>23</height>
|
||||||
</rect>
|
</rect>
|
||||||
|
@ -109,8 +122,8 @@
|
||||||
<widget class="QPushButton" name="failTestButton">
|
<widget class="QPushButton" name="failTestButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>330</x>
|
<x>120</x>
|
||||||
<y>600</y>
|
<y>790</y>
|
||||||
<width>75</width>
|
<width>75</width>
|
||||||
<height>23</height>
|
<height>23</height>
|
||||||
</rect>
|
</rect>
|
||||||
|
@ -122,36 +135,62 @@
|
||||||
<widget class="QPushButton" name="abortTestsButton">
|
<widget class="QPushButton" name="abortTestsButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>630</x>
|
<x>210</x>
|
||||||
<y>600</y>
|
<y>790</y>
|
||||||
<width>75</width>
|
<width>121</width>
|
||||||
<height>23</height>
|
<height>23</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Abort Tests</string>
|
<string>Abort current test</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QLabel" name="errorLabel">
|
<widget class="QLabel" name="errorLabel">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>810</x>
|
<x>30</x>
|
||||||
<y>600</y>
|
<y>850</y>
|
||||||
<width>720</width>
|
<width>500</width>
|
||||||
<height>28</height>
|
<height>28</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<pointsize>16</pointsize>
|
<pointsize>12</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>similarity</string>
|
<string>similarity</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>30</x>
|
||||||
|
<y>5</y>
|
||||||
|
<width>151</width>
|
||||||
|
<height>16</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Expected Image</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>930</x>
|
||||||
|
<y>5</y>
|
||||||
|
<width>151</width>
|
||||||
|
<height>16</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Actual Image</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
|
@ -195,9 +195,9 @@ void DomainBaker::enumerateEntities() {
|
||||||
auto filename = modelURL.fileName();
|
auto filename = modelURL.fileName();
|
||||||
auto baseName = filename.left(filename.lastIndexOf('.'));
|
auto baseName = filename.left(filename.lastIndexOf('.'));
|
||||||
auto subDirName = "/" + baseName;
|
auto subDirName = "/" + baseName;
|
||||||
int i = 0;
|
int i = 1;
|
||||||
while (QDir(_contentOutputPath + subDirName).exists()) {
|
while (QDir(_contentOutputPath + subDirName).exists()) {
|
||||||
subDirName = "/" + baseName + "-" + i++;
|
subDirName = "/" + baseName + "-" + QString::number(i++);
|
||||||
}
|
}
|
||||||
QSharedPointer<FBXBaker> baker {
|
QSharedPointer<FBXBaker> baker {
|
||||||
new FBXBaker(modelURL, []() -> QThread* {
|
new FBXBaker(modelURL, []() -> QThread* {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue