Merge remote-tracking branch 'upstream/master' into android

This commit is contained in:
Brad Davis 2018-01-18 17:33:10 -08:00
commit bdfa1273ba
63 changed files with 919 additions and 820 deletions

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

@ -610,8 +610,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) {
@ -2148,6 +2147,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";
}
@ -4420,8 +4424,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

@ -306,8 +306,6 @@ vectorType ModelOverlay::mapJoints(mapFunction<itemType> function) const {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and

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

@ -65,23 +65,7 @@ namespace render {
}
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
if (args) {
if (overlay->getAnchor() == Overlay::MY_AVATAR) {
auto batch = args->_batch;
auto avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::quat myAvatarRotation = avatar->getWorldOrientation();
glm::vec3 myAvatarPosition = avatar->getWorldPosition();
float angle = glm::degrees(glm::angle(myAvatarRotation));
glm::vec3 axis = glm::axis(myAvatarRotation);
float myAvatarScale = avatar->getModelScale();
Transform transform = Transform();
transform.setTranslation(myAvatarPosition);
transform.setRotation(glm::angleAxis(angle, axis));
transform.setScale(myAvatarScale);
batch->setModelTransform(transform);
overlay->render(args);
} else {
overlay->render(args);
}
overlay->render(args);
}
}
template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay) {

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 1
#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 1
#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

@ -319,11 +319,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

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