Merge branch 'master' of github.com:highfidelity/hifi into cauterize-head-av-entities-1

This commit is contained in:
Seth Alves 2018-01-19 09:53:54 -08:00
commit bce9b5f459
65 changed files with 971 additions and 845 deletions

View file

@ -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
```

View file

@ -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);

View file

@ -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('&');

View file

@ -392,7 +392,6 @@ Item {
width: 118;
height: paintedHeight;
wrapMode: Text.WordWrap;
font.bold: true;
// Alignment
horizontalAlignment: Text.AlignRight;
}

View file

@ -7,7 +7,7 @@ import "."
Overlay {
id: root
Image {
AnimatedImage {
id: image
property bool scaleFix: true
property real xStart: 0

View file

@ -573,8 +573,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) {
@ -2093,6 +2092,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 +4355,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();
}

View file

@ -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) {

View file

@ -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 };

View file

@ -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");

View file

@ -390,6 +390,10 @@ QString WindowScriptingInterface::checkVersion() {
return QCoreApplication::applicationVersion();
}
QString WindowScriptingInterface::protocolSignature() {
return protocolVersionsSignatureBase64();
}
int WindowScriptingInterface::getInnerWidth() {
return qApp->getDeviceSize().x;
}

View file

@ -305,6 +305,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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -307,8 +307,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

View file

@ -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();
}

View file

@ -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 };

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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,

View file

@ -72,23 +72,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) {

View file

@ -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;
}

View file

@ -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;
};

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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(); \

View file

@ -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)))) {

View file

@ -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();

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -335,11 +335,13 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs&
// Setup lighting model for all items;
batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer());
// Setup haze iff curretn zone has haze
// Setup haze iff current zone has haze
auto hazeStage = args->_scene->getStage<HazeStage>();
if (hazeStage && hazeStage->_currentFrame._hazes.size() > 0) {
graphics::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front());
batch.setUniformBuffer(render::ShapePipeline::Slot::HAZE_MODEL, hazePointer->getHazeParametersBuffer());
if (hazePointer) {
batch.setUniformBuffer(render::ShapePipeline::Slot::HAZE_MODEL, hazePointer->getHazeParametersBuffer());
}
}
// From the lighting model define a global shapKey ORED with individiual keys

View file

@ -87,7 +87,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p
slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::MAP::LIGHT_AMBIENT));
slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), Slot::MAP::FADE_MASK));
slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), Slot::BUFFER::FADE_PARAMETERS));
slotBindings.insert(gpu::Shader::Binding(std::string("hazeParametersBuffer"), Slot::BUFFER::HAZE_MODEL));
slotBindings.insert(gpu::Shader::Binding(std::string("hazeBuffer"), Slot::BUFFER::HAZE_MODEL));
gpu::Shader::makeProgram(*program, slotBindings);

View file

@ -0,0 +1,77 @@
"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,
sortOrder: 1
});
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);
});
}());

View 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"
}
]
}
}
}

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
inkscape:version="0.91 r13725"
sodipodi:docname="lod-a.svg"><metadata
id="metadata4241"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs4239" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2880"
inkscape:window-height="1724"
id="namedview4237"
showgrid="false"
inkscape:zoom="14.66"
inkscape:cx="22.9339"
inkscape:cy="22.463149"
inkscape:window-x="0"
inkscape:window-y="33"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><path
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:44.7959404px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 0.17382812 0.10351562 L 0.17382812 26.466797 L 12.335938 26.466797 C 12.295785 25.916449 12.273438 25.352399 12.273438 24.773438 C 12.273438 24.325519 12.298233 23.895635 12.322266 23.464844 L 5.6464844 23.464844 L 5.6464844 0.10351562 L 0.17382812 0.10351562 z M 12.322266 23.464844 L 16.9375 23.464844 C 17.131223 20.269693 18.048402 17.696004 19.695312 15.748047 C 21.57624 13.54041 24.107908 12.4375 27.291016 12.4375 C 30.474123 12.4375 32.99176 13.54041 34.84375 15.748047 C 36.522116 17.733209 37.438284 20.371891 37.607422 23.652344 C 39.352093 23.851854 40.904899 24.187106 42.263672 24.662109 C 42.242096 19.950324 40.88586 16.190967 38.1875 13.386719 C 35.46739 10.546406 31.834178 9.125 27.291016 9.125 C 22.733385 9.125 19.088094 10.546406 16.353516 13.386719 C 13.889087 15.947857 12.553913 19.312575 12.322266 23.464844 z M 42.263672 24.662109 C 42.263846 24.700089 42.267578 24.735334 42.267578 24.773438 C 42.267578 25.968111 42.179749 27.101932 42.007812 28.175781 C 42.345297 28.39193 42.664811 28.621521 42.951172 28.876953 C 44.826172 30.525965 45.763672 33.130435 45.763672 36.689453 C 45.763672 40.272198 44.826172 42.893812 42.951172 44.554688 C 41.089193 46.215563 38.146485 47.046875 34.123047 47.046875 L 29.357422 47.046875 L 29.357422 40.285156 C 28.688843 40.354889 28.004565 40.402344 27.291016 40.402344 C 26.644531 40.402344 26.021914 40.365471 25.412109 40.308594 L 25.412109 50 L 33.517578 50 C 39.142578 50 43.283203 48.926571 45.939453 46.779297 C 48.595703 44.632023 49.923828 41.268723 49.923828 36.689453 C 49.923828 32.13391 48.602214 28.787755 45.958984 26.652344 C 44.948178 25.831197 43.714341 25.169238 42.263672 24.662109 z M 25.412109 40.308594 L 25.412109 36.939453 C 23.099 36.57794 21.188155 35.53144 19.695312 33.779297 C 18.116681 31.9121 17.214144 29.470287 16.970703 26.466797 L 12.335938 26.466797 C 12.626268 30.446204 13.963889 33.678709 16.353516 36.162109 C 18.700203 38.587982 21.722877 39.964492 25.412109 40.308594 z M 16.970703 26.466797 L 25.34375 26.466797 L 25.34375 23.464844 L 16.9375 23.464844 C 16.911675 23.890786 16.896484 24.325331 16.896484 24.773438 C 16.896484 25.358829 16.926317 25.91918 16.970703 26.466797 z M 25.412109 36.939453 C 26.013434 37.033433 26.634257 37.091797 27.291016 37.091797 C 28.01543 37.091797 28.701951 37.02645 29.357422 36.912109 L 29.357422 26.386719 L 34.123047 26.386719 C 35.374898 26.386719 36.510129 26.475933 37.552734 26.636719 C 37.606917 26.036054 37.644531 25.420213 37.644531 24.773438 C 37.644531 24.389533 37.626377 24.01998 37.607422 23.652344 C 36.34394 23.507859 34.983448 23.431641 33.517578 23.431641 L 25.412109 23.431641 L 25.412109 36.939453 z M 37.552734 26.636719 C 37.288908 29.561476 36.3922 31.947799 34.84375 33.779297 C 33.413238 35.484518 31.582121 36.524034 29.357422 36.912109 L 29.357422 40.285156 C 32.945677 39.910903 35.894611 38.544976 38.1875 36.162109 C 40.223734 34.035893 41.495873 31.373159 42.007812 28.175781 C 40.833664 27.423776 39.345701 26.913222 37.552734 26.636719 z "
id="text4243" /></svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
inkscape:version="0.91 r13725"
sodipodi:docname="lod-i.svg"><metadata
id="metadata4241"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs4239" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2880"
inkscape:window-height="1724"
id="namedview4237"
showgrid="false"
inkscape:zoom="14.66"
inkscape:cx="22.9339"
inkscape:cy="22.463149"
inkscape:window-x="0"
inkscape:window-y="33"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><path
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:44.7959404px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 0.17382812 0.10351562 L 0.17382812 26.466797 L 12.335938 26.466797 C 12.295785 25.916449 12.273438 25.352399 12.273438 24.773438 C 12.273438 24.325519 12.298233 23.895635 12.322266 23.464844 L 5.6464844 23.464844 L 5.6464844 0.10351562 L 0.17382812 0.10351562 z M 12.322266 23.464844 L 16.9375 23.464844 C 17.131223 20.269693 18.048402 17.696004 19.695312 15.748047 C 21.57624 13.54041 24.107908 12.4375 27.291016 12.4375 C 30.474123 12.4375 32.99176 13.54041 34.84375 15.748047 C 36.522116 17.733209 37.438284 20.371891 37.607422 23.652344 C 39.352093 23.851854 40.904899 24.187106 42.263672 24.662109 C 42.242096 19.950324 40.88586 16.190967 38.1875 13.386719 C 35.46739 10.546406 31.834178 9.125 27.291016 9.125 C 22.733385 9.125 19.088094 10.546406 16.353516 13.386719 C 13.889087 15.947857 12.553913 19.312575 12.322266 23.464844 z M 42.263672 24.662109 C 42.263846 24.700089 42.267578 24.735334 42.267578 24.773438 C 42.267578 25.968111 42.179749 27.101932 42.007812 28.175781 C 42.345297 28.39193 42.664811 28.621521 42.951172 28.876953 C 44.826172 30.525965 45.763672 33.130435 45.763672 36.689453 C 45.763672 40.272198 44.826172 42.893812 42.951172 44.554688 C 41.089193 46.215563 38.146485 47.046875 34.123047 47.046875 L 29.357422 47.046875 L 29.357422 40.285156 C 28.688843 40.354889 28.004565 40.402344 27.291016 40.402344 C 26.644531 40.402344 26.021914 40.365471 25.412109 40.308594 L 25.412109 50 L 33.517578 50 C 39.142578 50 43.283203 48.926571 45.939453 46.779297 C 48.595703 44.632023 49.923828 41.268723 49.923828 36.689453 C 49.923828 32.13391 48.602214 28.787755 45.958984 26.652344 C 44.948178 25.831197 43.714341 25.169238 42.263672 24.662109 z M 25.412109 40.308594 L 25.412109 36.939453 C 23.099 36.57794 21.188155 35.53144 19.695312 33.779297 C 18.116681 31.9121 17.214144 29.470287 16.970703 26.466797 L 12.335938 26.466797 C 12.626268 30.446204 13.963889 33.678709 16.353516 36.162109 C 18.700203 38.587982 21.722877 39.964492 25.412109 40.308594 z M 16.970703 26.466797 L 25.34375 26.466797 L 25.34375 23.464844 L 16.9375 23.464844 C 16.911675 23.890786 16.896484 24.325331 16.896484 24.773438 C 16.896484 25.358829 16.926317 25.91918 16.970703 26.466797 z M 25.412109 36.939453 C 26.013434 37.033433 26.634257 37.091797 27.291016 37.091797 C 28.01543 37.091797 28.701951 37.02645 29.357422 36.912109 L 29.357422 26.386719 L 34.123047 26.386719 C 35.374898 26.386719 36.510129 26.475933 37.552734 26.636719 C 37.606917 26.036054 37.644531 25.420213 37.644531 24.773438 C 37.644531 24.389533 37.626377 24.01998 37.607422 23.652344 C 36.34394 23.507859 34.983448 23.431641 33.517578 23.431641 L 25.412109 23.431641 L 25.412109 36.939453 z M 37.552734 26.636719 C 37.288908 29.561476 36.3922 31.947799 34.84375 33.779297 C 33.413238 35.484518 31.582121 36.524034 29.357422 36.912109 L 29.357422 40.285156 C 32.945677 39.910903 35.894611 38.544976 38.1875 36.162109 C 40.223734 34.035893 41.495873 31.373159 42.007812 28.175781 C 40.833664 27.423776 39.345701 26.913222 37.552734 26.636719 z "
id="text4243" /></svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -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);
}

View file

@ -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('&');

View file

@ -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));
}
}

View file

@ -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)

View file

@ -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;
};
};

View file

@ -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());
}

View file

@ -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

View file

@ -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

View file

@ -17,4 +17,4 @@ int main(int argc, char *argv[]) {
autoTester.show();
return application.exec();
}
}

View file

@ -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);
}

View file

@ -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:

View file

@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If unchecked, will not show results during evaluation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>

View file

@ -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;
}

View file

@ -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

View file

@ -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>

View file

@ -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* {

View file

@ -133,7 +133,7 @@ var DEBUG_INFO = {
Reticle: {
supportsScale: 'scale' in Reticle,
},
protocolVersion: location.protocolVersion,
protocolVersion: Window.protocolSignature(),
};
var globalState = {

View file

@ -52,7 +52,7 @@ function CustomSettingsApp(options) {
this.extraParams = Object.assign(options.extraParams || {}, {
customSettingsVersion: CustomSettingsApp.version+'',
protocolVersion: location.protocolVersion && location.protocolVersion()
protocolVersion: Window.protocolSignature && Window.protocolSignature()
});
var params = {