mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 14:14:07 +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
BUILD_LINUX_CHEATSHEET.md
assignment-client/src
interface
resources
icons/tablet-icons
images
qml/hifi
src
Application.cppLODManager.cppLODManager.h
commerce
raypick
scripting
ui/overlays
Circle3DOverlay.cppCube3DOverlay.cppGrid3DOverlay.cppImage3DOverlay.cppLine3DOverlay.cppModelOverlay.cppOverlay.cppOverlay.hOverlayPanel.cppOverlayPanel.hOverlays.cppOverlays.hOverlaysPayload.cppPanelAttachable.cppPanelAttachable.hRectangle3DOverlay.cppShape3DOverlay.cppSphere3DOverlay.cppText3DOverlay.cppWeb3DOverlay.cpp
libraries
avatars-renderer/src/avatars-renderer
display-plugins/src/display-plugins/hmd
entities-renderer/src
entities/src
image/src/image
networking/src
physics/src
pointers/src
render-utils/src
shared/src
ui/src
scripts
developer
system
tests/gpu-test/src
tools
|
@ -1,5 +1,7 @@
|
|||
# this guide is specific to Ubuntu 16.04.
|
||||
# deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
|
||||
## This guide is specific to Ubuntu 16.04.
|
||||
Deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
|
||||
|
||||
```
|
||||
sudo su -
|
||||
apt-get -y update
|
||||
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 install -y hifi-domain-server
|
||||
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-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 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).
|
||||
|
||||
# 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).
|
||||
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.
|
||||
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
|
||||
```
|
||||
0 */1 * * * root apt-get update
|
||||
1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server
|
||||
2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client
|
||||
```
|
||||
|
|
|
@ -343,7 +343,6 @@ void Agent::scriptRequestFinished() {
|
|||
|
||||
void Agent::executeScript() {
|
||||
_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);
|
||||
|
||||
|
|
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 (image error) 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 ![]() (image error) Size: 52 KiB |
|
@ -102,7 +102,7 @@ Column {
|
|||
'include_actions=' + actions,
|
||||
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
||||
'require_online=true',
|
||||
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
|
||||
'protocol=' + encodeURIComponent(Window.protocolSignature()),
|
||||
'page=' + pageNumber
|
||||
];
|
||||
var url = metaverseBase + 'user_stories?' + options.join('&');
|
||||
|
|
|
@ -392,7 +392,6 @@ Item {
|
|||
width: 118;
|
||||
height: paintedHeight;
|
||||
wrapMode: Text.WordWrap;
|
||||
font.bold: true;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import "."
|
|||
Overlay {
|
||||
id: root
|
||||
|
||||
Image {
|
||||
AnimatedImage {
|
||||
id: image
|
||||
property bool scaleFix: true
|
||||
property real xStart: 0
|
||||
|
|
|
@ -209,6 +209,7 @@
|
|||
#include "commerce/QmlCommerce.h"
|
||||
|
||||
#include "webbrowser/WebBrowserSuggestionsEngine.h"
|
||||
#include <DesktopPreviewProvider.h>
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
|
@ -573,8 +574,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
}
|
||||
};
|
||||
reportAndQuit("--protocolVersion", [&](FILE* fp) {
|
||||
DependencyManager::set<AddressManager>();
|
||||
auto version = DependencyManager::get<AddressManager>()->protocolVersion();
|
||||
auto version = protocolVersionsSignatureBase64();
|
||||
fputs(version.toLatin1().data(), fp);
|
||||
});
|
||||
reportAndQuit("--version", [&](FILE* fp) {
|
||||
|
@ -632,6 +632,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<PointerScriptingInterface>();
|
||||
DependencyManager::set<PickScriptingInterface>();
|
||||
DependencyManager::set<Cursor::Manager>();
|
||||
DependencyManager::set<DesktopPreviewProvider>();
|
||||
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
|
||||
DependencyManager::set<StatTracker>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
|
||||
|
@ -2093,6 +2094,11 @@ void Application::cleanupBeforeQuit() {
|
|||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
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";
|
||||
}
|
||||
|
||||
|
@ -4351,8 +4357,9 @@ void Application::updateLOD(float deltaTime) const {
|
|||
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
|
||||
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
|
||||
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
|
||||
float maxRenderTime = glm::max(gpuTime, glm::max(presentTime, engineRunTime));
|
||||
DependencyManager::get<LODManager>()->autoAdjustLOD(maxRenderTime, deltaTime);
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
lodManager->setRenderTimes(presentTime, engineRunTime, gpuTime);
|
||||
lodManager->autoAdjustLOD(deltaTime);
|
||||
} else {
|
||||
DependencyManager::get<LODManager>()->resetLODAdjust();
|
||||
}
|
||||
|
@ -5758,6 +5765,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
|
||||
|
||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());
|
||||
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
||||
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get<Snapshot>().data());
|
||||
|
|
|
@ -26,43 +26,50 @@ Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DO
|
|||
LODManager::LODManager() {
|
||||
}
|
||||
|
||||
float LODManager::getLODDecreaseFPS() {
|
||||
float LODManager::getLODDecreaseFPS() const {
|
||||
if (qApp->isHMDMode()) {
|
||||
return getHMDLODDecreaseFPS();
|
||||
}
|
||||
return getDesktopLODDecreaseFPS();
|
||||
}
|
||||
|
||||
float LODManager::getLODIncreaseFPS() {
|
||||
float LODManager::getLODIncreaseFPS() const {
|
||||
if (qApp->isHMDMode()) {
|
||||
return getHMDLODIncreaseFPS();
|
||||
}
|
||||
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).
|
||||
//
|
||||
// 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
|
||||
// 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.
|
||||
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
|
||||
// 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
|
||||
// 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_INCREMENT_FACTOR = 1.2f;
|
||||
|
||||
void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
||||
// compute time-weighted running average renderTime
|
||||
void LODManager::setRenderTimes(float presentTime, float engineRunTime, float gpuTime) {
|
||||
_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
|
||||
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) {
|
||||
// early exit
|
||||
return;
|
||||
|
@ -84,6 +91,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
|||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
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;
|
||||
}
|
||||
|
@ -105,6 +116,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
|||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
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;
|
||||
}
|
||||
|
@ -119,11 +134,6 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
|||
if (lodToolsDialog) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
void LODManager::setDesktopLODDecreaseFPS(float fps) {
|
||||
|
|
|
@ -37,7 +37,7 @@ class AABox;
|
|||
class LODManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
|
||||
public:
|
||||
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
|
||||
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
|
||||
|
@ -49,34 +49,56 @@ public:
|
|||
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
|
||||
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
|
||||
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
|
||||
|
||||
|
||||
// User Tweakable LOD Items
|
||||
Q_INVOKABLE QString getLODFeedbackText();
|
||||
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
|
||||
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
|
||||
|
||||
|
||||
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
|
||||
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
|
||||
Q_INVOKABLE float getLODDecreaseFPS();
|
||||
Q_INVOKABLE float getLODIncreaseFPS();
|
||||
|
||||
|
||||
Q_INVOKABLE float getLODDecreaseFPS() const;
|
||||
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);
|
||||
void autoAdjustLOD(float renderTime, float realTimeDelta);
|
||||
|
||||
void setRenderTimes(float presentTime, float engineRunTime, float gpuTime);
|
||||
void autoAdjustLOD(float realTimeDelta);
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
void resetLODAdjust();
|
||||
|
||||
|
||||
float getAverageRenderTime() const { return _avgRenderTime; };
|
||||
float getMaxTheoreticalFPS() const { return (float)MSECS_PER_SECOND / _avgRenderTime; };
|
||||
float getLODLevel() const;
|
||||
|
||||
signals:
|
||||
void LODIncreased();
|
||||
void LODDecreased();
|
||||
|
||||
|
||||
private:
|
||||
LODManager();
|
||||
|
||||
|
||||
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 _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "Ledger.h"
|
||||
#include "CommerceLogging.h"
|
||||
#include <NetworkingConstants.h>
|
||||
#include <AddressManager.h>
|
||||
|
||||
// inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}}
|
||||
// balance answers {status: 'success', data: {balance: integer}}
|
||||
|
@ -122,24 +123,33 @@ QString hfcString(const QJsonValue& sentValue, const QJsonValue& receivedValue)
|
|||
int sent = sentValue.toInt();
|
||||
int received = receivedValue.toInt();
|
||||
if (sent <= 0 && received <= 0) {
|
||||
return QString("-");
|
||||
return QString("0 HFC");
|
||||
}
|
||||
QString result;
|
||||
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) {
|
||||
result += QString("<br>");
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
|
@ -153,13 +163,13 @@ QString transactionString(const QJsonObject& valueObject) {
|
|||
QDateTime createdAt(QDateTime::fromSecsSinceEpoch(dateInteger, Qt::UTC));
|
||||
QString result;
|
||||
|
||||
if (sentCerts <= 0 && receivedCerts <= 0) {
|
||||
if (sentCerts <= 0 && receivedCerts <= 0 && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) {
|
||||
// this is an hfc transfer.
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
if (!message.isEmpty()) {
|
||||
|
@ -168,8 +178,8 @@ QString transactionString(const QJsonObject& valueObject) {
|
|||
} else {
|
||||
result += valueObject["message"].toString();
|
||||
}
|
||||
|
||||
// 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));
|
||||
return result;
|
||||
}
|
||||
|
@ -310,6 +320,7 @@ void Ledger::transferHfcToNode(const QString& hfc_key, const QString& nodeID, co
|
|||
transaction["node_id"] = nodeID;
|
||||
transaction["quantity"] = amount;
|
||||
transaction["message"] = optionalMessage;
|
||||
transaction["place_name"] = DependencyManager::get<AddressManager>()->getPlaceName();
|
||||
QJsonDocument transactionDoc{ transaction };
|
||||
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||
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();
|
||||
}
|
||||
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); }
|
||||
|
||||
/**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
|
||||
* Check if a Pointer is associated with the left hand.
|
||||
* @function Pointers.isLeftHand
|
||||
|
|
|
@ -277,7 +277,8 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString&
|
|||
if (!result.isEmpty()) {
|
||||
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();
|
||||
}
|
||||
|
||||
QString WindowScriptingInterface::protocolSignature() {
|
||||
return protocolVersionsSignatureBase64();
|
||||
}
|
||||
|
||||
int WindowScriptingInterface::getInnerWidth() {
|
||||
return qApp->getDeviceSize().x;
|
||||
}
|
||||
|
|
|
@ -197,18 +197,19 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* 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.
|
||||
* @deprecated A deprecated {@link Window.openFileChanged|openFileChanged} signal is also emitted when a file is chosen.
|
||||
* @function Window.browseAsync
|
||||
* @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} 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.
|
||||
* @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);
|
||||
* }
|
||||
* Window.openFileChanged.connect(onOpenFileChanged);
|
||||
* Window.browseChanged.connect(onBrowseChanged);
|
||||
*
|
||||
* Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)");
|
||||
* print("Script continues without waiting");
|
||||
|
@ -305,6 +306,13 @@ public slots:
|
|||
*/
|
||||
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
|
||||
* Copies text to the operating system's clipboard.
|
||||
* @function Window.copyToClipboard
|
||||
|
@ -652,9 +660,18 @@ signals:
|
|||
*/
|
||||
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
|
||||
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
|
||||
* @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.
|
||||
* @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
|
||||
* used.)
|
||||
* @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 {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
|
||||
* used.)
|
||||
* @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 {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
|
||||
* used.)
|
||||
* @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 {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
|
||||
* used.)
|
||||
* @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 {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
|
||||
* used.)
|
||||
* @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 {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
|
||||
* used.)
|
||||
* @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 {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),
|
||||
_colorPulse(0.0f),
|
||||
_color(DEFAULT_OVERLAY_COLOR),
|
||||
_visible(true),
|
||||
_anchor(NO_ANCHOR)
|
||||
_visible(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -49,8 +48,7 @@ Overlay::Overlay(const Overlay* overlay) :
|
|||
_alphaPulse(overlay->_alphaPulse),
|
||||
_colorPulse(overlay->_colorPulse),
|
||||
_color(overlay->_color),
|
||||
_visible(overlay->_visible),
|
||||
_anchor(overlay->_anchor)
|
||||
_visible(overlay->_visible)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -92,13 +90,6 @@ void Overlay::setProperties(const QVariantMap& properties) {
|
|||
bool visible = properties["visible"].toBool();
|
||||
setVisible(visible);
|
||||
}
|
||||
|
||||
if (properties["anchor"].isValid()) {
|
||||
QString property = properties["anchor"].toString();
|
||||
if (property == "MyAvatar") {
|
||||
setAnchor(MY_AVATAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
* used.)
|
||||
* @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) {
|
||||
if (property == "type") {
|
||||
|
@ -150,9 +139,6 @@ QVariant Overlay::getProperty(const QString& property) {
|
|||
if (property == "visible") {
|
||||
return _visible;
|
||||
}
|
||||
if (property == "anchor") {
|
||||
return _anchor == MY_AVATAR ? "MyAvatar" : "";
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
|
|
@ -26,11 +26,6 @@ class Overlay : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Anchor {
|
||||
NO_ANCHOR,
|
||||
MY_AVATAR
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Overlay> Pointer;
|
||||
typedef render::Payload<Overlay> Payload;
|
||||
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
|
||||
|
@ -63,7 +58,6 @@ public:
|
|||
virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; };
|
||||
xColor getColor();
|
||||
float getAlpha();
|
||||
Anchor getAnchor() const { return _anchor; }
|
||||
|
||||
float getPulseMax() const { return _pulseMax; }
|
||||
float getPulseMin() const { return _pulseMin; }
|
||||
|
@ -78,7 +72,6 @@ public:
|
|||
void setDrawHUDLayer(bool drawHUDLayer);
|
||||
void setColor(const xColor& color) { _color = color; }
|
||||
void setAlpha(float alpha) { _alpha = alpha; }
|
||||
void setAnchor(Anchor anchor) { _anchor = anchor; }
|
||||
|
||||
void setPulseMax(float value) { _pulseMax = value; }
|
||||
void setPulseMin(float value) { _pulseMin = value; }
|
||||
|
@ -118,7 +111,6 @@ protected:
|
|||
|
||||
xColor _color;
|
||||
bool _visible; // should the overlay be drawn at all
|
||||
Anchor _anchor;
|
||||
|
||||
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) {
|
||||
_overlaysToDelete.push_back(overlay);
|
||||
}
|
||||
#if OVERLAY_PANELS
|
||||
_panels.clear();
|
||||
#endif
|
||||
cleanupOverlaysToDelete();
|
||||
}
|
||||
|
||||
void Overlays::init() {
|
||||
#if OVERLAY_PANELS
|
||||
_scriptEngine = new QScriptEngine();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Overlays::update(float deltatime) {
|
||||
|
@ -300,12 +294,6 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
|
|||
|
||||
if (thisOverlay) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
emit overlayDeleted(id);
|
||||
}
|
||||
|
@ -424,49 +403,6 @@ QObject* Overlays::getOverlayObject(OverlayID id) {
|
|||
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) {
|
||||
if (!_enabled) {
|
||||
return UNKNOWN_OVERLAY_ID;
|
||||
|
@ -717,62 +653,6 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) {
|
|||
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) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include "Overlay.h"
|
||||
|
||||
#include "PanelAttachable.h"
|
||||
#include "OverlayPanel.h"
|
||||
|
||||
class PickRay;
|
||||
|
||||
|
@ -93,9 +92,6 @@ public:
|
|||
void enable();
|
||||
|
||||
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
|
||||
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
|
||||
|
@ -468,30 +464,6 @@ public slots:
|
|||
*/
|
||||
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
|
||||
* Generate a mouse press event on an overlay.
|
||||
* @function Overlays.sendMousePressOnOverlay
|
||||
|
@ -612,10 +584,6 @@ signals:
|
|||
*/
|
||||
void overlayDeleted(OverlayID id);
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
void panelDeleted(OverlayID id);
|
||||
#endif
|
||||
|
||||
/**jsdoc
|
||||
* 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).
|
||||
|
@ -732,15 +700,9 @@ private:
|
|||
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
|
||||
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
QMap<OverlayID, OverlayPanel::Pointer> _panels;
|
||||
#endif
|
||||
QList<Overlay::Pointer> _overlaysToDelete;
|
||||
unsigned int _stackOrder { 1 };
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
QScriptEngine* _scriptEngine;
|
||||
#endif
|
||||
bool _enabled = true;
|
||||
|
||||
PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
|
||||
|
|
|
@ -65,23 +65,7 @@ namespace render {
|
|||
}
|
||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
|
||||
if (args) {
|
||||
if (overlay->getAnchor() == Overlay::MY_AVATAR) {
|
||||
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);
|
||||
}
|
||||
overlay->render(args);
|
||||
}
|
||||
}
|
||||
template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay) {
|
||||
|
|
|
@ -13,18 +13,8 @@
|
|||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "OverlayPanel.h"
|
||||
|
||||
bool PanelAttachable::getParentVisible() const {
|
||||
#if OVERLAY_PANELS
|
||||
if (getParentPanel()) {
|
||||
return getParentPanel()->getVisible() && getParentPanel()->getParentVisible();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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) {
|
||||
const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz
|
||||
_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;
|
||||
}
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#ifndef hifi_PanelAttachable_h
|
||||
#define hifi_PanelAttachable_h
|
||||
|
||||
#define OVERLAY_PANELS 0
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
@ -44,18 +42,12 @@ class OverlayPanel;
|
|||
class PanelAttachable {
|
||||
public:
|
||||
// getters
|
||||
#if OVERLAY_PANELS
|
||||
std::shared_ptr<OverlayPanel> getParentPanel() const { return _parentPanel; }
|
||||
#endif
|
||||
glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); }
|
||||
glm::quat getOffsetRotation() const { return _offset.getRotation(); }
|
||||
glm::vec3 getOffsetScale() const { return _offset.getScale(); }
|
||||
bool getParentVisible() const;
|
||||
|
||||
// setters
|
||||
#if OVERLAY_PANELS
|
||||
void setParentPanel(std::shared_ptr<OverlayPanel> panel) { _parentPanel = panel; }
|
||||
#endif
|
||||
void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); }
|
||||
void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); }
|
||||
void setOffsetScale(float scale) { _offset.setScale(scale); }
|
||||
|
@ -71,9 +63,6 @@ protected:
|
|||
quint64 _transformExpiry = 0;
|
||||
|
||||
private:
|
||||
#if OVERLAY_PANELS
|
||||
std::shared_ptr<OverlayPanel> _parentPanel = nullptr;
|
||||
#endif
|
||||
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
|
||||
* used.)
|
||||
* @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 {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
|
||||
* used.)
|
||||
* @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 {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
|
||||
* used.)
|
||||
* @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 {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
|
||||
* used.)
|
||||
* @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 {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
|
||||
* used.)
|
||||
* @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 {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();
|
||||
if (!recentlyDettachedAvatarEntities.empty()) {
|
||||
// only lock this thread when absolutely necessary
|
||||
AvatarEntityMap avatarEntityData;
|
||||
_avatarEntitiesLock.withReadLock([&] {
|
||||
foreach (auto entityID, recentlyDettachedAvatarEntities) {
|
||||
if (!_avatarEntityData.contains(entityID)) {
|
||||
entityTree->deleteEntity(entityID, true, true);
|
||||
}
|
||||
}
|
||||
avatarEntityData = _avatarEntityData;
|
||||
});
|
||||
foreach (auto entityID, recentlyDettachedAvatarEntities) {
|
||||
if (!avatarEntityData.contains(entityID)) {
|
||||
entityTree->deleteEntity(entityID, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
// remove stale data hashes
|
||||
foreach (auto entityID, recentlyDettachedAvatarEntities) {
|
||||
|
@ -789,10 +791,19 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
|||
|
||||
// virtual
|
||||
void Avatar::simulateAttachments(float deltaTime) {
|
||||
assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size());
|
||||
PerformanceTimer perfTimer("attachments");
|
||||
for (int i = 0; i < (int)_attachmentModels.size(); i++) {
|
||||
const AttachmentData& attachment = _attachmentData.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);
|
||||
glm::vec3 jointPosition;
|
||||
glm::quat jointRotation;
|
||||
|
@ -1317,6 +1328,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
while ((int)_attachmentModels.size() > attachmentData.size()) {
|
||||
auto attachmentModel = _attachmentModels.back();
|
||||
_attachmentModels.pop_back();
|
||||
_attachmentModelsTexturesLoaded.pop_back();
|
||||
_attachmentsToRemove.push_back(attachmentModel);
|
||||
}
|
||||
|
||||
|
@ -1324,11 +1336,16 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
if (i == (int)_attachmentModels.size()) {
|
||||
// if number of attachments has been increased, we need to allocate a new model
|
||||
_attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()));
|
||||
}
|
||||
else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) {
|
||||
_attachmentModelsTexturesLoaded.push_back(false);
|
||||
} 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.
|
||||
_attachmentsToRemove.push_back(_attachmentModels[i]);
|
||||
_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);
|
||||
}
|
||||
|
|
|
@ -306,6 +306,7 @@ protected:
|
|||
|
||||
glm::vec3 _skeletonOffset;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentModels;
|
||||
std::vector<bool> _attachmentModelsTexturesLoaded;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentsToDelete;
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "../Logging.h"
|
||||
#include "../CompositorHelper.h"
|
||||
|
||||
#include "DesktopPreviewProvider.h"
|
||||
#include "render-utils/hmd_ui_vert.h"
|
||||
#include "render-utils/hmd_ui_frag.h"
|
||||
|
||||
|
@ -254,17 +255,9 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
swapBuffers();
|
||||
|
||||
} 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();
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
if (!_previewTexture) {
|
||||
_previewTexture = gpu::Texture::createStrict(
|
||||
QImage image = DesktopPreviewProvider::getInstance()->getPreviewDisabledImage(_vsyncEnabled);
|
||||
_previewTexture = gpu::Texture::createStrict(
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||
image.width(), image.height(),
|
||||
gpu::Texture::MAX_NUM_MIPS,
|
||||
|
@ -274,7 +267,6 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
_previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
_previewTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
_previewTexture->setAutoGenerateMips(true);
|
||||
}
|
||||
|
||||
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
|
||||
|
||||
|
|
|
@ -1394,8 +1394,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
// That is where _currentFrame and _lastAnimated were updated.
|
||||
if (_animating) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
|
||||
|
||||
if (!jointsMapped()) {
|
||||
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())) {
|
||||
animate(entity);
|
||||
|
|
|
@ -2086,6 +2086,10 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
auto removedActionType = action->getType();
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->setIsMine(false);
|
||||
_objectActions.remove(actionID);
|
||||
|
||||
if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) {
|
||||
_dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING;
|
||||
_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
|
||||
// and/or when children were linked
|
||||
}
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->setIsMine(false);
|
||||
_objectActions.remove(actionID);
|
||||
|
||||
if (simulation) {
|
||||
action->removeFromSimulation(simulation);
|
||||
|
|
|
@ -368,6 +368,7 @@ public:
|
|||
void* getPhysicsInfo() const { return _physicsInfo; }
|
||||
|
||||
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
||||
|
||||
EntityTreeElementPointer getElement() const { return _element; }
|
||||
EntityTreePointer getTree() const;
|
||||
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_GETTER(ignoreForCollisions, bool, setCollisionless, getCollisionless); // legacy support
|
||||
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(dynamic, bool, setDynamic);
|
||||
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(textColor, xColor, setTextColor);
|
||||
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(lifespan, float, setLifespan);
|
||||
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(collisionSoundURL, QString, setCollisionSoundURL);
|
||||
|
||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(hazeMode, HazeMode);
|
||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(keyLightMode, KeyLightMode);
|
||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(ambientLightMode, AmbientLightMode);
|
||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(skyboxMode, SkyboxMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(hazeMode, HazeMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode);
|
||||
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl);
|
||||
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); \
|
||||
if (P.isValid()) { \
|
||||
QString newValue = P.toVariant().toString(); \
|
||||
|
|
|
@ -19,15 +19,6 @@
|
|||
#include <QBuffer>
|
||||
#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 <Profile.h>
|
||||
#include <StatTracker.h>
|
||||
|
@ -37,6 +28,12 @@
|
|||
|
||||
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 MAX_TEXTURE_SIZE(4096);
|
||||
|
@ -51,25 +48,21 @@ static std::atomic<bool> compressNormalTextures { false };
|
|||
static std::atomic<bool> compressGrayscaleTextures { false };
|
||||
static std::atomic<bool> compressCubeTextures { false };
|
||||
|
||||
bool needsSparseRectification(const glm::uvec2& size) {
|
||||
// Don't attempt to rectify small textures (textures less than the sparse page size in any dimension)
|
||||
if (glm::any(glm::lessThan(size, SPARSE_PAGE_SIZE))) {
|
||||
return false;
|
||||
uint rectifyDimension(const uint& dimension) {
|
||||
if (dimension < SPARSE_PAGE_SIZE.x) {
|
||||
uint newSize = SPARSE_PAGE_SIZE.x;
|
||||
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 pages = ((size / SPARSE_PAGE_SIZE) + glm::clamp(size % SPARSE_PAGE_SIZE, glm::uvec2(0), glm::uvec2(1)));
|
||||
glm::uvec2 result = pages * SPARSE_PAGE_SIZE;
|
||||
return result;
|
||||
glm::uvec2 rectifySize(const glm::uvec2& size) {
|
||||
return { rectifyDimension(size.x), rectifyDimension(size.y) };
|
||||
}
|
||||
|
||||
|
||||
|
@ -329,9 +322,12 @@ QImage processSourceImage(QImage&& srcImage, bool cubemap) {
|
|||
++DECIMATED_TEXTURE_COUNT;
|
||||
}
|
||||
|
||||
if (!cubemap && needsSparseRectification(targetSize)) {
|
||||
++RECTIFIED_TEXTURE_COUNT;
|
||||
targetSize = rectifyToSparseSize(targetSize);
|
||||
if (!cubemap) {
|
||||
auto rectifiedSize = rectifySize(targetSize);
|
||||
if (rectifiedSize != targetSize) {
|
||||
++RECTIFIED_TEXTURE_COUNT;
|
||||
targetSize = rectifiedSize;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
* @function location.protocolVersion
|
||||
* @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();
|
||||
|
||||
|
|
|
@ -63,13 +63,6 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
|
|||
_packetStatTimer(),
|
||||
_permissions(NodePermissions())
|
||||
{
|
||||
static bool firstCall = true;
|
||||
if (firstCall) {
|
||||
NodeType::init();
|
||||
|
||||
firstCall = false;
|
||||
}
|
||||
|
||||
qRegisterMetaType<ConnectionStep>("ConnectionStep");
|
||||
auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get();
|
||||
_nodeSocket.bind(QHostAddress::AnyIPv4, port);
|
||||
|
|
|
@ -29,28 +29,25 @@ int NodePtrMetaTypeId = qRegisterMetaType<Node*>("Node*");
|
|||
int sharedPtrNodeMetaTypeId = qRegisterMetaType<QSharedPointer<Node>>("QSharedPointer<Node>");
|
||||
int sharedNodePtrMetaTypeId = qRegisterMetaType<SharedNodePointer>("SharedNodePointer");
|
||||
|
||||
void NodeType::init() {
|
||||
QHash<NodeType_t, QString>& TypeNameHash = Node::getTypeNameHash();
|
||||
|
||||
TypeNameHash.insert(NodeType::DomainServer, "Domain Server");
|
||||
TypeNameHash.insert(NodeType::EntityServer, "Entity Server");
|
||||
TypeNameHash.insert(NodeType::Agent, "Agent");
|
||||
TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer");
|
||||
TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer");
|
||||
TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer");
|
||||
TypeNameHash.insert(NodeType::AssetServer, "Asset Server");
|
||||
TypeNameHash.insert(NodeType::EntityScriptServer, "Entity Script Server");
|
||||
TypeNameHash.insert(NodeType::UpstreamAudioMixer, "Upstream Audio Mixer");
|
||||
TypeNameHash.insert(NodeType::UpstreamAvatarMixer, "Upstream Avatar Mixer");
|
||||
TypeNameHash.insert(NodeType::DownstreamAudioMixer, "Downstream Audio Mixer");
|
||||
TypeNameHash.insert(NodeType::DownstreamAvatarMixer, "Downstream Avatar Mixer");
|
||||
TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
|
||||
}
|
||||
static const QHash<NodeType_t, QString> TYPE_NAME_HASH {
|
||||
{ NodeType::DomainServer, "Domain Server" },
|
||||
{ NodeType::EntityServer, "Entity Server" },
|
||||
{ NodeType::Agent, "Agent" },
|
||||
{ NodeType::AudioMixer, "Audio Mixer" },
|
||||
{ NodeType::AvatarMixer, "Avatar Mixer" },
|
||||
{ NodeType::MessagesMixer, "Messages Mixer" },
|
||||
{ NodeType::AssetServer, "Asset Server" },
|
||||
{ NodeType::EntityScriptServer, "Entity Script Server" },
|
||||
{ NodeType::UpstreamAudioMixer, "Upstream Audio Mixer" },
|
||||
{ NodeType::UpstreamAvatarMixer, "Upstream Avatar Mixer" },
|
||||
{ NodeType::DownstreamAudioMixer, "Downstream Audio Mixer" },
|
||||
{ NodeType::DownstreamAvatarMixer, "Downstream Avatar Mixer" },
|
||||
{ NodeType::Unassigned, "Unassigned" }
|
||||
};
|
||||
|
||||
const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
|
||||
QHash<NodeType_t, QString>& TypeNameHash = Node::getTypeNameHash();
|
||||
QHash<NodeType_t, QString>::iterator matchedTypeName = TypeNameHash.find(nodeType);
|
||||
return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
|
||||
const auto matchedTypeName = TYPE_NAME_HASH.find(nodeType);
|
||||
return matchedTypeName != TYPE_NAME_HASH.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
|
||||
}
|
||||
|
||||
bool NodeType::isUpstream(NodeType_t nodeType) {
|
||||
|
@ -84,8 +81,7 @@ NodeType_t NodeType::downstreamType(NodeType_t primaryType) {
|
|||
}
|
||||
|
||||
NodeType_t NodeType::fromString(QString type) {
|
||||
QHash<NodeType_t, QString>& TypeNameHash = Node::getTypeNameHash();
|
||||
return TypeNameHash.key(type, NodeType::Unassigned);
|
||||
return TYPE_NAME_HASH.key(type, NodeType::Unassigned);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -89,11 +89,6 @@ public:
|
|||
|
||||
bool isIgnoreRadiusEnabled() const { return _ignoreRadiusEnabled; }
|
||||
|
||||
static QHash<NodeType_t, QString>& getTypeNameHash() {
|
||||
static QHash<NodeType_t, QString> TypeNameHash;
|
||||
return TypeNameHash;
|
||||
}
|
||||
|
||||
private:
|
||||
// privatize copy and assignment operator to disallow Node copying
|
||||
Node(const Node &otherNode);
|
||||
|
|
|
@ -31,8 +31,6 @@ namespace NodeType {
|
|||
const NodeType_t DownstreamAvatarMixer = 'w';
|
||||
const NodeType_t Unassigned = 1;
|
||||
|
||||
void init();
|
||||
|
||||
const QString& getNodeTypeName(NodeType_t nodeType);
|
||||
bool isUpstream(NodeType_t nodeType);
|
||||
bool isDownstream(NodeType_t nodeType);
|
||||
|
|
|
@ -256,25 +256,32 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
|||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
measureBodyAcceleration();
|
||||
bool positionSuccess;
|
||||
_entity->setWorldPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false);
|
||||
if (!positionSuccess) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform "
|
||||
"setPosition failed.*");
|
||||
qCDebug(physics) << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID();
|
||||
|
||||
// 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) {
|
||||
// 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()) {
|
||||
_loopsWithoutOwner++;
|
||||
|
@ -530,9 +537,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
|
||||
if (!_body->isActive()) {
|
||||
// make sure all derivatives are zero
|
||||
_entity->setVelocity(Vectors::ZERO);
|
||||
_entity->setAngularVelocity(Vectors::ZERO);
|
||||
_entity->setAcceleration(Vectors::ZERO);
|
||||
zeroCleanObjectVelocities();
|
||||
_numInactiveUpdates++;
|
||||
} else {
|
||||
glm::vec3 gravity = _entity->getGravity();
|
||||
|
@ -559,9 +564,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
if (movingSlowly) {
|
||||
// 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
|
||||
glm::vec3 zero(0.0f);
|
||||
_entity->setVelocity(zero);
|
||||
_entity->setAngularVelocity(zero);
|
||||
zeroCleanObjectVelocities();
|
||||
}
|
||||
}
|
||||
_numInactiveUpdates = 0;
|
||||
|
@ -818,3 +821,22 @@ bool EntityMotionState::shouldBeLocallyOwned() const {
|
|||
void EntityMotionState::upgradeOutgoingPriority(uint8_t 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:
|
||||
// changes _outgoingPriority only if priority is larger
|
||||
void upgradeOutgoingPriority(uint8_t priority);
|
||||
void zeroCleanObjectVelocities() const;
|
||||
|
||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
||||
bool entityTreeIsLocked() const;
|
||||
|
|
|
@ -64,6 +64,12 @@ bool Pointer::isMouse() const {
|
|||
return DependencyManager::get<PickManager>()->isMouse(_pickUID);
|
||||
}
|
||||
|
||||
void Pointer::setDoesHover(bool doesHover) {
|
||||
withWriteLock([&] {
|
||||
_hover = doesHover;
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
withReadLock([&] {
|
||||
|
@ -95,7 +101,8 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
|||
}
|
||||
|
||||
// Hover events
|
||||
bool doHover = shouldHover(pickResult);
|
||||
bool doHover = _hover && shouldHover(pickResult);
|
||||
|
||||
Pointer::PickedObject hoveredObject = getHoveredObject(pickResult);
|
||||
PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult);
|
||||
hoveredEvent.setType(PointerEvent::Move);
|
||||
|
@ -104,7 +111,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
|||
hoveredEvent.setMoveOnHoverLeave(moveOnHoverLeave);
|
||||
|
||||
// if shouldHover && !_prevDoHover, only send hoverBegin
|
||||
if (_enabled && _hover && doHover && !_prevDoHover) {
|
||||
if (_enabled && doHover && !_prevDoHover) {
|
||||
if (hoveredObject.type == ENTITY) {
|
||||
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
|
||||
} else if (hoveredObject.type == OVERLAY) {
|
||||
|
@ -112,7 +119,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
|||
} else if (hoveredObject.type == HUD) {
|
||||
emit pointerManager->hoverBeginHUD(hoveredEvent);
|
||||
}
|
||||
} else if (_enabled && _hover && doHover) {
|
||||
} else if (_enabled && doHover) {
|
||||
if (hoveredObject.type == OVERLAY) {
|
||||
if (_prevHoveredObject.type == OVERLAY) {
|
||||
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 (_hover && ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover))) {
|
||||
if ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover)) {
|
||||
if (_prevHoveredObject.type == ENTITY) {
|
||||
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
|
||||
} else if (_prevHoveredObject.type == OVERLAY) {
|
||||
|
|
|
@ -62,6 +62,8 @@ public:
|
|||
virtual void setLength(float length) {}
|
||||
virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) {}
|
||||
|
||||
virtual void setDoesHover(bool hover);
|
||||
|
||||
void update(unsigned int pointerID);
|
||||
virtual void updateVisuals(const PickResultPointer& pickResult) = 0;
|
||||
void generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult);
|
||||
|
@ -101,7 +103,6 @@ private:
|
|||
std::unordered_map<std::string, PickedObject> _triggeredObjects;
|
||||
|
||||
PointerEvent::Button chooseButton(const std::string& button);
|
||||
|
||||
};
|
||||
|
||||
#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) {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
|
||||
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 setDoesHover(unsigned int uid, bool hover) const;
|
||||
|
||||
void update();
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ float specularDistribution(SurfaceData surface) {
|
|||
// Add geometric factors G1(n,l) and G1(n,v)
|
||||
float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl);
|
||||
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;
|
||||
return power;
|
||||
}
|
||||
|
@ -202,12 +202,11 @@ vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) {
|
|||
vec3 specular = fresnelColor * power * angleAttenuation;
|
||||
float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x);
|
||||
|
||||
diffuse /= 3.1415926;
|
||||
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
|
||||
// has a multiplier of PI, says Naty Hoffman.
|
||||
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
|
||||
// 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
|
||||
// page 23 paragraph "Punctual light sources")
|
||||
|
||||
return vec4(specular, diffuse);
|
||||
}
|
||||
|
||||
|
@ -222,9 +221,9 @@ vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) {
|
|||
vec3 specular = vec3(fresnelScalar) * power * angleAttenuation;
|
||||
float diffuse = angleAttenuation * (1.0 - fresnelScalar);
|
||||
|
||||
diffuse /= 3.1415926;
|
||||
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
|
||||
// has a multiplier of PI, says Naty Hoffman.
|
||||
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
|
||||
// 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
|
||||
// page 23 paragraph "Punctual light sources")
|
||||
return vec4(specular, diffuse);
|
||||
|
@ -239,8 +238,9 @@ vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) {
|
|||
float power = specularDistribution(surface);
|
||||
vec3 specular = fresnelColor * power * angleAttenuation;
|
||||
|
||||
// Specular isn't divided by PI because an infinitesimal volume light source
|
||||
// has a multiplier of PI, says Naty Hoffman.
|
||||
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
|
||||
// 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
|
||||
// page 23 paragraph "Punctual light sources")
|
||||
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::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);
|
||||
|
||||
|
|
|
@ -464,6 +464,36 @@ glm::vec3 SpatiallyNestable::localToWorldDimensions(const glm::vec3& 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 {
|
||||
return getTransform(success).getTranslation();
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
|
||||
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() const;
|
||||
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_TABLET = "com.highfidelity.interface.tablet.system";
|
||||
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() {
|
||||
QString username = "Unknown user";
|
||||
|
@ -74,11 +76,21 @@ QVariant TabletButtonListModel::data(const QModelIndex& index, int role) const {
|
|||
}
|
||||
|
||||
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();
|
||||
_buttons.push_back(tabletButtonProxy);
|
||||
int numButtons = (int)_buttons.size();
|
||||
if (index < numButtons) {
|
||||
_buttons.insert(_buttons.begin() + index, button);
|
||||
} else {
|
||||
_buttons.push_back(button);
|
||||
}
|
||||
endResetModel();
|
||||
return tabletButtonProxy.data();
|
||||
return button.data();
|
||||
}
|
||||
|
||||
void TabletButtonListModel::removeButton(TabletButtonProxy* button) {
|
||||
|
@ -92,6 +104,20 @@ void TabletButtonListModel::removeButton(TabletButtonProxy* button) {
|
|||
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)
|
||||
: QSortFilterProxyModel(parent) {
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ protected:
|
|||
friend class TabletProxy;
|
||||
TabletButtonProxy* addButton(const QVariant& properties);
|
||||
void removeButton(TabletButtonProxy* button);
|
||||
int computeNewButtonIndex(const QVariantMap& newButtonProperties);
|
||||
using List = std::list<QSharedPointer<TabletButtonProxy>>;
|
||||
static QHash<int, QByteArray> _roles;
|
||||
static Qt::ItemFlags _flags;
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
|
||||
var button = tablet.addButton({
|
||||
icon: Script.resolvePath("dynamicsTests.svg"),
|
||||
text: "Dynamics",
|
||||
sortOrder: 15
|
||||
text: "Dynamics"
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -32,8 +32,7 @@
|
|||
var button = tablet.addButton({
|
||||
text: TABLET_BUTTON_NAME,
|
||||
icon: ICON_URL,
|
||||
activeIcon: ACTIVE_ICON_URL,
|
||||
sortOrder: 1
|
||||
activeIcon: ACTIVE_ICON_URL
|
||||
});
|
||||
|
||||
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({
|
||||
text: TABLET_BUTTON_NAME,
|
||||
icon: ICON_URL,
|
||||
activeIcon: ACTIVE_ICON_URL,
|
||||
sortOrder: 1
|
||||
activeIcon: ACTIVE_ICON_URL
|
||||
});
|
||||
|
||||
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 (image error) 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 (image error) Size: 4.9 KiB |
|
@ -16,10 +16,10 @@
|
|||
var button;
|
||||
// Used for animating and disappearing the bubble
|
||||
var bubbleOverlayTimestamp;
|
||||
// Used for rate limiting the bubble sound
|
||||
var lastBubbleSoundTimestamp = 0;
|
||||
// Used for flashing the HUD button upon activation
|
||||
var bubbleButtonFlashState = false;
|
||||
// Used for flashing the HUD button upon activation
|
||||
var bubbleButtonTimestamp;
|
||||
// Affects bubble height
|
||||
var BUBBLE_HEIGHT_SCALE = 0.15;
|
||||
// The bubble model itself
|
||||
|
@ -36,9 +36,11 @@
|
|||
var bubbleActivateSound = SoundCache.getSound(Script.resolvePath("assets/sounds/bubble.wav"));
|
||||
// Is the update() function connected?
|
||||
var updateConnected = false;
|
||||
var bubbleFlashTimer = false;
|
||||
|
||||
var BUBBLE_VISIBLE_DURATION_MS = 3000;
|
||||
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
|
||||
function hideOverlays() {
|
||||
|
@ -50,11 +52,15 @@
|
|||
|
||||
// Make the bubble overlay visible, set its position, and play the sound
|
||||
function createOverlays() {
|
||||
Audio.playSound(bubbleActivateSound, {
|
||||
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
|
||||
localOnly: true,
|
||||
volume: 0.2
|
||||
});
|
||||
var nowTimestamp = Date.now();
|
||||
if (nowTimestamp - lastBubbleSoundTimestamp >= BUBBLE_SOUND_RATE_LIMIT_MS) {
|
||||
Audio.playSound(bubbleActivateSound, {
|
||||
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
|
||||
localOnly: true,
|
||||
volume: 0.2
|
||||
});
|
||||
lastBubbleSoundTimestamp = nowTimestamp;
|
||||
}
|
||||
hideOverlays();
|
||||
if (updateConnected === true) {
|
||||
updateConnected = false;
|
||||
|
@ -80,10 +86,17 @@
|
|||
},
|
||||
visible: true
|
||||
});
|
||||
bubbleOverlayTimestamp = Date.now();
|
||||
bubbleButtonTimestamp = bubbleOverlayTimestamp;
|
||||
bubbleOverlayTimestamp = nowTimestamp;
|
||||
Script.update.connect(update);
|
||||
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
|
||||
|
@ -103,12 +116,6 @@
|
|||
var delay = (timestamp - bubbleOverlayTimestamp);
|
||||
var overlayAlpha = 1.0 - (delay / BUBBLE_VISIBLE_DURATION_MS);
|
||||
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) {
|
||||
Overlays.editOverlay(bubbleOverlay, {
|
||||
|
@ -157,8 +164,11 @@
|
|||
Script.update.disconnect(update);
|
||||
updateConnected = false;
|
||||
}
|
||||
var bubbleActive = Users.getIgnoreRadiusEnabled();
|
||||
writeButtonProperties(bubbleActive);
|
||||
if (bubbleFlashTimer) {
|
||||
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
|
||||
// just for not logging the initial state of the bubble when we startup.
|
||||
function onBubbleToggled(enabled, doNotLog) {
|
||||
if (bubbleFlashTimer) {
|
||||
Script.clearTimeout(bubbleFlashTimer);
|
||||
bubbleFlashTimer = false;
|
||||
}
|
||||
writeButtonProperties(enabled);
|
||||
if (doNotLog !== true) {
|
||||
UserActivityLogger.bubbleToggled(enabled);
|
||||
|
@ -200,6 +214,10 @@
|
|||
// Cleanup the tablet button and overlays when script is stopped
|
||||
Script.scriptEnding.connect(function () {
|
||||
button.clicked.disconnect(Users.toggleIgnoreRadius);
|
||||
if (bubbleFlashTimer) {
|
||||
Script.clearTimeout(bubbleFlashTimer);
|
||||
bubbleFlashTimer = false;
|
||||
}
|
||||
if (tablet) {
|
||||
tablet.removeButton(button);
|
||||
}
|
||||
|
|
|
@ -945,8 +945,7 @@
|
|||
tabletButton = tablet.addButton({
|
||||
icon: tabletButtonIcon,
|
||||
activeIcon: tabletButtonActiveIcon,
|
||||
text: tabletButtonName,
|
||||
sortOrder: 0
|
||||
text: tabletButtonName
|
||||
});
|
||||
|
||||
Messages.subscribe(channelName);
|
||||
|
|
|
@ -562,9 +562,11 @@
|
|||
break;
|
||||
case 'disableHmdPreview':
|
||||
isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview");
|
||||
DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN");
|
||||
Menu.setIsOptionChecked("Disable Preview", true);
|
||||
break;
|
||||
case 'maybeEnableHmdPreview':
|
||||
DesktopPreviewProvider.setPreviewDisabledReason("USER");
|
||||
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
|
||||
break;
|
||||
case 'passphraseReset':
|
||||
|
@ -635,7 +637,11 @@
|
|||
// -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.
|
||||
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);
|
||||
// Change button to active when window is first openend, false otherwise.
|
||||
if (button) {
|
||||
|
|
|
@ -44,7 +44,12 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
this.highVarianceCount = 0;
|
||||
this.veryhighVarianceCount = 0;
|
||||
this.tabletID = null;
|
||||
this.TABLET_UI_UUIDS = [];
|
||||
this.blacklist = [];
|
||||
this.leftPointerDoesHover = true;
|
||||
this.leftPointerDoesHoverChanged = false;
|
||||
this.rightPointerDoesHover = true;
|
||||
this.rightPointerDoesHoverChanged = false;
|
||||
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
|
||||
|
@ -122,6 +127,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
return getControllerWorldLocation(Controller.Standard.RightHand, true);
|
||||
};
|
||||
|
||||
this.isTabletID = function (uuid) {
|
||||
return _this.TABLET_UI_UUIDS.indexOf(uuid) !== -1;
|
||||
};
|
||||
|
||||
this.updateTimings = function () {
|
||||
_this.intervalCount++;
|
||||
var thisInterval = Date.now();
|
||||
|
@ -148,11 +157,35 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
this.setIgnorePointerItems = function() {
|
||||
if (HMD.tabletID !== this.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.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 () {
|
||||
try {
|
||||
_this.updateInternal();
|
||||
|
@ -324,6 +357,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
_this.runningPluginNames[orderedPluginName] = true;
|
||||
_this.markSlots(candidatePlugin, orderedPluginName);
|
||||
_this.pointerManager.makePointerVisible(candidatePlugin.parameters.handLaser);
|
||||
_this.updateDoesHover(candidatePlugin.parameters.handLaser,
|
||||
candidatePlugin.parameters.handLaser.doesHover);
|
||||
if (DEBUG) {
|
||||
print("controllerDispatcher running " + orderedPluginName);
|
||||
}
|
||||
|
@ -354,12 +389,15 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
Script.beginProfileRange("dispatch.run." + runningPluginName);
|
||||
}
|
||||
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
|
||||
// of running plugins and mark its activity-slots as "not in use"
|
||||
delete _this.runningPluginNames[runningPluginName];
|
||||
_this.markSlots(plugin, false);
|
||||
_this.pointerManager.makePointerInvisible(plugin.parameters.handLaser);
|
||||
_this.updateDoesHover(plugin.parameters.handLaser, true);
|
||||
if (DEBUG) {
|
||||
print("controllerDispatcher stopping " + runningPluginName);
|
||||
}
|
||||
|
@ -372,6 +410,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
}
|
||||
}
|
||||
_this.pointerManager.updatePointersRenderState(controllerData.triggerClicks, controllerData.triggerValues);
|
||||
_this.updateHovering();
|
||||
|
||||
if (PROFILE) {
|
||||
Script.endProfileRange("dispatch.run");
|
||||
}
|
||||
|
|
|
@ -370,20 +370,23 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
};
|
||||
|
||||
this.isReady = function (controllerData) {
|
||||
if (this.notPointingAtEntity(controllerData)) {
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
if (HMD.active) {
|
||||
if (this.notPointingAtEntity(controllerData)) {
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
this.distanceHolding = false;
|
||||
this.distanceRotating = false;
|
||||
this.distanceHolding = false;
|
||||
this.distanceRotating = false;
|
||||
|
||||
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
|
||||
this.prepareDistanceRotatingData(controllerData);
|
||||
return makeRunningValues(true, [], []);
|
||||
} else {
|
||||
this.destroyContextOverlay();
|
||||
return makeRunningValues(false, [], []);
|
||||
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
|
||||
this.prepareDistanceRotatingData(controllerData);
|
||||
return makeRunningValues(true, [], []);
|
||||
} else {
|
||||
this.destroyContextOverlay();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
}
|
||||
return makeRunningValues(false, [], []);
|
||||
};
|
||||
|
||||
this.run = function (controllerData) {
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
|
||||
this.isReady = function (controllerData) {
|
||||
var otherModuleRunning = this.getOtherModule().running;
|
||||
if (!otherModuleRunning) {
|
||||
if (!otherModuleRunning && HMD.active) {
|
||||
if (this.processLaser(controllerData)) {
|
||||
this.running = true;
|
||||
return ControllerDispatcherUtils.makeRunningValues(true, [], []);
|
||||
|
|
|
@ -87,41 +87,89 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
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) {
|
||||
var otherModuleRunning = this.getOtherModule().running;
|
||||
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))) {
|
||||
if (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)) {
|
||||
this.updateAllwaysOn();
|
||||
if (isTriggerPressed) {
|
||||
this.dominantHandOverride = true; // Override dominant hand.
|
||||
this.getOtherModule().dominantHandOverride = false;
|
||||
}
|
||||
|
||||
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
|
||||
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, [], []);
|
||||
};
|
||||
|
||||
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);
|
||||
if (!otherModuleRunning && !grabModuleNeedsToRun && (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE
|
||||
|| this.parameters.handLaser.allwaysOn
|
||||
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
|
||||
if (!grabModuleNeedsToRun && (isTriggerPressed || this.parameters.handLaser.allwaysOn
|
||||
&& (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)))) {
|
||||
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, [], []);
|
||||
}
|
||||
this.deleteContextOverlay();
|
||||
this.running = false;
|
||||
this.dominantHandOverride = false;
|
||||
|
||||
this.parameters.handLaser.doesHover = false;
|
||||
this.hoverItem = null;
|
||||
|
||||
return makeRunningValues(false, [], []);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -448,7 +448,7 @@ var toolBar = (function () {
|
|||
});
|
||||
|
||||
addButton("importEntitiesButton", "assets-01.svg", function() {
|
||||
Window.openFileChanged.connect(onFileOpenChanged);
|
||||
Window.browseChanged.connect(onFileOpenChanged);
|
||||
Window.browseAsync("Select Model to Import", "", "*.json");
|
||||
});
|
||||
|
||||
|
@ -1497,7 +1497,7 @@ function onFileOpenChanged(filename) {
|
|||
// disconnect the event, otherwise the requests will stack up
|
||||
try {
|
||||
// Not all calls to onFileOpenChanged() connect an event.
|
||||
Window.openFileChanged.disconnect(onFileOpenChanged);
|
||||
Window.browseChanged.disconnect(onFileOpenChanged);
|
||||
} catch (e) {
|
||||
// Ignore.
|
||||
}
|
||||
|
@ -1549,7 +1549,7 @@ function handeMenuEvent(menuItem) {
|
|||
}
|
||||
} else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") {
|
||||
if (menuItem === "Import Entities") {
|
||||
Window.openFileChanged.connect(onFileOpenChanged);
|
||||
Window.browseChanged.connect(onFileOpenChanged);
|
||||
Window.browseAsync("Select Model to Import", "", "*.json");
|
||||
} else {
|
||||
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
|
||||
|
||||
button = tablet.addButton({
|
||||
//icon: "icons/tablet-icons/emote.svg", // TODO - we need graphics for this
|
||||
icon: "icons/tablet-icons/EmoteAppIcon.svg",
|
||||
text: EMOTE_LABEL,
|
||||
sortOrder: EMOTE_APP_SORT_ORDER
|
||||
});
|
||||
|
|
|
@ -37,8 +37,7 @@
|
|||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
button = tablet.addButton({
|
||||
icon: "icons/tablet-icons/goto-i.svg",
|
||||
text: buttonName,
|
||||
sortOrder: 8
|
||||
text: buttonName
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -41,8 +41,7 @@ if (Settings.getValue("HUDUIEnabled")) {
|
|||
button = tablet.addButton({
|
||||
icon: "icons/tablet-icons/goto-i.svg",
|
||||
activeIcon: "icons/tablet-icons/goto-a.svg",
|
||||
text: buttonName,
|
||||
sortOrder: 8
|
||||
text: buttonName
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -44,11 +44,11 @@
|
|||
input[type=button] {
|
||||
font-family: 'Raleway';
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
font-size: 20px;
|
||||
text-transform: uppercase;
|
||||
vertical-align: top;
|
||||
height: 28px;
|
||||
min-width: 120px;
|
||||
height: 105px;
|
||||
min-width: 190px;
|
||||
padding: 0px 18px;
|
||||
margin-right: 6px;
|
||||
border-radius: 5px;
|
||||
|
@ -98,14 +98,14 @@
|
|||
</div>
|
||||
<div class="content">
|
||||
<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="Surprised"></p>
|
||||
<p><input type="button" class="emote-button white" value="Dancing"></p>
|
||||
<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="Fall"></p>
|
||||
<p><input type="button" class="emote-button white" value="Pointing"></p>
|
||||
<p><input type="button" class="emote-button white" value="Clapping"></p>
|
||||
<p><input type="button" class="emote-button white" value="Crying">
|
||||
<input type="button" class="emote-button white" value="Surprised"></p>
|
||||
<p><input type="button" class="emote-button white" value="Dancing">
|
||||
<input type="button" class="emote-button white" value="Cheering"></p>
|
||||
<p><input type="button" class="emote-button white" value="Waving">
|
||||
<input type="button" class="emote-button white" value="Fall"></p>
|
||||
<p><input type="button" class="emote-button white" value="Pointing">
|
||||
<input type="button" class="emote-button white" value="Clapping"></p>
|
||||
</div>
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||
|
|
|
@ -539,8 +539,14 @@
|
|||
return startingUp;
|
||||
}
|
||||
|
||||
function onDomainConnectionRefused(reason) {
|
||||
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
||||
function onDomainConnectionRefused(reason, reasonCode) {
|
||||
// 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) {
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var button = tablet.addButton({
|
||||
icon: Script.resolvePath("assets/images/run.svg"),
|
||||
text: "RUN",
|
||||
sortOrder: 15
|
||||
text: "RUN"
|
||||
});
|
||||
|
||||
function onClicked() {
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
'include_actions=' + actions,
|
||||
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
||||
'require_online=true',
|
||||
'protocol=' + encodeURIComponent(location.protocolVersion()),
|
||||
'protocol=' + encodeURIComponent(Window.protocolSignature()),
|
||||
'per_page=' + count
|
||||
];
|
||||
var url = Account.metaverseServerURL + '/api/v1/user_stories?' + options.join('&');
|
||||
|
|
|
@ -37,8 +37,7 @@
|
|||
var button = tablet.addButton({
|
||||
icon: "icons/tablet-icons/users-i.svg",
|
||||
activeIcon: "icons/tablet-icons/users-a.svg",
|
||||
text: "USERS",
|
||||
sortOrder: 11
|
||||
text: "USERS"
|
||||
});
|
||||
|
||||
var onUsersScreen = false;
|
||||
|
|
|
@ -163,46 +163,31 @@ class MyTestWindow : public TestWindow {
|
|||
}
|
||||
};
|
||||
|
||||
extern bool needsSparseRectification(const uvec2& size);
|
||||
extern uvec2 rectifyToSparseSize(const uvec2& size);
|
||||
extern uvec2 rectifySize(const uvec2& size);
|
||||
|
||||
void testSparseRectify() {
|
||||
std::vector<std::pair<uvec2, bool>> NEEDS_SPARSE_TESTS {{
|
||||
std::vector<std::pair<uvec2, uvec2>> SPARSE_SIZE_TESTS {
|
||||
// Already sparse
|
||||
{ {1024, 1024 }, false },
|
||||
{ { 128, 128 }, false },
|
||||
{ {1024, 1024 }, { 1024, 1024 } },
|
||||
{ { 128, 128 }, { 128, 128 } },
|
||||
// Too small in one dimension
|
||||
{ { 127, 127 }, false },
|
||||
{ { 1, 1 }, false },
|
||||
{ { 1000, 1 }, false },
|
||||
{ { 1024, 1 }, false },
|
||||
{ { 100, 100 }, false },
|
||||
// needs rectification
|
||||
{ { 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 { {
|
||||
{ { 127, 127 }, { 128, 128 } },
|
||||
{ { 1, 1 }, { 1, 1 } },
|
||||
{ { 1000, 1 }, { 1024, 1 } },
|
||||
{ { 1024, 1 }, { 1024, 1 } },
|
||||
{ { 100, 100 }, { 128, 128 } },
|
||||
{ { 57, 510 }, { 64, 512 } },
|
||||
// needs rectification
|
||||
{ { 1000, 1000 }, { 1024, 1024 } },
|
||||
{ { 1024, 1000 }, { 1024, 1024 } },
|
||||
} };
|
||||
};
|
||||
|
||||
for (const auto& test : SPARSE_SIZE_TESTS) {
|
||||
const auto& size = test.first;
|
||||
const auto& expected = test.second;
|
||||
auto result = rectifyToSparseSize(size);
|
||||
auto result = rectifySize(size);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,47 +1,41 @@
|
|||
set(TARGET_NAME auto-tester)
|
||||
set (TARGET_NAME auto-tester)
|
||||
project(${TARGET_NAME})
|
||||
|
||||
# Automatically run UIC and MOC. This replaces the older WRAP macros
|
||||
SET(CMAKE_AUTOUIC ON)
|
||||
SET(CMAKE_AUTOMOC ON)
|
||||
SET (CMAKE_AUTOUIC ON)
|
||||
SET (CMAKE_AUTOMOC ON)
|
||||
|
||||
setup_hifi_project(Core Widgets)
|
||||
link_hifi_libraries()
|
||||
setup_hifi_project (Core Widgets)
|
||||
link_hifi_libraries ()
|
||||
|
||||
# FIX: Qt was built with -reduce-relocations
|
||||
if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
SET (CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
|
||||
# Qt includes
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
include_directories(${Qt5Core_INCLUDE_DIRS})
|
||||
include_directories(${Qt5Widgets_INCLUDE_DIRS})
|
||||
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
|
||||
include_directories (${Qt5Core_INCLUDE_DIRS})
|
||||
include_directories (${Qt5Widgets_INCLUDE_DIRS})
|
||||
|
||||
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)
|
||||
set (QT_LIBRARIES Qt5::Core Qt5::Widgets)
|
||||
|
||||
if (WIN32)
|
||||
# Do not show Console
|
||||
set_property(TARGET auto-tester PROPERTY WIN32_EXECUTABLE true)
|
||||
set_property (TARGET auto-tester PROPERTY WIN32_EXECUTABLE true)
|
||||
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})
|
||||
|
||||
# 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>
|
||||
)
|
||||
package_libraries_for_deployment()
|
||||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs (${QUAZIP_DLL_PATH})
|
||||
|
||||
find_program(WINDEPLOYQT_COMMAND windeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH)
|
||||
|
||||
if (NOT WINDEPLOYQT_COMMAND)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "ImageComparer.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
@ -26,11 +27,6 @@ double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) co
|
|||
const double c1 = pow((K1 * 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
|
||||
// This is done in 3 loops
|
||||
// 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;
|
||||
};
|
||||
};
|
|
@ -13,18 +13,62 @@
|
|||
#include <QtCore/QTextStream>
|
||||
#include <QDirIterator>
|
||||
|
||||
#include <quazip5/quazip.h>
|
||||
#include <quazip5/JlCompress.h>
|
||||
|
||||
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");
|
||||
|
||||
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
|
||||
// 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 keepOn{ true };
|
||||
for (int i = 0; keepOn && i < expectedImages.length(); ++i) {
|
||||
|
@ -45,42 +89,107 @@ bool Test::compareImageLists(QStringList expectedImages, QStringList resultImage
|
|||
}
|
||||
|
||||
if (similarityIndex < THRESHOLD) {
|
||||
mismatchWindow.setTestFailure(TestFailure{
|
||||
TestFailure testFailure = TestFailure{
|
||||
(float)similarityIndex,
|
||||
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(resultImages[i].toStdString().c_str()).fileName() // filename of result image
|
||||
});
|
||||
};
|
||||
|
||||
mismatchWindow.exec();
|
||||
mismatchWindow.setTestFailure(testFailure);
|
||||
|
||||
switch (mismatchWindow.getUserResponse()) {
|
||||
case USER_RESPONSE_PASS:
|
||||
break;
|
||||
case USE_RESPONSE_FAIL:
|
||||
success = false;
|
||||
break;
|
||||
case USER_RESPONSE_ABORT:
|
||||
keepOn = false;
|
||||
success = false;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
if (!interactiveMode) {
|
||||
appendTestResultsToFile(testResultsFolderPath, testFailure, mismatchWindow.getComparisonImage());
|
||||
success = false;
|
||||
} else {
|
||||
mismatchWindow.exec();
|
||||
|
||||
switch (mismatchWindow.getUserResponse()) {
|
||||
case USER_RESPONSE_PASS:
|
||||
break;
|
||||
case USE_RESPONSE_FAIL:
|
||||
appendTestResultsToFile(testResultsFolderPath, testFailure, mismatchWindow.getComparisonImage());
|
||||
success = false;
|
||||
break;
|
||||
case USER_RESPONSE_ABORT:
|
||||
keepOn = false;
|
||||
success = false;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
progressBar->setValue(i);
|
||||
}
|
||||
|
||||
progressBar->setVisible(false);
|
||||
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
|
||||
QString pathToImageDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
||||
if (pathToImageDirectory == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Leave if test results folder could not be created
|
||||
if (!createTestResultsFolderPathIfNeeded(pathToImageDirectory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList sortedImageFilenames = createListOfAllJPEGimagesInDirectory(pathToImageDirectory);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
bool success = compareImageLists(expectedImages, resultImages);
|
||||
bool success = compareImageLists(expectedImages, resultImages, pathToImageDirectory, interactiveMode, progressBar);
|
||||
|
||||
if (success) {
|
||||
messageBox.information(0, "Success", "All images are as expected");
|
||||
} else {
|
||||
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.
|
||||
// 1) a 'test'js' file exists in the folder
|
||||
// 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
|
||||
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", ".", QFileDialog::ShowDirsOnly);
|
||||
if (topLevelDirectory == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Leave if test results folder could not be created
|
||||
if (!createTestResultsFolderPathIfNeeded(topLevelDirectory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool success{ true };
|
||||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
if (directory[directory.length() - 1] == '.') {
|
||||
// ignore '.', '..' directories
|
||||
|
||||
if (!isAValidDirectory(directory)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
const QString testPathname{ directory + "/" + testFilename };
|
||||
const QString testPathname{ directory + "/" + TEST_FILENAME };
|
||||
QFileInfo fileInfo(testPathname);
|
||||
if (!fileInfo.exists()) {
|
||||
// Folder does not contain 'test.js'
|
||||
|
@ -164,7 +294,7 @@ void Test::evaluateTestsRecursively() {
|
|||
}
|
||||
|
||||
// Set success to false if any test has failed
|
||||
success &= compareImageLists(expectedImages, resultImages);
|
||||
success &= compareImageLists(expectedImages, resultImages, directory, interactiveMode, progressBar);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
|
@ -172,6 +302,8 @@ void Test::evaluateTestsRecursively() {
|
|||
} else {
|
||||
messageBox.information(0, "Failure", "One or more images are not as expected");
|
||||
}
|
||||
|
||||
zipAndDeleteTestResultsFolder();
|
||||
}
|
||||
|
||||
void Test::importTest(QTextStream& textStream, const QString& testPathname, int testNumber) {
|
||||
|
@ -191,7 +323,8 @@ void Test::createRecursiveScript() {
|
|||
if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
messageBox.critical(0,
|
||||
"Internal Error",
|
||||
"Failed to create \"allTests.js\" in directory \"" + topLevelDirectory + "\"");
|
||||
"Failed to create \"allTests.js\" in directory \"" + topLevelDirectory + "\""
|
||||
);
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
@ -206,7 +339,7 @@ void Test::createRecursiveScript() {
|
|||
QVector<QString> testPathnames;
|
||||
|
||||
// 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);
|
||||
if (fileInfo.exists()) {
|
||||
// Current folder contains a test
|
||||
|
@ -219,12 +352,14 @@ void Test::createRecursiveScript() {
|
|||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
if (directory[directory.length() - 1] == '.') {
|
||||
// ignore '.', '..' directories
|
||||
|
||||
// Only process directories
|
||||
QDir dir(directory);
|
||||
if (!isAValidDirectory(directory)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QString testPathname{ directory + "/" + testFilename };
|
||||
const QString testPathname{ directory + "/" + TEST_FILENAME };
|
||||
QFileInfo fileInfo(testPathname);
|
||||
if (fileInfo.exists()) {
|
||||
// Current folder contains a test
|
||||
|
@ -264,7 +399,7 @@ void Test::createRecursiveScript() {
|
|||
// The script produced will look as follows:
|
||||
// if (test1HasNotStarted) {
|
||||
// test1HasNotStarted = false;
|
||||
// test1.test();
|
||||
// test1.test("auto");
|
||||
// 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 << 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 << "}" << endl << endl;
|
||||
|
@ -366,6 +501,41 @@ void Test::createTest() {
|
|||
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) {
|
||||
imageDirectory = QDir(pathToImageDirectory);
|
||||
QStringList nameFilters;
|
||||
|
@ -374,6 +544,7 @@ QStringList Test::createListOfAllJPEGimagesInDirectory(QString pathToImageDirect
|
|||
return imageDirectory.entryList(nameFilters, QDir::Files, QDir::Name);
|
||||
}
|
||||
|
||||
// Use regular expressions to check if files are in specific format
|
||||
bool Test::isInSnapshotFilenameFormat(QString filename) {
|
||||
return (snapshotFilenameFormat.match(filename).hasMatch());
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//
|
||||
// Test.h
|
||||
// zone/ambientLightInheritence
|
||||
//
|
||||
// Created by Nissim Hadar on 2 Nov 2017.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -15,6 +14,7 @@
|
|||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QProgressBar>
|
||||
|
||||
#include "ImageComparer.h"
|
||||
#include "ui/MismatchWindow.h"
|
||||
|
@ -23,10 +23,13 @@ class Test {
|
|||
public:
|
||||
Test();
|
||||
|
||||
void evaluateTests();
|
||||
void evaluateTestsRecursively();
|
||||
void evaluateTests(bool interactiveMode, QProgressBar* progressBar);
|
||||
void evaluateTestsRecursively(bool interactiveMode, QProgressBar* progressBar);
|
||||
void createRecursiveScript();
|
||||
void createTest();
|
||||
void deleteOldSnapshots();
|
||||
|
||||
bool compareImageLists(QStringList expectedImages, QStringList resultImages, QString testDirectory, bool interactiveMode, QProgressBar* progressBar);
|
||||
|
||||
QStringList createListOfAllJPEGimagesInDirectory(QString pathToImageDirectory);
|
||||
|
||||
|
@ -35,8 +38,17 @@ public:
|
|||
|
||||
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:
|
||||
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;
|
||||
|
||||
|
@ -49,7 +61,9 @@ private:
|
|||
|
||||
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
|
||||
};
|
||||
|
||||
#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();
|
||||
|
||||
return application.exec();
|
||||
}
|
||||
}
|
|
@ -12,14 +12,18 @@
|
|||
|
||||
AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
|
||||
ui.setupUi(this);
|
||||
|
||||
ui.checkBoxInteractiveMode->setChecked(true);
|
||||
|
||||
ui.progressBar->setVisible(false);
|
||||
}
|
||||
|
||||
void AutoTester::on_evaluateTestsButton_clicked() {
|
||||
test.evaluateTests();
|
||||
test.evaluateTests(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
|
||||
}
|
||||
|
||||
void AutoTester::on_evaluateTestsRecursivelyButton_clicked() {
|
||||
test.evaluateTestsRecursively();
|
||||
test.evaluateTestsRecursively(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
|
||||
}
|
||||
|
||||
void AutoTester::on_createRecursiveScriptButton_clicked() {
|
||||
|
@ -30,6 +34,10 @@ void AutoTester::on_createTestButton_clicked() {
|
|||
test.createTest();
|
||||
}
|
||||
|
||||
void AutoTester::on_deleteOldSnapshotsButton_clicked() {
|
||||
test.deleteOldSnapshots();
|
||||
}
|
||||
|
||||
void AutoTester::on_closeButton_clicked() {
|
||||
exit(0);
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
//
|
||||
// AutoTester.h
|
||||
// zone/ambientLightInheritence
|
||||
//
|
||||
// Created by Nissim Hadar on 2 Nov 2017.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -22,10 +21,11 @@ public:
|
|||
AutoTester(QWidget *parent = Q_NULLPTR);
|
||||
|
||||
private slots:
|
||||
void on_evaluateTestsButton_clicked();
|
||||
void on_evaluateTestsRecursivelyButton_clicked();
|
||||
void on_createRecursiveScriptButton_clicked();
|
||||
void on_evaluateTestsButton_clicked();
|
||||
void on_evaluateTestsRecursivelyButton_clicked();
|
||||
void on_createRecursiveScriptButton_clicked();
|
||||
void on_createTestButton_clicked();
|
||||
void on_deleteOldSnapshotsButton_clicked();
|
||||
void on_closeButton_clicked();
|
||||
|
||||
private:
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>286</width>
|
||||
<height>470</height>
|
||||
<width>607</width>
|
||||
<height>395</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -17,9 +17,9 @@
|
|||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>60</x>
|
||||
<y>360</y>
|
||||
<width>160</width>
|
||||
<x>190</x>
|
||||
<y>300</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -30,9 +30,9 @@
|
|||
<widget class="QPushButton" name="createTestButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>60</x>
|
||||
<y>270</y>
|
||||
<width>160</width>
|
||||
<x>360</x>
|
||||
<y>130</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -43,9 +43,9 @@
|
|||
<widget class="QPushButton" name="evaluateTestsButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>60</x>
|
||||
<y>20</y>
|
||||
<width>160</width>
|
||||
<x>20</x>
|
||||
<y>75</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -56,9 +56,9 @@
|
|||
<widget class="QPushButton" name="createRecursiveScriptButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>60</x>
|
||||
<y>210</y>
|
||||
<width>160</width>
|
||||
<x>360</x>
|
||||
<y>75</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -69,9 +69,9 @@
|
|||
<widget class="QPushButton" name="evaluateTestsRecursivelyButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>60</x>
|
||||
<y>75</y>
|
||||
<width>160</width>
|
||||
<x>20</x>
|
||||
<y>130</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -79,13 +79,55 @@
|
|||
<string>Evaluate Tests Recursively</string>
|
||||
</property>
|
||||
</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 class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>286</width>
|
||||
<width>607</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -103,4 +145,4 @@
|
|||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
</ui>
|
|
@ -11,11 +11,48 @@
|
|||
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
MismatchWindow::MismatchWindow(QWidget *parent) : QDialog(parent) {
|
||||
setupUi(this);
|
||||
|
||||
expectedImage->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) {
|
||||
|
@ -24,10 +61,19 @@ void MismatchWindow::setTestFailure(TestFailure testFailure) {
|
|||
imagePath->setText("Path to test: " + testFailure._pathname);
|
||||
|
||||
expectedFilename->setText(testFailure._expectedImageFilename);
|
||||
expectedImage->setPixmap(QPixmap(testFailure._pathname + testFailure._expectedImageFilename));
|
||||
|
||||
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() {
|
||||
|
@ -44,3 +90,7 @@ void MismatchWindow::on_abortTestsButton_clicked() {
|
|||
_userResponse = USER_RESPONSE_ABORT;
|
||||
close();
|
||||
}
|
||||
|
||||
QPixmap MismatchWindow::getComparisonImage() {
|
||||
return diffPixmap;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,9 @@ public:
|
|||
|
||||
UserResponse getUserResponse() { return _userResponse; }
|
||||
|
||||
QPixmap computeDiffPixmap(QImage expectedImage, QImage resultImage);
|
||||
QPixmap getComparisonImage();
|
||||
|
||||
private slots:
|
||||
void on_passTestButton_clicked();
|
||||
void on_failTestButton_clicked();
|
||||
|
@ -32,7 +35,9 @@ private slots:
|
|||
|
||||
private:
|
||||
UserResponse _userResponse{ USER_RESPONSE_INVALID };
|
||||
|
||||
QPixmap diffPixmap;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_MismatchWindow_h
|
||||
#endif // hifi_MismatchWindow_h
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1585</width>
|
||||
<height>694</height>
|
||||
<width>1782</width>
|
||||
<height>942</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -16,10 +16,10 @@
|
|||
<widget class="QLabel" name="expectedImage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>170</y>
|
||||
<width>720</width>
|
||||
<height>362</height>
|
||||
<x>10</x>
|
||||
<y>25</y>
|
||||
<width>800</width>
|
||||
<height>450</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -29,28 +29,41 @@
|
|||
<widget class="QLabel" name="resultImage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>760</x>
|
||||
<y>170</y>
|
||||
<width>720</width>
|
||||
<height>362</height>
|
||||
<x>900</x>
|
||||
<y>25</y>
|
||||
<width>800</width>
|
||||
<height>450</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>result image</string>
|
||||
</property>
|
||||
</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">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>760</x>
|
||||
<y>90</y>
|
||||
<width>800</width>
|
||||
<x>60</x>
|
||||
<y>660</y>
|
||||
<width>480</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -60,15 +73,15 @@
|
|||
<widget class="QLabel" name="expectedFilename">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>90</y>
|
||||
<width>700</width>
|
||||
<x>60</x>
|
||||
<y>630</y>
|
||||
<width>480</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -78,15 +91,15 @@
|
|||
<widget class="QLabel" name="imagePath">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>30</y>
|
||||
<x>20</x>
|
||||
<y>600</y>
|
||||
<width>1200</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -97,7 +110,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>600</y>
|
||||
<y>790</y>
|
||||
<width>75</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
|
@ -109,8 +122,8 @@
|
|||
<widget class="QPushButton" name="failTestButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>330</x>
|
||||
<y>600</y>
|
||||
<x>120</x>
|
||||
<y>790</y>
|
||||
<width>75</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
|
@ -122,36 +135,62 @@
|
|||
<widget class="QPushButton" name="abortTestsButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>630</x>
|
||||
<y>600</y>
|
||||
<width>75</width>
|
||||
<x>210</x>
|
||||
<y>790</y>
|
||||
<width>121</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Abort Tests</string>
|
||||
<string>Abort current test</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="errorLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>810</x>
|
||||
<y>600</y>
|
||||
<width>720</width>
|
||||
<x>30</x>
|
||||
<y>850</y>
|
||||
<width>500</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>similarity</string>
|
||||
</property>
|
||||
</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>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
</ui>
|
|
@ -195,9 +195,9 @@ void DomainBaker::enumerateEntities() {
|
|||
auto filename = modelURL.fileName();
|
||||
auto baseName = filename.left(filename.lastIndexOf('.'));
|
||||
auto subDirName = "/" + baseName;
|
||||
int i = 0;
|
||||
int i = 1;
|
||||
while (QDir(_contentOutputPath + subDirName).exists()) {
|
||||
subDirName = "/" + baseName + "-" + i++;
|
||||
subDirName = "/" + baseName + "-" + QString::number(i++);
|
||||
}
|
||||
QSharedPointer<FBXBaker> baker {
|
||||
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