mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 14:02:59 +02:00
Merge remote-tracking branch 'upstream/master' into hysteresis
This commit is contained in:
commit
4fbf5db697
246 changed files with 11237 additions and 3137 deletions
assignment-client
cmake
domain-server
ice-server/src
interface
resources
controllers
html
qml
src
Application.cppApplication.hApplication_render.cppFancyCamera.hMenu.cpp
audio
avatar
commerce
networking
raypick
scripting
Audio.cppAudio.hClipboardScriptingInterface.cppClipboardScriptingInterface.hMenuScriptingInterface.hSelectionScriptingInterface.cppSelectionScriptingInterface.hWalletScriptingInterface.cppWalletScriptingInterface.hWindowScriptingInterface.cppWindowScriptingInterface.h
ui
AddressBarDialog.hApplicationOverlay.cppApplicationOverlay.h
overlays
Base3DOverlay.cppBase3DOverlay.hBillboardable.cppCircle3DOverlay.cppCircle3DOverlay.hContextOverlayInterface.cppContextOverlayInterface.hCube3DOverlay.cppGrid3DOverlay.cppImage3DOverlay.cppImageOverlay.cppLine3DOverlay.cppModelOverlay.cppModelOverlay.hOverlay.cppOverlay.hOverlay2D.cppOverlay2D.hOverlays.cppOverlays.hOverlaysPayload.cppPanelAttachable.cppPlanar3DOverlay.cppPlanar3DOverlay.hQmlOverlay.cppRectangle3DOverlay.cppRectangleOverlay.cppShape3DOverlay.cppShape3DOverlay.hSphere3DOverlay.cppText3DOverlay.cppTextOverlay.cppVolume3DOverlay.cppWeb3DOverlay.cppWeb3DOverlay.h
libraries
|
@ -13,9 +13,25 @@ setup_memory_debugger()
|
|||
link_hifi_libraries(
|
||||
audio avatars octree gpu model fbx entities
|
||||
networking animation recording shared script-engine embedded-webserver
|
||||
controllers physics plugins midi baking image
|
||||
controllers physics plugins midi image
|
||||
)
|
||||
|
||||
add_dependencies(${TARGET_NAME} oven)
|
||||
|
||||
if (WIN32)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
$<TARGET_FILE_DIR:oven>
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>)
|
||||
else()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
$<TARGET_FILE:oven>
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/oven)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
package_libraries_for_deployment()
|
||||
endif()
|
||||
|
|
|
@ -29,11 +29,10 @@
|
|||
#include <QtCore/QUrlQuery>
|
||||
|
||||
#include <ClientServerUtils.h>
|
||||
#include <FBXBaker.h>
|
||||
#include <JSBaker.h>
|
||||
#include <NodeType.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <PathUtils.h>
|
||||
#include <image/Image.h>
|
||||
|
||||
#include "AssetServerLogging.h"
|
||||
#include "BakeAssetTask.h"
|
||||
|
@ -250,7 +249,7 @@ AssetServer::AssetServer(ReceivedMessage& message) :
|
|||
image::setNormalTexturesCompressionEnabled(true);
|
||||
image::setCubeTexturesCompressionEnabled(true);
|
||||
|
||||
BAKEABLE_TEXTURE_EXTENSIONS = TextureBaker::getSupportedFormats();
|
||||
BAKEABLE_TEXTURE_EXTENSIONS = image::getSupportedFormats();
|
||||
qDebug() << "Supported baking texture formats:" << BAKEABLE_MODEL_EXTENSIONS;
|
||||
|
||||
// Most of the work will be I/O bound, reading from disk and constructing packet objects,
|
||||
|
@ -416,6 +415,9 @@ void AssetServer::completeSetup() {
|
|||
if (assetsFilesizeLimit != 0 && assetsFilesizeLimit < MAX_UPLOAD_SIZE) {
|
||||
_filesizeLimit = assetsFilesizeLimit * BITS_PER_MEGABITS;
|
||||
}
|
||||
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
PathUtils::removeTemporaryApplicationDirs("Oven");
|
||||
}
|
||||
|
||||
void AssetServer::cleanupUnmappedFiles() {
|
||||
|
|
|
@ -11,11 +11,18 @@
|
|||
|
||||
#include "BakeAssetTask.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <mutex>
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <FBXBaker.h>
|
||||
#include <PathUtils.h>
|
||||
#include <JSBaker.h>
|
||||
|
||||
static const int OVEN_STATUS_CODE_SUCCESS { 0 };
|
||||
static const int OVEN_STATUS_CODE_FAIL { 1 };
|
||||
static const int OVEN_STATUS_CODE_ABORT { 2 };
|
||||
|
||||
std::once_flag registerMetaTypesFlag;
|
||||
|
||||
BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) :
|
||||
_assetHash(assetHash),
|
||||
|
@ -23,6 +30,10 @@ BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetP
|
|||
_filePath(filePath)
|
||||
{
|
||||
|
||||
std::call_once(registerMetaTypesFlag, []() {
|
||||
qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
|
||||
qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
|
||||
});
|
||||
}
|
||||
|
||||
void cleanupTempFiles(QString tempOutputDir, std::vector<QString> files) {
|
||||
|
@ -41,67 +52,76 @@ void cleanupTempFiles(QString tempOutputDir, std::vector<QString> files) {
|
|||
};
|
||||
|
||||
void BakeAssetTask::run() {
|
||||
_isBaking.store(true);
|
||||
|
||||
qRegisterMetaType<QVector<QString> >("QVector<QString>");
|
||||
TextureBakerThreadGetter fn = []() -> QThread* { return QThread::currentThread(); };
|
||||
|
||||
QString tempOutputDir;
|
||||
|
||||
if (_assetPath.endsWith(".fbx")) {
|
||||
tempOutputDir = PathUtils::generateTemporaryDir();
|
||||
_baker = std::unique_ptr<FBXBaker> {
|
||||
new FBXBaker(QUrl("file:///" + _filePath), fn, tempOutputDir)
|
||||
};
|
||||
} else if (_assetPath.endsWith(".js", Qt::CaseInsensitive)) {
|
||||
_baker = std::unique_ptr<JSBaker>{
|
||||
new JSBaker(QUrl("file:///" + _filePath), PathUtils::generateTemporaryDir())
|
||||
};
|
||||
} else {
|
||||
tempOutputDir = PathUtils::generateTemporaryDir();
|
||||
_baker = std::unique_ptr<TextureBaker> {
|
||||
new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE,
|
||||
tempOutputDir)
|
||||
};
|
||||
if (_isBaking.exchange(true)) {
|
||||
qWarning() << "Tried to start bake asset task while already baking";
|
||||
return;
|
||||
}
|
||||
|
||||
QEventLoop loop;
|
||||
connect(_baker.get(), &Baker::finished, &loop, &QEventLoop::quit);
|
||||
connect(_baker.get(), &Baker::aborted, &loop, &QEventLoop::quit);
|
||||
QMetaObject::invokeMethod(_baker.get(), "bake", Qt::QueuedConnection);
|
||||
loop.exec();
|
||||
QString tempOutputDir = PathUtils::generateTemporaryDir();
|
||||
auto base = QFileInfo(QCoreApplication::applicationFilePath()).absoluteDir();
|
||||
QString path = base.absolutePath() + "/oven";
|
||||
QString extension = _assetPath.mid(_assetPath.lastIndexOf('.') + 1);
|
||||
QStringList args {
|
||||
"-i", _filePath,
|
||||
"-o", tempOutputDir,
|
||||
"-t", extension,
|
||||
};
|
||||
|
||||
if (_baker->wasAborted()) {
|
||||
qDebug() << "Aborted baking: " << _assetHash << _assetPath;
|
||||
_ovenProcess.reset(new QProcess());
|
||||
|
||||
_wasAborted.store(true);
|
||||
connect(_ovenProcess.get(), static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this, [this, tempOutputDir](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
qDebug() << "Baking process finished: " << exitCode << exitStatus;
|
||||
|
||||
cleanupTempFiles(tempOutputDir, _baker->getOutputFiles());
|
||||
if (exitStatus == QProcess::CrashExit) {
|
||||
if (_wasAborted) {
|
||||
emit bakeAborted(_assetHash, _assetPath);
|
||||
} else {
|
||||
QString errors = "Fatal error occurred while baking";
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
}
|
||||
} else if (exitCode == OVEN_STATUS_CODE_SUCCESS) {
|
||||
QDir outputDir = tempOutputDir;
|
||||
auto files = outputDir.entryInfoList(QDir::Files);
|
||||
QVector<QString> outputFiles;
|
||||
for (auto& file : files) {
|
||||
outputFiles.push_back(file.absoluteFilePath());
|
||||
}
|
||||
|
||||
emit bakeAborted(_assetHash, _assetPath);
|
||||
} else if (_baker->hasErrors()) {
|
||||
qDebug() << "Failed to bake: " << _assetHash << _assetPath << _baker->getErrors();
|
||||
emit bakeComplete(_assetHash, _assetPath, tempOutputDir, outputFiles);
|
||||
} else if (exitStatus == QProcess::NormalExit && exitCode == OVEN_STATUS_CODE_ABORT) {
|
||||
_wasAborted.store(true);
|
||||
emit bakeAborted(_assetHash, _assetPath);
|
||||
} else {
|
||||
QString errors;
|
||||
if (exitCode == OVEN_STATUS_CODE_FAIL) {
|
||||
QDir outputDir = tempOutputDir;
|
||||
auto errorFilePath = outputDir.absoluteFilePath("errors.txt");
|
||||
QFile errorFile { errorFilePath };
|
||||
if (errorFile.open(QIODevice::ReadOnly)) {
|
||||
errors = errorFile.readAll();
|
||||
errorFile.close();
|
||||
} else {
|
||||
errors = "Unknown error occurred while baking";
|
||||
}
|
||||
}
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
}
|
||||
|
||||
auto errors = _baker->getErrors().join('\n'); // Join error list into a single string for convenience
|
||||
|
||||
_didFinish.store(true);
|
||||
|
||||
cleanupTempFiles(tempOutputDir, _baker->getOutputFiles());
|
||||
});
|
||||
|
||||
qDebug() << "Starting oven for " << _assetPath;
|
||||
_ovenProcess->start(path, args, QIODevice::ReadOnly);
|
||||
if (!_ovenProcess->waitForStarted(-1)) {
|
||||
QString errors = "Oven process failed to start";
|
||||
emit bakeFailed(_assetHash, _assetPath, errors);
|
||||
} else {
|
||||
auto vectorOutputFiles = QVector<QString>::fromStdVector(_baker->getOutputFiles());
|
||||
|
||||
qDebug() << "Finished baking: " << _assetHash << _assetPath << vectorOutputFiles;
|
||||
|
||||
_didFinish.store(true);
|
||||
|
||||
emit bakeComplete(_assetHash, _assetPath, tempOutputDir, vectorOutputFiles);
|
||||
return;
|
||||
}
|
||||
_ovenProcess->waitForFinished();
|
||||
}
|
||||
|
||||
void BakeAssetTask::abort() {
|
||||
if (_baker) {
|
||||
_baker->abort();
|
||||
if (!_wasAborted.exchange(true)) {
|
||||
_ovenProcess->terminate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,10 @@
|
|||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QRunnable>
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
|
||||
#include <AssetUtils.h>
|
||||
#include <Baker.h>
|
||||
|
||||
class BakeAssetTask : public QObject, public QRunnable {
|
||||
Q_OBJECT
|
||||
|
@ -32,7 +33,6 @@ public:
|
|||
|
||||
void abort();
|
||||
bool wasAborted() const { return _wasAborted.load(); }
|
||||
bool didFinish() const { return _didFinish.load(); }
|
||||
|
||||
signals:
|
||||
void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir, QVector<QString> outputFiles);
|
||||
|
@ -44,9 +44,8 @@ private:
|
|||
AssetHash _assetHash;
|
||||
AssetPath _assetPath;
|
||||
QString _filePath;
|
||||
std::unique_ptr<Baker> _baker;
|
||||
std::unique_ptr<QProcess> _ovenProcess { nullptr };
|
||||
std::atomic<bool> _wasAborted { false };
|
||||
std::atomic<bool> _didFinish { false };
|
||||
};
|
||||
|
||||
#endif // hifi_BakeAssetTask_h
|
||||
|
|
|
@ -870,8 +870,8 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node
|
|||
node->setLinkedData(std::unique_ptr<NodeData> { new AvatarMixerClientData(node->getUUID()) });
|
||||
clientData = dynamic_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||
auto& avatar = clientData->getAvatar();
|
||||
avatar.setDomainMinimumScale(_domainMinimumScale);
|
||||
avatar.setDomainMaximumScale(_domainMaximumScale);
|
||||
avatar.setDomainMinimumHeight(_domainMinimumHeight);
|
||||
avatar.setDomainMaximumHeight(_domainMaximumHeight);
|
||||
}
|
||||
|
||||
return clientData;
|
||||
|
@ -939,21 +939,21 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
|||
|
||||
const QString AVATARS_SETTINGS_KEY = "avatars";
|
||||
|
||||
static const QString MIN_SCALE_OPTION = "min_avatar_scale";
|
||||
float settingMinScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE);
|
||||
_domainMinimumScale = glm::clamp(settingMinScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
||||
static const QString MIN_HEIGHT_OPTION = "min_avatar_height";
|
||||
float settingMinHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT);
|
||||
_domainMinimumHeight = glm::clamp(settingMinHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||
|
||||
static const QString MAX_SCALE_OPTION = "max_avatar_scale";
|
||||
float settingMaxScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE);
|
||||
_domainMaximumScale = glm::clamp(settingMaxScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
||||
static const QString MAX_HEIGHT_OPTION = "max_avatar_height";
|
||||
float settingMaxHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT);
|
||||
_domainMaximumHeight = glm::clamp(settingMaxHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||
|
||||
// make sure that the domain owner didn't flip min and max
|
||||
if (_domainMinimumScale > _domainMaximumScale) {
|
||||
std::swap(_domainMinimumScale, _domainMaximumScale);
|
||||
if (_domainMinimumHeight > _domainMaximumHeight) {
|
||||
std::swap(_domainMinimumHeight, _domainMaximumHeight);
|
||||
}
|
||||
|
||||
qCDebug(avatars) << "This domain requires a minimum avatar scale of" << _domainMinimumScale
|
||||
<< "and a maximum avatar scale of" << _domainMaximumScale;
|
||||
qCDebug(avatars) << "This domain requires a minimum avatar height of" << _domainMinimumHeight
|
||||
<< "and a maximum avatar height of" << _domainMaximumHeight;
|
||||
|
||||
const QString AVATAR_WHITELIST_DEFAULT{ "" };
|
||||
static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist";
|
||||
|
|
|
@ -90,8 +90,8 @@ private:
|
|||
|
||||
float _maxKbpsPerNode = 0.0f;
|
||||
|
||||
float _domainMinimumScale { MIN_AVATAR_SCALE };
|
||||
float _domainMaximumScale { MAX_AVATAR_SCALE };
|
||||
float _domainMinimumHeight { MIN_AVATAR_HEIGHT };
|
||||
float _domainMaximumHeight { MAX_AVATAR_HEIGHT };
|
||||
|
||||
RateCounter<> _broadcastRate;
|
||||
p_high_resolution_clock::time_point _lastDebugMessage;
|
||||
|
|
|
@ -25,6 +25,23 @@ AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) :
|
|||
_avatar->setID(nodeID);
|
||||
}
|
||||
|
||||
uint64_t AvatarMixerClientData::getLastOtherAvatarEncodeTime(QUuid otherAvatar) const {
|
||||
std::unordered_map<QUuid, uint64_t>::const_iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar);
|
||||
if (itr != _lastOtherAvatarEncodeTime.end()) {
|
||||
return itr->second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time) {
|
||||
std::unordered_map<QUuid, uint64_t>::iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar);
|
||||
if (itr != _lastOtherAvatarEncodeTime.end()) {
|
||||
itr->second = time;
|
||||
} else {
|
||||
_lastOtherAvatarEncodeTime.emplace(std::pair<QUuid, uint64_t>(otherAvatar, time));
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) {
|
||||
if (!_packetQueue.node) {
|
||||
_packetQueue.node = node;
|
||||
|
|
|
@ -110,16 +110,10 @@ public:
|
|||
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
|
||||
|
||||
ViewFrustum getViewFrustom() const { return _currentViewFrustum; }
|
||||
ViewFrustum getViewFrustum() const { return _currentViewFrustum; }
|
||||
|
||||
quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) {
|
||||
quint64 result = 0;
|
||||
if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) {
|
||||
result = _lastOtherAvatarEncodeTime[otherAvatar];
|
||||
}
|
||||
_lastOtherAvatarEncodeTime[otherAvatar] = usecTimestampNow();
|
||||
return result;
|
||||
}
|
||||
uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
|
||||
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time);
|
||||
|
||||
QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) {
|
||||
_lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount());
|
||||
|
@ -143,7 +137,7 @@ private:
|
|||
|
||||
// this is a map of the last time we encoded an "other" avatar for
|
||||
// sending to "this" node
|
||||
std::unordered_map<QUuid, quint64> _lastOtherAvatarEncodeTime;
|
||||
std::unordered_map<QUuid, uint64_t> _lastOtherAvatarEncodeTime;
|
||||
std::unordered_map<QUuid, QVector<JointData>> _lastOtherAvatarSentJoints;
|
||||
|
||||
uint64_t _identityChangeTimestamp;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <NodeList.h>
|
||||
#include <Node.h>
|
||||
#include <OctreeConstants.h>
|
||||
#include <PrioritySortUtil.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StDev.h>
|
||||
|
@ -32,7 +33,6 @@
|
|||
#include "AvatarMixerClientData.h"
|
||||
#include "AvatarMixerSlave.h"
|
||||
|
||||
|
||||
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
|
||||
_begin = begin;
|
||||
_end = end;
|
||||
|
@ -184,10 +184,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
|
||||
// setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes
|
||||
// for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes
|
||||
QList<AvatarSharedPointer> avatarList;
|
||||
std::vector<AvatarSharedPointer> avatarsToSort;
|
||||
std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes;
|
||||
|
||||
std::unordered_map<QUuid, uint64_t> avatarEncodeTimes;
|
||||
std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
|
||||
// make sure this is an agent that we have avatar data for before considering it for inclusion
|
||||
if (otherNode->getType() == NodeType::Agent
|
||||
|
@ -195,36 +194,56 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||
|
||||
AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer();
|
||||
avatarList << otherAvatar;
|
||||
avatarsToSort.push_back(otherAvatar);
|
||||
avatarDataToNodes[otherAvatar] = otherNode;
|
||||
QUuid id = otherAvatar->getSessionUUID();
|
||||
avatarEncodeTimes[id] = nodeData->getLastOtherAvatarEncodeTime(id);
|
||||
}
|
||||
});
|
||||
|
||||
AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer();
|
||||
ViewFrustum cameraView = nodeData->getViewFrustom();
|
||||
std::priority_queue<AvatarPriority> sortedAvatars;
|
||||
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars,
|
||||
[&](AvatarSharedPointer avatar)->uint64_t {
|
||||
auto avatarNode = avatarDataToNodes[avatar];
|
||||
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
|
||||
return nodeData->getLastBroadcastTime(avatarNode->getUUID());
|
||||
}, [&](AvatarSharedPointer avatar)->float{
|
||||
glm::vec3 nodeBoxHalfScale = (avatar->getWorldPosition() - avatar->getGlobalBoundingBoxCorner() * avatar->getSensorToWorldScale());
|
||||
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
|
||||
}, [&](AvatarSharedPointer avatar)->bool {
|
||||
class SortableAvatar: public PrioritySortUtil::Sortable {
|
||||
public:
|
||||
SortableAvatar() = delete;
|
||||
SortableAvatar(const AvatarSharedPointer& avatar, uint64_t lastEncodeTime)
|
||||
: _avatar(avatar), _lastEncodeTime(lastEncodeTime) {}
|
||||
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
|
||||
float getRadius() const override {
|
||||
glm::vec3 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale());
|
||||
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
|
||||
}
|
||||
uint64_t getTimestamp() const override {
|
||||
return _lastEncodeTime;
|
||||
}
|
||||
const AvatarSharedPointer& getAvatar() const { return _avatar; }
|
||||
|
||||
private:
|
||||
AvatarSharedPointer _avatar;
|
||||
uint64_t _lastEncodeTime;
|
||||
};
|
||||
|
||||
// prepare to sort
|
||||
ViewFrustum cameraView = nodeData->getViewFrustum();
|
||||
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView,
|
||||
AvatarData::_avatarSortCoefficientSize,
|
||||
AvatarData::_avatarSortCoefficientCenter,
|
||||
AvatarData::_avatarSortCoefficientAge);
|
||||
|
||||
// ignore or sort
|
||||
const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer();
|
||||
for (const auto& avatar : avatarsToSort) {
|
||||
if (avatar == thisAvatar) {
|
||||
return true; // ignore ourselves...
|
||||
// don't echo updates to self
|
||||
continue;
|
||||
}
|
||||
|
||||
bool shouldIgnore = false;
|
||||
|
||||
// We will also ignore other nodes for a couple of different reasons:
|
||||
// We ignore other nodes for a couple of reasons:
|
||||
// 1) ignore bubbles and ignore specific node
|
||||
// 2) the node hasn't really updated it's frame data recently, this can
|
||||
// happen if for example the avatar is connected on a desktop and sending
|
||||
// updates at ~30hz. So every 3 frames we skip a frame.
|
||||
auto avatarNode = avatarDataToNodes[avatar];
|
||||
|
||||
auto avatarNode = avatarDataToNodes[avatar];
|
||||
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
|
||||
|
||||
const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
|
||||
|
@ -240,7 +259,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|| (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) {
|
||||
shouldIgnore = true;
|
||||
} else {
|
||||
|
||||
// Check to see if the space bubble is enabled
|
||||
// Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored
|
||||
if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
|
||||
|
@ -267,8 +285,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID());
|
||||
}
|
||||
}
|
||||
quint64 endIgnoreCalculation = usecTimestampNow();
|
||||
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
|
||||
|
||||
if (!shouldIgnore) {
|
||||
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID());
|
||||
|
@ -292,20 +308,26 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
++numAvatarsWithSkippedFrames;
|
||||
}
|
||||
}
|
||||
return shouldIgnore;
|
||||
});
|
||||
quint64 endIgnoreCalculation = usecTimestampNow();
|
||||
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
|
||||
|
||||
if (!shouldIgnore) {
|
||||
// sort this one for later
|
||||
uint64_t lastEncodeTime = 0;
|
||||
std::unordered_map<QUuid, uint64_t>::const_iterator itr = avatarEncodeTimes.find(avatar->getSessionUUID());
|
||||
if (itr != avatarEncodeTimes.end()) {
|
||||
lastEncodeTime = itr->second;
|
||||
}
|
||||
sortedAvatars.push(SortableAvatar(avatar, lastEncodeTime));
|
||||
}
|
||||
}
|
||||
|
||||
// loop through our sorted avatars and allocate our bandwidth to them accordingly
|
||||
int avatarRank = 0;
|
||||
|
||||
// this is overly conservative, because it includes some avatars we might not consider
|
||||
int remainingAvatars = (int)sortedAvatars.size();
|
||||
|
||||
while (!sortedAvatars.empty()) {
|
||||
AvatarPriority sortData = sortedAvatars.top();
|
||||
const auto& avatarData = sortedAvatars.top().getAvatar();
|
||||
sortedAvatars.pop();
|
||||
const auto& avatarData = sortData.avatar;
|
||||
avatarRank++;
|
||||
remainingAvatars--;
|
||||
|
||||
auto otherNode = avatarDataToNodes[avatarData];
|
||||
|
@ -332,10 +354,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
|
||||
}
|
||||
|
||||
// determine if avatar is in view which determines how much data to send
|
||||
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
|
||||
|
||||
|
||||
// determine if avatar is in view, to determine how much data to include...
|
||||
glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale();
|
||||
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
|
||||
bool isInView = nodeData->otherAvatarInView(otherNodeBox);
|
||||
|
@ -405,14 +425,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
// set the last sent sequence number for this sender on the receiver
|
||||
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
||||
otherNodeData->getLastReceivedSequenceNumber());
|
||||
nodeData->setLastOtherAvatarEncodeTime(otherNode->getUUID(), usecTimestampNow());
|
||||
}
|
||||
} else {
|
||||
// TODO? this avatar is not included now, and will probably not be included next frame.
|
||||
// It would be nice if we could tweak its future sort priority to put it at the back of the list.
|
||||
}
|
||||
|
||||
avatarPacketList->endSegment();
|
||||
|
||||
quint64 endAvatarDataPacking = usecTimestampNow();
|
||||
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
||||
};
|
||||
}
|
||||
|
||||
quint64 startPacketSending = usecTimestampNow();
|
||||
|
||||
|
|
|
@ -477,7 +477,7 @@ void EntityServer::startDynamicDomainVerification() {
|
|||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = i.key();
|
||||
|
|
|
@ -29,10 +29,6 @@ macro(GENERATE_INSTALLERS)
|
|||
|
||||
|
||||
if (WIN32)
|
||||
# Do not install the Visual Studio C runtime libraries. The installer will do this automatically
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
|
||||
|
||||
include(InstallRequiredSystemLibraries)
|
||||
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
|
||||
|
||||
# install and reference the Add/Remove icon
|
||||
|
@ -49,6 +45,10 @@ macro(GENERATE_INSTALLERS)
|
|||
set(_UNINSTALLER_HEADER_BAD_PATH "${HF_CMAKE_DIR}/installer/uninstaller-header.bmp")
|
||||
set(UNINSTALLER_HEADER_IMAGE "")
|
||||
fix_path_for_nsis(${_UNINSTALLER_HEADER_BAD_PATH} UNINSTALLER_HEADER_IMAGE)
|
||||
|
||||
# grab the latest VC redist (2017) and add it to the installer, our NSIS template
|
||||
# will call it during the install
|
||||
install(CODE "file(DOWNLOAD https://go.microsoft.com/fwlink/?LinkId=746572 \"\${CMAKE_INSTALL_PREFIX}/vcredist_x64.exe\")")
|
||||
elseif (APPLE)
|
||||
# produce a drag and drop DMG on OS X
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
|
@ -84,4 +84,3 @@ macro(GENERATE_INSTALLERS)
|
|||
|
||||
include(CPack)
|
||||
endmacro()
|
||||
|
||||
|
|
|
@ -45,5 +45,4 @@ else()
|
|||
endif()
|
||||
|
||||
file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
|
||||
fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@")
|
||||
|
||||
fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@" IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": 2.0,
|
||||
"version": 2.1,
|
||||
"settings": [
|
||||
{
|
||||
"name": "label",
|
||||
|
@ -1015,20 +1015,20 @@
|
|||
"assignment-types": [ 1, 2 ],
|
||||
"settings": [
|
||||
{
|
||||
"name": "min_avatar_scale",
|
||||
"name": "min_avatar_height",
|
||||
"type": "double",
|
||||
"label": "Minimum Avatar Scale",
|
||||
"help": "Limits the scale of avatars in your domain. Must be at least 0.005.",
|
||||
"placeholder": 0.25,
|
||||
"default": 0.25
|
||||
"label": "Minimum Avatar Height (meters)",
|
||||
"help": "Limits the height of avatars in your domain. Must be at least 0.009.",
|
||||
"placeholder": 0.4,
|
||||
"default": 0.4
|
||||
},
|
||||
{
|
||||
"name": "max_avatar_scale",
|
||||
"name": "max_avatar_height",
|
||||
"type": "double",
|
||||
"label": "Maximum Avatar Scale",
|
||||
"help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.",
|
||||
"placeholder": 3.0,
|
||||
"default": 3.0
|
||||
"label": "Maximum Avatar Height (meters)",
|
||||
"help": "Limits the scale of avatars in your domain. Cannot be greater than 1755.",
|
||||
"placeholder": 5.2,
|
||||
"default": 5.2
|
||||
},
|
||||
{
|
||||
"name": "avatar_whitelist",
|
||||
|
|
|
@ -94,7 +94,7 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
root.insert(requestSubobjectKey, subobject);
|
||||
QJsonDocument doc { root };
|
||||
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + metaversePath };
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + metaversePath };
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
|
@ -420,7 +420,7 @@ bool DomainServer::optionallySetupOAuth() {
|
|||
|
||||
// if we don't have an oauth provider URL then we default to the default node auth url
|
||||
if (_oauthProviderURL.isEmpty()) {
|
||||
_oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
_oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
}
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
@ -2159,7 +2159,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
QJsonDocument doc(root);
|
||||
|
||||
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/api/v1/places/" + place_id };
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/v1/places/" + place_id };
|
||||
|
||||
url.setQuery("access_token=" + accessTokenVariant->toString());
|
||||
|
||||
|
|
|
@ -304,6 +304,26 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
|
||||
*wizardCompletedOnce = QVariant(true);
|
||||
}
|
||||
if (oldVersion < 2.1) {
|
||||
// convert old avatar scale settings into avatar height.
|
||||
|
||||
const QString AVATAR_MIN_SCALE_KEYPATH = "avatars.min_avatar_scale";
|
||||
const QString AVATAR_MAX_SCALE_KEYPATH = "avatars.max_avatar_scale";
|
||||
const QString AVATAR_MIN_HEIGHT_KEYPATH = "avatars.min_avatar_height";
|
||||
const QString AVATAR_MAX_HEIGHT_KEYPATH = "avatars.max_avatar_height";
|
||||
|
||||
QVariant* avatarMinScale = _configMap.valueForKeyPath(AVATAR_MIN_SCALE_KEYPATH);
|
||||
if (avatarMinScale) {
|
||||
float scale = avatarMinScale->toFloat();
|
||||
_configMap.valueForKeyPath(AVATAR_MIN_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT);
|
||||
}
|
||||
|
||||
QVariant* avatarMaxScale = _configMap.valueForKeyPath(AVATAR_MAX_SCALE_KEYPATH);
|
||||
if (avatarMaxScale) {
|
||||
float scale = avatarMaxScale->toFloat();
|
||||
_configMap.valueForKeyPath(AVATAR_MAX_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// write the current description version to our settings
|
||||
*versionVariant = _descriptionVersion;
|
||||
|
|
|
@ -208,7 +208,7 @@ void IceServer::requestDomainPublicKey(const QUuid& domainID) {
|
|||
// send a request to the metaverse API for the public key for this domain
|
||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL };
|
||||
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL() };
|
||||
QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
|
||||
publicKeyURL.setPath(publicKeyPath);
|
||||
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
|
||||
{ "from": "OculusTouch.LY", "to": "Standard.LY",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.3 },
|
||||
{ "type": "deadZone", "min": 0.7 },
|
||||
"invert"
|
||||
]
|
||||
},
|
||||
{ "from": "OculusTouch.LX", "filters": { "type": "deadZone", "min": 0.3 }, "to": "Standard.LX" },
|
||||
{ "from": "OculusTouch.LX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.LX" },
|
||||
{ "from": "OculusTouch.LT", "to": "Standard.LTClick",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
|
||||
|
@ -29,11 +29,11 @@
|
|||
|
||||
{ "from": "OculusTouch.RY", "to": "Standard.RY",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.3 },
|
||||
{ "type": "deadZone", "min": 0.7 },
|
||||
"invert"
|
||||
]
|
||||
},
|
||||
{ "from": "OculusTouch.RX", "filters": { "type": "deadZone", "min": 0.3 }, "to": "Standard.RX" },
|
||||
{ "from": "OculusTouch.RX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.RX" },
|
||||
{ "from": "OculusTouch.RT", "to": "Standard.RTClick",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
|
||||
|
|
|
@ -34,7 +34,7 @@ var EventBridge;
|
|||
var tempEventBridge = EventBridge;
|
||||
EventBridge = channel.objects.eventBridge;
|
||||
EventBridge.audioOutputDeviceChanged.connect(function(deviceName) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(function(mediaStream) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function(mediaStream) {
|
||||
navigator.mediaDevices.enumerateDevices().then(function(devices) {
|
||||
devices.forEach(function(device) {
|
||||
if (device.kind == "audiooutput") {
|
||||
|
@ -65,21 +65,23 @@ var EventBridge;
|
|||
// we need to listen to events that might precede the addition of this elements.
|
||||
// A more robust hack will be to add a setInterval that look for DOM changes every 100-300 ms (low performance?)
|
||||
|
||||
window.onload = function(){
|
||||
window.addEventListener("load",function(event) {
|
||||
setTimeout(function() {
|
||||
EventBridge.forceHtmlAudioOutputDeviceUpdate();
|
||||
}, 1200);
|
||||
};
|
||||
document.onclick = function(){
|
||||
}, false);
|
||||
|
||||
document.addEventListener("click",function(){
|
||||
setTimeout(function() {
|
||||
EventBridge.forceHtmlAudioOutputDeviceUpdate();
|
||||
}, 1200);
|
||||
};
|
||||
document.onchange = function(){
|
||||
}, false);
|
||||
|
||||
document.addEventListener("change",function(){
|
||||
setTimeout(function() {
|
||||
EventBridge.forceHtmlAudioOutputDeviceUpdate();
|
||||
}, 1200);
|
||||
};
|
||||
}, false);
|
||||
|
||||
tempEventBridge._callbacks.forEach(function (callback) {
|
||||
EventBridge.scriptEventReceived.connect(callback);
|
||||
|
|
634
interface/resources/qml/AudioScope.qml
Normal file
634
interface/resources/qml/AudioScope.qml
Normal file
|
@ -0,0 +1,634 @@
|
|||
//
|
||||
// AudioScope.qml
|
||||
//
|
||||
// Created by Luis Cuenca on 11/22/2017
|
||||
// Copyright 2017 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 "styles-uit"
|
||||
import "controls-uit" as HifiControlsUit
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
property var _scopeInputData
|
||||
property var _scopeOutputLeftData
|
||||
property var _scopeOutputRightData
|
||||
|
||||
property var _triggerInputData
|
||||
property var _triggerOutputLeftData
|
||||
property var _triggerOutputRightData
|
||||
|
||||
property var _triggerValues: QtObject{
|
||||
property int x: parent.width/2
|
||||
property int y: parent.height/3
|
||||
}
|
||||
|
||||
property var _triggered: false
|
||||
property var _steps
|
||||
property var _refreshMs: 32
|
||||
property var _framesPerSecond: AudioScope.getFramesPerSecond()
|
||||
property var _isFrameUnits: true
|
||||
|
||||
property var _holdStart: QtObject{
|
||||
property int x: 0
|
||||
property int y: 0
|
||||
}
|
||||
|
||||
property var _holdEnd: QtObject{
|
||||
property int x: 0
|
||||
property int y: 0
|
||||
}
|
||||
|
||||
property var _timeBeforeHold: 300
|
||||
property var _pressedTime: 0
|
||||
property var _isPressed: false
|
||||
|
||||
property var _recOpacity : 0.0
|
||||
property var _recSign : 0.05
|
||||
|
||||
property var _outputLeftState: false
|
||||
property var _outputRightState: false
|
||||
|
||||
property var _wavFilePath: ""
|
||||
|
||||
function isHolding() {
|
||||
return (_pressedTime > _timeBeforeHold);
|
||||
}
|
||||
|
||||
function updateMeasureUnits() {
|
||||
timeButton.text = _isFrameUnits ? "Display Frames" : "Milliseconds";
|
||||
fiveLabel.text = _isFrameUnits ? "5" : "" + (Math.round(1000 * 5.0/_framesPerSecond));
|
||||
twentyLabel.text = _isFrameUnits ? "20" : "" + (Math.round(1000 * 20.0/_framesPerSecond));
|
||||
fiftyLabel.text = _isFrameUnits ? "50" : "" + (Math.round(1000 * 50.0/_framesPerSecond));
|
||||
}
|
||||
|
||||
function collectScopeData() {
|
||||
if (inputCh.checked) {
|
||||
_scopeInputData = AudioScope.scopeInput;
|
||||
}
|
||||
if (outputLeftCh.checked) {
|
||||
_scopeOutputLeftData = AudioScope.scopeOutputLeft;
|
||||
}
|
||||
if (outputRightCh.checked) {
|
||||
_scopeOutputRightData = AudioScope.scopeOutputRight;
|
||||
}
|
||||
}
|
||||
|
||||
function collectTriggerData() {
|
||||
if (inputCh.checked) {
|
||||
_triggerInputData = AudioScope.triggerInput;
|
||||
}
|
||||
if (outputLeftCh.checked) {
|
||||
_triggerOutputLeftData = AudioScope.triggerOutputLeft;
|
||||
}
|
||||
if (outputRightCh.checked) {
|
||||
_triggerOutputRightData = AudioScope.triggerOutputRight;
|
||||
}
|
||||
}
|
||||
|
||||
function setRecordingLabelOpacity(opacity) {
|
||||
_recOpacity = opacity;
|
||||
recCircle.opacity = _recOpacity;
|
||||
recText.opacity = _recOpacity;
|
||||
}
|
||||
|
||||
function updateRecordingLabel() {
|
||||
_recOpacity += _recSign;
|
||||
if (_recOpacity > 1.0 || _recOpacity < 0.0) {
|
||||
_recOpacity = _recOpacity > 1.0 ? 1.0 : 0.0;
|
||||
_recSign *= -1;
|
||||
}
|
||||
setRecordingLabelOpacity(_recOpacity);
|
||||
}
|
||||
|
||||
function pullFreshValues() {
|
||||
if (Audio.getRecording()) {
|
||||
updateRecordingLabel();
|
||||
}
|
||||
|
||||
if (!AudioScope.getPause()) {
|
||||
if (!_triggered) {
|
||||
collectScopeData();
|
||||
}
|
||||
}
|
||||
if (inputCh.checked || outputLeftCh.checked || outputRightCh.checked) {
|
||||
mycanvas.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
function startRecording() {
|
||||
_wavFilePath = (new Date()).toISOString(); // yyyy-mm-ddThh:mm:ss.sssZ
|
||||
_wavFilePath = _wavFilePath.replace(/[\-:]|\.\d*Z$/g, "").replace("T", "-") + ".wav";
|
||||
// Using controller recording default directory
|
||||
_wavFilePath = Recording.getDefaultRecordingSaveDirectory() + _wavFilePath;
|
||||
if (!Audio.startRecording(_wavFilePath)) {
|
||||
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Error creating: "+_wavFilePath}));
|
||||
updateRecordingUI(false);
|
||||
}
|
||||
}
|
||||
|
||||
function stopRecording() {
|
||||
Audio.stopRecording();
|
||||
setRecordingLabelOpacity(0.0);
|
||||
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Saved: "+_wavFilePath}));
|
||||
}
|
||||
|
||||
function updateRecordingUI(isRecording) {
|
||||
if (!isRecording) {
|
||||
recordButton.text = "Record";
|
||||
recordButton.color = hifi.buttons.black;
|
||||
outputLeftCh.checked = _outputLeftState;
|
||||
outputRightCh.checked = _outputRightState;
|
||||
} else {
|
||||
recordButton.text = "Stop";
|
||||
recordButton.color = hifi.buttons.red;
|
||||
_outputLeftState = outputLeftCh.checked;
|
||||
_outputRightState = outputRightCh.checked;
|
||||
outputLeftCh.checked = true;
|
||||
outputRightCh.checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleRecording() {
|
||||
if (Audio.getRecording()) {
|
||||
updateRecordingUI(false);
|
||||
stopRecording();
|
||||
} else {
|
||||
updateRecordingUI(true);
|
||||
startRecording();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: _refreshMs; running: true; repeat: true
|
||||
onTriggered: pullFreshValues()
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: mycanvas
|
||||
anchors.fill:parent
|
||||
|
||||
onPaint: {
|
||||
|
||||
function displayMeasureArea(ctx) {
|
||||
|
||||
ctx.fillStyle = Qt.rgba(0.1, 0.1, 0.1, 1);
|
||||
ctx.fillRect(_holdStart.x, 0, _holdEnd.x - _holdStart.x, height);
|
||||
|
||||
ctx.lineWidth = "2";
|
||||
ctx.strokeStyle = "#555555";
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(_holdStart.x, 0);
|
||||
ctx.lineTo(_holdStart.x, height);
|
||||
ctx.moveTo(_holdEnd.x, 0);
|
||||
ctx.lineTo(_holdEnd.x, height);
|
||||
|
||||
ctx.moveTo(_holdStart.x, _holdStart.y);
|
||||
ctx.lineTo(_holdEnd.x, _holdStart.y);
|
||||
ctx.moveTo(_holdEnd.x, _holdEnd.y);
|
||||
ctx.lineTo(_holdStart.x, _holdEnd.y);
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function displayTrigger(ctx, lineWidth, color) {
|
||||
var crossSize = 3;
|
||||
var holeSize = 2;
|
||||
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.strokeStyle = color;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(_triggerValues.x - (crossSize + holeSize), _triggerValues.y);
|
||||
ctx.lineTo(_triggerValues.x - holeSize, _triggerValues.y);
|
||||
ctx.moveTo(_triggerValues.x + holeSize, _triggerValues.y);
|
||||
ctx.lineTo(_triggerValues.x + (crossSize + holeSize), _triggerValues.y);
|
||||
|
||||
ctx.moveTo(_triggerValues.x, _triggerValues.y - (crossSize + holeSize));
|
||||
ctx.lineTo(_triggerValues.x, _triggerValues.y - holeSize);
|
||||
ctx.moveTo(_triggerValues.x, _triggerValues.y + holeSize);
|
||||
ctx.lineTo(_triggerValues.x, _triggerValues.y + (crossSize + holeSize));
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function displayBackground(ctx, datawidth, steps, lineWidth, color) {
|
||||
var verticalPadding = 100;
|
||||
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = lineWidth;
|
||||
|
||||
ctx.moveTo(0, height/2);
|
||||
ctx.lineTo(datawidth, height/2);
|
||||
|
||||
var gap = datawidth/steps;
|
||||
for (var i = 0; i < steps; i++) {
|
||||
ctx.moveTo(i*gap + 1, verticalPadding);
|
||||
ctx.lineTo(i*gap + 1, height-verticalPadding);
|
||||
}
|
||||
ctx.moveTo(datawidth-1, verticalPadding);
|
||||
ctx.lineTo(datawidth-1, height-verticalPadding);
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function drawScope(ctx, data, width, color) {
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = width;
|
||||
var x = 0;
|
||||
for (var i = 0; i < data.length-1; i++) {
|
||||
ctx.moveTo(x, data[i] + height/2);
|
||||
ctx.lineTo(++x, data[i+1] + height/2);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function getMeasurementText(dist) {
|
||||
var datasize = _scopeInputData.length;
|
||||
var value = 0;
|
||||
if (fiveFrames.checked) {
|
||||
value = (_isFrameUnits) ? 5.0*dist/datasize : (Math.round(1000 * 5.0/_framesPerSecond))*dist/datasize;
|
||||
} else if (twentyFrames.checked) {
|
||||
value = (_isFrameUnits) ? 20.0*dist/datasize : (Math.round(1000 * 20.0/_framesPerSecond))*dist/datasize;
|
||||
} else if (fiftyFrames.checked) {
|
||||
value = (_isFrameUnits) ? 50.0*dist/datasize : (Math.round(1000 * 50.0/_framesPerSecond))*dist/datasize;
|
||||
}
|
||||
value = Math.abs(Math.round(value*100)/100);
|
||||
var measureText = "" + value + (_isFrameUnits ? " frames" : " milliseconds");
|
||||
return measureText;
|
||||
}
|
||||
|
||||
function drawMeasurements(ctx, color) {
|
||||
ctx.fillStyle = color;
|
||||
ctx.font = "normal 16px sans-serif";
|
||||
var fontwidth = 8;
|
||||
var measureText = getMeasurementText(_holdEnd.x - _holdStart.x);
|
||||
if (_holdStart.x < _holdEnd.x) {
|
||||
ctx.fillText("" + height/2 - _holdStart.y, _holdStart.x-40, _holdStart.y);
|
||||
ctx.fillText("" + height/2 - _holdEnd.y, _holdStart.x-40, _holdEnd.y);
|
||||
ctx.fillText(measureText, _holdEnd.x+10, _holdEnd.y);
|
||||
} else {
|
||||
ctx.fillText("" + height/2 - _holdStart.y, _holdStart.x+10, _holdStart.y);
|
||||
ctx.fillText("" + height/2 - _holdEnd.y, _holdStart.x+10, _holdEnd.y);
|
||||
ctx.fillText(measureText, _holdEnd.x-fontwidth*measureText.length, _holdEnd.y);
|
||||
}
|
||||
}
|
||||
|
||||
var ctx = getContext("2d");
|
||||
|
||||
ctx.fillStyle = Qt.rgba(0, 0, 0, 1);
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
if (isHolding()) {
|
||||
displayMeasureArea(ctx);
|
||||
}
|
||||
|
||||
var guideLinesColor = "#555555"
|
||||
var guideLinesWidth = "1"
|
||||
|
||||
displayBackground(ctx, _scopeInputData.length, _steps, guideLinesWidth, guideLinesColor);
|
||||
|
||||
var triggerWidth = "3"
|
||||
var triggerColor = "#EFB400"
|
||||
|
||||
if (AudioScope.getAutoTrigger()) {
|
||||
displayTrigger(ctx, triggerWidth, triggerColor);
|
||||
}
|
||||
|
||||
var scopeWidth = "2"
|
||||
var scopeInputColor = "#00B4EF"
|
||||
var scopeOutputLeftColor = "#BB0000"
|
||||
var scopeOutputRightColor = "#00BB00"
|
||||
|
||||
if (!_triggered) {
|
||||
if (inputCh.checked) {
|
||||
drawScope(ctx, _scopeInputData, scopeWidth, scopeInputColor);
|
||||
}
|
||||
if (outputLeftCh.checked) {
|
||||
drawScope(ctx, _scopeOutputLeftData, scopeWidth, scopeOutputLeftColor);
|
||||
}
|
||||
if (outputRightCh.checked) {
|
||||
drawScope(ctx, _scopeOutputRightData, scopeWidth, scopeOutputRightColor);
|
||||
}
|
||||
} else {
|
||||
if (inputCh.checked) {
|
||||
drawScope(ctx, _triggerInputData, scopeWidth, scopeInputColor);
|
||||
}
|
||||
if (outputLeftCh.checked) {
|
||||
drawScope(ctx, _triggerOutputLeftData, scopeWidth, scopeOutputLeftColor);
|
||||
}
|
||||
if (outputRightCh.checked) {
|
||||
drawScope(ctx, _triggerOutputRightData, scopeWidth, scopeOutputRightColor);
|
||||
}
|
||||
}
|
||||
|
||||
if (isHolding()) {
|
||||
drawMeasurements(ctx, "#eeeeee");
|
||||
}
|
||||
|
||||
if (_isPressed) {
|
||||
_pressedTime += _refreshMs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: hitbox
|
||||
anchors.fill: mycanvas
|
||||
hoverEnabled: true
|
||||
onPressed: {
|
||||
_isPressed = true;
|
||||
_pressedTime = 0;
|
||||
_holdStart.x = mouseX;
|
||||
_holdStart.y = mouseY;
|
||||
}
|
||||
onPositionChanged: {
|
||||
_holdEnd.x = mouseX;
|
||||
_holdEnd.y = mouseY;
|
||||
}
|
||||
onReleased: {
|
||||
if (!isHolding() && AudioScope.getAutoTrigger()) {
|
||||
_triggerValues.x = mouseX
|
||||
_triggerValues.y = mouseY
|
||||
AudioScope.setTriggerValues(mouseX, mouseY-height/2);
|
||||
}
|
||||
_isPressed = false;
|
||||
_pressedTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.CheckBox {
|
||||
id: activated
|
||||
boxSize: 20
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.topMargin: 8;
|
||||
anchors.leftMargin: 20;
|
||||
checked: AudioScope.getVisible();
|
||||
onCheckedChanged: {
|
||||
AudioScope.setVisible(checked);
|
||||
activelabel.text = AudioScope.getVisible() ? "On" : "Off"
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Label {
|
||||
id: activelabel
|
||||
text: AudioScope.getVisible() ? "On" : "Off"
|
||||
anchors.top: activated.top;
|
||||
anchors.left: activated.right;
|
||||
}
|
||||
|
||||
HifiControlsUit.CheckBox {
|
||||
id: outputLeftCh
|
||||
boxSize: 20
|
||||
text: "Output L"
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 8;
|
||||
onCheckedChanged: {
|
||||
AudioScope.setServerEcho(outputLeftCh.checked || outputRightCh.checked);
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Label {
|
||||
text: "Channels";
|
||||
anchors.horizontalCenter: outputLeftCh.horizontalCenter;
|
||||
anchors.bottom: outputLeftCh.top;
|
||||
anchors.bottomMargin: 8;
|
||||
}
|
||||
|
||||
HifiControlsUit.CheckBox {
|
||||
id: inputCh
|
||||
boxSize: 20
|
||||
text: "Input Mono"
|
||||
anchors.bottom: outputLeftCh.bottom;
|
||||
anchors.right: outputLeftCh.left;
|
||||
anchors.rightMargin: 40;
|
||||
onCheckedChanged: {
|
||||
AudioScope.setLocalEcho(checked);
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.CheckBox {
|
||||
id: outputRightCh
|
||||
boxSize: 20
|
||||
text: "Output R"
|
||||
anchors.bottom: outputLeftCh.bottom;
|
||||
anchors.left: outputLeftCh.right;
|
||||
anchors.leftMargin: 40;
|
||||
onCheckedChanged: {
|
||||
AudioScope.setServerEcho(outputLeftCh.checked || outputRightCh.checked);
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: recordButton;
|
||||
text: "Record";
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.rightMargin: 30;
|
||||
anchors.bottomMargin: 8;
|
||||
width: 95;
|
||||
height: 55;
|
||||
onClicked: {
|
||||
toggleRecording();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: pauseButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.right: recordButton.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.rightMargin: 30;
|
||||
anchors.bottomMargin: 8;
|
||||
height: 55;
|
||||
width: 95;
|
||||
text: " Pause ";
|
||||
onClicked: {
|
||||
AudioScope.togglePause();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.CheckBox {
|
||||
id: twentyFrames
|
||||
boxSize: 20
|
||||
anchors.left: parent.horizontalCenter;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 8;
|
||||
onCheckedChanged: {
|
||||
if (checked){
|
||||
fiftyFrames.checked = false;
|
||||
fiveFrames.checked = false;
|
||||
AudioScope.selectAudioScopeTwentyFrames();
|
||||
_steps = 20;
|
||||
AudioScope.setPause(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Label {
|
||||
id:twentyLabel
|
||||
anchors.left: twentyFrames.right;
|
||||
anchors.verticalCenter: twentyFrames.verticalCenter;
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: timeButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
text: "Display Frames";
|
||||
anchors.horizontalCenter: twentyFrames.horizontalCenter;
|
||||
anchors.bottom: twentyFrames.top;
|
||||
anchors.bottomMargin: 8;
|
||||
height: 26;
|
||||
onClicked: {
|
||||
_isFrameUnits = !_isFrameUnits;
|
||||
updateMeasureUnits();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.CheckBox {
|
||||
id: fiveFrames
|
||||
boxSize: 20
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 8;
|
||||
anchors.horizontalCenterOffset: -50;
|
||||
checked: true;
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
fiftyFrames.checked = false;
|
||||
twentyFrames.checked = false;
|
||||
AudioScope.selectAudioScopeFiveFrames();
|
||||
_steps = 5;
|
||||
AudioScope.setPause(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Label {
|
||||
id:fiveLabel
|
||||
anchors.left: fiveFrames.right;
|
||||
anchors.verticalCenter: fiveFrames.verticalCenter;
|
||||
}
|
||||
|
||||
HifiControlsUit.CheckBox {
|
||||
id: fiftyFrames
|
||||
boxSize: 20
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 8;
|
||||
anchors.horizontalCenterOffset: 70;
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
twentyFrames.checked = false;
|
||||
fiveFrames.checked = false;
|
||||
AudioScope.selectAudioScopeFiftyFrames();
|
||||
_steps = 50;
|
||||
AudioScope.setPause(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Label {
|
||||
id:fiftyLabel
|
||||
anchors.left: fiftyFrames.right;
|
||||
anchors.verticalCenter: fiftyFrames.verticalCenter;
|
||||
}
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: triggerSwitch;
|
||||
height: 26;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.leftMargin: 75;
|
||||
anchors.bottomMargin: 8;
|
||||
labelTextOff: "Off";
|
||||
labelTextOn: "On";
|
||||
onCheckedChanged: {
|
||||
if (!checked) AudioScope.setPause(false);
|
||||
AudioScope.setPause(false);
|
||||
AudioScope.setAutoTrigger(checked);
|
||||
AudioScope.setTriggerValues(_triggerValues.x, _triggerValues.y-root.height/2);
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Label {
|
||||
text: "Trigger";
|
||||
anchors.left: triggerSwitch.left;
|
||||
anchors.leftMargin: -15;
|
||||
anchors.bottom: triggerSwitch.top;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: recordIcon;
|
||||
width:110;
|
||||
height:40;
|
||||
anchors.right: parent.right;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 8;
|
||||
color: "transparent"
|
||||
|
||||
Text {
|
||||
id: recText
|
||||
text: "REC"
|
||||
color: "red"
|
||||
font.pixelSize: 30;
|
||||
anchors.left: recCircle.right;
|
||||
anchors.leftMargin: 10;
|
||||
opacity: _recOpacity;
|
||||
y: -8;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: recCircle;
|
||||
width: 25;
|
||||
height: 25;
|
||||
radius: width*0.5
|
||||
opacity: _recOpacity;
|
||||
color: "red";
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
_steps = AudioScope.getFramesPerScope();
|
||||
AudioScope.setTriggerValues(_triggerValues.x, _triggerValues.y-root.height/2);
|
||||
activated.checked = true;
|
||||
inputCh.checked = true;
|
||||
updateMeasureUnits();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AudioScope
|
||||
onPauseChanged: {
|
||||
if (!AudioScope.getPause()) {
|
||||
pauseButton.text = "Pause";
|
||||
pauseButton.color = hifi.buttons.black;
|
||||
AudioScope.setTriggered(false);
|
||||
_triggered = false;
|
||||
} else {
|
||||
pauseButton.text = "Continue";
|
||||
pauseButton.color = hifi.buttons.blue;
|
||||
}
|
||||
}
|
||||
onTriggered: {
|
||||
_triggered = true;
|
||||
collectTriggerData();
|
||||
AudioScope.setPause(true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ Item {
|
|||
signal newViewRequestedCallback(var request)
|
||||
signal loadingChangedCallback(var loadRequest)
|
||||
|
||||
width: parent.width
|
||||
|
||||
property bool interactive: false
|
||||
|
||||
StylesUIt.HifiConstants {
|
||||
|
@ -58,7 +60,8 @@ Item {
|
|||
WebEngineView {
|
||||
id: webViewCore
|
||||
|
||||
anchors.fill: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
profile: HFWebEngineProfile;
|
||||
settings.pluginsEnabled: true
|
||||
|
@ -91,20 +94,19 @@ Item {
|
|||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
property string newUrl: ""
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
|
||||
if (webViewCoreUserAgent !== undefined) {
|
||||
webViewCore.profile.httpUserAgent = webViewCoreUserAgent
|
||||
} else {
|
||||
webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
|
||||
}
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
|
|
|
@ -26,6 +26,7 @@ Rectangle {
|
|||
// Style
|
||||
color: "#E3E3E3";
|
||||
// Properties
|
||||
property bool debug: false;
|
||||
property int myCardWidth: width - upperRightInfoContainer.width;
|
||||
property int myCardHeight: 80;
|
||||
property int rowHeight: 60;
|
||||
|
@ -1120,7 +1121,9 @@ Rectangle {
|
|||
break;
|
||||
case 'connections':
|
||||
var data = message.params;
|
||||
console.log('Got connection data: ', JSON.stringify(data));
|
||||
if (pal.debug) {
|
||||
console.log('Got connection data: ', JSON.stringify(data));
|
||||
}
|
||||
connectionsUserModelData = data;
|
||||
sortConnectionsModel();
|
||||
connectionsLoading.visible = false;
|
||||
|
|
|
@ -79,10 +79,12 @@ Rectangle {
|
|||
if (result.status !== 'success') {
|
||||
failureErrorText.text = result.message;
|
||||
root.activeView = "checkoutFailure";
|
||||
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemPrice, !root.alreadyOwned, result.message);
|
||||
} else {
|
||||
root.itemHref = result.data.download_url;
|
||||
root.isWearable = result.data.categories.indexOf("Wearables") > -1;
|
||||
root.activeView = "checkoutSuccess";
|
||||
UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemPrice, !root.alreadyOwned);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -599,6 +601,7 @@ Rectangle {
|
|||
sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
|
||||
rezzedNotifContainer.visible = true;
|
||||
rezzedNotifContainerTimer.start();
|
||||
UserActivityLogger.commerceEntityRezzed(root.itemId, "checkout", root.isWearable ? "rez" : "wear");
|
||||
}
|
||||
}
|
||||
RalewaySemiBold {
|
||||
|
@ -902,7 +905,7 @@ Rectangle {
|
|||
}
|
||||
buyTextContainer.color = "#FFC3CD";
|
||||
buyTextContainer.border.color = "#F3808F";
|
||||
buyGlyph.text = hifi.glyphs.error;
|
||||
buyGlyph.text = hifi.glyphs.alert;
|
||||
buyGlyph.size = 54;
|
||||
} else {
|
||||
if (root.alreadyOwned) {
|
||||
|
|
|
@ -349,6 +349,7 @@ Item {
|
|||
sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
|
||||
rezzedNotifContainer.visible = true;
|
||||
rezzedNotifContainerTimer.start();
|
||||
UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.isWearable ? "rez" : "wear");
|
||||
}
|
||||
|
||||
style: ButtonStyle {
|
||||
|
|
|
@ -206,16 +206,6 @@ Item {
|
|||
root.isPasswordField = (focus && passphraseField.echoMode === TextInput.Password);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
|
||||
onClicked: {
|
||||
root.keyboardRaised = true;
|
||||
root.isPasswordField = (passphraseField.echoMode === TextInput.Password);
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
submitPassphraseInputButton.enabled = false;
|
||||
commerce.setPassphrase(passphraseField.text);
|
||||
|
@ -362,25 +352,6 @@ Item {
|
|||
right: parent.right;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: lowerKeyboardButton;
|
||||
z: 999;
|
||||
source: "images/lowerKeyboard.png";
|
||||
anchors.right: keyboard.right;
|
||||
anchors.top: keyboard.showMirrorText ? keyboard.top : undefined;
|
||||
anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom;
|
||||
height: 50;
|
||||
width: 60;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
|
||||
onClicked: {
|
||||
root.keyboardRaised = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Keyboard {
|
||||
id: keyboard;
|
||||
raised: HMD.mounted && root.keyboardRaised;
|
||||
|
|
|
@ -82,17 +82,6 @@ Item {
|
|||
if (focus) {
|
||||
var hidePassword = (currentPassphraseField.echoMode === TextInput.Password);
|
||||
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
} else if (!passphraseFieldAgain.focus) {
|
||||
sendSignalToWallet({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onPressed: {
|
||||
var hidePassword = (currentPassphraseField.echoMode === TextInput.Password);
|
||||
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,21 +104,10 @@ Item {
|
|||
activeFocusOnPress: true;
|
||||
activeFocusOnTab: true;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onPressed: {
|
||||
var hidePassword = (passphraseField.echoMode === TextInput.Password);
|
||||
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
var hidePassword = (passphraseField.echoMode === TextInput.Password);
|
||||
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
} else if (!passphraseFieldAgain.focus) {
|
||||
sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,21 +129,10 @@ Item {
|
|||
activeFocusOnPress: true;
|
||||
activeFocusOnTab: true;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onPressed: {
|
||||
var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password);
|
||||
sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password);
|
||||
sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword});
|
||||
} else if (!passphraseField.focus) {
|
||||
sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,11 @@ Rectangle {
|
|||
if (root.activeView !== "walletSetup") {
|
||||
root.activeView = "walletSetup";
|
||||
commerce.resetLocalWalletOnly();
|
||||
var timestamp = new Date();
|
||||
walletSetup.startingTimestamp = timestamp;
|
||||
walletSetup.setupAttemptID = generateUUID();
|
||||
UserActivityLogger.commerceWalletSetupStarted(timestamp, setupAttemptID, walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app",
|
||||
(AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : ''));
|
||||
}
|
||||
} else if (walletStatus === 2) {
|
||||
if (root.activeView !== "passphraseModal") {
|
||||
|
@ -173,7 +178,7 @@ Rectangle {
|
|||
Connections {
|
||||
onSendSignalToWallet: {
|
||||
if (msg.method === 'walletSetup_finished') {
|
||||
if (msg.referrer === '') {
|
||||
if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
|
||||
root.activeView = "initialize";
|
||||
commerce.getWalletStatus();
|
||||
} else if (msg.referrer === 'purchases') {
|
||||
|
@ -667,25 +672,6 @@ Rectangle {
|
|||
right: parent.right;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: lowerKeyboardButton;
|
||||
z: 999;
|
||||
source: "images/lowerKeyboard.png";
|
||||
anchors.right: keyboard.right;
|
||||
anchors.top: keyboard.showMirrorText ? keyboard.top : undefined;
|
||||
anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom;
|
||||
height: 50;
|
||||
width: 60;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
|
||||
onClicked: {
|
||||
root.keyboardRaised = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Keyboard {
|
||||
id: keyboard;
|
||||
raised: HMD.mounted && root.keyboardRaised;
|
||||
|
@ -720,12 +706,28 @@ Rectangle {
|
|||
case 'updateWalletReferrer':
|
||||
walletSetup.referrer = message.referrer;
|
||||
break;
|
||||
case 'inspectionCertificate_resetCert':
|
||||
// NOP
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
signal sendToScript(var message);
|
||||
|
||||
// generateUUID() taken from:
|
||||
// https://stackoverflow.com/a/8809472
|
||||
function generateUUID() { // Public Domain/MIT
|
||||
var d = new Date().getTime();
|
||||
if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
|
||||
d += performance.now(); //use high-precision timer if available
|
||||
}
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
}
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
|
|
|
@ -68,6 +68,7 @@ Item {
|
|||
Connections {
|
||||
target: GlobalServices
|
||||
onMyUsernameChanged: {
|
||||
transactionHistoryModel.clear();
|
||||
usernameText.text = Account.username;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@ Item {
|
|||
property bool hasShownSecurityImageTip: false;
|
||||
property string referrer;
|
||||
property string keyFilePath;
|
||||
property date startingTimestamp;
|
||||
property string setupAttemptID;
|
||||
readonly property int setupFlowVersion: 1;
|
||||
readonly property var setupStepNames: [ "Setup Prompt", "Security Image Selection", "Passphrase Selection", "Private Keys Ready" ];
|
||||
|
||||
Image {
|
||||
anchors.fill: parent;
|
||||
|
@ -67,6 +71,13 @@ Item {
|
|||
anchors.fill: parent;
|
||||
}
|
||||
|
||||
onActiveViewChanged: {
|
||||
var timestamp = new Date();
|
||||
var currentStepNumber = root.activeView.substring(5);
|
||||
UserActivityLogger.commerceWalletSetupProgress(timestamp, root.setupAttemptID,
|
||||
Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]);
|
||||
}
|
||||
|
||||
//
|
||||
// TITLE BAR START
|
||||
//
|
||||
|
@ -730,6 +741,9 @@ Item {
|
|||
root.visible = false;
|
||||
root.hasShownSecurityImageTip = false;
|
||||
sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""});
|
||||
|
||||
var timestamp = new Date();
|
||||
UserActivityLogger.commerceWalletSetupFinished(timestamp, setupAttemptID, Math.round((timestamp - root.startingTimestamp)/1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ Item {
|
|||
}
|
||||
|
||||
Connections {
|
||||
id: onAttachmentsChangedConnection
|
||||
target: MyAvatar
|
||||
onAttachmentsChanged: reload()
|
||||
}
|
||||
|
@ -34,6 +35,12 @@ Item {
|
|||
reload()
|
||||
}
|
||||
|
||||
function setAttachmentsVariant(attachments) {
|
||||
onAttachmentsChangedConnection.enabled = false;
|
||||
MyAvatar.setAttachmentsVariant(attachments);
|
||||
onAttachmentsChangedConnection.enabled = true;
|
||||
}
|
||||
|
||||
Column {
|
||||
width: pane.width
|
||||
|
||||
|
@ -92,11 +99,15 @@ Item {
|
|||
attachments.splice(index, 1);
|
||||
listView.model.remove(index, 1);
|
||||
}
|
||||
onUpdateAttachment: MyAvatar.setAttachmentsVariant(attachments);
|
||||
onUpdateAttachment: {
|
||||
setAttachmentsVariant(attachments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCountChanged: MyAvatar.setAttachmentsVariant(attachments);
|
||||
onCountChanged: {
|
||||
setAttachmentsVariant(attachments);
|
||||
}
|
||||
|
||||
/*
|
||||
// DEBUG
|
||||
|
@ -220,7 +231,7 @@ Item {
|
|||
};
|
||||
attachments.push(template);
|
||||
listView.model.append({});
|
||||
MyAvatar.setAttachmentsVariant(attachments);
|
||||
setAttachmentsVariant(attachments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,7 +261,7 @@ Item {
|
|||
id: cancelAction
|
||||
text: "Cancel"
|
||||
onTriggered: {
|
||||
MyAvatar.setAttachmentsVariant(originalAttachments);
|
||||
setAttachmentsVariant(originalAttachments);
|
||||
closeDialog();
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +274,7 @@ Item {
|
|||
console.log("Attachment " + i + ": " + attachments[i]);
|
||||
}
|
||||
|
||||
MyAvatar.setAttachmentsVariant(attachments);
|
||||
setAttachmentsVariant(attachments);
|
||||
closeDialog();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,33 +9,30 @@ Overlay {
|
|||
|
||||
Image {
|
||||
id: image
|
||||
property bool scaleFix: true;
|
||||
property real xOffset: 0
|
||||
property real yOffset: 0
|
||||
property bool scaleFix: true
|
||||
property real xStart: 0
|
||||
property real yStart: 0
|
||||
property real xSize: 0
|
||||
property real ySize: 0
|
||||
property real imageScale: 1.0
|
||||
property var resizer: Timer {
|
||||
interval: 50
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
var targetAspect = root.width / root.height;
|
||||
var sourceAspect = image.sourceSize.width / image.sourceSize.height;
|
||||
if (sourceAspect <= targetAspect) {
|
||||
if (root.width === image.sourceSize.width) {
|
||||
return;
|
||||
}
|
||||
image.imageScale = root.width / image.sourceSize.width;
|
||||
} else if (sourceAspect > targetAspect){
|
||||
if (root.height === image.sourceSize.height) {
|
||||
return;
|
||||
}
|
||||
image.imageScale = root.height / image.sourceSize.height;
|
||||
if (image.xSize === 0) {
|
||||
image.xSize = image.sourceSize.width - image.xStart;
|
||||
}
|
||||
image.sourceSize = Qt.size(image.sourceSize.width * image.imageScale, image.sourceSize.height * image.imageScale);
|
||||
if (image.ySize === 0) {
|
||||
image.ySize = image.sourceSize.height - image.yStart;
|
||||
}
|
||||
|
||||
image.anchors.leftMargin = -image.xStart * root.width / image.xSize;
|
||||
image.anchors.topMargin = -image.yStart * root.height / image.ySize;
|
||||
image.anchors.rightMargin = (image.xStart + image.xSize - image.sourceSize.width) * root.width / image.xSize;
|
||||
image.anchors.bottomMargin = (image.yStart + image.ySize - image.sourceSize.height) * root.height / image.ySize;
|
||||
}
|
||||
}
|
||||
x: -1 * xOffset * imageScale
|
||||
y: -1 * yOffset * imageScale
|
||||
|
||||
onSourceSizeChanged: {
|
||||
if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) {
|
||||
|
@ -43,6 +40,8 @@ Overlay {
|
|||
resizer.start();
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
|
@ -57,8 +56,10 @@ Overlay {
|
|||
var key = keys[i];
|
||||
var value = subImage[key];
|
||||
switch (key) {
|
||||
case "x": image.xOffset = value; break;
|
||||
case "y": image.yOffset = value; break;
|
||||
case "x": image.xStart = value; break;
|
||||
case "y": image.yStart = value; break;
|
||||
case "width": image.xSize = value; break;
|
||||
case "height": image.ySize = value; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import "../"
|
|||
import "../toolbars"
|
||||
import "../../styles-uit" as HifiStyles
|
||||
import "../../controls-uit" as HifiControls
|
||||
import QtQuick.Controls 2.2 as QQC2
|
||||
import QtQuick.Templates 2.2 as T
|
||||
|
||||
// references HMD, AddressManager, AddressBarDialog from root context
|
||||
|
||||
|
@ -223,7 +225,7 @@ StackView {
|
|||
visible: addressLine.text.length === 0
|
||||
}
|
||||
|
||||
TextField {
|
||||
QQC2.TextField {
|
||||
id: addressLine
|
||||
width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin;
|
||||
anchors {
|
||||
|
@ -238,16 +240,36 @@ StackView {
|
|||
addressBarDialog.keyboardEnabled = false;
|
||||
toggleOrGo();
|
||||
}
|
||||
placeholderText: "Type domain address here"
|
||||
|
||||
// unfortunately TextField from Quick Controls 2 disallow customization of placeHolderText color without creation of new style
|
||||
property string placeholderText2: "Type domain address here"
|
||||
verticalAlignment: TextInput.AlignBottom
|
||||
style: TextFieldStyle {
|
||||
textColor: hifi.colors.text
|
||||
placeholderTextColor: "gray"
|
||||
font {
|
||||
family: hifi.fonts.fontFamily
|
||||
pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
|
||||
font {
|
||||
family: hifi.fonts.fontFamily
|
||||
pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
}
|
||||
|
||||
color: hifi.colors.text
|
||||
background: Item {}
|
||||
|
||||
QQC2.Label {
|
||||
T.TextField {
|
||||
id: control
|
||||
|
||||
padding: 6 // numbers taken from Qt\5.9.2\Src\qtquickcontrols2\src\imports\controls\TextField.qml
|
||||
leftPadding: padding + 4
|
||||
}
|
||||
background: Item {}
|
||||
|
||||
font: parent.font
|
||||
|
||||
x: control.leftPadding
|
||||
y: control.topPadding
|
||||
|
||||
text: parent.placeholderText2
|
||||
verticalAlignment: "AlignVCenter"
|
||||
color: 'gray'
|
||||
visible: parent.text === ''
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -971,7 +971,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
// set the account manager's root URL and trigger a login request if we don't have the access token
|
||||
accountManager->setIsAgent(true);
|
||||
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
||||
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL());
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
|
||||
|
@ -2812,10 +2812,10 @@ static int getEventQueueSize(QThread* thread) {
|
|||
static void dumpEventQueue(QThread* thread) {
|
||||
auto threadData = QThreadData::get2(thread);
|
||||
QMutexLocker locker(&threadData->postEventList.mutex);
|
||||
qDebug() << "AJT: event list, size =" << threadData->postEventList.size();
|
||||
qDebug() << "Event list, size =" << threadData->postEventList.size();
|
||||
for (auto& postEvent : threadData->postEventList) {
|
||||
QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None);
|
||||
qDebug() << "AJT: " << type;
|
||||
qDebug() << " " << type;
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_EVENT_QUEUE
|
||||
|
@ -5974,7 +5974,7 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
|
|||
}
|
||||
}
|
||||
|
||||
if (defaultUpload) {
|
||||
if (defaultUpload && !url.fileName().isEmpty() && url.isLocalFile()) {
|
||||
showAssetServerWidget(urlString);
|
||||
}
|
||||
return defaultUpload;
|
||||
|
@ -7075,11 +7075,11 @@ QRect Application::getRecommendedHUDRect() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
QSize Application::getDeviceSize() const {
|
||||
glm::vec2 Application::getDeviceSize() const {
|
||||
static const int MIN_SIZE = 1;
|
||||
QSize result(MIN_SIZE, MIN_SIZE);
|
||||
glm::vec2 result(MIN_SIZE);
|
||||
if (_displayPlugin) {
|
||||
result = fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize());
|
||||
result = getActiveDisplayPlugin()->getRecommendedRenderSize();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -7098,10 +7098,6 @@ bool Application::hasFocus() const {
|
|||
return (QApplication::activeWindow() != nullptr);
|
||||
}
|
||||
|
||||
glm::vec2 Application::getViewportDimensions() const {
|
||||
return toGlm(getDeviceSize());
|
||||
}
|
||||
|
||||
void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) {
|
||||
if (maxOctreePPS != _maxOctreePPS) {
|
||||
_maxOctreePPS = maxOctreePPS;
|
||||
|
|
|
@ -158,7 +158,7 @@ public:
|
|||
|
||||
glm::uvec2 getUiSize() const;
|
||||
QRect getRecommendedHUDRect() const;
|
||||
QSize getDeviceSize() const;
|
||||
glm::vec2 getDeviceSize() const;
|
||||
bool hasFocus() const;
|
||||
|
||||
void showCursor(const Cursor::Icon& cursor);
|
||||
|
@ -228,8 +228,6 @@ public:
|
|||
|
||||
FileLogger* getLogger() const { return _logger; }
|
||||
|
||||
glm::vec2 getViewportDimensions() const;
|
||||
|
||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||
|
||||
float getRenderResolutionScale() const;
|
||||
|
|
|
@ -104,8 +104,7 @@ void Application::paintGL() {
|
|||
PerformanceTimer perfTimer("renderOverlay");
|
||||
// NOTE: There is no batch associated with this renderArgs
|
||||
// the ApplicationOverlay class assumes it's viewport is setup to be the device size
|
||||
QSize size = getDeviceSize();
|
||||
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
|
||||
renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize());
|
||||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,14 @@ class FancyCamera : public Camera {
|
|||
Q_OBJECT
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Camera
|
||||
* @property cameraEntity {EntityID} The position and rotation properties of
|
||||
* the entity specified by this ID are then used as the camera's position and
|
||||
* orientation. Only works when <code>mode</code> is "entity".
|
||||
* @namespace
|
||||
* @augments Camera
|
||||
*/
|
||||
|
||||
// FIXME: JSDoc 3.5.5 doesn't augment @property definitions. The following definition is repeated in Camera.h.
|
||||
/**jsdoc
|
||||
* @property cameraEntity {Uuid} The ID of the entity that the camera position and orientation follow when the camera is in
|
||||
* entity mode.
|
||||
*/
|
||||
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
|
||||
|
||||
|
@ -34,7 +38,25 @@ public:
|
|||
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Get the ID of the entity that the camera is set to use the position and orientation from when it's in entity mode. You can
|
||||
* also get the entity ID using the <code>Camera.cameraEntity</code> property.
|
||||
* @function Camera.getCameraEntity
|
||||
* @returns {Uuid} The ID of the entity that the camera is set to follow when in entity mode; <code>null</code> if no camera
|
||||
* entity has been set.
|
||||
*/
|
||||
QUuid getCameraEntity() const;
|
||||
|
||||
/**jsdoc
|
||||
* Set the entity that the camera should use the position and orientation from when it's in entity mode. You can also set the
|
||||
* entity using the <code>Camera.cameraEntity</code> property.
|
||||
* @function Camera.setCameraEntity
|
||||
* @param {Uuid} entityID The entity that the camera should follow when it's in entity mode.
|
||||
* @example <caption>Move your camera to the position and orientation of the closest entity.</caption>
|
||||
* Camera.setModeString("entity");
|
||||
* var entity = Entities.findClosestEntity(MyAvatar.position, 100.0);
|
||||
* Camera.setCameraEntity(entity);
|
||||
*/
|
||||
void setCameraEntity(QUuid entityID);
|
||||
|
||||
private:
|
||||
|
|
|
@ -679,36 +679,16 @@ Menu::Menu() {
|
|||
});
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false,
|
||||
audioIO.data(), SLOT(toggleServerEcho()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false,
|
||||
audioIO.data(), SLOT(toggleLocalEcho()));
|
||||
addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteEnvironment, 0,
|
||||
audioIO.data(), SLOT(sendMuteEnvironmentPacket()));
|
||||
|
||||
auto scope = DependencyManager::get<AudioScope>();
|
||||
MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_F2, false,
|
||||
scope.data(), SLOT(toggle()));
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause, Qt::CTRL | Qt::SHIFT | Qt::Key_F2, false,
|
||||
scope.data(), SLOT(togglePause()));
|
||||
|
||||
addDisabledActionAndSeparator(audioScopeMenu, "Display Frames");
|
||||
{
|
||||
QAction* fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames,
|
||||
0, true, scope.data(), SLOT(selectAudioScopeFiveFrames()));
|
||||
|
||||
QAction* twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames,
|
||||
0, false, scope.data(), SLOT(selectAudioScopeTwentyFrames()));
|
||||
|
||||
QAction* fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames,
|
||||
0, false, scope.data(), SLOT(selectAudioScopeFiftyFrames()));
|
||||
|
||||
QActionGroup* audioScopeFramesGroup = new QActionGroup(audioScopeMenu);
|
||||
audioScopeFramesGroup->addAction(fiveFrames);
|
||||
audioScopeFramesGroup->addAction(twentyFrames);
|
||||
audioScopeFramesGroup->addAction(fiftyFrames);
|
||||
}
|
||||
action = addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/audio/audioScope.js");
|
||||
scriptEngines->loadScript(defaultScriptsLoc.toString());
|
||||
});
|
||||
|
||||
// Developer > Physics >>>
|
||||
MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics");
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <qvector2d.h>
|
||||
#include <limits>
|
||||
|
||||
#include <AudioClient.h>
|
||||
|
@ -21,13 +22,14 @@
|
|||
#include "AudioScope.h"
|
||||
|
||||
static const unsigned int DEFAULT_FRAMES_PER_SCOPE = 5;
|
||||
static const unsigned int SCOPE_WIDTH = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * DEFAULT_FRAMES_PER_SCOPE;
|
||||
static const unsigned int MULTIPLIER_SCOPE_HEIGHT = 20;
|
||||
static const unsigned int SCOPE_HEIGHT = 2 * 15 * MULTIPLIER_SCOPE_HEIGHT;
|
||||
|
||||
AudioScope::AudioScope() :
|
||||
_isEnabled(false),
|
||||
_isPaused(false),
|
||||
_isTriggered(false),
|
||||
_autoTrigger(false),
|
||||
_scopeInputOffset(0),
|
||||
_scopeOutputOffset(0),
|
||||
_framesPerScope(DEFAULT_FRAMES_PER_SCOPE),
|
||||
|
@ -43,6 +45,7 @@ AudioScope::AudioScope() :
|
|||
_outputRightD(DependencyManager::get<GeometryCache>()->allocateID())
|
||||
{
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
|
||||
connect(&audioIO->getReceivedAudioStream(), &MixedProcessedAudioStream::addedSilence,
|
||||
this, &AudioScope::addStereoSilenceToScope);
|
||||
connect(&audioIO->getReceivedAudioStream(), &MixedProcessedAudioStream::addedLastFrameRepeatedWithFade,
|
||||
|
@ -75,6 +78,18 @@ void AudioScope::selectAudioScopeFiftyFrames() {
|
|||
reallocateScope(50);
|
||||
}
|
||||
|
||||
void AudioScope::setLocalEcho(bool localEcho) {
|
||||
DependencyManager::get<AudioClient>()->setLocalEcho(localEcho);
|
||||
}
|
||||
|
||||
void AudioScope::setServerEcho(bool serverEcho) {
|
||||
DependencyManager::get<AudioClient>()->setServerEcho(serverEcho);
|
||||
}
|
||||
|
||||
float AudioScope::getFramesPerSecond(){
|
||||
return AudioConstants::NETWORK_FRAMES_PER_SEC;
|
||||
}
|
||||
|
||||
void AudioScope::allocateScope() {
|
||||
_scopeInputOffset = 0;
|
||||
_scopeOutputOffset = 0;
|
||||
|
@ -108,63 +123,14 @@ void AudioScope::freeScope() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
|
||||
|
||||
if (!_isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const glm::vec4 backgroundColor = { 0.4f, 0.4f, 0.4f, 0.6f };
|
||||
static const glm::vec4 gridColor = { 0.7f, 0.7f, 0.7f, 1.0f };
|
||||
static const glm::vec4 inputColor = { 0.3f, 1.0f, 0.3f, 1.0f };
|
||||
static const glm::vec4 outputLeftColor = { 1.0f, 0.3f, 0.3f, 1.0f };
|
||||
static const glm::vec4 outputRightColor = { 0.3f, 0.3f, 1.0f, 1.0f };
|
||||
static const int gridCols = 2;
|
||||
int gridRows = _framesPerScope;
|
||||
|
||||
int x = (width - (int)SCOPE_WIDTH) / 2;
|
||||
int y = (height - (int)SCOPE_HEIGHT) / 2;
|
||||
int w = (int)SCOPE_WIDTH;
|
||||
int h = (int)SCOPE_HEIGHT;
|
||||
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
// Grid uses its own pipeline, so draw it before setting another
|
||||
const float GRID_EDGE = 0.005f;
|
||||
geometryCache->renderGrid(batch, glm::vec2(x, y), glm::vec2(x + w, y + h),
|
||||
gridRows, gridCols, GRID_EDGE, gridColor, true, _audioScopeGrid);
|
||||
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
batch.setResourceTexture(0, textureCache->getWhiteTexture());
|
||||
|
||||
// FIXME - do we really need to reset this here? we know that we're called inside of ApplicationOverlay::renderOverlays
|
||||
// which already set up our batch for us to have these settings
|
||||
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, -1000, 1000);
|
||||
batch.setProjectionTransform(legacyProjection);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.resetViewTransform();
|
||||
|
||||
geometryCache->renderQuad(batch, x, y, w, h, backgroundColor, _audioScopeBackground);
|
||||
renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);
|
||||
renderLineStrip(batch, _outputLeftID, outputLeftColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputLeft);
|
||||
renderLineStrip(batch, _outputRightD, outputRightColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputRight);
|
||||
}
|
||||
|
||||
void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray) {
|
||||
|
||||
QVector<int> AudioScope::getScopeVector(const QByteArray* byteArray, int offset) {
|
||||
int16_t sample;
|
||||
int16_t* samples = ((int16_t*) byteArray->data()) + offset;
|
||||
QVector<int> points;
|
||||
if (!_isEnabled || byteArray == NULL) return points;
|
||||
int16_t* samples = ((int16_t*)byteArray->data()) + offset;
|
||||
int numSamplesToAverage = _framesPerScope / DEFAULT_FRAMES_PER_SCOPE;
|
||||
int count = (n - offset) / numSamplesToAverage;
|
||||
int remainder = (n - offset) % numSamplesToAverage;
|
||||
y += SCOPE_HEIGHT / 2;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
QVector<glm::vec2> points;
|
||||
|
||||
int count = (_samplesPerScope - offset) / numSamplesToAverage;
|
||||
int remainder = (_samplesPerScope - offset) % numSamplesToAverage;
|
||||
|
||||
// Compute and draw the sample averages from the offset position
|
||||
for (int i = count; --i >= 0; ) {
|
||||
|
@ -173,7 +139,7 @@ void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& col
|
|||
sample += *samples++;
|
||||
}
|
||||
sample /= numSamplesToAverage;
|
||||
points << glm::vec2(x++, y - sample);
|
||||
points << -sample;
|
||||
}
|
||||
|
||||
// Compute and draw the sample average across the wrap boundary
|
||||
|
@ -182,16 +148,17 @@ void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& col
|
|||
for (int j = remainder; --j >= 0; ) {
|
||||
sample += *samples++;
|
||||
}
|
||||
|
||||
samples = (int16_t*) byteArray->data();
|
||||
|
||||
samples = (int16_t*)byteArray->data();
|
||||
|
||||
for (int j = numSamplesToAverage - remainder; --j >= 0; ) {
|
||||
sample += *samples++;
|
||||
}
|
||||
sample /= numSamplesToAverage;
|
||||
points << glm::vec2(x++, y - sample);
|
||||
} else {
|
||||
samples = (int16_t*) byteArray->data();
|
||||
points << -sample;
|
||||
}
|
||||
else {
|
||||
samples = (int16_t*)byteArray->data();
|
||||
}
|
||||
|
||||
// Compute and draw the sample average from the beginning to the offset
|
||||
|
@ -202,12 +169,51 @@ void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& col
|
|||
sample += *samples++;
|
||||
}
|
||||
sample /= numSamplesToAverage;
|
||||
points << glm::vec2(x++, y - sample);
|
||||
|
||||
points << -sample;
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
bool AudioScope::shouldTrigger(const QVector<int>& scope) {
|
||||
int threshold = 4;
|
||||
if (_autoTrigger && _triggerValues.x < scope.size()) {
|
||||
for (int i = -4*threshold; i < +4*threshold; i++) {
|
||||
int idx = _triggerValues.x + i;
|
||||
idx = (idx < 0) ? 0 : (idx < scope.size() ? idx : scope.size() - 1);
|
||||
int dif = abs(_triggerValues.y - scope[idx]);
|
||||
if (dif < threshold) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioScope::storeTriggerValues() {
|
||||
_triggerInputData = _scopeInputData;
|
||||
_triggerOutputLeftData = _scopeOutputLeftData;
|
||||
_triggerOutputRightData = _scopeOutputRightData;
|
||||
_isTriggered = true;
|
||||
emit triggered();
|
||||
}
|
||||
|
||||
void AudioScope::computeInputData() {
|
||||
_scopeInputData = getScopeVector(_scopeInput, _scopeInputOffset);
|
||||
if (shouldTrigger(_scopeInputData)) {
|
||||
storeTriggerValues();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioScope::computeOutputData() {
|
||||
_scopeOutputLeftData = getScopeVector(_scopeOutputLeft, _scopeOutputOffset);
|
||||
if (shouldTrigger(_scopeOutputLeftData)) {
|
||||
storeTriggerValues();
|
||||
}
|
||||
_scopeOutputRightData = getScopeVector(_scopeOutputRight, _scopeOutputOffset);
|
||||
if (shouldTrigger(_scopeOutputRightData)) {
|
||||
storeTriggerValues();
|
||||
}
|
||||
|
||||
|
||||
geometryCache->updateVertices(id, points, color);
|
||||
geometryCache->renderVertices(batch, gpu::LINE_STRIP, id);
|
||||
}
|
||||
|
||||
int AudioScope::addBufferToScope(QByteArray* byteArray, int frameOffset, const int16_t* source, int sourceSamplesPerChannel,
|
||||
|
@ -231,7 +237,7 @@ int AudioScope::addBufferToScope(QByteArray* byteArray, int frameOffset, const i
|
|||
}
|
||||
|
||||
int AudioScope::addSilenceToScope(QByteArray* byteArray, int frameOffset, int silentSamples) {
|
||||
|
||||
|
||||
// Short int pointer to mapped samples in byte array
|
||||
int16_t* destination = (int16_t*)byteArray->data();
|
||||
|
||||
|
@ -271,6 +277,7 @@ void AudioScope::addStereoSamplesToScope(const QByteArray& samples) {
|
|||
_scopeOutputOffset = addBufferToScope(_scopeOutputRight, _scopeOutputOffset, samplesData, samplesPerChannel, 1, AudioConstants::STEREO);
|
||||
|
||||
_scopeLastFrame = samples.right(AudioConstants::NETWORK_FRAME_BYTES_STEREO);
|
||||
computeOutputData();
|
||||
}
|
||||
|
||||
void AudioScope::addLastFrameRepeatedWithFadeToScope(int samplesPerChannel) {
|
||||
|
@ -302,4 +309,5 @@ void AudioScope::addInputToScope(const QByteArray& inputSamples) {
|
|||
_scopeInputOffset = addBufferToScope(_scopeInput, _scopeInputOffset,
|
||||
reinterpret_cast<const int16_t*>(inputSamples.data()),
|
||||
inputSamples.size() / sizeof(int16_t), INPUT_AUDIO_CHANNEL, NUM_INPUT_CHANNELS);
|
||||
computeInputData();
|
||||
}
|
||||
|
|
|
@ -24,27 +24,60 @@
|
|||
class AudioScope : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
Q_PROPERTY(QVector<int> scopeInput READ getScopeInput)
|
||||
Q_PROPERTY(QVector<int> scopeOutputLeft READ getScopeOutputLeft)
|
||||
Q_PROPERTY(QVector<int> scopeOutputRight READ getScopeOutputRight)
|
||||
|
||||
Q_PROPERTY(QVector<int> triggerInput READ getTriggerInput)
|
||||
Q_PROPERTY(QVector<int> triggerOutputLeft READ getTriggerOutputLeft)
|
||||
Q_PROPERTY(QVector<int> triggerOutputRight READ getTriggerOutputRight)
|
||||
|
||||
public:
|
||||
// Audio scope methods for allocation/deallocation
|
||||
void allocateScope();
|
||||
void freeScope();
|
||||
void reallocateScope(int frames);
|
||||
|
||||
void render(RenderArgs* renderArgs, int width, int height);
|
||||
|
||||
public slots:
|
||||
void toggle() { setVisible(!_isEnabled); }
|
||||
void setVisible(bool visible);
|
||||
bool getVisible() const { return _isEnabled; }
|
||||
|
||||
void togglePause() { _isPaused = !_isPaused; }
|
||||
void setPause(bool paused) { _isPaused = paused; }
|
||||
void togglePause() { setPause(!_isPaused); }
|
||||
void setPause(bool paused) { _isPaused = paused; emit pauseChanged(); }
|
||||
bool getPause() { return _isPaused; }
|
||||
|
||||
void toggleTrigger() { _autoTrigger = !_autoTrigger; }
|
||||
bool getAutoTrigger() { return _autoTrigger; }
|
||||
void setAutoTrigger(bool autoTrigger) { _isTriggered = false; _autoTrigger = autoTrigger; }
|
||||
|
||||
void setTriggerValues(int x, int y) { _triggerValues.x = x; _triggerValues.y = y; }
|
||||
void setTriggered(bool triggered) { _isTriggered = triggered; }
|
||||
bool getTriggered() { return _isTriggered; }
|
||||
|
||||
float getFramesPerSecond();
|
||||
int getFramesPerScope() { return _framesPerScope; }
|
||||
|
||||
void selectAudioScopeFiveFrames();
|
||||
void selectAudioScopeTwentyFrames();
|
||||
void selectAudioScopeFiftyFrames();
|
||||
|
||||
|
||||
QVector<int> getScopeInput() { return _scopeInputData; };
|
||||
QVector<int> getScopeOutputLeft() { return _scopeOutputLeftData; };
|
||||
QVector<int> getScopeOutputRight() { return _scopeOutputRightData; };
|
||||
|
||||
QVector<int> getTriggerInput() { return _triggerInputData; };
|
||||
QVector<int> getTriggerOutputLeft() { return _triggerOutputLeftData; };
|
||||
QVector<int> getTriggerOutputRight() { return _triggerOutputRightData; };
|
||||
|
||||
void setLocalEcho(bool serverEcho);
|
||||
void setServerEcho(bool serverEcho);
|
||||
|
||||
signals:
|
||||
void pauseChanged();
|
||||
void triggered();
|
||||
|
||||
protected:
|
||||
AudioScope();
|
||||
|
||||
|
@ -55,24 +88,44 @@ private slots:
|
|||
void addInputToScope(const QByteArray& inputSamples);
|
||||
|
||||
private:
|
||||
// Audio scope methods for rendering
|
||||
void renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray);
|
||||
|
||||
// Audio scope methods for data acquisition
|
||||
int addBufferToScope(QByteArray* byteArray, int frameOffset, const int16_t* source, int sourceSamples,
|
||||
unsigned int sourceChannel, unsigned int sourceNumberOfChannels, float fade = 1.0f);
|
||||
int addSilenceToScope(QByteArray* byteArray, int frameOffset, int silentSamples);
|
||||
|
||||
QVector<int> getScopeVector(const QByteArray* scope, int offset);
|
||||
|
||||
bool shouldTrigger(const QVector<int>& scope);
|
||||
void computeInputData();
|
||||
void computeOutputData();
|
||||
|
||||
void storeTriggerValues();
|
||||
|
||||
bool _isEnabled;
|
||||
bool _isPaused;
|
||||
bool _isTriggered;
|
||||
bool _autoTrigger;
|
||||
int _scopeInputOffset;
|
||||
int _scopeOutputOffset;
|
||||
int _framesPerScope;
|
||||
int _samplesPerScope;
|
||||
|
||||
QByteArray* _scopeInput;
|
||||
QByteArray* _scopeOutputLeft;
|
||||
QByteArray* _scopeOutputRight;
|
||||
QByteArray _scopeLastFrame;
|
||||
|
||||
QVector<int> _scopeInputData;
|
||||
QVector<int> _scopeOutputLeftData;
|
||||
QVector<int> _scopeOutputRightData;
|
||||
|
||||
QVector<int> _triggerInputData;
|
||||
QVector<int> _triggerOutputLeftData;
|
||||
QVector<int> _triggerOutputRightData;
|
||||
|
||||
|
||||
glm::ivec2 _triggerValues;
|
||||
|
||||
int _audioScopeBackground;
|
||||
int _audioScopeGrid;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <shared/QtHelpers.h>
|
||||
#include <AvatarData.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PrioritySortUtil.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <Rig.h>
|
||||
#include <SettingHandle.h>
|
||||
|
@ -142,32 +143,39 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
PerformanceTimer perfTimer("otherAvatars");
|
||||
|
||||
auto avatarMap = getHashCopy();
|
||||
QList<AvatarSharedPointer> avatarList = avatarMap.values();
|
||||
class SortableAvatar: public PrioritySortUtil::Sortable {
|
||||
public:
|
||||
SortableAvatar() = delete;
|
||||
SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {}
|
||||
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
|
||||
float getRadius() const override { return std::static_pointer_cast<Avatar>(_avatar)->getBoundingRadius(); }
|
||||
uint64_t getTimestamp() const override { return std::static_pointer_cast<Avatar>(_avatar)->getLastRenderUpdateTime(); }
|
||||
const AvatarSharedPointer& getAvatar() const { return _avatar; }
|
||||
private:
|
||||
AvatarSharedPointer _avatar;
|
||||
};
|
||||
|
||||
ViewFrustum cameraView;
|
||||
qApp->copyDisplayViewFrustum(cameraView);
|
||||
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView,
|
||||
AvatarData::_avatarSortCoefficientSize,
|
||||
AvatarData::_avatarSortCoefficientCenter,
|
||||
AvatarData::_avatarSortCoefficientAge);
|
||||
|
||||
std::priority_queue<AvatarPriority> sortedAvatars;
|
||||
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars,
|
||||
|
||||
[](AvatarSharedPointer avatar)->uint64_t{
|
||||
return std::static_pointer_cast<Avatar>(avatar)->getLastRenderUpdateTime();
|
||||
},
|
||||
|
||||
[](AvatarSharedPointer avatar)->float{
|
||||
return std::static_pointer_cast<Avatar>(avatar)->getBoundingRadius();
|
||||
},
|
||||
|
||||
[this](AvatarSharedPointer avatar)->bool{
|
||||
const auto& castedAvatar = std::static_pointer_cast<Avatar>(avatar);
|
||||
if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) {
|
||||
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
||||
// DO NOT update or fade out uninitialized Avatars
|
||||
return true; // ignore it
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// sort
|
||||
auto avatarMap = getHashCopy();
|
||||
AvatarHash::iterator itr = avatarMap.begin();
|
||||
while (itr != avatarMap.end()) {
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
|
||||
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
||||
// DO NOT update or fade out uninitialized Avatars
|
||||
if (avatar != _myAvatar && avatar->isInitialized()) {
|
||||
sortedAvatars.push(SortableAvatar(avatar));
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
|
||||
// process in sorted order
|
||||
uint64_t startTime = usecTimestampNow();
|
||||
const uint64_t UPDATE_BUDGET = 2000; // usec
|
||||
uint64_t updateExpiry = startTime + UPDATE_BUDGET;
|
||||
|
@ -176,8 +184,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
render::Transaction transaction;
|
||||
while (!sortedAvatars.empty()) {
|
||||
const AvatarPriority& sortData = sortedAvatars.top();
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
|
||||
const SortableAvatar& sortData = sortedAvatars.top();
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
|
||||
|
||||
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
|
||||
if (ignoring) {
|
||||
|
@ -207,7 +215,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
uint64_t now = usecTimestampNow();
|
||||
if (now < updateExpiry) {
|
||||
// we're within budget
|
||||
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD;
|
||||
bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
if (inView && avatar->hasNewJointData()) {
|
||||
numAvatarsUpdated++;
|
||||
}
|
||||
|
@ -221,7 +229,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
// --> some avatar velocity measurements may be a little off
|
||||
|
||||
// no time simulate, but we take the time to count how many were tragically missed
|
||||
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD;
|
||||
bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
if (!inView) {
|
||||
break;
|
||||
}
|
||||
|
@ -230,9 +238,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
}
|
||||
sortedAvatars.pop();
|
||||
while (inView && !sortedAvatars.empty()) {
|
||||
const AvatarPriority& newSortData = sortedAvatars.top();
|
||||
const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.avatar);
|
||||
inView = newSortData.priority > OUT_OF_VIEW_THRESHOLD;
|
||||
const SortableAvatar& newSortData = sortedAvatars.top();
|
||||
const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
|
||||
inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
if (inView && newAvatar->hasNewJointData()) {
|
||||
numAVatarsNotUpdated++;
|
||||
}
|
||||
|
|
|
@ -114,7 +114,8 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
|
||||
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);
|
||||
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
|
||||
|
||||
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
|
||||
connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);
|
||||
|
||||
using namespace recording;
|
||||
_skeletonModel->flagAsCauterized();
|
||||
|
@ -1516,9 +1517,19 @@ void MyAvatar::updateMotors() {
|
|||
_characterController.clearMotors();
|
||||
glm::quat motorRotation;
|
||||
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
|
||||
|
||||
const float FLYING_MOTOR_TIMESCALE = 0.05f;
|
||||
const float WALKING_MOTOR_TIMESCALE = 0.2f;
|
||||
const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
|
||||
|
||||
float horizontalMotorTimescale;
|
||||
float verticalMotorTimescale;
|
||||
|
||||
if (_characterController.getState() == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
motorRotation = getMyHead()->getHeadOrientation();
|
||||
horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE;
|
||||
verticalMotorTimescale = FLYING_MOTOR_TIMESCALE;
|
||||
} else {
|
||||
// non-hovering = walking: follow camera twist about vertical but not lift
|
||||
// we decompose camera's rotation and store the twist part in motorRotation
|
||||
|
@ -1529,11 +1540,12 @@ void MyAvatar::updateMotors() {
|
|||
glm::quat liftRotation;
|
||||
swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation);
|
||||
motorRotation = orientation * motorRotation;
|
||||
horizontalMotorTimescale = WALKING_MOTOR_TIMESCALE;
|
||||
verticalMotorTimescale = INVALID_MOTOR_TIMESCALE;
|
||||
}
|
||||
const float DEFAULT_MOTOR_TIMESCALE = 0.2f;
|
||||
const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
|
||||
|
||||
if (_isPushing || _isBraking || !_isBeingPushed) {
|
||||
_characterController.addMotor(_actionMotorVelocity, motorRotation, DEFAULT_MOTOR_TIMESCALE, INVALID_MOTOR_TIMESCALE);
|
||||
_characterController.addMotor(_actionMotorVelocity, motorRotation, horizontalMotorTimescale, verticalMotorTimescale);
|
||||
} else {
|
||||
// _isBeingPushed must be true --> disable action motor by giving it a long timescale,
|
||||
// otherwise it's attempt to "stand in in place" could defeat scripted motor/thrusts
|
||||
|
@ -1799,6 +1811,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
|||
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
||||
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
||||
initAnimGraph();
|
||||
_isAnimatingScale = true;
|
||||
}
|
||||
|
||||
if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) {
|
||||
|
@ -1956,27 +1969,33 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
|
||||
// Use head/HMD roll to turn while flying, but not when standing still.
|
||||
if (qApp->isHMDMode() && getCharacterController()->getState() == CharacterController::State::Hover && _hmdRollControlEnabled && hasDriveInput()) {
|
||||
|
||||
// Turn with head roll.
|
||||
const float MIN_CONTROL_SPEED = 0.01f;
|
||||
float speed = glm::length(getWorldVelocity());
|
||||
if (speed >= MIN_CONTROL_SPEED) {
|
||||
// Feather turn when stopping moving.
|
||||
float speedFactor;
|
||||
if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) {
|
||||
_lastDrivenSpeed = speed;
|
||||
speedFactor = 1.0f;
|
||||
} else {
|
||||
speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f);
|
||||
}
|
||||
const float MIN_CONTROL_SPEED = 2.0f * getSensorToWorldScale(); // meters / sec
|
||||
const glm::vec3 characterForward = getWorldOrientation() * Vectors::UNIT_NEG_Z;
|
||||
float forwardSpeed = glm::dot(characterForward, getWorldVelocity());
|
||||
|
||||
float direction = glm::dot(getWorldVelocity(), getWorldOrientation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f;
|
||||
// only enable roll-turns if we are moving forward or backward at greater then MIN_CONTROL_SPEED
|
||||
if (fabsf(forwardSpeed) >= MIN_CONTROL_SPEED) {
|
||||
|
||||
float direction = forwardSpeed > 0.0f ? 1.0f : -1.0f;
|
||||
float rollAngle = glm::degrees(asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT)));
|
||||
float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f;
|
||||
rollAngle = fabsf(rollAngle);
|
||||
rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f;
|
||||
|
||||
totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlRate;
|
||||
const float MIN_ROLL_ANGLE = _hmdRollControlDeadZone;
|
||||
const float MAX_ROLL_ANGLE = 90.0f; // degrees
|
||||
|
||||
if (rollAngle > MIN_ROLL_ANGLE) {
|
||||
// rate of turning is linearly proportional to rollAngle
|
||||
rollAngle = glm::clamp(rollAngle, MIN_ROLL_ANGLE, MAX_ROLL_ANGLE);
|
||||
|
||||
// scale rollAngle into a value from zero to one.
|
||||
float rollFactor = (rollAngle - MIN_ROLL_ANGLE) / (MAX_ROLL_ANGLE - MIN_ROLL_ANGLE);
|
||||
|
||||
float angularSpeed = rollSign * rollFactor * _hmdRollControlRate;
|
||||
totalBodyYaw += direction * angularSpeed * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2022,12 +2041,13 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
_isBraking = _wasPushing || (_isBraking && speed > MIN_ACTION_BRAKE_SPEED);
|
||||
}
|
||||
|
||||
CharacterController::State state = _characterController.getState();
|
||||
|
||||
// compute action input
|
||||
glm::vec3 forward = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FORWARD;
|
||||
glm::vec3 right = (getDriveKey(TRANSLATE_X)) * IDENTITY_RIGHT;
|
||||
|
||||
glm::vec3 direction = forward + right;
|
||||
CharacterController::State state = _characterController.getState();
|
||||
if (state == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
// we can fly --> support vertical motion
|
||||
|
@ -2161,41 +2181,6 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
|
|||
// target scale to match the new scale they have chosen. When they leave the domain they will not return to the scale they were
|
||||
// before they entered the limiting domain.
|
||||
|
||||
void MyAvatar::clampTargetScaleToDomainLimits() {
|
||||
// when we're about to change the target scale because the user has asked to increase or decrease their scale,
|
||||
// we first make sure that we're starting from a target scale that is allowed by the current domain
|
||||
|
||||
auto clampedTargetScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale);
|
||||
|
||||
if (clampedTargetScale != _targetScale) {
|
||||
qCDebug(interfaceapp, "Clamped scale to %f since original target scale %f was not allowed by domain",
|
||||
(double)clampedTargetScale, (double)_targetScale);
|
||||
|
||||
setTargetScale(clampedTargetScale);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) {
|
||||
auto clampedTargetScale = glm::clamp(desiredScale, _domainMinimumScale, _domainMaximumScale);
|
||||
|
||||
if (clampedTargetScale != desiredScale) {
|
||||
qCDebug(interfaceapp, "Forcing scale to %f since %f is not allowed by domain",
|
||||
clampedTargetScale, desiredScale);
|
||||
}
|
||||
|
||||
setTargetScale(clampedTargetScale);
|
||||
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
|
||||
emit(scaleChanged());
|
||||
}
|
||||
|
||||
float MyAvatar::getDomainMinScale() {
|
||||
return _domainMinimumScale;
|
||||
}
|
||||
|
||||
float MyAvatar::getDomainMaxScale() {
|
||||
return _domainMaximumScale;
|
||||
}
|
||||
|
||||
void MyAvatar::setGravity(float gravity) {
|
||||
_characterController.setGravity(gravity);
|
||||
}
|
||||
|
@ -2205,70 +2190,58 @@ float MyAvatar::getGravity() {
|
|||
}
|
||||
|
||||
void MyAvatar::increaseSize() {
|
||||
// make sure we're starting from an allowable scale
|
||||
clampTargetScaleToDomainLimits();
|
||||
float minScale = getDomainMinScale();
|
||||
float maxScale = getDomainMaxScale();
|
||||
|
||||
// calculate what our new scale should be
|
||||
float updatedTargetScale = _targetScale * (1.0f + SCALING_RATIO);
|
||||
float clampedTargetScale = glm::clamp(_targetScale, minScale, maxScale);
|
||||
float newTargetScale = glm::clamp(clampedTargetScale * (1.0f + SCALING_RATIO), minScale, maxScale);
|
||||
|
||||
// attempt to change to desired scale (clamped to the domain limits)
|
||||
clampScaleChangeToDomainLimits(updatedTargetScale);
|
||||
setTargetScale(newTargetScale);
|
||||
}
|
||||
|
||||
void MyAvatar::decreaseSize() {
|
||||
// make sure we're starting from an allowable scale
|
||||
clampTargetScaleToDomainLimits();
|
||||
float minScale = getDomainMinScale();
|
||||
float maxScale = getDomainMaxScale();
|
||||
|
||||
// calculate what our new scale should be
|
||||
float updatedTargetScale = _targetScale * (1.0f - SCALING_RATIO);
|
||||
float clampedTargetScale = glm::clamp(_targetScale, minScale, maxScale);
|
||||
float newTargetScale = glm::clamp(clampedTargetScale * (1.0f - SCALING_RATIO), minScale, maxScale);
|
||||
|
||||
// attempt to change to desired scale (clamped to the domain limits)
|
||||
clampScaleChangeToDomainLimits(updatedTargetScale);
|
||||
setTargetScale(newTargetScale);
|
||||
}
|
||||
|
||||
void MyAvatar::resetSize() {
|
||||
// attempt to reset avatar size to the default (clamped to domain limits)
|
||||
const float DEFAULT_AVATAR_SCALE = 1.0f;
|
||||
|
||||
clampScaleChangeToDomainLimits(DEFAULT_AVATAR_SCALE);
|
||||
setTargetScale(DEFAULT_AVATAR_SCALE);
|
||||
}
|
||||
|
||||
void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject) {
|
||||
// pull out the minimum and maximum scale and set them to restrict our scale
|
||||
// pull out the minimum and maximum height and set them to restrict our scale
|
||||
|
||||
static const QString AVATAR_SETTINGS_KEY = "avatars";
|
||||
auto avatarsObject = domainSettingsObject[AVATAR_SETTINGS_KEY].toObject();
|
||||
|
||||
static const QString MIN_SCALE_OPTION = "min_avatar_scale";
|
||||
float settingMinScale = avatarsObject[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE);
|
||||
setDomainMinimumScale(settingMinScale);
|
||||
static const QString MIN_HEIGHT_OPTION = "min_avatar_height";
|
||||
float settingMinHeight = avatarsObject[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT);
|
||||
setDomainMinimumHeight(settingMinHeight);
|
||||
|
||||
static const QString MAX_SCALE_OPTION = "max_avatar_scale";
|
||||
float settingMaxScale = avatarsObject[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE);
|
||||
setDomainMaximumScale(settingMaxScale);
|
||||
static const QString MAX_HEIGHT_OPTION = "max_avatar_height";
|
||||
float settingMaxHeight = avatarsObject[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT);
|
||||
setDomainMaximumHeight(settingMaxHeight);
|
||||
|
||||
// make sure that the domain owner didn't flip min and max
|
||||
if (_domainMinimumScale > _domainMaximumScale) {
|
||||
std::swap(_domainMinimumScale, _domainMaximumScale);
|
||||
if (_domainMinimumHeight > _domainMaximumHeight) {
|
||||
std::swap(_domainMinimumHeight, _domainMaximumHeight);
|
||||
}
|
||||
// Set avatar current scale
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||
|
||||
qCDebug(interfaceapp) << "This domain requires a minimum avatar scale of " << _domainMinimumScale
|
||||
<< " and a maximum avatar scale of " << _domainMaximumScale
|
||||
<< ". Current avatar scale is " << _targetScale;
|
||||
qCDebug(interfaceapp) << "This domain requires a minimum avatar scale of " << _domainMinimumHeight
|
||||
<< " and a maximum avatar scale of " << _domainMaximumHeight;
|
||||
|
||||
// debug to log if this avatar's scale in this domain will be clamped
|
||||
float clampedScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale);
|
||||
|
||||
if (_targetScale != clampedScale) {
|
||||
qCDebug(interfaceapp) << "Current avatar scale is clamped to " << clampedScale
|
||||
<< " because " << _targetScale << " is not allowed by current domain";
|
||||
// The current scale of avatar should not be more than domain's max_avatar_scale and not less than domain's min_avatar_scale .
|
||||
_targetScale = clampedScale;
|
||||
}
|
||||
_isAnimatingScale = true;
|
||||
|
||||
setModelScale(_targetScale);
|
||||
rebuildCollisionShape();
|
||||
|
@ -2288,8 +2261,8 @@ void MyAvatar::saveAvatarScale() {
|
|||
}
|
||||
|
||||
void MyAvatar::clearScaleRestriction() {
|
||||
_domainMinimumScale = MIN_AVATAR_SCALE;
|
||||
_domainMaximumScale = MAX_AVATAR_SCALE;
|
||||
_domainMinimumHeight = MIN_AVATAR_HEIGHT;
|
||||
_domainMaximumHeight = MAX_AVATAR_HEIGHT;
|
||||
}
|
||||
|
||||
void MyAvatar::goToLocation(const QVariant& propertiesVar) {
|
||||
|
@ -3248,6 +3221,7 @@ void MyAvatar::setModelScale(float scale) {
|
|||
if (changed) {
|
||||
float sensorToWorldScale = getEyeHeight() / getUserEyeHeight();
|
||||
emit sensorToWorldScaleChanged(sensorToWorldScale);
|
||||
emit scaleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,10 @@ class MyAvatar : public Avatar {
|
|||
* @property userEyeHeight {number} Estimated height of the users eyes in sensor space. (meters)
|
||||
* @property SELF_ID {string} READ-ONLY. UUID representing "my avatar". Only use for local-only entities and overlays in situations where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain).
|
||||
* Note: Likely to be deprecated.
|
||||
* @property hmdRollControlEnabled {bool} When enabled the roll angle of your HMD will turn your avatar while flying.
|
||||
* @property hmdRollControlDeadZone {number} If hmdRollControlEnabled is true, this value can be used to tune what roll angle is required to begin turning.
|
||||
* This angle is specified in degrees.
|
||||
* @property hmdRollControlRate {number} If hmdRollControlEnabled is true, this value determines the maximum turn rate of your avatar when rolling your HMD in degrees per second.
|
||||
*/
|
||||
|
||||
// FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type
|
||||
|
@ -158,7 +162,7 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(float userEyeHeight READ getUserEyeHeight)
|
||||
|
||||
Q_PROPERTY(QUuid SELF_ID READ getSelfID CONSTANT)
|
||||
|
||||
|
||||
const QString DOMINANT_LEFT_HAND = "left";
|
||||
const QString DOMINANT_RIGHT_HAND = "right";
|
||||
|
||||
|
@ -558,8 +562,6 @@ public slots:
|
|||
void increaseSize();
|
||||
void decreaseSize();
|
||||
void resetSize();
|
||||
float getDomainMinScale();
|
||||
float getDomainMaxScale();
|
||||
|
||||
void setGravity(float gravity);
|
||||
float getGravity();
|
||||
|
@ -737,12 +739,12 @@ private:
|
|||
bool _clearOverlayWhenMoving { true };
|
||||
QString _dominantHand { DOMINANT_RIGHT_HAND };
|
||||
|
||||
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg
|
||||
const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg
|
||||
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // degrees
|
||||
const float ROLL_CONTROL_RATE_DEFAULT = 114.0f; // degrees / sec
|
||||
|
||||
bool _hmdRollControlEnabled { true };
|
||||
float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT };
|
||||
float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT };
|
||||
float _lastDrivenSpeed { 0.0f };
|
||||
|
||||
// working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access
|
||||
glm::mat4 _sensorToWorldMatrix { glm::mat4() };
|
||||
|
|
|
@ -130,7 +130,7 @@ QString amountString(const QString& label, const QString&color, const QJsonValue
|
|||
return result + QString("</font>");
|
||||
}
|
||||
|
||||
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/";
|
||||
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
|
||||
void Ledger::historySuccess(QNetworkReply& reply) {
|
||||
// here we send a historyResult with some extra stuff in it
|
||||
// Namely, the styled text we'd like to show. The issue is the
|
||||
|
|
|
@ -83,19 +83,28 @@ void QmlCommerce::buy(const QString& assetId, int cost, const bool controlledFai
|
|||
void QmlCommerce::balance() {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
ledger->balance(wallet->listPublicKeys());
|
||||
QStringList cachedPublicKeys = wallet->listPublicKeys();
|
||||
if (!cachedPublicKeys.isEmpty()) {
|
||||
ledger->balance(cachedPublicKeys);
|
||||
}
|
||||
}
|
||||
|
||||
void QmlCommerce::inventory() {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
ledger->inventory(wallet->listPublicKeys());
|
||||
QStringList cachedPublicKeys = wallet->listPublicKeys();
|
||||
if (!cachedPublicKeys.isEmpty()) {
|
||||
ledger->inventory(cachedPublicKeys);
|
||||
}
|
||||
}
|
||||
|
||||
void QmlCommerce::history() {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
ledger->history(wallet->listPublicKeys());
|
||||
QStringList cachedPublicKeys = wallet->listPublicKeys();
|
||||
if (!cachedPublicKeys.isEmpty()) {
|
||||
ledger->history(cachedPublicKeys);
|
||||
}
|
||||
}
|
||||
|
||||
void QmlCommerce::changePassphrase(const QString& oldPassphrase, const QString& newPassphrase) {
|
||||
|
|
|
@ -315,12 +315,21 @@ Wallet::Wallet() {
|
|||
}
|
||||
|
||||
walletScriptingInterface->setWalletStatus(status);
|
||||
emit walletStatusResult(status);
|
||||
});
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
|
||||
getWalletStatus();
|
||||
_publicKeys.clear();
|
||||
|
||||
if (_securityImage) {
|
||||
delete _securityImage;
|
||||
}
|
||||
_securityImage = nullptr;
|
||||
|
||||
// tell the provider we got nothing
|
||||
updateImageProvider();
|
||||
_passphrase->clear();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -481,6 +490,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
|
|||
}
|
||||
if (_publicKeys.count() > 0) {
|
||||
// we _must_ be authenticated if the publicKeys are there
|
||||
DependencyManager::get<WalletScriptingInterface>()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -493,6 +503,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
|
|||
|
||||
// be sure to add the public key so we don't do this over and over
|
||||
_publicKeys.push_back(publicKey.toBase64());
|
||||
DependencyManager::get<WalletScriptingInterface>()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -791,15 +802,12 @@ void Wallet::account() {
|
|||
|
||||
void Wallet::getWalletStatus() {
|
||||
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
|
||||
uint status;
|
||||
|
||||
if (DependencyManager::get<AccountManager>()->isLoggedIn()) {
|
||||
// This will set account info for the wallet, allowing us to decrypt and display the security image.
|
||||
account();
|
||||
} else {
|
||||
status = (uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN;
|
||||
emit walletStatusResult(status);
|
||||
walletScriptingInterface->setWalletStatus(status);
|
||||
walletScriptingInterface->setWalletStatus((uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ QNetworkRequest createNetworkRequest() {
|
|||
|
||||
QNetworkRequest request;
|
||||
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath(USER_ACTIVITY_URL);
|
||||
|
||||
request.setUrl(requestURL);
|
||||
|
|
|
@ -84,18 +84,7 @@ glm::vec2 RayPick::projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3
|
|||
glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos, bool unNormalized) {
|
||||
glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value);
|
||||
glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value);
|
||||
glm::vec3 dimensions;
|
||||
|
||||
float dpi = qApp->getOverlays().getProperty(overlayID, "dpi").value.toFloat();
|
||||
if (dpi > 0) {
|
||||
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale.
|
||||
glm::vec3 resolution = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "resolution").value), 1);
|
||||
glm::vec3 scale = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01f);
|
||||
const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
||||
dimensions = (resolution * INCHES_TO_METERS / dpi) * scale;
|
||||
} else {
|
||||
dimensions = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01);
|
||||
}
|
||||
glm::vec3 dimensions = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01f);
|
||||
|
||||
return projectOntoXYPlane(worldPos, position, rotation, dimensions, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT, unNormalized);
|
||||
}
|
||||
|
|
|
@ -58,6 +58,21 @@ Audio::Audio() : _devices(_contextIsHMD) {
|
|||
enableNoiseReduction(enableNoiseReductionSetting.get());
|
||||
}
|
||||
|
||||
bool Audio::startRecording(const QString& filepath) {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
return client->startRecording(filepath);
|
||||
}
|
||||
|
||||
bool Audio::getRecording() {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
return client->getRecording();
|
||||
}
|
||||
|
||||
void Audio::stopRecording() {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
client->stopRecording();
|
||||
}
|
||||
|
||||
void Audio::setMuted(bool isMuted) {
|
||||
if (_isMuted != isMuted) {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "AudioDevices.h"
|
||||
#include "AudioEffectOptions.h"
|
||||
#include "SettingHandle.h"
|
||||
#include "AudioFileWav.h"
|
||||
|
||||
namespace scripting {
|
||||
|
||||
|
@ -55,6 +56,10 @@ public:
|
|||
Q_INVOKABLE void setReverb(bool enable);
|
||||
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
|
||||
|
||||
Q_INVOKABLE bool startRecording(const QString& filename);
|
||||
Q_INVOKABLE void stopRecording();
|
||||
Q_INVOKABLE bool getRecording();
|
||||
|
||||
signals:
|
||||
void nop();
|
||||
void mutedChanged(bool isMuted);
|
||||
|
@ -83,7 +88,6 @@ private:
|
|||
bool _isMuted { false };
|
||||
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
|
||||
bool _contextIsHMD { false };
|
||||
|
||||
AudioDevices* getDevices() { return &_devices; }
|
||||
AudioDevices _devices;
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, const
|
|||
return retVal;
|
||||
}
|
||||
|
||||
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) {
|
||||
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float scale) {
|
||||
bool retVal;
|
||||
BLOCKING_INVOKE_METHOD(qApp, "exportEntities",
|
||||
Q_RETURN_ARG(bool, retVal),
|
||||
|
@ -42,7 +42,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float
|
|||
Q_ARG(float, x),
|
||||
Q_ARG(float, y),
|
||||
Q_ARG(float, z),
|
||||
Q_ARG(float, s));
|
||||
Q_ARG(float, scale));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <EntityItemID.h>
|
||||
|
||||
/**jsdoc
|
||||
* The Clipboard API enables you to export and import entities to and from JSON files.
|
||||
*
|
||||
* @namespace Clipboard
|
||||
*/
|
||||
class ClipboardScriptingInterface : public QObject {
|
||||
|
@ -25,48 +27,56 @@ class ClipboardScriptingInterface : public QObject {
|
|||
public:
|
||||
ClipboardScriptingInterface();
|
||||
|
||||
signals:
|
||||
void readyToImport();
|
||||
|
||||
public:
|
||||
/**jsdoc
|
||||
* Compute the extents of the contents held in the clipboard.
|
||||
* @function Clipboard.getContentsDimensions
|
||||
* @return {Vec3} The extents of the contents held in the clipboard.
|
||||
* @returns {Vec3} The extents of the contents held in the clipboard.
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getContentsDimensions();
|
||||
|
||||
/**jsdoc
|
||||
* Compute largest dimension of the extents of the contents held in the clipboard
|
||||
* Compute the largest dimension of the extents of the contents held in the clipboard.
|
||||
* @function Clipboard.getClipboardContentsLargestDimension
|
||||
* @return {float} The largest dimension computed.
|
||||
* @returns {number} The largest dimension computed.
|
||||
*/
|
||||
Q_INVOKABLE float getClipboardContentsLargestDimension();
|
||||
|
||||
/**jsdoc
|
||||
* Import entities from a .json file containing entity data into the clipboard.
|
||||
* You can generate * a .json file using {Clipboard.exportEntities}.
|
||||
* Import entities from a JSON file containing entity data into the clipboard.
|
||||
* You can generate a JSON file using {@link Clipboard.exportEntities}.
|
||||
* @function Clipboard.importEntities
|
||||
* @param {string} filename Filename of file to import.
|
||||
* @return {bool} True if the import was succesful, otherwise false.
|
||||
* @param {string} filename Path and name of file to import.
|
||||
* @returns {boolean} <code>true</code> if the import was successful, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool importEntities(const QString& filename);
|
||||
|
||||
/**jsdoc
|
||||
* Export the entities listed in `entityIDs` to the file `filename`
|
||||
* Export the entities specified to a JSON file.
|
||||
* @function Clipboard.exportEntities
|
||||
* @param {string} filename Path to the file to export entities to.
|
||||
* @param {EntityID[]} entityIDs IDs of entities to export.
|
||||
* @return {bool} True if the export was succesful, otherwise false.
|
||||
* @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {Uuid[]} entityIDs Array of IDs of the entities to export.
|
||||
* @returns {boolean} <code>true</code> if the export was successful, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs);
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float s);
|
||||
|
||||
/**jsdoc
|
||||
* Export the entities with centers within a cube to a JSON file.
|
||||
* @function Clipboard.exportEntities
|
||||
* @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {number} x X-coordinate of the cube center.
|
||||
* @param {number} y Y-coordinate of the cube center.
|
||||
* @param {number} z Z-coordinate of the cube center.
|
||||
* @param {number} scale Half dimension of the cube.
|
||||
* @returns {boolean} <code>true</code> if the export was successful, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
||||
|
||||
/**jsdoc
|
||||
* Paste the contents of the clipboard into the world.
|
||||
* @function Clipboard.pasteEntities
|
||||
* @param {Vec3} position Position to paste clipboard at.
|
||||
* @return {EntityID[]} Array of entity IDs for the new entities that were
|
||||
* created as a result of the paste operation.
|
||||
* @param {Vec3} position Position to paste the clipboard contents at.
|
||||
* @returns {Uuid[]} Array of entity IDs for the new entities that were created as a result of the paste operation.
|
||||
*/
|
||||
Q_INVOKABLE QVector<EntityItemID> pasteEntities(glm::vec3 position);
|
||||
};
|
||||
|
|
|
@ -18,15 +18,18 @@
|
|||
class MenuItemProperties;
|
||||
|
||||
/**jsdoc
|
||||
* The `Menu` provides access to the menu that is shown at the top of the window
|
||||
* shown on a user's desktop and the right click menu that is accessible
|
||||
* in both Desktop and HMD mode.
|
||||
* The Menu API provides access to the menu that is displayed at the top of the window
|
||||
* on a user's desktop and in the tablet when the "MENU" button is pressed.
|
||||
*
|
||||
* <p />
|
||||
*
|
||||
* <h3>Groupings</h3>
|
||||
* A `grouping` is a way to group a set of menus and/or menu items together
|
||||
* so that they can all be set visible or invisible as a group. There are
|
||||
* 2 available groups: "Advanced" and "Developer"
|
||||
*
|
||||
* A "grouping" provides a way to group a set of menus or menu items together so
|
||||
* that they can all be set visible or invisible as a group.
|
||||
* There are two available groups: <code>"Advanced"</code> and <code>"Developer"</code>.
|
||||
* These groupings can be toggled in the "Settings" menu.
|
||||
* If a menu item doesn't belong to a group it is always displayed.
|
||||
*
|
||||
* @namespace Menu
|
||||
*/
|
||||
|
@ -55,86 +58,113 @@ public slots:
|
|||
/**jsdoc
|
||||
* Add a new top-level menu.
|
||||
* @function Menu.addMenu
|
||||
* @param {string} menuName Name that will be shown in the menu.
|
||||
* @param {string} grouping Name of the grouping to add this menu to.
|
||||
* @param {string} menuName - Name that will be displayed for the menu. Nested menus can be described using the ">" symbol.
|
||||
* @param {string} [grouping] - Name of the grouping, if any, to add this menu to.
|
||||
*
|
||||
* @example <caption>Add a menu and a nested submenu.</caption>
|
||||
* Menu.addMenu("Test Menu");
|
||||
* Menu.addMenu("Test Menu > Test Sub Menu");
|
||||
*
|
||||
* @example <caption>Add a menu to the Settings menu that is only visible if Settings > Advanced is enabled.</caption>
|
||||
* Menu.addMenu("Settings > Test Grouping Menu", "Advanced");
|
||||
*/
|
||||
void addMenu(const QString& menuName, const QString& grouping = QString());
|
||||
|
||||
/**jsdoc
|
||||
* Remove a top-level menu.
|
||||
* @function Menu.removeMenu
|
||||
* @param {string} menuName Name of the menu to remove.
|
||||
* @param {string} menuName - Name of the menu to remove.
|
||||
* @example <caption>Remove a menu and nested submenu.</caption>
|
||||
* Menu.removeMenu("Test Menu > Test Sub Menu");
|
||||
* Menu.removeMenu("Test Menu");
|
||||
*/
|
||||
void removeMenu(const QString& menuName);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether a top-level menu exists.
|
||||
* @function Menu.menuExists
|
||||
* @param {string} menuName Name of the menu to check for existence.
|
||||
* @return {bool} `true` if the menu exists, otherwise `false`.
|
||||
* @param {string} menuName - Name of the menu to check for existence.
|
||||
* @returns {boolean} <code>true</code> if the menu exists, otherwise <code>false</code>.
|
||||
* @example <caption>Check if the "Developer" menu exists.</caption>
|
||||
* if (Menu.menuExists("Developer")) {
|
||||
* print("Developer menu exists.");
|
||||
* }
|
||||
*/
|
||||
bool menuExists(const QString& menuName);
|
||||
|
||||
/**jsdoc
|
||||
* Add a separator with an unclickable label below it.
|
||||
* The line will be placed at the bottom of the menu.
|
||||
* Add a separator with an unclickable label below it. The separator will be placed at the bottom of the menu.
|
||||
* If you want to add a separator at a specific point in the menu, use {@link Menu.addMenuItem} with
|
||||
* {@link Menu.MenuItemProperties} instead.
|
||||
* @function Menu.addSeparator
|
||||
* @param {string} menuName Name of the menu to add a separator to.
|
||||
* @param {string} separatorName Name of the separator that will be shown (but unclickable) below the separator line.
|
||||
* @param {string} menuName - Name of the menu to add a separator to.
|
||||
* @param {string} separatorName - Name of the separator that will be displayed as the label below the separator line.
|
||||
* @example <caption>Add a separator.</caption>
|
||||
* Menu.addSeparator("Developer","Test Separator");
|
||||
*/
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
||||
/**jsdoc
|
||||
* Remove a separator and its label from a menu.
|
||||
* Remove a separator from a menu.
|
||||
* @function Menu.removeSeparator
|
||||
* @param {string} menuName Name of the menu to remove a separator from.
|
||||
* @param {string} separatorName Name of the separator to remove.
|
||||
* @param {string} menuName - Name of the menu to remove the separator from.
|
||||
* @param {string} separatorName - Name of the separator to remove.
|
||||
* @example <caption>Remove a separator.</caption>
|
||||
* Menu.removeSeparator("Developer","Test Separator");
|
||||
*/
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
||||
/**jsdoc
|
||||
* Add a new menu item to a menu.
|
||||
* @function Menu.addMenuItem
|
||||
* @param {Menu.MenuItemProperties} properties
|
||||
* @param {Menu.MenuItemProperties} properties - Properties of the menu item to create.
|
||||
* @example <caption>Add a menu item using {@link Menu.MenuItemProperties}.</caption>
|
||||
* Menu.addMenuItem({
|
||||
* menuName: "Developer",
|
||||
* menuItemName: "Test",
|
||||
* afterItem: "Log",
|
||||
* shortcutKey: "Ctrl+Shift+T",
|
||||
* grouping: "Advanced"
|
||||
* });
|
||||
*/
|
||||
void addMenuItem(const MenuItemProperties& properties);
|
||||
|
||||
/**jsdoc
|
||||
* Add a new menu item to a menu.
|
||||
* Add a new menu item to a menu. The new item is added at the end of the menu.
|
||||
* @function Menu.addMenuItem
|
||||
* @param {string} menuName Name of the menu to add a menu item to.
|
||||
* @param {string} menuItem Name of the menu item. This is what will be displayed in the menu.
|
||||
* @param {string} shortcutKey A shortcut key that can be used to trigger the menu item.
|
||||
* @param {string} menuName - Name of the menu to add a menu item to.
|
||||
* @param {string} menuItem - Name of the menu item. This is what will be displayed in the menu.
|
||||
* @param {string} [shortcutKey] A shortcut key that can be used to trigger the menu item.
|
||||
* @example <caption>Add a menu item to the end of the "Developer" menu.</caption>
|
||||
* Menu.addMenuItem("Developer", "Test", "Ctrl+Shift+T");
|
||||
*/
|
||||
void addMenuItem(const QString& menuName, const QString& menuitem, const QString& shortcutKey);
|
||||
|
||||
/**jsdoc
|
||||
* Add a new menu item to a menu.
|
||||
* @function Menu.addMenuItem
|
||||
* @param {string} menuName Name of the menu to add a menu item to.
|
||||
* @param {string} menuItem Name of the menu item. This is what will be displayed in the menu.
|
||||
*/
|
||||
void addMenuItem(const QString& menuName, const QString& menuitem);
|
||||
|
||||
/**jsdoc
|
||||
* Remove a menu item from a menu.
|
||||
* @function Menu.removeMenuItem
|
||||
* @param {string} menuName Name of the menu to remove a menu item from.
|
||||
* @param {string} menuItem Name of the menu item to remove.
|
||||
* @param {string} menuName - Name of the menu to remove a menu item from.
|
||||
* @param {string} menuItem - Name of the menu item to remove.
|
||||
* Menu.removeMenuItem("Developer", "Test");
|
||||
*/
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
|
||||
/**jsdoc
|
||||
* Check if a menu item exists.
|
||||
* @function Menu.menuItemExists
|
||||
* @param {string} menuName Name of the menu that the menu item is in.
|
||||
* @param {string} menuItem Name of the menu item to check for existence of.
|
||||
* @return {bool} `true` if the menu item exists, otherwise `false`.
|
||||
* @param {string} menuName - Name of the menu that the menu item is in.
|
||||
* @param {string} menuItem - Name of the menu item to check for existence of.
|
||||
* @returns {boolean} <code>true</code> if the menu item exists, otherwise <code>false</code>.
|
||||
* @example <caption>Determine if the Developer > Stats menu exists.</caption>
|
||||
* if (Menu.menuItemExists("Developer", "Stats")) {
|
||||
* print("Developer > Stats menu item exists.");
|
||||
* }
|
||||
*/
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
|
||||
/**
|
||||
* Not working, will not document until fixed
|
||||
* TODO: Not working; don't document until fixed.
|
||||
*/
|
||||
void addActionGroup(const QString& groupName, const QStringList& actionList,
|
||||
const QString& selected = QString());
|
||||
|
@ -143,53 +173,73 @@ public slots:
|
|||
/**jsdoc
|
||||
* Check whether a checkable menu item is checked.
|
||||
* @function Menu.isOptionChecked
|
||||
* @param {string} menuOption The name of the menu item.
|
||||
* @return `true` if the option is checked, otherwise false.
|
||||
* @param {string} menuOption - The name of the menu item.
|
||||
* @returns {boolean} <code>true</code> if the option is checked, otherwise <code>false</code>.
|
||||
* @example <caption>Report whether the Settings > Advanced menu item is turned on.</caption>
|
||||
* print(Menu.isOptionChecked("Advanced Menus")); // true or false
|
||||
*/
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
|
||||
/**jsdoc
|
||||
* Set a checkable menu item as checked or unchecked.
|
||||
* @function Menu.setIsOptionChecked
|
||||
* @param {string} menuOption The name of the menu item to modify.
|
||||
* @param {bool} isChecked If `true`, the menu item will be checked, otherwise it will not be checked.
|
||||
* @param {string} menuOption - The name of the menu item to modify.
|
||||
* @param {boolean} isChecked - If <code>true</code>, the menu item will be checked, otherwise it will not be checked.
|
||||
* @example <caption>Turn on Settings > Advanced Menus.</caption>
|
||||
* Menu.setIsOptionChecked("Advanced Menus", true);
|
||||
* print(Menu.isOptionChecked("Advanced Menus")); // true
|
||||
*/
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
/**jsdoc
|
||||
* Toggle the status of a checkable menu item. If it is checked, it will be unchecked.
|
||||
* If it is unchecked, it will be checked.
|
||||
* @function Menu.setIsOptionChecked
|
||||
* @param {string} menuOption The name of the menu item to toggle.
|
||||
* Trigger the menu item as if the user clicked on it.
|
||||
* @function Menu.triggerOption
|
||||
* @param {string} menuOption - The name of the menu item to trigger.
|
||||
* @example <caption>Open the help window.</caption>
|
||||
* Menu.triggerOption('Help...');
|
||||
*/
|
||||
void triggerOption(const QString& menuOption);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether a menu is enabled. If a menu is disabled it will be greyed out
|
||||
* and unselectable.
|
||||
* Check whether a menu or menu item is enabled. If disabled, the item is grayed out and unusable.
|
||||
* Menus are enabled by default.
|
||||
* @function Menu.isMenuEnabled
|
||||
* @param {string} menuName The name of the menu to check.
|
||||
* @return {bool} `true` if the menu is enabled, otherwise false.
|
||||
* @param {string} menuName The name of the menu or menu item to check.
|
||||
* @returns {boolean} <code>true</code> if the menu is enabled, otherwise <code>false</code>.
|
||||
* @example <caption>Report with the Settings > Advanced Menus menu item is enabled.</caption>
|
||||
* print(Menu.isMenuEnabled("Settings > Advanced Menus")); // true or false
|
||||
*/
|
||||
bool isMenuEnabled(const QString& menuName);
|
||||
|
||||
/**jsdoc
|
||||
* Set a menu to be enabled or disabled.
|
||||
* Set a menu or menu item to be enabled or disabled. If disabled, the item is grayed out and unusable.
|
||||
* @function Menu.setMenuEnabled
|
||||
* @param {string} menuName The name of the menu to modify.
|
||||
* @param {bool} isEnabled Whether the menu will be enabled or not.
|
||||
* @param {string} menuName - The name of the menu or menu item to modify.
|
||||
* @param {boolean} isEnabled - If <code>true</code>, the menu will be enabled, otherwise it will be disabled.
|
||||
* @example <caption>Disable the Settings > Advanced Menus menu item.</caption>
|
||||
* Menu.setMenuEnabled("Settings > Advanced Menus", false);
|
||||
* print(Menu.isMenuEnabled("Settings > Advanced Menus")); // false
|
||||
*/
|
||||
void setMenuEnabled(const QString& menuName, bool isEnabled);
|
||||
|
||||
/**
|
||||
* TODO: Not used or useful; will not document until used.
|
||||
*/
|
||||
void closeInfoView(const QString& path);
|
||||
bool isInfoViewVisible(const QString& path);
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* This is a signal that is emitted when a menu item is clicked.
|
||||
* Triggered when a menu item is clicked (or triggered by {@link Menu.triggerOption}).
|
||||
* @function Menu.menuItemEvent
|
||||
* @param {string} menuItem Name of the menu item that was triggered.
|
||||
* @param {string} menuItem - Name of the menu item that was clicked.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Detect menu item events.</caption>
|
||||
* function onMenuItemEvent(menuItem) {
|
||||
* print("You clicked on " + menuItem);
|
||||
* }
|
||||
*
|
||||
* Menu.menuItemEvent.connect(onMenuItemEvent);
|
||||
*/
|
||||
void menuItemEvent(const QString& menuItem);
|
||||
};
|
||||
|
|
|
@ -18,7 +18,9 @@ GameplayObjects::GameplayObjects() {
|
|||
|
||||
bool GameplayObjects::addToGameplayObjects(const QUuid& avatarID) {
|
||||
containsData = true;
|
||||
_avatarIDs.push_back(avatarID);
|
||||
if (std::find(_avatarIDs.begin(), _avatarIDs.end(), avatarID) == _avatarIDs.end()) {
|
||||
_avatarIDs.push_back(avatarID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool GameplayObjects::removeFromGameplayObjects(const QUuid& avatarID) {
|
||||
|
@ -28,7 +30,9 @@ bool GameplayObjects::removeFromGameplayObjects(const QUuid& avatarID) {
|
|||
|
||||
bool GameplayObjects::addToGameplayObjects(const EntityItemID& entityID) {
|
||||
containsData = true;
|
||||
_entityIDs.push_back(entityID);
|
||||
if (std::find(_entityIDs.begin(), _entityIDs.end(), entityID) == _entityIDs.end()) {
|
||||
_entityIDs.push_back(entityID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool GameplayObjects::removeFromGameplayObjects(const EntityItemID& entityID) {
|
||||
|
@ -38,7 +42,9 @@ bool GameplayObjects::removeFromGameplayObjects(const EntityItemID& entityID) {
|
|||
|
||||
bool GameplayObjects::addToGameplayObjects(const OverlayID& overlayID) {
|
||||
containsData = true;
|
||||
_overlayIDs.push_back(overlayID);
|
||||
if (std::find(_overlayIDs.begin(), _overlayIDs.end(), overlayID) == _overlayIDs.end()) {
|
||||
_overlayIDs.push_back(overlayID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool GameplayObjects::removeFromGameplayObjects(const OverlayID& overlayID) {
|
||||
|
@ -72,28 +78,125 @@ bool SelectionScriptingInterface::removeFromSelectedItemsList(const QString& lis
|
|||
}
|
||||
|
||||
bool SelectionScriptingInterface::clearSelectedItemsList(const QString& listName) {
|
||||
_selectedItemsListMap.insert(listName, GameplayObjects());
|
||||
emit selectedItemsListChanged(listName);
|
||||
{
|
||||
QWriteLocker lock(&_selectionListsLock);
|
||||
_selectedItemsListMap.insert(listName, GameplayObjects());
|
||||
}
|
||||
onSelectedItemsListChanged(listName);
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList SelectionScriptingInterface::getListNames() const {
|
||||
QStringList list;
|
||||
QReadLocker lock(&_selectionListsLock);
|
||||
list = _selectedItemsListMap.keys();
|
||||
return list;
|
||||
}
|
||||
|
||||
QStringList SelectionScriptingInterface::getHighlightedListNames() const {
|
||||
QStringList list;
|
||||
QReadLocker lock(&_highlightStylesLock);
|
||||
list = _highlightStyleMap.keys();
|
||||
return list;
|
||||
}
|
||||
|
||||
bool SelectionScriptingInterface::enableListHighlight(const QString& listName, const QVariantMap& highlightStyleValues) {
|
||||
QWriteLocker lock(&_highlightStylesLock);
|
||||
|
||||
auto highlightStyle = _highlightStyleMap.find(listName);
|
||||
if (highlightStyle == _highlightStyleMap.end()) {
|
||||
highlightStyle = _highlightStyleMap.insert(listName, SelectionHighlightStyle());
|
||||
|
||||
}
|
||||
|
||||
if (!(*highlightStyle).isBoundToList()) {
|
||||
setupHandler(listName);
|
||||
(*highlightStyle).setBoundToList(true);
|
||||
}
|
||||
|
||||
(*highlightStyle).fromVariantMap(highlightStyleValues);
|
||||
|
||||
auto mainScene = qApp->getMain3DScene();
|
||||
if (mainScene) {
|
||||
render::Transaction transaction;
|
||||
transaction.resetSelectionHighlight(listName.toStdString(), (*highlightStyle).getStyle());
|
||||
mainScene->enqueueTransaction(transaction);
|
||||
}
|
||||
else {
|
||||
qWarning() << "SelectionToSceneHandler::highlightStyleChanged(), Unexpected null scene, possibly during application shutdown";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SelectionScriptingInterface::disableListHighlight(const QString& listName) {
|
||||
QWriteLocker lock(&_highlightStylesLock);
|
||||
auto highlightStyle = _highlightStyleMap.find(listName);
|
||||
if (highlightStyle != _highlightStyleMap.end()) {
|
||||
if ((*highlightStyle).isBoundToList()) {
|
||||
}
|
||||
|
||||
_highlightStyleMap.erase(highlightStyle);
|
||||
|
||||
auto mainScene = qApp->getMain3DScene();
|
||||
if (mainScene) {
|
||||
render::Transaction transaction;
|
||||
transaction.removeHighlightFromSelection(listName.toStdString());
|
||||
mainScene->enqueueTransaction(transaction);
|
||||
}
|
||||
else {
|
||||
qWarning() << "SelectionToSceneHandler::highlightStyleChanged(), Unexpected null scene, possibly during application shutdown";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariantMap SelectionScriptingInterface::getListHighlightStyle(const QString& listName) const {
|
||||
QReadLocker lock(&_highlightStylesLock);
|
||||
auto highlightStyle = _highlightStyleMap.find(listName);
|
||||
if (highlightStyle == _highlightStyleMap.end()) {
|
||||
return QVariantMap();
|
||||
} else {
|
||||
return (*highlightStyle).toVariantMap();
|
||||
}
|
||||
}
|
||||
|
||||
render::HighlightStyle SelectionScriptingInterface::getHighlightStyle(const QString& listName) const {
|
||||
QReadLocker lock(&_highlightStylesLock);
|
||||
auto highlightStyle = _highlightStyleMap.find(listName);
|
||||
if (highlightStyle == _highlightStyleMap.end()) {
|
||||
return render::HighlightStyle();
|
||||
} else {
|
||||
return (*highlightStyle).getStyle();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
|
||||
GameplayObjects currentList = _selectedItemsListMap.value(listName);
|
||||
currentList.addToGameplayObjects(idToAdd);
|
||||
_selectedItemsListMap.insert(listName, currentList);
|
||||
|
||||
emit selectedItemsListChanged(listName);
|
||||
{
|
||||
QWriteLocker lock(&_selectionListsLock);
|
||||
GameplayObjects currentList = _selectedItemsListMap.value(listName);
|
||||
currentList.addToGameplayObjects(idToAdd);
|
||||
_selectedItemsListMap.insert(listName, currentList);
|
||||
}
|
||||
onSelectedItemsListChanged(listName);
|
||||
return true;
|
||||
}
|
||||
template <class T> bool SelectionScriptingInterface::removeFromGameplayObjects(const QString& listName, T idToRemove) {
|
||||
GameplayObjects currentList = _selectedItemsListMap.value(listName);
|
||||
if (currentList.getContainsData()) {
|
||||
currentList.removeFromGameplayObjects(idToRemove);
|
||||
_selectedItemsListMap.insert(listName, currentList);
|
||||
|
||||
emit selectedItemsListChanged(listName);
|
||||
bool listExist = false;
|
||||
{
|
||||
QWriteLocker lock(&_selectionListsLock);
|
||||
auto currentList = _selectedItemsListMap.find(listName);
|
||||
if (currentList != _selectedItemsListMap.end()) {
|
||||
listExist = true;
|
||||
(*currentList).removeFromGameplayObjects(idToRemove);
|
||||
}
|
||||
}
|
||||
if (listExist) {
|
||||
onSelectedItemsListChanged(listName);
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -102,50 +205,123 @@ template <class T> bool SelectionScriptingInterface::removeFromGameplayObjects(c
|
|||
//
|
||||
|
||||
GameplayObjects SelectionScriptingInterface::getList(const QString& listName) {
|
||||
QReadLocker lock(&_selectionListsLock);
|
||||
return _selectedItemsListMap.value(listName);
|
||||
}
|
||||
|
||||
void SelectionScriptingInterface::printList(const QString& listName) {
|
||||
GameplayObjects currentList = _selectedItemsListMap.value(listName);
|
||||
if (currentList.getContainsData()) {
|
||||
QReadLocker lock(&_selectionListsLock);
|
||||
auto currentList = _selectedItemsListMap.find(listName);
|
||||
if (currentList != _selectedItemsListMap.end()) {
|
||||
if ((*currentList).getContainsData()) {
|
||||
|
||||
qDebug() << "Avatar IDs:";
|
||||
for (auto i : currentList.getAvatarIDs()) {
|
||||
qDebug() << i << ';';
|
||||
}
|
||||
qDebug() << "";
|
||||
qDebug() << "List named " << listName << ":";
|
||||
qDebug() << "Avatar IDs:";
|
||||
for (auto i : (*currentList).getAvatarIDs()) {
|
||||
qDebug() << i << ';';
|
||||
}
|
||||
qDebug() << "";
|
||||
|
||||
qDebug() << "Entity IDs:";
|
||||
for (auto j : currentList.getEntityIDs()) {
|
||||
qDebug() << j << ';';
|
||||
}
|
||||
qDebug() << "";
|
||||
qDebug() << "Entity IDs:";
|
||||
for (auto j : (*currentList).getEntityIDs()) {
|
||||
qDebug() << j << ';';
|
||||
}
|
||||
qDebug() << "";
|
||||
|
||||
qDebug() << "Overlay IDs:";
|
||||
for (auto k : currentList.getOverlayIDs()) {
|
||||
qDebug() << k << ';';
|
||||
qDebug() << "Overlay IDs:";
|
||||
for (auto k : (*currentList).getOverlayIDs()) {
|
||||
qDebug() << k << ';';
|
||||
}
|
||||
qDebug() << "";
|
||||
}
|
||||
else {
|
||||
qDebug() << "List named " << listName << " empty";
|
||||
}
|
||||
qDebug() << "";
|
||||
} else {
|
||||
qDebug() << "List named" << listName << "doesn't exist.";
|
||||
qDebug() << "List named " << listName << " doesn't exist.";
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap SelectionScriptingInterface::getSelectedItemsList(const QString& listName) const {
|
||||
QReadLocker lock(&_selectionListsLock);
|
||||
QVariantMap list;
|
||||
auto currentList = _selectedItemsListMap.find(listName);
|
||||
if (currentList != _selectedItemsListMap.end()) {
|
||||
QList<QVariant> avatarIDs;
|
||||
QList<QVariant> entityIDs;
|
||||
QList<QVariant> overlayIDs;
|
||||
|
||||
if ((*currentList).getContainsData()) {
|
||||
if (!(*currentList).getAvatarIDs().empty()) {
|
||||
for (auto j : (*currentList).getAvatarIDs()) {
|
||||
avatarIDs.push_back((QUuid)j);
|
||||
}
|
||||
}
|
||||
if (!(*currentList).getEntityIDs().empty()) {
|
||||
for (auto j : (*currentList).getEntityIDs()) {
|
||||
entityIDs.push_back((QUuid)j );
|
||||
}
|
||||
}
|
||||
if (!(*currentList).getOverlayIDs().empty()) {
|
||||
for (auto j : (*currentList).getOverlayIDs()) {
|
||||
overlayIDs.push_back((QUuid)j);
|
||||
}
|
||||
}
|
||||
}
|
||||
list["avatars"] = (avatarIDs);
|
||||
list["entities"] = (entityIDs);
|
||||
list["overlays"] = (overlayIDs);
|
||||
|
||||
return list;
|
||||
}
|
||||
else {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
bool SelectionScriptingInterface::removeListFromMap(const QString& listName) {
|
||||
if (_selectedItemsListMap.remove(listName)) {
|
||||
emit selectedItemsListChanged(listName);
|
||||
bool removed = false;
|
||||
{
|
||||
QWriteLocker lock(&_selectionListsLock);
|
||||
removed = _selectedItemsListMap.remove(listName);
|
||||
}
|
||||
if (removed) {
|
||||
onSelectedItemsListChanged(listName);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionScriptingInterface::setupHandler(const QString& selectionName) {
|
||||
QWriteLocker lock(&_selectionHandlersLock);
|
||||
auto handler = _handlerMap.find(selectionName);
|
||||
if (handler == _handlerMap.end()) {
|
||||
handler = _handlerMap.insert(selectionName, new SelectionToSceneHandler());
|
||||
}
|
||||
|
||||
(*handler)->initialize(selectionName);
|
||||
}
|
||||
|
||||
void SelectionScriptingInterface::onSelectedItemsListChanged(const QString& listName) {
|
||||
{
|
||||
QWriteLocker lock(&_selectionHandlersLock);
|
||||
auto handler = _handlerMap.find(listName);
|
||||
if (handler != _handlerMap.end()) {
|
||||
(*handler)->updateSceneFromSelectedList();
|
||||
}
|
||||
}
|
||||
|
||||
emit selectedItemsListChanged(listName);
|
||||
}
|
||||
|
||||
|
||||
SelectionToSceneHandler::SelectionToSceneHandler() {
|
||||
}
|
||||
|
||||
void SelectionToSceneHandler::initialize(const QString& listName) {
|
||||
_listName = listName;
|
||||
updateSceneFromSelectedList();
|
||||
}
|
||||
|
||||
void SelectionToSceneHandler::selectedItemsListChanged(const QString& listName) {
|
||||
|
@ -199,3 +375,85 @@ void SelectionToSceneHandler::updateSceneFromSelectedList() {
|
|||
qWarning() << "SelectionToSceneHandler::updateRendererSelectedList(), Unexpected null scene, possibly during application shutdown";
|
||||
}
|
||||
}
|
||||
|
||||
bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) {
|
||||
auto colorVariant = properties["outlineUnoccludedColor"];
|
||||
if (colorVariant.isValid()) {
|
||||
bool isValid;
|
||||
auto color = xColorFromVariant(colorVariant, isValid);
|
||||
if (isValid) {
|
||||
_style._outlineUnoccluded.color = toGlm(color);
|
||||
}
|
||||
}
|
||||
colorVariant = properties["outlineOccludedColor"];
|
||||
if (colorVariant.isValid()) {
|
||||
bool isValid;
|
||||
auto color = xColorFromVariant(colorVariant, isValid);
|
||||
if (isValid) {
|
||||
_style._outlineOccluded.color = toGlm(color);
|
||||
}
|
||||
}
|
||||
colorVariant = properties["fillUnoccludedColor"];
|
||||
if (colorVariant.isValid()) {
|
||||
bool isValid;
|
||||
auto color = xColorFromVariant(colorVariant, isValid);
|
||||
if (isValid) {
|
||||
_style._fillUnoccluded.color = toGlm(color);
|
||||
}
|
||||
}
|
||||
colorVariant = properties["fillOccludedColor"];
|
||||
if (colorVariant.isValid()) {
|
||||
bool isValid;
|
||||
auto color = xColorFromVariant(colorVariant, isValid);
|
||||
if (isValid) {
|
||||
_style._fillOccluded.color = toGlm(color);
|
||||
}
|
||||
}
|
||||
|
||||
auto intensityVariant = properties["outlineUnoccludedAlpha"];
|
||||
if (intensityVariant.isValid()) {
|
||||
_style._outlineUnoccluded.alpha = intensityVariant.toFloat();
|
||||
}
|
||||
intensityVariant = properties["outlineOccludedAlpha"];
|
||||
if (intensityVariant.isValid()) {
|
||||
_style._outlineOccluded.alpha = intensityVariant.toFloat();
|
||||
}
|
||||
intensityVariant = properties["fillUnoccludedAlpha"];
|
||||
if (intensityVariant.isValid()) {
|
||||
_style._fillUnoccluded.alpha = intensityVariant.toFloat();
|
||||
}
|
||||
intensityVariant = properties["fillOccludedAlpha"];
|
||||
if (intensityVariant.isValid()) {
|
||||
_style._fillOccluded.alpha = intensityVariant.toFloat();
|
||||
}
|
||||
|
||||
auto outlineWidth = properties["outlineWidth"];
|
||||
if (outlineWidth.isValid()) {
|
||||
_style._outlineWidth = outlineWidth.toFloat();
|
||||
}
|
||||
auto isOutlineSmooth = properties["isOutlineSmooth"];
|
||||
if (isOutlineSmooth.isValid()) {
|
||||
_style._isOutlineSmooth = isOutlineSmooth.toBool();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariantMap SelectionHighlightStyle::toVariantMap() const {
|
||||
QVariantMap properties;
|
||||
|
||||
properties["outlineUnoccludedColor"] = xColorToVariant(xColorFromGlm(_style._outlineUnoccluded.color));
|
||||
properties["outlineOccludedColor"] = xColorToVariant(xColorFromGlm(_style._outlineOccluded.color));
|
||||
properties["fillUnoccludedColor"] = xColorToVariant(xColorFromGlm(_style._fillUnoccluded.color));
|
||||
properties["fillOccludedColor"] = xColorToVariant(xColorFromGlm(_style._fillOccluded.color));
|
||||
|
||||
properties["outlineUnoccludedAlpha"] = _style._outlineUnoccluded.alpha;
|
||||
properties["outlineOccludedAlpha"] = _style._outlineOccluded.alpha;
|
||||
properties["fillUnoccludedAlpha"] = _style._fillUnoccluded.alpha;
|
||||
properties["fillOccludedAlpha"] = _style._fillOccluded.alpha;
|
||||
|
||||
properties["outlineWidth"] = _style._outlineWidth;
|
||||
properties["isOutlineSmooth"] = _style._isOutlineSmooth;
|
||||
|
||||
return properties;
|
||||
}
|
|
@ -21,22 +21,23 @@
|
|||
#include "RenderableEntityItem.h"
|
||||
#include "ui/overlays/Overlay.h"
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <render/HighlightStyle.h>
|
||||
|
||||
class GameplayObjects {
|
||||
public:
|
||||
GameplayObjects();
|
||||
|
||||
bool getContainsData() { return containsData; }
|
||||
bool getContainsData() const { return containsData; }
|
||||
|
||||
std::vector<QUuid> getAvatarIDs() { return _avatarIDs; }
|
||||
std::vector<QUuid> getAvatarIDs() const { return _avatarIDs; }
|
||||
bool addToGameplayObjects(const QUuid& avatarID);
|
||||
bool removeFromGameplayObjects(const QUuid& avatarID);
|
||||
|
||||
std::vector<EntityItemID> getEntityIDs() { return _entityIDs; }
|
||||
std::vector<EntityItemID> getEntityIDs() const { return _entityIDs; }
|
||||
bool addToGameplayObjects(const EntityItemID& entityID);
|
||||
bool removeFromGameplayObjects(const EntityItemID& entityID);
|
||||
|
||||
std::vector<OverlayID> getOverlayIDs() { return _overlayIDs; }
|
||||
std::vector<OverlayID> getOverlayIDs() const { return _overlayIDs; }
|
||||
bool addToGameplayObjects(const OverlayID& overlayID);
|
||||
bool removeFromGameplayObjects(const OverlayID& overlayID);
|
||||
|
||||
|
@ -48,20 +49,52 @@ private:
|
|||
};
|
||||
|
||||
|
||||
class SelectionToSceneHandler : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SelectionToSceneHandler();
|
||||
void initialize(const QString& listName);
|
||||
|
||||
void updateSceneFromSelectedList();
|
||||
|
||||
public slots:
|
||||
void selectedItemsListChanged(const QString& listName);
|
||||
|
||||
private:
|
||||
QString _listName{ "" };
|
||||
};
|
||||
using SelectionToSceneHandlerPointer = QSharedPointer<SelectionToSceneHandler>;
|
||||
|
||||
class SelectionHighlightStyle {
|
||||
public:
|
||||
SelectionHighlightStyle() {}
|
||||
|
||||
void setBoundToList(bool bound) { _isBoundToList = bound; }
|
||||
bool isBoundToList() const { return _isBoundToList; }
|
||||
|
||||
bool fromVariantMap(const QVariantMap& properties);
|
||||
QVariantMap toVariantMap() const;
|
||||
|
||||
render::HighlightStyle getStyle() const { return _style; }
|
||||
|
||||
protected:
|
||||
bool _isBoundToList{ false };
|
||||
render::HighlightStyle _style;
|
||||
};
|
||||
|
||||
class SelectionScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SelectionScriptingInterface();
|
||||
|
||||
GameplayObjects getList(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Prints out the list of avatars, entities and overlays stored in a particular selection.
|
||||
* @function Selection.printList
|
||||
* @param listName {string} name of the selection
|
||||
* Query the names of all the selection lists
|
||||
* @function Selection.getListNames
|
||||
* @return An array of names of all the selection lists
|
||||
*/
|
||||
Q_INVOKABLE void printList(const QString& listName);
|
||||
Q_INVOKABLE QStringList getListNames() const;
|
||||
|
||||
/**jsdoc
|
||||
* Removes a named selection from the list of selections.
|
||||
* @function Selection.removeListFromMap
|
||||
|
@ -96,30 +129,103 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE bool clearSelectedItemsList(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Prints out the list of avatars, entities and overlays stored in a particular selection.
|
||||
* @function Selection.printList
|
||||
* @param listName {string} name of the selection
|
||||
*/
|
||||
Q_INVOKABLE void printList(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Query the list of avatars, entities and overlays stored in a particular selection.
|
||||
* @function Selection.getList
|
||||
* @param listName {string} name of the selection
|
||||
* @return a js object describing the content of a selection list with the following properties:
|
||||
* - "entities": [ and array of the entityID of the entities in the selection]
|
||||
* - "avatars": [ and array of the avatarID of the avatars in the selection]
|
||||
* - "overlays": [ and array of the overlayID of the overlays in the selection]
|
||||
* If the list name doesn't exist, the function returns an empty js object with no properties.
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getSelectedItemsList(const QString& listName) const;
|
||||
|
||||
/**jsdoc
|
||||
* Query the names of the highlighted selection lists
|
||||
* @function Selection.getHighlightedListNames
|
||||
* @return An array of names of the selection list currently highlight enabled
|
||||
*/
|
||||
Q_INVOKABLE QStringList getHighlightedListNames() const;
|
||||
|
||||
/**jsdoc
|
||||
* Enable highlighting for the named selection.
|
||||
* If the Selection doesn't exist, it will be created.
|
||||
* All objects in the list will be displayed with the highlight effect as specified from the highlightStyle.
|
||||
* The function can be called several times with different values in the style to modify it.
|
||||
*
|
||||
* @function Selection.enableListHighlight
|
||||
* @param listName {string} name of the selection
|
||||
* @param highlightStyle {jsObject} highlight style fields (see Selection.getListHighlightStyle for a detailed description of the highlightStyle).
|
||||
* @returns {bool} true if the selection was successfully enabled for highlight.
|
||||
*/
|
||||
Q_INVOKABLE bool enableListHighlight(const QString& listName, const QVariantMap& highlightStyle);
|
||||
/**jsdoc
|
||||
* Disable highlighting for the named selection.
|
||||
* If the Selection doesn't exist or wasn't enabled for highliting then nothing happens simply returning false.
|
||||
*
|
||||
* @function Selection.disableListHighlight
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the selection was successfully disabled for highlight, false otherwise.
|
||||
*/
|
||||
Q_INVOKABLE bool disableListHighlight(const QString& listName);
|
||||
/**jsdoc
|
||||
* Query the highlight style values for the named selection.
|
||||
* If the Selection doesn't exist or hasn't been highlight enabled yet, it will return an empty object.
|
||||
* Otherwise, the jsObject describes the highlight style properties:
|
||||
* - outlineUnoccludedColor: {xColor} Color of the specified highlight region
|
||||
* - outlineOccludedColor: {xColor} "
|
||||
* - fillUnoccludedColor: {xColor} "
|
||||
* - fillOccludedColor: {xColor} "
|
||||
*
|
||||
* - outlineUnoccludedAlpha: {float} Alpha value ranging from 0.0 (not visible) to 1.0 (fully opaque) for the specified highlight region
|
||||
* - outlineOccludedAlpha: {float} "
|
||||
* - fillUnoccludedAlpha: {float} "
|
||||
* - fillOccludedAlpha: {float} "
|
||||
*
|
||||
* - outlineWidth: {float} width of the outline expressed in pixels
|
||||
* - isOutlineSmooth: {bool} true to enable oultine smooth falloff
|
||||
*
|
||||
* @function Selection.getListHighlightStyle
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {jsObject} highlight style as described above
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getListHighlightStyle(const QString& listName) const;
|
||||
|
||||
|
||||
GameplayObjects getList(const QString& listName);
|
||||
|
||||
render::HighlightStyle getHighlightStyle(const QString& listName) const;
|
||||
|
||||
void onSelectedItemsListChanged(const QString& listName);
|
||||
|
||||
signals:
|
||||
void selectedItemsListChanged(const QString& listName);
|
||||
|
||||
private:
|
||||
mutable QReadWriteLock _selectionListsLock;
|
||||
QMap<QString, GameplayObjects> _selectedItemsListMap;
|
||||
|
||||
mutable QReadWriteLock _selectionHandlersLock;
|
||||
QMap<QString, SelectionToSceneHandler*> _handlerMap;
|
||||
|
||||
mutable QReadWriteLock _highlightStylesLock;
|
||||
QMap<QString, SelectionHighlightStyle> _highlightStyleMap;
|
||||
|
||||
template <class T> bool addToGameplayObjects(const QString& listName, T idToAdd);
|
||||
template <class T> bool removeFromGameplayObjects(const QString& listName, T idToRemove);
|
||||
};
|
||||
|
||||
void setupHandler(const QString& selectionName);
|
||||
|
||||
|
||||
class SelectionToSceneHandler : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SelectionToSceneHandler();
|
||||
void initialize(const QString& listName);
|
||||
|
||||
void updateSceneFromSelectedList();
|
||||
|
||||
public slots:
|
||||
void selectedItemsListChanged(const QString& listName);
|
||||
|
||||
private:
|
||||
QString _listName { "" };
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_SelectionScriptingInterface_h
|
||||
|
|
|
@ -23,30 +23,7 @@ void WalletScriptingInterface::refreshWalletStatus() {
|
|||
wallet->getWalletStatus();
|
||||
}
|
||||
|
||||
static const QString CHECKOUT_QML_PATH = qApp->applicationDirPath() + "../../../qml/hifi/commerce/checkout/Checkout.qml";
|
||||
void WalletScriptingInterface::buy(const QString& name, const QString& id, const int& price, const QString& href) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "buy", Q_ARG(const QString&, name), Q_ARG(const QString&, id), Q_ARG(const int&, price), Q_ARG(const QString&, href));
|
||||
return;
|
||||
}
|
||||
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
|
||||
tablet->loadQMLSource(CHECKOUT_QML_PATH);
|
||||
DependencyManager::get<HMDScriptingInterface>()->openTablet();
|
||||
|
||||
QQuickItem* root = nullptr;
|
||||
if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !qApp->isHMDMode())) {
|
||||
root = DependencyManager::get<OffscreenUi>()->getRootItem();
|
||||
} else {
|
||||
root = tablet->getTabletRoot();
|
||||
}
|
||||
CheckoutProxy* checkout = new CheckoutProxy(root->findChild<QObject*>("checkout"));
|
||||
|
||||
// Example: Wallet.buy("Test Flaregun", "0d90d21c-ce7a-4990-ad18-e9d2cf991027", 17, "http://mpassets.highfidelity.com/0d90d21c-ce7a-4990-ad18-e9d2cf991027-v1/flaregun.json");
|
||||
checkout->writeProperty("itemName", name);
|
||||
checkout->writeProperty("itemId", id);
|
||||
checkout->writeProperty("itemPrice", price);
|
||||
checkout->writeProperty("itemHref", href);
|
||||
void WalletScriptingInterface::setWalletStatus(const uint& status) {
|
||||
_walletStatus = status;
|
||||
emit DependencyManager::get<Wallet>()->walletStatusResult(status);
|
||||
}
|
|
@ -39,9 +39,9 @@ public:
|
|||
|
||||
Q_INVOKABLE void refreshWalletStatus();
|
||||
Q_INVOKABLE uint getWalletStatus() { return _walletStatus; }
|
||||
void setWalletStatus(const uint& status) { _walletStatus = status; }
|
||||
|
||||
Q_INVOKABLE void buy(const QString& name, const QString& id, const int& price, const QString& href);
|
||||
// setWalletStatus() should never be made Q_INVOKABLE. If it were,
|
||||
// scripts could cause the Wallet to incorrectly report its status.
|
||||
void setWalletStatus(const uint& status);
|
||||
|
||||
signals:
|
||||
void walletStatusChanged();
|
||||
|
|
|
@ -176,6 +176,10 @@ bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
|
|||
return offscreenUi->isPointOnDesktopWindow(point);
|
||||
}
|
||||
|
||||
glm::vec2 WindowScriptingInterface::getDeviceSize() const {
|
||||
return qApp->getDeviceSize();
|
||||
}
|
||||
|
||||
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
|
||||
/// might be in same thread as a script that sets the reticle to invisible
|
||||
void WindowScriptingInterface::ensureReticleVisible() const {
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef hifi_WindowScriptingInterface_h
|
||||
#define hifi_WindowScriptingInterface_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtQuick/QQuickItem>
|
||||
|
@ -73,6 +75,7 @@ public slots:
|
|||
bool isPhysicsEnabled();
|
||||
bool setDisplayTexture(const QString& name);
|
||||
bool isPointOnDesktopWindow(QVariant point);
|
||||
glm::vec2 getDeviceSize() const;
|
||||
|
||||
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
|
||||
void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton);
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
bool forwardEnabled() { return _forwardEnabled; }
|
||||
bool useFeed() { return _useFeed; }
|
||||
void setUseFeed(bool useFeed) { if (_useFeed != useFeed) { _useFeed = useFeed; emit useFeedChanged(); } }
|
||||
QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL.toString(); }
|
||||
QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL().toString(); }
|
||||
|
||||
signals:
|
||||
void backEnabledChanged();
|
||||
|
|
|
@ -82,7 +82,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
|||
|
||||
// Now render the overlay components together into a single texture
|
||||
renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line
|
||||
renderAudioScope(renderArgs); // audio scope in the very back - NOTE: this is the debug audio scope, not the VU meter
|
||||
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
|
||||
renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts
|
||||
});
|
||||
|
@ -118,25 +117,6 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
|||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _qmlGeometryId);
|
||||
}
|
||||
|
||||
void ApplicationOverlay::renderAudioScope(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(app, __FUNCTION__);
|
||||
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
batch.setResourceTexture(0, textureCache->getWhiteTexture());
|
||||
int width = renderArgs->_viewport.z;
|
||||
int height = renderArgs->_viewport.w;
|
||||
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
|
||||
batch.setProjectionTransform(legacyProjection);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.resetViewTransform();
|
||||
|
||||
// Render the audio scope
|
||||
DependencyManager::get<AudioScope>()->render(renderArgs, width, height);
|
||||
}
|
||||
|
||||
void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(app, __FUNCTION__);
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ private:
|
|||
void renderStatsAndLogs(RenderArgs* renderArgs);
|
||||
void renderDomainConnectionStatusBorder(RenderArgs* renderArgs);
|
||||
void renderQmlUi(RenderArgs* renderArgs);
|
||||
void renderAudioScope(RenderArgs* renderArgs);
|
||||
void renderOverlays(RenderArgs* renderArgs);
|
||||
void buildFramebufferObject();
|
||||
|
||||
|
|
|
@ -200,6 +200,31 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Base3DOverlay.
|
||||
/**jsdoc
|
||||
* @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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*/
|
||||
QVariant Base3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "name") {
|
||||
return _name;
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
// getters
|
||||
virtual bool is3D() const override { return true; }
|
||||
|
||||
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return (uint32_t) subItems.size(); }
|
||||
|
||||
// TODO: consider implementing registration points in this class
|
||||
glm::vec3 getCenter() const { return getWorldPosition(); }
|
||||
|
||||
|
@ -59,8 +61,8 @@ public:
|
|||
|
||||
void notifyRenderVariableChange() const;
|
||||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
virtual void setProperties(const QVariantMap& properties) override;
|
||||
virtual QVariant getProperty(const QString& property) override;
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
|
|
|
@ -23,6 +23,11 @@ void Billboardable::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Billboardable.
|
||||
/**jsdoc
|
||||
* @property {boolean} isFacingAvatar - If <code>true</code>, the overlay is rotated to face the user's camera about an axis
|
||||
* parallel to the user's avatar's "up" direction.
|
||||
*/
|
||||
QVariant Billboardable::getProperty(const QString &property) {
|
||||
if (property == "isFacingAvatar") {
|
||||
return isFacingAvatar();
|
||||
|
|
|
@ -16,10 +16,7 @@
|
|||
|
||||
QString const Circle3DOverlay::TYPE = "circle3d";
|
||||
|
||||
Circle3DOverlay::Circle3DOverlay() {
|
||||
memset(&_minorTickMarksColor, 0, sizeof(_minorTickMarksColor));
|
||||
memset(&_majorTickMarksColor, 0, sizeof(_majorTickMarksColor));
|
||||
}
|
||||
Circle3DOverlay::Circle3DOverlay() {}
|
||||
|
||||
Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) :
|
||||
Planar3DOverlay(circle3DOverlay),
|
||||
|
@ -27,17 +24,21 @@ Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) :
|
|||
_endAt(circle3DOverlay->_endAt),
|
||||
_outerRadius(circle3DOverlay->_outerRadius),
|
||||
_innerRadius(circle3DOverlay->_innerRadius),
|
||||
_innerStartColor(circle3DOverlay->_innerStartColor),
|
||||
_innerEndColor(circle3DOverlay->_innerEndColor),
|
||||
_outerStartColor(circle3DOverlay->_outerStartColor),
|
||||
_outerEndColor(circle3DOverlay->_outerEndColor),
|
||||
_innerStartAlpha(circle3DOverlay->_innerStartAlpha),
|
||||
_innerEndAlpha(circle3DOverlay->_innerEndAlpha),
|
||||
_outerStartAlpha(circle3DOverlay->_outerStartAlpha),
|
||||
_outerEndAlpha(circle3DOverlay->_outerEndAlpha),
|
||||
_hasTickMarks(circle3DOverlay->_hasTickMarks),
|
||||
_majorTickMarksAngle(circle3DOverlay->_majorTickMarksAngle),
|
||||
_minorTickMarksAngle(circle3DOverlay->_minorTickMarksAngle),
|
||||
_majorTickMarksLength(circle3DOverlay->_majorTickMarksLength),
|
||||
_minorTickMarksLength(circle3DOverlay->_minorTickMarksLength),
|
||||
_majorTickMarksColor(circle3DOverlay->_majorTickMarksColor),
|
||||
_minorTickMarksColor(circle3DOverlay->_minorTickMarksColor),
|
||||
_quadVerticesID(GeometryCache::UNKNOWN_ID),
|
||||
_lineVerticesID(GeometryCache::UNKNOWN_ID),
|
||||
_majorTicksVerticesID(GeometryCache::UNKNOWN_ID),
|
||||
_minorTicksVerticesID(GeometryCache::UNKNOWN_ID)
|
||||
_minorTickMarksColor(circle3DOverlay->_minorTickMarksColor)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -80,9 +81,8 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
Q_ASSERT(args->_batch);
|
||||
auto& batch = *args->_batch;
|
||||
if (args->_shapePipeline) {
|
||||
batch.setPipeline(args->_shapePipeline->pipeline);
|
||||
}
|
||||
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, isTransparent(), false, !getIsSolid(), true);
|
||||
|
||||
batch.setModelTransform(getRenderTransform());
|
||||
|
||||
|
@ -185,11 +185,10 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
|
||||
// we just draw a line...
|
||||
if (getHasTickMarks()) {
|
||||
|
||||
if (_majorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
if (!_majorTicksVerticesID) {
|
||||
_majorTicksVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
if (_minorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
if (!_minorTicksVerticesID) {
|
||||
_minorTicksVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
|
@ -368,6 +367,98 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// Overlay's color and alpha properties are overridden. And the dimensions property is not used.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>circle3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Circle3DProperties
|
||||
*
|
||||
* @property {string} type=circle3d - Has the value <code>"circle3d"</code>. <em>Read-only.</em>
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
* <em>Not used.</em>
|
||||
*
|
||||
* @property {number} startAt=0 - The counter-clockwise angle from the overlay's x-axis that drawing starts at, in degrees.
|
||||
* @property {number} endAt=360 - The counter-clockwise angle from the overlay's x-axis that drawing ends at, in degrees.
|
||||
* @property {number} outerRadius=1 - The outer radius of the overlay, in meters. Synonym: <code>radius</code>.
|
||||
* @property {number} innerRadius=0 - The inner radius of the overlay, in meters.
|
||||
* @property {Color} color=255,255,255 - The color of the overlay. Setting this value also sets the values of
|
||||
* <code>innerStartColor</code>, <code>innerEndColor</code>, <code>outerStartColor</code>, and <code>outerEndColor</code>.
|
||||
* @property {Color} startColor - Sets the values of <code>innerStartColor</code> and <code>outerStartColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} endColor - Sets the values of <code>innerEndColor</code> and <code>outerEndColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} innerColor - Sets the values of <code>innerStartColor</code> and <code>innerEndColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} outerColor - Sets the values of <code>outerStartColor</code> and <code>outerEndColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} innerStartcolor - The color at the inner start point of the overlay. <em>Write-only.</em>
|
||||
* @property {Color} innerEndColor - The color at the inner end point of the overlay. <em>Write-only.</em>
|
||||
* @property {Color} outerStartColor - The color at the outer start point of the overlay. <em>Write-only.</em>
|
||||
* @property {Color} outerEndColor - The color at the outer end point of the overlay. <em>Write-only.</em>
|
||||
* @property {number} alpha=0.5 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>. Setting this value also sets
|
||||
* the values of <code>innerStartAlpha</code>, <code>innerEndAlpha</code>, <code>outerStartAlpha</code>, and
|
||||
* <code>outerEndAlpha</code>. Synonym: <code>Alpha</code>; <em>write-only</em>.
|
||||
* @property {number} startAlpha - Sets the values of <code>innerStartAlpha</code> and <code>outerStartAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} endAlpha - Sets the values of <code>innerEndAlpha</code> and <code>outerEndAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} innerAlpha - Sets the values of <code>innerStartAlpha</code> and <code>innerEndAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} outerAlpha - Sets the values of <code>outerStartAlpha</code> and <code>outerEndAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} innerStartAlpha=0 - The alpha at the inner start point of the overlay. <em>Write-only.</em>
|
||||
* @property {number} innerEndAlpha=0 - The alpha at the inner end point of the overlay. <em>Write-only.</em>
|
||||
* @property {number} outerStartAlpha=0 - The alpha at the outer start point of the overlay. <em>Write-only.</em>
|
||||
* @property {number} outerEndAlpha=0 - The alpha at the outer end point of the overlay. <em>Write-only.</em>
|
||||
|
||||
* @property {boolean} hasTickMarks=false - If <code>true</code>, tick marks are drawn.
|
||||
* @property {number} majorTickMarksAngle=0 - The angle between major tick marks, in degrees.
|
||||
* @property {number} minorTickMarksAngle=0 - The angle between minor tick marks, in degrees.
|
||||
* @property {number} majorTickMarksLength=0 - The length of the major tick marks, in meters. A positive value draws tick marks
|
||||
* outwards from the inner radius; a negative value draws tick marks inwards from the outer radius.
|
||||
* @property {number} minorTickMarksLength=0 - The length of the minor tick marks, in meters. A positive value draws tick marks
|
||||
* outwards from the inner radius; a negative value draws tick marks inwards from the outer radius.
|
||||
* @property {Color} majorTickMarksColor=0,0,0 - The color of the major tick marks.
|
||||
* @property {Color} minorTickMarksColor=0,0,0 - The color of the minor tick marks.
|
||||
*/
|
||||
QVariant Circle3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "startAt") {
|
||||
return _startAt;
|
||||
|
@ -384,6 +475,30 @@ QVariant Circle3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "innerRadius") {
|
||||
return _innerRadius;
|
||||
}
|
||||
if (property == "innerStartColor") {
|
||||
return xColorToVariant(_innerStartColor);
|
||||
}
|
||||
if (property == "innerEndColor") {
|
||||
return xColorToVariant(_innerEndColor);
|
||||
}
|
||||
if (property == "outerStartColor") {
|
||||
return xColorToVariant(_outerStartColor);
|
||||
}
|
||||
if (property == "outerEndColor") {
|
||||
return xColorToVariant(_outerEndColor);
|
||||
}
|
||||
if (property == "innerStartAlpha") {
|
||||
return _innerStartAlpha;
|
||||
}
|
||||
if (property == "innerEndAlpha") {
|
||||
return _innerEndAlpha;
|
||||
}
|
||||
if (property == "outerStartAlpha") {
|
||||
return _outerStartAlpha;
|
||||
}
|
||||
if (property == "outerEndAlpha") {
|
||||
return _outerEndAlpha;
|
||||
}
|
||||
if (property == "hasTickMarks") {
|
||||
return _hasTickMarks;
|
||||
}
|
||||
|
@ -409,7 +524,6 @@ QVariant Circle3DOverlay::getProperty(const QString& property) {
|
|||
return Planar3DOverlay::getProperty(property);
|
||||
}
|
||||
|
||||
|
||||
bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
|
||||
|
|
|
@ -65,22 +65,22 @@ protected:
|
|||
float _outerRadius { 1 };
|
||||
float _innerRadius { 0 };
|
||||
|
||||
xColor _innerStartColor;
|
||||
xColor _innerEndColor;
|
||||
xColor _outerStartColor;
|
||||
xColor _outerEndColor;
|
||||
float _innerStartAlpha;
|
||||
float _innerEndAlpha;
|
||||
float _outerStartAlpha;
|
||||
float _outerEndAlpha;
|
||||
xColor _innerStartColor { DEFAULT_OVERLAY_COLOR };
|
||||
xColor _innerEndColor { DEFAULT_OVERLAY_COLOR };
|
||||
xColor _outerStartColor { DEFAULT_OVERLAY_COLOR };
|
||||
xColor _outerEndColor { DEFAULT_OVERLAY_COLOR };
|
||||
float _innerStartAlpha { DEFAULT_ALPHA };
|
||||
float _innerEndAlpha { DEFAULT_ALPHA };
|
||||
float _outerStartAlpha { DEFAULT_ALPHA };
|
||||
float _outerEndAlpha { DEFAULT_ALPHA };
|
||||
|
||||
bool _hasTickMarks { false };
|
||||
float _majorTickMarksAngle { 0 };
|
||||
float _minorTickMarksAngle { 0 };
|
||||
float _majorTickMarksLength { 0 };
|
||||
float _minorTickMarksLength { 0 };
|
||||
xColor _majorTickMarksColor;
|
||||
xColor _minorTickMarksColor;
|
||||
xColor _majorTickMarksColor { DEFAULT_OVERLAY_COLOR };
|
||||
xColor _minorTickMarksColor { DEFAULT_OVERLAY_COLOR };
|
||||
gpu::Primitive _solidPrimitive { gpu::TRIANGLE_FAN };
|
||||
int _quadVerticesID { 0 };
|
||||
int _lineVerticesID { 0 };
|
||||
|
|
|
@ -72,14 +72,7 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay);
|
||||
|
||||
{
|
||||
render::Transaction transaction;
|
||||
initializeSelectionToSceneHandler(_selectionToSceneHandlers[0], "contextOverlayHighlightList", transaction);
|
||||
for (auto i = 1; i < MAX_SELECTION_COUNT; i++) {
|
||||
auto selectionName = QString("highlightList") + QString::number(i);
|
||||
initializeSelectionToSceneHandler(_selectionToSceneHandlers[i], selectionName, transaction);
|
||||
}
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
scene->enqueueTransaction(transaction);
|
||||
_selectionScriptingInterface->enableListHighlight("contextOverlayHighlightList", QVariantMap());
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -88,12 +81,6 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
_challengeOwnershipTimeoutTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
void ContextOverlayInterface::initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction) {
|
||||
handler.initialize(selectionName);
|
||||
connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &handler, &SelectionToSceneHandler::selectedItemsListChanged);
|
||||
transaction.resetSelectionHighlight(selectionName.toStdString());
|
||||
}
|
||||
|
||||
static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||
static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters
|
||||
static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims
|
||||
|
@ -302,7 +289,7 @@ void ContextOverlayInterface::openInspectionCertificate() {
|
|||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = entityProperties.getCertificateID();
|
||||
|
@ -372,7 +359,7 @@ void ContextOverlayInterface::openInspectionCertificate() {
|
|||
}
|
||||
}
|
||||
|
||||
static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/";
|
||||
static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
|
||||
|
||||
void ContextOverlayInterface::openMarketplace() {
|
||||
// lets open the tablet and go to the current item in
|
||||
|
|
|
@ -96,9 +96,6 @@ private:
|
|||
void disableEntityHighlight(const EntityItemID& entityItemID);
|
||||
|
||||
void deletingEntity(const EntityItemID& entityItemID);
|
||||
void initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction);
|
||||
|
||||
SelectionToSceneHandler _selectionToSceneHandlers[MAX_SELECTION_COUNT];
|
||||
|
||||
Q_INVOKABLE void startChallengeOwnershipTimer();
|
||||
QTimer _challengeOwnershipTimeoutTimer;
|
||||
|
|
|
@ -134,6 +134,56 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>cube</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.CubeProperties
|
||||
*
|
||||
* @property {string} type=cube - Has the value <code>"cube"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {number} borderSize - Not used.
|
||||
*/
|
||||
QVariant Cube3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "borderSize") {
|
||||
return _borderSize;
|
||||
|
|
|
@ -112,6 +112,61 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
updateGrid();
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>grid</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.GridProperties
|
||||
*
|
||||
* @property {string} type=grid - Has the value <code>"grid"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {boolean} followCamera=true - If <code>true</code>, the grid is always visible even as the camera moves to another
|
||||
* position.
|
||||
* @property {number} majorGridEvery=5 - Integer number of <code>minorGridEvery</code> intervals at which to draw a thick grid
|
||||
* line. Minimum value = <code>1</code>.
|
||||
* @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value =
|
||||
* <code>0.001</code>.
|
||||
*/
|
||||
QVariant Grid3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "followCamera") {
|
||||
return _followCamera;
|
||||
|
|
|
@ -188,6 +188,62 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of an <code>image3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Image3DProperties
|
||||
*
|
||||
* @property {string} type=image3d - Has the value <code>"image3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {boolean} isFacingAvatar - If <code>true</code>, the overlay is rotated to face the user's camera about an axis
|
||||
* parallel to the user's avatar's "up" direction.
|
||||
*
|
||||
* @property {string} url - The URL of the PNG or JPG image to display.
|
||||
* @property {Rect} subImage - The portion of the image to display. Defaults to the full image.
|
||||
* @property {boolean} emissive - If <code>true</code>, the overlay is displayed at full brightness, otherwise it is rendered
|
||||
* with scene lighting.
|
||||
*/
|
||||
QVariant Image3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "url") {
|
||||
return _url;
|
||||
|
|
|
@ -19,6 +19,29 @@
|
|||
QString const ImageOverlay::TYPE = "image";
|
||||
QUrl const ImageOverlay::URL(QString("hifi/overlays/ImageOverlay.qml"));
|
||||
|
||||
// ImageOverlay's properties are defined in the QML file specified above.
|
||||
/**jsdoc
|
||||
* These are the properties of an <code>image</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.ImageProperties
|
||||
*
|
||||
* @property {Rect} bounds - The position and size of the image display area, in pixels. <em>Write-only.</em>
|
||||
* @property {number} x - Integer left, x-coordinate value of the image display area = <code>bounds.x</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} y - Integer top, y-coordinate value of the image display area = <code>bounds.y</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} width - Integer width of the image display area = <code>bounds.width</code>. <em>Write-only.</em>
|
||||
* @property {number} height - Integer height of the image display area = <code>bounds.height</code>. <em>Write-only.</em>
|
||||
* @property {string} imageURL - The URL of the image file to display. The image is scaled to fit to the <code>bounds</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Vec2} subImage=0,0 - Integer coordinates of the top left pixel to start using image content from.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} color=0,0,0 - The color to apply over the top of the image to colorize it. <em>Write-only.</em>
|
||||
* @property {number} alpha=0.0 - The opacity of the color applied over the top of the image, <code>0.0</code> -
|
||||
* <code>1.0</code>. <em>Write-only.</em>
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* <em>Write-only.</em>
|
||||
*/
|
||||
|
||||
ImageOverlay::ImageOverlay()
|
||||
: QmlOverlay(URL) { }
|
||||
|
||||
|
|
|
@ -255,6 +255,67 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>line3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Line3DProperties
|
||||
*
|
||||
* @property {string} type=line3d - Has the value <code>"line3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Uuid} endParentID=null - The avatar, entity, or overlay that the end point of the line is parented to.
|
||||
* @property {number} endParentJointIndex=65535 - Integer value specifying the skeleton joint that the end point of the line is
|
||||
* attached to if <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
* @property {Vec3} start - The start point of the line. Synonyms: <code>startPoint</code>, <code>p1</code>, and
|
||||
* <code>position</code>.
|
||||
* @property {Vec3} end - The end point of the line. Synonyms: <code>endPoint</code> and <code>p2</code>.
|
||||
* @property {Vec3} localStart - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>start</code>. Synonym: <code>localPosition</code>.
|
||||
* @property {Vec3} localEnd - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>endParentID</code> set, otherwise the same value as <code>end</code>.
|
||||
* @property {number} length - The length of the line, in meters. This can be set after creating a line with start and end
|
||||
* points.
|
||||
* @property {number} glow=0 - If <code>glow > 0</code>, the line is rendered with a glow.
|
||||
* @property {number} lineWidth=0.02 - If <code>glow > 0</code>, this is the width of the glow, in meters.
|
||||
*/
|
||||
QVariant Line3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "start" || property == "startPoint" || property == "p1") {
|
||||
return vec3toVariant(getStart());
|
||||
|
|
|
@ -35,7 +35,8 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
|||
_modelTextures(QVariantMap()),
|
||||
_url(modelOverlay->_url),
|
||||
_updateModel(false),
|
||||
_loadPriority(modelOverlay->getLoadPriority())
|
||||
_scaleToFit(modelOverlay->_scaleToFit),
|
||||
_loadPriority(modelOverlay->_loadPriority)
|
||||
{
|
||||
_model->init();
|
||||
_model->setLoadingPriority(_loadPriority);
|
||||
|
@ -78,6 +79,12 @@ void ModelOverlay::update(float deltatime) {
|
|||
if (_model->needsFixupInScene()) {
|
||||
_model->removeFromScene(scene, transaction);
|
||||
_model->addToScene(scene, transaction);
|
||||
|
||||
auto newRenderItemIDs{ _model->fetchRenderItemIDs() };
|
||||
transaction.updateItem<Overlay>(getRenderItemID(), [newRenderItemIDs](Overlay& data) {
|
||||
auto modelOverlay = static_cast<ModelOverlay*>(&data);
|
||||
modelOverlay->setSubRenderItemIDs(newRenderItemIDs);
|
||||
});
|
||||
}
|
||||
if (_visibleDirty) {
|
||||
_visibleDirty = false;
|
||||
|
@ -103,6 +110,10 @@ bool ModelOverlay::addToScene(Overlay::Pointer overlay, const render::ScenePoint
|
|||
void ModelOverlay::removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
Volume3DOverlay::removeFromScene(overlay, scene, transaction);
|
||||
_model->removeFromScene(scene, transaction);
|
||||
transaction.updateItem<Overlay>(getRenderItemID(), [](Overlay& data) {
|
||||
auto modelOverlay = static_cast<ModelOverlay*>(&data);
|
||||
modelOverlay->clearSubRenderItemIDs();
|
||||
});
|
||||
}
|
||||
|
||||
void ModelOverlay::setVisible(bool visible) {
|
||||
|
@ -141,7 +152,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
|||
_scaleToFit = true;
|
||||
setDimensions(vec3FromVariant(dimensions));
|
||||
} else if (scale.isValid()) {
|
||||
// if "scale" property is set but "dimentions" is not.
|
||||
// if "scale" property is set but "dimensions" is not.
|
||||
// do NOT scale to fit.
|
||||
_scaleToFit = false;
|
||||
}
|
||||
|
@ -170,6 +181,10 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
|||
Q_ARG(const QVariantMap&, textureMap));
|
||||
}
|
||||
|
||||
// jointNames is read-only.
|
||||
// jointPositions is read-only.
|
||||
// jointOrientations is read-only.
|
||||
|
||||
// relative
|
||||
auto jointTranslationsValue = properties["jointTranslations"];
|
||||
if (jointTranslationsValue.canConvert(QVariant::List)) {
|
||||
|
@ -265,6 +280,75 @@ vectorType ModelOverlay::mapJoints(mapFunction<itemType> function) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
// Note: ModelOverlay overrides Volume3DOverlay's "dimensions" and "scale" properties.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>model</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.ModelProperties
|
||||
*
|
||||
* @property {string} type=sphere - Has the value <code>"model"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {string} url - The URL of the FBX or OBJ model used for the overlay.
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonym: <code>size</code>.
|
||||
* @property {Vec3} scale - The scale factor applied to the model's dimensions.
|
||||
* @property {object.<name, url>} textures - Maps the named textures in the model to the JPG or PNG images in the urls.
|
||||
* @property {Array.<string>} jointNames - The names of the joints - if any - in the model. <em>Read-only</em>
|
||||
* @property {Array.<Quat>} jointRotations - The relative rotations of the model's joints.
|
||||
* @property {Array.<Vec3>} jointTranslations - The relative translations of the model's joints.
|
||||
* @property {Array.<Quat>} jointOrientations - The absolute orientations of the model's joints, in world coordinates.
|
||||
* <em>Read-only</em>
|
||||
* @property {Array.<Vec3>} jointPositions - The absolute positions of the model's joints, in world coordinates.
|
||||
* <em>Read-only</em>
|
||||
* @property {string} animationSettings.url="" - The URL of an FBX file containing an animation to play.
|
||||
* @property {number} animationSettings.fps=0 - The frame rate (frames/sec) to play the animation at.
|
||||
* @property {number} animationSettings.firstFrame=0 - The frame to start playing at.
|
||||
* @property {number} animationSettings.lastFrame=0 - The frame to finish playing at.
|
||||
* @property {boolean} animationSettings.running=false - Whether or not the animation is playing.
|
||||
* @property {boolean} animationSettings.loop=false - Whether or not the animation should repeat in a loop.
|
||||
* @property {boolean} animationSettings.hold=false - Whether or not when the animation finishes, the rotations and
|
||||
* translations of the last frame played should be maintained.
|
||||
* @property {boolean} animationSettings.allowTranslation=false - Whether or not translations contained in the animation should
|
||||
* be played.
|
||||
*/
|
||||
QVariant ModelOverlay::getProperty(const QString& property) {
|
||||
if (property == "url") {
|
||||
return _url.toString();
|
||||
|
@ -528,3 +612,19 @@ void ModelOverlay::copyAnimationJointDataToModel(QVector<JointData> jointsData)
|
|||
_updateModel = true;
|
||||
}
|
||||
|
||||
void ModelOverlay::clearSubRenderItemIDs() {
|
||||
_subRenderItemIDs.clear();
|
||||
}
|
||||
|
||||
void ModelOverlay::setSubRenderItemIDs(const render::ItemIDs& ids) {
|
||||
_subRenderItemIDs = ids;
|
||||
}
|
||||
|
||||
uint32_t ModelOverlay::fetchMetaSubItems(render::ItemIDs& subItems) const {
|
||||
if (_model) {
|
||||
auto metaSubItems = _subRenderItemIDs;
|
||||
subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end());
|
||||
return (uint32_t)metaSubItems.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,12 @@ public:
|
|||
|
||||
virtual void update(float deltatime) override;
|
||||
virtual void render(RenderArgs* args) override {};
|
||||
|
||||
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override;
|
||||
|
||||
void clearSubRenderItemIDs();
|
||||
void setSubRenderItemIDs(const render::ItemIDs& ids);
|
||||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
|
@ -74,9 +80,11 @@ private:
|
|||
ModelPointer _model;
|
||||
QVariantMap _modelTextures;
|
||||
|
||||
render::ItemIDs _subRenderItemIDs;
|
||||
|
||||
QUrl _url;
|
||||
bool _updateModel = { false };
|
||||
bool _scaleToFit = { false };
|
||||
bool _updateModel { false };
|
||||
bool _scaleToFit { false };
|
||||
float _loadPriority { 0.0f };
|
||||
|
||||
AnimationPointer _animation;
|
||||
|
@ -87,7 +95,7 @@ private:
|
|||
bool _animationRunning { false };
|
||||
bool _animationLoop { false };
|
||||
float _animationFirstFrame { 0.0f };
|
||||
float _animationLastFrame = { 0.0f };
|
||||
float _animationLastFrame { 0.0f };
|
||||
bool _animationHold { false };
|
||||
bool _animationAllowTranslation { false };
|
||||
uint64_t _lastAnimated { 0 };
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
#include "Application.h"
|
||||
|
||||
static const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||
static const float DEFAULT_ALPHA = 0.7f;
|
||||
const xColor Overlay::DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||
const float Overlay::DEFAULT_ALPHA = 0.7f;
|
||||
|
||||
Overlay::Overlay() :
|
||||
_renderItemID(render::Item::INVALID_ITEM_ID),
|
||||
|
@ -101,6 +101,27 @@ void Overlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Overlay.
|
||||
/**jsdoc
|
||||
* @property {string} type=TODO - Has the value <code>"TODO"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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") {
|
||||
return QVariant(getType());
|
||||
|
|
|
@ -53,6 +53,8 @@ public:
|
|||
|
||||
virtual const render::ShapeKey getShapeKey() { return render::ShapeKey::Builder::ownPipeline(); }
|
||||
|
||||
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const { return 0; }
|
||||
|
||||
// getters
|
||||
virtual QString getType() const = 0;
|
||||
virtual bool is3D() const = 0;
|
||||
|
@ -120,6 +122,9 @@ protected:
|
|||
|
||||
unsigned int _stackOrder { 0 };
|
||||
|
||||
static const xColor DEFAULT_OVERLAY_COLOR;
|
||||
static const float DEFAULT_ALPHA;
|
||||
|
||||
private:
|
||||
OverlayID _overlayID; // only used for non-3d overlays
|
||||
};
|
||||
|
@ -130,6 +135,7 @@ namespace render {
|
|||
template <> int payloadGetLayer(const Overlay::Pointer& overlay);
|
||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args);
|
||||
template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay);
|
||||
template <> uint32_t metaFetchMetaSubItems(const Overlay::Pointer& overlay, ItemIDs& subItems);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(OverlayID);
|
||||
|
|
|
@ -23,6 +23,15 @@ AABox Overlay2D::getBounds() const {
|
|||
glm::vec3(_bounds.width(), _bounds.height(), 0.01f));
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Overlay2D.
|
||||
// QmlOverlay-derived classes don't support getProperty().
|
||||
/**jsdoc
|
||||
* @property {Rect} bounds - The position and size of the rectangle. <em>Write-only.</em>
|
||||
* @property {number} x - Integer left, x-coordinate value = <code>bounds.x</code>. <em>Write-only.</em>
|
||||
* @property {number} y - Integer top, y-coordinate value = <code>bounds.y</code>. <em>Write-only.</em>
|
||||
* @property {number} width - Integer width of the rectangle = <code>bounds.width</code>. <em>Write-only.</em>
|
||||
* @property {number} height - Integer height of the rectangle = <code>bounds.height</code>. <em>Write-only.</em>
|
||||
*/
|
||||
void Overlay2D::setProperties(const QVariantMap& properties) {
|
||||
Overlay::setProperties(properties);
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ public:
|
|||
|
||||
virtual bool is3D() const override { return false; }
|
||||
|
||||
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return 1; }
|
||||
|
||||
// getters
|
||||
int getX() const { return _bounds.x(); }
|
||||
int getY() const { return _bounds.y(); }
|
||||
|
|
|
@ -172,6 +172,63 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties)
|
|||
|
||||
Overlay::Pointer thisOverlay = nullptr;
|
||||
|
||||
/**jsdoc
|
||||
* <p>An overlay may be one of the following types:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>2D/3D</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>circle3d</code></td><td>3D</td><td>A circle.</td></tr>
|
||||
* <tr><td><code>cube</code></td><td>3D</td><td>A cube. Can also use a <code>shape</code> overlay to create a
|
||||
* cube.</td></tr>
|
||||
* <tr><td><code>grid</code></td><td>3D</td><td>A grid of lines in a plane.</td></tr>
|
||||
* <tr><td><code>image</code></td><td>2D</td><td>An image. Synonym: <code>billboard</code>.</td></tr>
|
||||
* <tr><td><code>image3d</code></td><td>3D</td><td>An image.</td></tr>
|
||||
* <tr><td><code>line3d</code></td><td>3D</td><td>A line.</td></tr>
|
||||
* <tr><td><code>model</code></td><td>3D</td><td>A model.</td></tr>
|
||||
* <tr><td><code>rectangle</code></td><td>2D</td><td>A rectangle.</td></tr>
|
||||
* <tr><td><code>rectangle3d</code></td><td>3D</td><td>A rectangle.</td></tr>
|
||||
* <tr><td><code>shape</code></td><td>3D</td><td>A geometric shape, such as a cube, sphere, or cylinder.</td></tr>
|
||||
* <tr><td><code>sphere</code></td><td>3D</td><td>A sphere. Can also use a <code>shape</code> overlay to create a
|
||||
* sphere.</td></tr>
|
||||
* <tr><td><code>text</code></td><td>2D</td><td>Text.</td></tr>
|
||||
* <tr><td><code>text3d</code></td><td>3D</td><td>Text.</td></tr>
|
||||
* <tr><td><code>web3d</code></td><td>3D</td><td>Web content.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* <p>2D overlays are rendered on the display surface in desktop mode and on the HUD surface in HMD mode. 3D overlays are
|
||||
* rendered at a position and orientation in-world.<p>
|
||||
* <p>Each overlay type has different {@link Overlays.OverlayProperties|OverlayProperties}.</p>
|
||||
* @typedef {string} Overlays.OverlayType
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* <p>Different overlay types have different properties:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>{@link Overlays.OverlayType|OverlayType}</th><th>Overlay Properties</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>circle3d</code></td><td>{@link Overlays.Circle3DProperties|Circle3DProperties}</td></tr>
|
||||
* <tr><td><code>cube</code></td><td>{@link Overlays.CubeProperties|CubeProperties}</td></tr>
|
||||
* <tr><td><code>grid</code></td><td>{@link Overlays.GridProperties|GridProperties}</td></tr>
|
||||
* <tr><td><code>image</code></td><td>{@link Overlays.ImageProperties|ImageProperties}</td></tr>
|
||||
* <tr><td><code>image3d</code></td><td>{@link Overlays.Image3DProperties|Image3DProperties}</td></tr>
|
||||
* <tr><td><code>line3d</code></td><td>{@link Overlays.Line3DProperties|Line3DProperties}</td></tr>
|
||||
* <tr><td><code>model</code></td><td>{@link Overlays.ModelProperties|ModelProperties}</td></tr>
|
||||
* <tr><td><code>rectangle</code></td><td>{@link Overlays.RectangleProperties|RectangleProperties}</td></tr>
|
||||
* <tr><td><code>rectangle3d</code></td><td>{@link Overlays.Rectangle3DProperties|Rectangle3DProperties}</td></tr>
|
||||
* <tr><td><code>shape</code></td><td>{@link Overlays.ShapeProperties|ShapeProperties}</td></tr>
|
||||
* <tr><td><code>sphere</code></td><td>{@link Overlays.SphereProperties|SphereProperties}</td></tr>
|
||||
* <tr><td><code>text</code></td><td>{@link Overlays.TextProperties|TextProperties}</td></tr>
|
||||
* <tr><td><code>text3d</code></td><td>{@link Overlays.Text3DProperties|Text3DProperties}</td></tr>
|
||||
* <tr><td><code>web3d</code></td><td>{@link Overlays.Web3DProperties|Web3DProperties}</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {object} Overlays.OverlayProperties
|
||||
*/
|
||||
|
||||
if (type == ImageOverlay::TYPE) {
|
||||
thisOverlay = Overlay::Pointer(new ImageOverlay(), [](Overlay* ptr) { ptr->deleteLater(); });
|
||||
} else if (type == Image3DOverlay::TYPE || type == "billboard") { // "billboard" for backwards compatibility
|
||||
|
|
|
@ -45,12 +45,13 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro
|
|||
const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
|
||||
|
||||
/**jsdoc
|
||||
* @typedef Overlays.RayToOverlayIntersectionResult
|
||||
* @property {bool} intersects True if the PickRay intersected with a 3D overlay.
|
||||
* @property {Overlays.OverlayID} overlayID The ID of the overlay that was intersected with.
|
||||
* @property {float} distance The distance from the PickRay origin to the intersection point.
|
||||
* @property {Vec3} surfaceNormal The normal of the surface that was intersected with.
|
||||
* @property {Vec3} intersection The point at which the PickRay intersected with the overlay.
|
||||
* @typedef {object} Overlays.RayToOverlayIntersectionResult
|
||||
* @property {boolean} intersects - <code>true</code> if the {@link PickRay} intersected with a 3D overlay, otherwise
|
||||
* <code>false</code>.
|
||||
* @property {Uuid} overlayID - The UUID of the overlay that was intersected.
|
||||
* @property {number} distance - The distance from the {@link PickRay} origin to the intersection point.
|
||||
* @property {Vec3} surfaceNormal - The normal of the overlay surface at the intersection point.
|
||||
* @property {Vec3} intersection - The position of the intersection point.
|
||||
*/
|
||||
class RayToOverlayIntersectionResult {
|
||||
public:
|
||||
|
@ -70,13 +71,11 @@ QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine,
|
|||
void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value);
|
||||
|
||||
/**jsdoc
|
||||
* @typedef {int} Overlays.OverlayID
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
*
|
||||
* Overlays namespace...
|
||||
* The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to
|
||||
* yourself and that aren't persisted to the domain. They are used for UI.
|
||||
* @namespace Overlays
|
||||
* @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus.
|
||||
* If no overlay is set, get returns <code>null</code>; set to <code>null</code> to clear keyboard focus.
|
||||
*/
|
||||
|
||||
class Overlays : public QObject {
|
||||
|
@ -111,100 +110,246 @@ public:
|
|||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Add an overlays to the scene. The properties specified will depend
|
||||
* on the type of overlay that is being created.
|
||||
*
|
||||
* Add an overlay to the scene.
|
||||
* @function Overlays.addOverlay
|
||||
* @param {string} type The type of the overlay to add.
|
||||
* @param {Overlays.OverlayProperties} The properties of the overlay that you want to add.
|
||||
* @return {Overlays.OverlayID} The ID of the newly created overlay.
|
||||
* @param {Overlays.OverlayType} type - The type of the overlay to add.
|
||||
* @param {Overlays.OverlayProperties} properties - The properties of the overlay to add.
|
||||
* @returns {Uuid} The ID of the newly created overlay.
|
||||
* @example <caption>Add a cube overlay in front of your avatar.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*/
|
||||
OverlayID addOverlay(const QString& type, const QVariant& properties);
|
||||
|
||||
/**jsdoc
|
||||
* Create a clone of an existing overlay.
|
||||
*
|
||||
* @function Overlays.cloneOverlay
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to clone.
|
||||
* @return {Overlays.OverlayID} The ID of the new overlay.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to clone.
|
||||
* @returns {Uuid} The ID of the new overlay.
|
||||
* @example <caption>Add an overlay in front of your avatar, clone it, and move the clone to be above the
|
||||
* original.</caption>
|
||||
* var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 }));
|
||||
* var original = Overlays.addOverlay("cube", {
|
||||
* position: position,
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*
|
||||
* var clone = Overlays.cloneOverlay(original);
|
||||
* Overlays.editOverlay(clone, {
|
||||
* position: Vec3.sum({ x: 0, y: 0.5, z: 0}, position)
|
||||
* });
|
||||
*/
|
||||
OverlayID cloneOverlay(OverlayID id);
|
||||
|
||||
/**jsdoc
|
||||
* Edit an overlay's properties.
|
||||
*
|
||||
* @function Overlays.editOverlay
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to edit.
|
||||
* @return {bool} `true` if the overlay was found and edited, otherwise false.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to edit.
|
||||
* @param {Overlays.OverlayProperties} properties - The properties changes to make.
|
||||
* @returns {boolean} <code>true</code> if the overlay was found and edited, otherwise <code>false</code>.
|
||||
* @example <caption>Add an overlay in front of your avatar then change its color.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*
|
||||
* var success = Overlays.editOverlay(overlay, {
|
||||
* color: { red: 255, green: 0, blue: 0 }
|
||||
* });
|
||||
* print("Success: " + success);
|
||||
*/
|
||||
bool editOverlay(OverlayID id, const QVariant& properties);
|
||||
|
||||
/// edits an overlay updating only the included properties, will return the identified OverlayID in case of
|
||||
/// successful edit, if the input id is for an unknown overlay this function will have no effect
|
||||
/**jsdoc
|
||||
* Edit multiple overlays' properties.
|
||||
* @function Overlays.editOverlays
|
||||
* @param propertiesById {object.<Uuid, Overlays.OverlayProperties>} - An object with overlay IDs as keys and
|
||||
* {@link Overlays.OverlayProperties|OverlayProperties} edits to make as values.
|
||||
* @returns {boolean} <code>true</code> if all overlays were found and edited, otherwise <code>false</code> (some may have
|
||||
* been found and edited).
|
||||
* @example <caption>Create two overlays in front of your avatar then change their colors.</caption>
|
||||
* var overlayA = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var overlayB = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*
|
||||
* var overlayEdits = {};
|
||||
* overlayEdits[overlayA] = { color: { red: 255, green: 0, blue: 0 } };
|
||||
* overlayEdits[overlayB] = { color: { red: 0, green: 255, blue: 0 } };
|
||||
* var success = Overlays.editOverlays(overlayEdits);
|
||||
* print("Success: " + success);
|
||||
*/
|
||||
bool editOverlays(const QVariant& propertiesById);
|
||||
|
||||
/**jsdoc
|
||||
* Delete an overlay.
|
||||
*
|
||||
* @function Overlays.deleteOverlay
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to delete.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to delete.
|
||||
* @example <caption>Create an overlay in front of your avatar then delete it.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay: " + overlay);
|
||||
* Overlays.deleteOverlay(overlay);
|
||||
*/
|
||||
void deleteOverlay(OverlayID id);
|
||||
|
||||
/**jsdoc
|
||||
* Get the type of an overlay.
|
||||
*
|
||||
* @function Overlays.getOverlayType
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to get the type of.
|
||||
* @return {string} The type of the overlay if found, otherwise the empty string.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to get the type of.
|
||||
* @returns {Overlays.OverlayType} The type of the overlay if found, otherwise an empty string.
|
||||
* @example <caption>Create an overlay in front of your avatar then get and report its type.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var type = Overlays.getOverlayType(overlay);
|
||||
* print("Type: " + type);
|
||||
*/
|
||||
QString getOverlayType(OverlayID overlayId);
|
||||
|
||||
/**jsdoc
|
||||
* Get the overlay Script object.
|
||||
*
|
||||
* @function Overlays.getOverlayObject
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to get the script object of.
|
||||
* @return {Object} The script object for the overlay if found.
|
||||
*/
|
||||
* Get the overlay script object.
|
||||
* @function Overlays.getOverlayObject
|
||||
* @param {Uuid} overlayID - The ID of the overlay to get the script object of.
|
||||
* @returns {object} The script object for the overlay if found.
|
||||
*/
|
||||
QObject* getOverlayObject(OverlayID id);
|
||||
|
||||
/**jsdoc
|
||||
* Get the ID of the overlay at a particular point on the HUD/screen.
|
||||
*
|
||||
* Get the ID of the 2D overlay at a particular point on the screen or HUD.
|
||||
* @function Overlays.getOverlayAtPoint
|
||||
* @param {Vec2} point The point to check for an overlay.
|
||||
* @return {Overlays.OverlayID} The ID of the overlay at the point specified.
|
||||
* If no overlay is found, `0` will be returned.
|
||||
* @param {Vec2} point - The point to check for an overlay.
|
||||
* @returns {Uuid} The ID of the 2D overlay at the specified point if found, otherwise <code>null</code>.
|
||||
* @example <caption>Create a 2D overlay and add an event function that reports the overlay clicked on, if any.</caption>
|
||||
* var overlay = Overlays.addOverlay("rectangle", {
|
||||
* bounds: { x: 100, y: 100, width: 200, height: 100 },
|
||||
* color: { red: 255, green: 255, blue: 255 }
|
||||
* });
|
||||
* print("Created: " + overlay);
|
||||
*
|
||||
* Controller.mousePressEvent.connect(function (event) {
|
||||
* var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
* print("Clicked: " + overlay);
|
||||
* });
|
||||
*/
|
||||
OverlayID getOverlayAtPoint(const glm::vec2& point);
|
||||
|
||||
/**jsdoc
|
||||
* Get the value of a an overlay's property.
|
||||
*
|
||||
* Get the value of a 3D overlay's property.
|
||||
* @function Overlays.getProperty
|
||||
* @param {Overlays.OverlayID} The ID of the overlay to get the property of.
|
||||
* @param {string} The name of the property to get the value of.
|
||||
* @return {Object} The value of the property. If the overlay or the property could
|
||||
* not be found, `undefined` will be returned.
|
||||
* @param {Uuid} overlayID - The ID of the overlay. <em>Must be for a 3D {@link Overlays.OverlayType|OverlayType}.</em>
|
||||
* @param {string} property - The name of the property value to get.
|
||||
* @returns {object} The value of the property if the 3D overlay and property can be found, otherwise
|
||||
* <code>undefined</code>.
|
||||
* @example <caption>Create an overlay in front of your avatar then report its alpha property value.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var alpha = Overlays.getProperty(overlay, "alpha");
|
||||
* print("Overlay alpha: " + alpha);
|
||||
*/
|
||||
OverlayPropertyResult getProperty(OverlayID id, const QString& property);
|
||||
|
||||
/**jsdoc
|
||||
* Get the values of an overlay's properties.
|
||||
* @function Overlays.getProperties
|
||||
* @param {Uuid} overlayID - The ID of the overlay.
|
||||
* @param {Array.<string>} properties - An array of names of properties to get the values of.
|
||||
* @returns {Overlays.OverlayProperties} The values of valid properties if the overlay can be found, otherwise
|
||||
* <code>undefined</code>.
|
||||
* @example <caption>Create an overlay in front of your avatar then report some of its properties.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var properties = Overlays.getProperties(overlay, ["color", "alpha", "grabbable"]);
|
||||
* print("Overlay properties: " + JSON.stringify(properties));
|
||||
*/
|
||||
OverlayPropertyResult getProperties(const OverlayID& id, const QStringList& properties);
|
||||
|
||||
/**jsdoc
|
||||
* Get the values of multiple overlays' properties.
|
||||
* @function Overlays.getOverlaysProperties
|
||||
* @param propertiesById {object.<Uuid, Array.<string>>} - An object with overlay IDs as keys and arrays of the names of
|
||||
* properties to get for each as values.
|
||||
* @returns {object.<Uuid, Overlays.OverlayProperties>} An object with overlay IDs as keys and
|
||||
* {@link Overlays.OverlayProperties|OverlayProperties} as values.
|
||||
* @example <caption>Create two cube overlays in front of your avatar then get some of their properties.</caption>
|
||||
* var overlayA = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var overlayB = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var propertiesToGet = {};
|
||||
* propertiesToGet[overlayA] = ["color", "alpha"];
|
||||
* propertiesToGet[overlayB] = ["dimensions"];
|
||||
* var properties = Overlays.getOverlaysProperties(propertiesToGet);
|
||||
* print("Overlays properties: " + JSON.stringify(properties));
|
||||
*/
|
||||
OverlayPropertyResult getOverlaysProperties(const QVariant& overlaysProperties);
|
||||
|
||||
/*jsdoc
|
||||
* Find the closest 3D overlay hit by a pick ray.
|
||||
*
|
||||
/**jsdoc
|
||||
* Find the closest 3D overlay intersected by a {@link PickRay}.
|
||||
* @function Overlays.findRayIntersection
|
||||
* @param {PickRay} The PickRay to use for finding overlays.
|
||||
* @param {bool} Unused; Exists to match Entity interface
|
||||
* @param {List of Overlays.OverlayID} Whitelist for intersection test.
|
||||
* @param {List of Overlays.OverlayID} Blacklist for intersection test.
|
||||
* @param {bool} Unused; Exists to match Entity interface
|
||||
* @param {bool} Unused; Exists to match Entity interface
|
||||
* @return {Overlays.RayToOverlayIntersectionResult} The result of the ray cast.
|
||||
* @param {PickRay} pickRay - The PickRay to use for finding overlays.
|
||||
* @param {boolean} [precisionPicking=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {Array.<Uuid>} [overlayIDsToInclude=[]] - Whitelist for intersection test. If empty then the result isn't limited
|
||||
* to overlays in the list.
|
||||
* @param {Array.<Uuid>} [overlayIDsToExclude=[]] - Blacklist for intersection test. If empty then the result doesn't
|
||||
* exclude overlays in the list.
|
||||
* @param {boolean} [visibleOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {boolean} [collidableOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by <code>pickRay</code>, taking
|
||||
* into account <code>overlayIDsToInclude</code> and <code>overlayIDsToExclude</code> if they're not empty.
|
||||
* @example <caption>Create a cube overlay in front of your avatar. Report 3D overlay intersection details for mouse
|
||||
* clicks.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*
|
||||
* Controller.mousePressEvent.connect(function (event) {
|
||||
* var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
* var intersection = Overlays.findRayIntersection(pickRay);
|
||||
* print("Intersection: " + JSON.stringify(intersection));
|
||||
* });
|
||||
*/
|
||||
RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray,
|
||||
bool precisionPicking = false,
|
||||
|
@ -213,61 +358,113 @@ public slots:
|
|||
bool visibleOnly = false,
|
||||
bool collidableOnly = false);
|
||||
|
||||
// Same as above but with QVectors
|
||||
// TODO: Apart from the name, this function signature on JavaScript is identical to that of findRayIntersection() and should
|
||||
// probably be removed from the JavaScript API so as not to cause confusion.
|
||||
/**jsdoc
|
||||
* Find the closest 3D overlay intersected by a {@link PickRay}.
|
||||
* @function Overlays.findRayIntersectionVector
|
||||
* @deprecated Use {@link Overlays.findRayIntersection} instead; it has identical parameters and results.
|
||||
* @param {PickRay} pickRay - The PickRay to use for finding overlays.
|
||||
* @param {boolean} [precisionPicking=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {Array.<Uuid>} [overlayIDsToInclude=[]] - Whitelist for intersection test. If empty then the result isn't limited
|
||||
* to overlays in the list.
|
||||
* @param {Array.<Uuid>} [overlayIDsToExclude=[]] - Blacklist for intersection test. If empty then the result doesn't
|
||||
* exclude overlays in the list.
|
||||
* @param {boolean} [visibleOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {boolean} [collidableOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by <code>pickRay</code>, taking
|
||||
* into account <code>overlayIDsToInclude</code> and <code>overlayIDsToExclude</code> if they're not empty.
|
||||
*/
|
||||
RayToOverlayIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking,
|
||||
const QVector<OverlayID>& overlaysToInclude,
|
||||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
bool visibleOnly = false, bool collidableOnly = false);
|
||||
|
||||
/**jsdoc
|
||||
* Return a list of 3d overlays with bounding boxes that touch the given sphere
|
||||
*
|
||||
* Return a list of 3D overlays with bounding boxes that touch a search sphere.
|
||||
* @function Overlays.findOverlays
|
||||
* @param {Vec3} center the point to search from.
|
||||
* @param {float} radius search radius
|
||||
* @return {Overlays.OverlayID[]} list of overlays withing the radius
|
||||
* @param {Vec3} center - The center of the search sphere.
|
||||
* @param {number} radius - The radius of the search sphere.
|
||||
* @returns {Uuid[]} An array of overlay IDs with bounding boxes that touch a search sphere.
|
||||
* @example <caption>Create two cube overlays in front of your avatar then search for overlays near your avatar.</caption>
|
||||
* var overlayA = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay A: " + overlayA);
|
||||
* var overlayB = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay B: " + overlayB);
|
||||
*
|
||||
* var overlaysFound = Overlays.findOverlays(MyAvatar.position, 5.0);
|
||||
* print("Overlays found: " + JSON.stringify(overlaysFound));
|
||||
*/
|
||||
QVector<QUuid> findOverlays(const glm::vec3& center, float radius);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether an overlay's assets have been loaded. For example, if the
|
||||
* overlay is an "image" overlay, this will indicate whether the its image
|
||||
* has loaded.
|
||||
* Check whether an overlay's assets have been loaded. For example, for an <code>image</code> overlay the result indicates
|
||||
* whether its image has been loaded.
|
||||
* @function Overlays.isLoaded
|
||||
* @param {Overlays.OverlayID} The ID of the overlay to check.
|
||||
* @return {bool} `true` if the overlay's assets have been loaded, otherwise `false`.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to check.
|
||||
* @returns {boolean} <code>true</code> if the overlay's assets have been loaded, otherwise <code>false</code>.
|
||||
* @example <caption>Create an image overlay and report whether its image is loaded after 1s.</caption>
|
||||
* var overlay = Overlays.addOverlay("image", {
|
||||
* bounds: { x: 100, y: 100, width: 200, height: 200 },
|
||||
* imageURL: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png"
|
||||
* });
|
||||
* Script.setTimeout(function () {
|
||||
* var isLoaded = Overlays.isLoaded(overlay);
|
||||
* print("Image loaded: " + isLoaded);
|
||||
* }, 1000);
|
||||
*/
|
||||
bool isLoaded(OverlayID id);
|
||||
|
||||
/**jsdoc
|
||||
* Calculates the size of the given text in the specified overlay if it is a text overlay.
|
||||
* If it is a 2D text overlay, the size will be in pixels.
|
||||
* If it is a 3D text overlay, the size will be in meters.
|
||||
*
|
||||
* @function Overlays.textSize
|
||||
* @param {Overlays.OverlayID} The ID of the overlay to measure.
|
||||
* @param {string} The string to measure.
|
||||
* @return {Vec2} The size of the text.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to use for calculation.
|
||||
* @param {string} text - The string to calculate the size of.
|
||||
* @returns {Size} The size of the <code>text</code> if the overlay is a text overlay, otherwise
|
||||
* <code>{ height: 0, width : 0 }</code>. If the overlay is a 2D overlay, the size is in pixels; if the overlay is a 3D
|
||||
* overlay, the size is in meters.
|
||||
* @example <caption>Calculate the size of "hello" in a 3D text overlay.</caption>
|
||||
* var overlay = Overlays.addOverlay("text3d", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -2 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* text: "hello",
|
||||
* lineHeight: 0.2
|
||||
* });
|
||||
* var textSize = Overlays.textSize(overlay, "hello");
|
||||
* print("Size of \"hello\": " + JSON.stringify(textSize));
|
||||
*/
|
||||
QSizeF textSize(OverlayID id, const QString& text);
|
||||
|
||||
/**jsdoc
|
||||
* Get the width of the virtual 2D HUD.
|
||||
*
|
||||
* Get the width of the window or HUD.
|
||||
* @function Overlays.width
|
||||
* @return {float} The width of the 2D HUD.
|
||||
* @returns {number} The width, in pixels, of the Interface window if in desktop mode or the HUD if in HMD mode.
|
||||
*/
|
||||
float width();
|
||||
|
||||
/**jsdoc
|
||||
* Get the height of the virtual 2D HUD.
|
||||
*
|
||||
* Get the height of the window or HUD.
|
||||
* @function Overlays.height
|
||||
* @return {float} The height of the 2D HUD.
|
||||
* @returns {number} The height, in pixels, of the Interface window if in desktop mode or the HUD if in HMD mode.
|
||||
*/
|
||||
float height();
|
||||
|
||||
/// return true if there is an overlay with that id else false
|
||||
/**jsdoc
|
||||
* Check if there is an overlay of a given ID.
|
||||
* @function Overlays.isAddedOverly
|
||||
* @param {Uuid} overlayID - The ID to check.
|
||||
* @returns {boolean} <code>true</code> if an overlay with the given ID exists, <code>false</code> otherwise.
|
||||
*/
|
||||
bool isAddedOverlay(OverlayID id);
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
|
@ -294,43 +491,237 @@ public slots:
|
|||
|
||||
#endif
|
||||
|
||||
/**jsdoc
|
||||
* Generate a mouse press event on an overlay.
|
||||
* @function Overlays.sendMousePressOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a mouse press event on.
|
||||
* @param {PointerEvent} event - The mouse press event details.
|
||||
* @example <caption>Create a 2D rectangle overlay plus a 3D cube overlay and generate mousePressOnOverlay events for the 2D
|
||||
* overlay.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("3D overlay: " + overlay);
|
||||
*
|
||||
* var overlay = Overlays.addOverlay("rectangle", {
|
||||
* bounds: { x: 100, y: 100, width: 200, height: 100 },
|
||||
* color: { red: 255, green: 255, blue: 255 }
|
||||
* });
|
||||
* print("2D overlay: " + overlay);
|
||||
*
|
||||
* // Overlays.mousePressOnOverlay by default applies only to 3D overlays.
|
||||
* Overlays.mousePressOnOverlay.connect(function(overlayID, event) {
|
||||
* print("Clicked: " + overlayID);
|
||||
* });
|
||||
*
|
||||
* Controller.mousePressEvent.connect(function (event) {
|
||||
* // Overlays.getOverlayAtPoint applies only to 2D overlays.
|
||||
* var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
* if (overlay) {
|
||||
* Overlays.sendMousePressOnOverlay(overlay, {
|
||||
* type: "press",
|
||||
* id: 0,
|
||||
* pos2D: event
|
||||
* });
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
void sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a mouse release event on an overlay.
|
||||
* @function Overlays.sendMouseReleaseOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a mouse release event on.
|
||||
* @param {PointerEvent} event - The mouse release event details.
|
||||
*/
|
||||
void sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a mouse move event on an overlay.
|
||||
* @function Overlays.sendMouseMoveOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a mouse move event on.
|
||||
* @param {PointerEvent} event - The mouse move event details.
|
||||
*/
|
||||
void sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a hover enter event on an overlay.
|
||||
* @function Overlays.sendHoverEnterOverlay
|
||||
* @param {Uuid} id - The ID of the overlay to generate a hover enter event on.
|
||||
* @param {PointerEvent} event - The hover enter event details.
|
||||
*/
|
||||
void sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a hover over event on an overlay.
|
||||
* @function Overlays.sendHoverOverOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a hover over event on.
|
||||
* @param {PointerEvent} event - The hover over event details.
|
||||
*/
|
||||
void sendHoverOverOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a hover leave event on an overlay.
|
||||
* @function Overlays.sendHoverLeaveOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a hover leave event on.
|
||||
* @param {PointerEvent} event - The hover leave event details.
|
||||
*/
|
||||
void sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Get the ID of the Web3D overlay that has keyboard focus.
|
||||
* @function Overlays.getKeyboardFocusOverlay
|
||||
* @returns {Uuid} The ID of the {@link Overlays.OverlayType|web3d} overlay that has focus, if any, otherwise
|
||||
* <code>null</code>.
|
||||
*/
|
||||
OverlayID getKeyboardFocusOverlay();
|
||||
void setKeyboardFocusOverlay(const OverlayID& id);
|
||||
|
||||
void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
/**jsdoc
|
||||
* Set the Web3D overlay that has keyboard focus.
|
||||
* @function Overlays.setKeyboardFocusOverlay
|
||||
* @param {Uuid} overlayID - The ID of the {@link Overlays.OverlayType|web3d} overlay to set keyboard focus to. Use
|
||||
* {@link Uuid|Uuid.NULL} or <code>null</code> to unset keyboard focus from an overlay.
|
||||
*/
|
||||
void setKeyboardFocusOverlay(const OverlayID& id);
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* Emitted when an overlay is deleted
|
||||
*
|
||||
* Triggered when an overlay is deleted.
|
||||
* @function Overlays.overlayDeleted
|
||||
* @param {OverlayID} The ID of the overlay that was deleted.
|
||||
* @param {Uuid} overlayID - The ID of the overlay that was deleted.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Create an overlay then delete it after 1s.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay: " + overlay);
|
||||
*
|
||||
* Overlays.overlayDeleted.connect(function(overlayID) {
|
||||
* print("Deleted: " + overlayID);
|
||||
* });
|
||||
* Script.setTimeout(function () {
|
||||
* Overlays.deleteOverlay(overlay);
|
||||
* }, 1000);
|
||||
*/
|
||||
void overlayDeleted(OverlayID id);
|
||||
void panelDeleted(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).
|
||||
* @function Overlays.mousePressOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse press event occurred on.
|
||||
* @param {PointerEvent} event - The mouse press event details.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Create a cube overlay in front of your avatar and report mouse clicks on it.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("My overlay: " + overlay);
|
||||
*
|
||||
* Overlays.mousePressOnOverlay.connect(function(overlayID, event) {
|
||||
* if (overlayID === overlay) {
|
||||
* print("Clicked on my overlay");
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse double press event occurs on an overlay. Only occurs for 3D overlays.
|
||||
* @function Overlays.mouseDoublePressOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse double press event occurred on.
|
||||
* @param {PointerEvent} event - The mouse double press event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseDoublePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse release event occurs on an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendMouseReleaseOnOverlay|sendMouseReleaseOnOverlay} for a 2D overlay).
|
||||
* @function Overlays.mouseReleaseOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse release event occurred on.
|
||||
* @param {PointerEvent} event - The mouse release event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse move event occurs on an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendMouseMoveOnOverlay|sendMouseMoveOnOverlay} for a 2D overlay).
|
||||
* @function Overlays.mouseMoveOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse moved event occurred on.
|
||||
* @param {PointerEvent} event - The mouse move event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse press event occurs on something other than a 3D overlay.
|
||||
* @function Overlays.mousePressOffOverlay
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mousePressOffOverlay();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse double press event occurs on something other than a 3D overlay.
|
||||
* @function Overlays.mouseDoublePressOffOverlay
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseDoublePressOffOverlay();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse cursor starts hovering over an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendHoverEnterOverlay|sendHoverEnterOverlay} for a 2D overlay).
|
||||
* @function Overlays.hoverEnterOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse moved event occurred on.
|
||||
* @param {PointerEvent} event - The mouse move event details.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Create a cube overlay in front of your avatar and report when you start hovering your mouse over
|
||||
* it.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay: " + overlay);
|
||||
* Overlays.hoverEnterOverlay.connect(function(overlayID, event) {
|
||||
* print("Hover enter: " + overlayID);
|
||||
* });
|
||||
*/
|
||||
void hoverEnterOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse cursor continues hovering over an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendHoverOverOverlay|sendHoverOverOverlay} for a 2D overlay).
|
||||
* @function Overlays.hoverOverOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the hover over event occurred on.
|
||||
* @param {PointerEvent} event - The hover over event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void hoverOverOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse cursor finishes hovering over an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendHoverLeaveOverlay|sendHoverLeaveOverlay} for a 2D overlay).
|
||||
* @function Overlays.hoverLeaveOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the hover leave event occurred on.
|
||||
* @param {PointerEvent} event - The hover leave event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void hoverLeaveOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
private:
|
||||
|
@ -358,6 +749,14 @@ private:
|
|||
OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
|
||||
|
||||
RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray);
|
||||
|
||||
private slots:
|
||||
void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
};
|
||||
|
||||
#endif // hifi_Overlays_h
|
||||
|
|
|
@ -87,4 +87,10 @@ namespace render {
|
|||
template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay) {
|
||||
return overlay->getShapeKey();
|
||||
}
|
||||
|
||||
|
||||
template <> uint32_t metaFetchMetaSubItems(const Overlay::Pointer& overlay, ItemIDs& subItems) {
|
||||
return overlay->fetchMetaSubItems(subItems);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ bool PanelAttachable::getParentVisible() const {
|
|||
#endif
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit PanelAttachable.
|
||||
// No JSDoc because these properties are not actually used.
|
||||
QVariant PanelAttachable::getProperty(const QString& property) {
|
||||
if (property == "offsetPosition") {
|
||||
return vec3toVariant(getOffsetPosition());
|
||||
|
|
|
@ -58,6 +58,10 @@ void Planar3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Planar3DOverlay.
|
||||
/**jsdoc
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*/
|
||||
QVariant Planar3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "dimensions" || property == "scale" || property == "size") {
|
||||
return vec2toVariant(getDimensions());
|
||||
|
|
|
@ -27,8 +27,8 @@ public:
|
|||
void setDimensions(float value) { setDimensions(glm::vec2(value)); }
|
||||
void setDimensions(const glm::vec2& value);
|
||||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
virtual void setProperties(const QVariantMap& properties) override;
|
||||
virtual QVariant getProperty(const QString& property) override;
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) override;
|
||||
|
|
|
@ -53,6 +53,7 @@ QmlOverlay::~QmlOverlay() {
|
|||
_qmlElement.reset();
|
||||
}
|
||||
|
||||
// QmlOverlay replaces Overlay's properties with those defined in the QML file used but keeps Overlay2D's properties.
|
||||
void QmlOverlay::setProperties(const QVariantMap& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setProperties", Q_ARG(QVariantMap, properties));
|
||||
|
|
|
@ -107,6 +107,54 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>rectangle3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Rectangle3DProperties
|
||||
*
|
||||
* @property {string} type=rectangle3d - Has the value <code>"rectangle3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*/
|
||||
void Rectangle3DOverlay::setProperties(const QVariantMap& properties) {
|
||||
Planar3DOverlay::setProperties(properties);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,29 @@
|
|||
QString const RectangleOverlay::TYPE = "rectangle";
|
||||
QUrl const RectangleOverlay::URL(QString("hifi/overlays/RectangleOverlay.qml"));
|
||||
|
||||
// RectangleOverlay's properties are defined in the QML file specified above.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>rectangle</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.RectangleProperties
|
||||
*
|
||||
* @property {Rect} bounds - The position and size of the rectangle, in pixels. <em>Write-only.</em>
|
||||
* @property {number} x - Integer left, x-coordinate value = <code>bounds.x</code>. <em>Write-only.</em>
|
||||
* @property {number} y - Integer top, y-coordinate value = <code>bounds.y</code>. <em>Write-only.</em>
|
||||
* @property {number} width - Integer width of the rectangle = <code>bounds.width</code>. <em>Write-only.</em>
|
||||
* @property {number} height - Integer height of the rectangle = <code>bounds.height</code>. <em>Write-only.</em>
|
||||
*
|
||||
* @property {Color} color=0,0,0 - The color of the overlay. <em>Write-only.</em>
|
||||
* @property {number} alpha=1.0 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>. <em>Write-only.</em>
|
||||
* @property {number} borderWidth=1 - Integer width of the border, in pixels. The border is drawn within the rectangle's bounds.
|
||||
* It is not drawn unless either <code>borderColor</code> or <code>borderAlpha</code> are specified. <em>Write-only.</em>
|
||||
* @property {number} radius=0 - Integer corner radius, in pixels. <em>Write-only.</em>
|
||||
* @property {Color} borderColor=0,0,0 - The color of the border. <em>Write-only.</em>
|
||||
* @property {number} borderAlpha=1.0 - The opacity of the border, <code>0.0</code> - <code>1.0</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* <em>Write-only.</em>
|
||||
*/
|
||||
|
||||
RectangleOverlay::RectangleOverlay() : QmlOverlay(URL) {}
|
||||
|
||||
RectangleOverlay::RectangleOverlay(const RectangleOverlay* rectangleOverlay)
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
|
||||
QString const Shape3DOverlay::TYPE = "shape";
|
||||
|
||||
Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) :
|
||||
Volume3DOverlay(Shape3DOverlay)
|
||||
Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* shape3DOverlay) :
|
||||
Volume3DOverlay(shape3DOverlay),
|
||||
_shape(shape3DOverlay->_shape)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -107,6 +108,57 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>shape</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.ShapeProperties
|
||||
*
|
||||
* @property {string} type=shape - Has the value <code>"shape"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {Shape} shape=Hexagon - The geometrical shape of the overlay.
|
||||
* @property {number} borderSize - Not used.
|
||||
*/
|
||||
QVariant Shape3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "borderSize") {
|
||||
return _borderSize;
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
virtual QString getType() const override { return TYPE; }
|
||||
|
||||
Shape3DOverlay() {}
|
||||
Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay);
|
||||
Shape3DOverlay(const Shape3DOverlay* shape3DOverlay);
|
||||
|
||||
virtual void render(RenderArgs* args) override;
|
||||
virtual const render::ShapeKey getShapeKey() override;
|
||||
|
|
|
@ -26,6 +26,56 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
|
|||
{
|
||||
}
|
||||
|
||||
// If Sphere3DOverlay had a getProperty() method then it would go here; do JSDoc here.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>sphere</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.SphereProperties
|
||||
*
|
||||
* @property {string} type=sphere - Has the value <code>"sphere"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*/
|
||||
|
||||
void Sphere3DOverlay::render(RenderArgs* args) {
|
||||
if (!_renderVisible) {
|
||||
return; // do nothing if we're not visible
|
||||
|
|
|
@ -204,6 +204,68 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>text3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Text3DProperties
|
||||
*
|
||||
* @property {string} type=text3d - Has the value <code>"text3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {boolean} isFacingAvatar - If <code>true</code>, the overlay is rotated to face the user's camera about an axis
|
||||
* parallel to the user's avatar's "up" direction.
|
||||
*
|
||||
* @property {string} text="" - The text to display. Text does not automatically wrap; use <code>\n</code> for a line break.
|
||||
* @property {number} textAlpha=1 - The text alpha value.
|
||||
* @property {Color} backgroundColor=0,0,0 - The background color.
|
||||
* @property {number} backgroundAlpha=0.7 - The background alpha value.
|
||||
* @property {number} lineHeight=1 - The height of a line of text in meters.
|
||||
* @property {number} leftMargin=0.1 - The left margin, in meters.
|
||||
* @property {number} topMargin=0.1 - The top margin, in meters.
|
||||
* @property {number} rightMargin=0.1 - The right margin, in meters.
|
||||
* @property {number} bottomMargin=0.1 - The bottom margin, in meters.
|
||||
*/
|
||||
|
||||
QVariant Text3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "text") {
|
||||
return getText();
|
||||
|
|
|
@ -27,6 +27,33 @@
|
|||
QString const TextOverlay::TYPE = "text";
|
||||
QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml"));
|
||||
|
||||
// TextOverlay's properties are defined in the QML file specified above.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>text</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.TextProperties
|
||||
*
|
||||
* @property {Rect} bounds - The position and size of the rectangle, in pixels. <em>Write-only.</em>
|
||||
* @property {number} x - Integer left, x-coordinate value = <code>bounds.x</code>. <em>Write-only.</em>
|
||||
* @property {number} y - Integer top, y-coordinate value = <code>bounds.y</code>. <em>Write-only.</em>
|
||||
* @property {number} width - Integer width of the rectangle = <code>bounds.width</code>. <em>Write-only.</em>
|
||||
* @property {number} height - Integer height of the rectangle = <code>bounds.height</code>. <em>Write-only.</em>
|
||||
*
|
||||
* @property {number} margin=0 - Sets the <code>leftMargin</code> and <code>topMargin</code> values, in pixels.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} leftMargin=0 - The left margin's size, in pixels. <em>Write-only.</em>
|
||||
* @property {number} topMargin=0 - The top margin's size, in pixels. <em>Write-only.</em>
|
||||
* @property {string} text="" - The text to display. Text does not automatically wrap; use <code>\n</code> for a line break. Text
|
||||
* is clipped to the <code>bounds</code>. <em>Write-only.</em>
|
||||
* @property {number} font.size=18 - The size of the text, in pixels. <em>Write-only.</em>
|
||||
* @property {number} lineHeight=18 - The height of a line of text, in pixels. <em>Write-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the text. Synonym: <code>textColor</code>. <em>Write-only.</em>
|
||||
* @property {number} alpha=1.0 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>. <em>Write-only.</em>
|
||||
* @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. <em>Write-only.</em>
|
||||
* @property {number} backgroundAlpha=0.7 - The opacity of the background rectangle. <em>Write-only.</em>
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* <em>Write-only.</em>
|
||||
*/
|
||||
|
||||
TextOverlay::TextOverlay() : QmlOverlay(URL) { }
|
||||
|
||||
TextOverlay::TextOverlay(const TextOverlay* textOverlay)
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) :
|
||||
Base3DOverlay(volume3DOverlay)
|
||||
Base3DOverlay(volume3DOverlay),
|
||||
_localBoundingBox(volume3DOverlay->_localBoundingBox)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -61,6 +62,11 @@ void Volume3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Volume3DOverlay.
|
||||
/**jsdoc
|
||||
* @typedef
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*/
|
||||
QVariant Volume3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "dimensions" || property == "scale" || property == "size") {
|
||||
return vec3toVariant(getDimensions());
|
||||
|
|
|
@ -55,17 +55,15 @@
|
|||
#include <plugins/InputConfiguration.h>
|
||||
#include "ui/Snapshot.h"
|
||||
#include "SoundCache.h"
|
||||
|
||||
#include "raypick/PointerScriptingInterface.h"
|
||||
|
||||
static const float DPI = 30.47f;
|
||||
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
||||
static int MAX_WINDOW_SIZE = 4096;
|
||||
static const float METERS_TO_INCHES = 39.3701f;
|
||||
static const float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||
|
||||
const QString Web3DOverlay::TYPE = "web3d";
|
||||
const QString Web3DOverlay::QML = "Web3DOverlay.qml";
|
||||
Web3DOverlay::Web3DOverlay() : _dpi(DPI) {
|
||||
Web3DOverlay::Web3DOverlay() {
|
||||
_touchDevice.setCapabilities(QTouchDevice::Position);
|
||||
_touchDevice.setType(QTouchDevice::TouchScreen);
|
||||
_touchDevice.setName("Web3DOverlayTouchDevice");
|
||||
|
@ -82,7 +80,6 @@ Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) :
|
|||
_url(Web3DOverlay->_url),
|
||||
_scriptURL(Web3DOverlay->_scriptURL),
|
||||
_dpi(Web3DOverlay->_dpi),
|
||||
_resolution(Web3DOverlay->_resolution),
|
||||
_showKeyboardFocusHighlight(Web3DOverlay->_showKeyboardFocusHighlight)
|
||||
{
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
|
@ -154,7 +151,7 @@ void Web3DOverlay::buildWebSurface() {
|
|||
setupQmlSurface();
|
||||
}
|
||||
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition()));
|
||||
_webSurface->resize(QSize(_resolution.x, _resolution.y));
|
||||
onResizeWebSurface();
|
||||
_webSurface->resume();
|
||||
});
|
||||
|
||||
|
@ -244,8 +241,16 @@ void Web3DOverlay::setMaxFPS(uint8_t maxFPS) {
|
|||
}
|
||||
|
||||
void Web3DOverlay::onResizeWebSurface() {
|
||||
_mayNeedResize = false;
|
||||
_webSurface->resize(QSize(_resolution.x, _resolution.y));
|
||||
glm::vec2 dims = glm::vec2(getDimensions());
|
||||
dims *= METERS_TO_INCHES * _dpi;
|
||||
|
||||
// ensure no side is never larger then MAX_WINDOW_SIZE
|
||||
float max = (dims.x > dims.y) ? dims.x : dims.y;
|
||||
if (max > MAX_WINDOW_SIZE) {
|
||||
dims *= MAX_WINDOW_SIZE / max;
|
||||
}
|
||||
|
||||
_webSurface->resize(QSize(dims.x, dims.y));
|
||||
}
|
||||
|
||||
unsigned int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) {
|
||||
|
@ -266,14 +271,14 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (_currentMaxFPS != _desiredMaxFPS) {
|
||||
setMaxFPS(_desiredMaxFPS);
|
||||
}
|
||||
|
||||
if (_mayNeedResize) {
|
||||
emit resizeWebSurface();
|
||||
}
|
||||
|
||||
if (_currentMaxFPS != _desiredMaxFPS) {
|
||||
setMaxFPS(_desiredMaxFPS);
|
||||
}
|
||||
|
||||
vec4 color(toGlm(getColor()), getAlpha());
|
||||
|
||||
if (!_texture) {
|
||||
|
@ -310,7 +315,7 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
Transform Web3DOverlay::evalRenderTransform() {
|
||||
Transform transform = Parent::evalRenderTransform();
|
||||
transform.setScale(1.0f);
|
||||
transform.postScale(glm::vec3(getSize(), 1.0f));
|
||||
transform.postScale(glm::vec3(getDimensions(), 1.0f));
|
||||
return transform;
|
||||
}
|
||||
|
||||
|
@ -434,18 +439,10 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
auto resolution = properties["resolution"];
|
||||
if (resolution.isValid()) {
|
||||
bool valid;
|
||||
auto res = vec2FromVariant(resolution, valid);
|
||||
if (valid) {
|
||||
_resolution = res;
|
||||
}
|
||||
}
|
||||
|
||||
auto dpi = properties["dpi"];
|
||||
if (dpi.isValid()) {
|
||||
_dpi = dpi.toFloat();
|
||||
_mayNeedResize = true;
|
||||
}
|
||||
|
||||
auto maxFPS = properties["maxFPS"];
|
||||
|
@ -467,10 +464,69 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
_inputMode = Touch;
|
||||
}
|
||||
}
|
||||
|
||||
_mayNeedResize = true;
|
||||
}
|
||||
|
||||
// Web3DOverlay overrides the meaning of Planar3DOverlay's dimensions property.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>web3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Web3DProperties
|
||||
*
|
||||
* @property {string} type=web3d - Has the value <code>"web3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* 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
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {boolean} isFacingAvatar - If <code>true</code>, the overlay is rotated to face the user's camera about an axis
|
||||
* parallel to the user's avatar's "up" direction.
|
||||
*
|
||||
* @property {string} url - The URL of the Web page to display.
|
||||
* @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page.
|
||||
* @property {Vec2} resolution - <strong>Deprecated:</strong> This property has been removed.
|
||||
* @property {number} dpi=30 - The dots per inch to display the Web page at, on the overlay.
|
||||
* @property {Vec2} dimensions=1,1 - The size of the overlay to display the Web page on, in meters. Synonyms:
|
||||
* <code>scale</code>, <code>size</code>.
|
||||
* @property {number} maxFPS=10 - The maximum update rate for the Web overlay content, in frames/second.
|
||||
* @property {boolean} showKeyboardFocusHighlight=true - If <code>true</code>, the Web overlay is highlighted when it has
|
||||
* keyboard focus.
|
||||
* @property {string} inputMode=Touch - The user input mode to use - either <code>"Touch"</code> or <code>"Mouse"</code>.
|
||||
*/
|
||||
QVariant Web3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "url") {
|
||||
return _url;
|
||||
|
@ -478,9 +534,6 @@ QVariant Web3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "scriptURL") {
|
||||
return _scriptURL;
|
||||
}
|
||||
if (property == "resolution") {
|
||||
return vec2toVariant(_resolution);
|
||||
}
|
||||
if (property == "dpi") {
|
||||
return _dpi;
|
||||
}
|
||||
|
@ -536,17 +589,18 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) {
|
|||
}
|
||||
}
|
||||
|
||||
glm::vec2 Web3DOverlay::getSize() const {
|
||||
return _resolution / _dpi * INCHES_TO_METERS * getDimensions();
|
||||
};
|
||||
|
||||
bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
// FIXME - face and surfaceNormal not being returned
|
||||
glm::vec2 dimensions = getDimensions();
|
||||
glm::quat rotation = getWorldOrientation();
|
||||
glm::vec3 position = getWorldPosition();
|
||||
|
||||
// Don't call applyTransformTo() or setTransform() here because this code runs too frequently.
|
||||
|
||||
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
|
||||
return findRayRectangleIntersection(origin, direction, getWorldOrientation(), getWorldPosition(), getSize(), distance);
|
||||
if (findRayRectangleIntersection(origin, direction, rotation, position, dimensions, distance)) {
|
||||
surfaceNormal = rotation * Vectors::UNIT_Z;
|
||||
face = glm::dot(surfaceNormal, direction) > 0 ? MIN_Z_FACE : MAX_Z_FACE;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Web3DOverlay* Web3DOverlay::createClone() const {
|
||||
|
@ -555,4 +609,4 @@ Web3DOverlay* Web3DOverlay::createClone() const {
|
|||
|
||||
void Web3DOverlay::emitScriptEvent(const QVariant& message) {
|
||||
QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message));
|
||||
}
|
||||
}
|
|
@ -52,8 +52,6 @@ public:
|
|||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
|
||||
glm::vec2 getSize() const override;
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) override;
|
||||
|
||||
|
@ -93,10 +91,9 @@ private:
|
|||
gpu::TexturePointer _texture;
|
||||
QString _url;
|
||||
QString _scriptURL;
|
||||
float _dpi;
|
||||
vec2 _resolution{ 640, 480 };
|
||||
float _dpi { 30.0f };
|
||||
int _geometryId { 0 };
|
||||
bool _showKeyboardFocusHighlight{ true };
|
||||
bool _showKeyboardFocusHighlight { true };
|
||||
|
||||
QTouchDevice _touchDevice;
|
||||
|
||||
|
|
|
@ -231,6 +231,9 @@ public:
|
|||
|
||||
const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; }
|
||||
|
||||
const AnimPose& getModelOffsetPose() const { return _modelOffset; }
|
||||
const AnimPose& getGeometryOffsetPose() const { return _geometryOffset; }
|
||||
|
||||
void setEnableDebugDrawIKTargets(bool enableDebugDrawIKTargets) { _enableDebugDrawIKTargets = enableDebugDrawIKTargets; }
|
||||
void setEnableDebugDrawIKConstraints(bool enableDebugDrawIKConstraints) { _enableDebugDrawIKConstraints = enableDebugDrawIKConstraints; }
|
||||
void setEnableDebugDrawIKChains(bool enableDebugDrawIKChains) { _enableDebugDrawIKChains = enableDebugDrawIKChains; }
|
||||
|
|
|
@ -79,6 +79,7 @@ Setting::Handle<int> staticJitterBufferFrames("staticJitterBufferFrames",
|
|||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
Mutex _deviceMutex;
|
||||
Mutex _recordMutex;
|
||||
|
||||
// thread-safe
|
||||
QList<QAudioDeviceInfo> getAvailableDevices(QAudio::Mode mode) {
|
||||
|
@ -222,8 +223,7 @@ AudioClient::AudioClient() :
|
|||
// initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash
|
||||
getAvailableDevices(QAudio::AudioInput);
|
||||
getAvailableDevices(QAudio::AudioOutput);
|
||||
|
||||
|
||||
|
||||
// start a thread to detect any device changes
|
||||
_checkDevicesTimer = new QTimer(this);
|
||||
connect(_checkDevicesTimer, &QTimer::timeout, [this] {
|
||||
|
@ -1845,11 +1845,9 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped, _receivedAudioStream.getSamplesAvailable(), samplesRequested);
|
||||
AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput();
|
||||
lastPopOutput.readSamples(scratchBuffer, networkSamplesPopped);
|
||||
|
||||
for (int i = 0; i < networkSamplesPopped; i++) {
|
||||
mixBuffer[i] = convertToFloat(scratchBuffer[i]);
|
||||
}
|
||||
|
||||
samplesRequested = networkSamplesPopped;
|
||||
}
|
||||
|
||||
|
@ -1911,6 +1909,13 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
bytesWritten = maxSize;
|
||||
}
|
||||
|
||||
// send output buffer for recording
|
||||
if (_audio->_isRecording) {
|
||||
Lock lock(_recordMutex);
|
||||
_audio->_audioFileWav.addRawAudioChunk(reinterpret_cast<char*>(scratchBuffer), bytesWritten);
|
||||
}
|
||||
|
||||
|
||||
int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree();
|
||||
float msecsAudioOutputUnplayed = bytesAudioOutputUnplayed / (float)_audio->_outputFormat.bytesForDuration(USECS_PER_MSEC);
|
||||
_audio->_stats.updateOutputMsUnplayed(msecsAudioOutputUnplayed);
|
||||
|
@ -1922,6 +1927,22 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
return bytesWritten;
|
||||
}
|
||||
|
||||
bool AudioClient::startRecording(const QString& filepath) {
|
||||
if (!_audioFileWav.create(_outputFormat, filepath)) {
|
||||
qDebug() << "Error creating audio file: " + filepath;
|
||||
return false;
|
||||
}
|
||||
_isRecording = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioClient::stopRecording() {
|
||||
if (_isRecording) {
|
||||
_isRecording = false;
|
||||
_audioFileWav.close();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::loadSettings() {
|
||||
_receivedAudioStream.setDynamicJitterBufferEnabled(dynamicJitterBufferEnabled.get());
|
||||
_receivedAudioStream.setStaticJitterBufferFrames(staticJitterBufferFrames.get());
|
||||
|
|
|
@ -47,11 +47,13 @@
|
|||
#include <AudioConstants.h>
|
||||
#include <AudioGate.h>
|
||||
|
||||
|
||||
#include <shared/RateCounter.h>
|
||||
|
||||
#include <plugins/CodecPlugin.h>
|
||||
|
||||
#include "AudioIOStats.h"
|
||||
#include "AudioFileWav.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning( push )
|
||||
|
@ -67,7 +69,6 @@ class QAudioInput;
|
|||
class QAudioOutput;
|
||||
class QIODevice;
|
||||
|
||||
|
||||
class Transform;
|
||||
class NLPacket;
|
||||
|
||||
|
@ -118,6 +119,8 @@ public:
|
|||
const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; }
|
||||
MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; }
|
||||
|
||||
const QAudioFormat& getOutputFormat() const { return _outputFormat; }
|
||||
|
||||
float getLastInputLoudness() const { return _lastInputLoudness; } // TODO: relative to noise floor?
|
||||
|
||||
float getTimeSinceLastClip() const { return _timeSinceLastClip; }
|
||||
|
@ -142,7 +145,7 @@ public:
|
|||
void setIsPlayingBackRecording(bool isPlayingBackRecording) { _isPlayingBackRecording = isPlayingBackRecording; }
|
||||
|
||||
Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale);
|
||||
|
||||
|
||||
bool outputLocalInjector(const AudioInjectorPointer& injector) override;
|
||||
|
||||
QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const;
|
||||
|
@ -155,6 +158,13 @@ public:
|
|||
|
||||
bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName);
|
||||
|
||||
void setRecording(bool isRecording) { _isRecording = isRecording; };
|
||||
bool getRecording() { return _isRecording; };
|
||||
|
||||
bool startRecording(const QString& filename);
|
||||
void stopRecording();
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
static QString getWinDeviceName(wchar_t* guid);
|
||||
#endif
|
||||
|
@ -184,13 +194,17 @@ public slots:
|
|||
void toggleMute();
|
||||
bool isMuted() { return _muted; }
|
||||
|
||||
|
||||
virtual void setIsStereoInput(bool stereo) override;
|
||||
|
||||
void setNoiseReduction(bool isNoiseGateEnabled);
|
||||
bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; }
|
||||
|
||||
bool getLocalEcho() { return _shouldEchoLocally; }
|
||||
void setLocalEcho(bool localEcho) { _shouldEchoLocally = localEcho; }
|
||||
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
|
||||
|
||||
bool getServerEcho() { return _shouldEchoToServer; }
|
||||
void setServerEcho(bool serverEcho) { _shouldEchoToServer = serverEcho; }
|
||||
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
|
||||
|
||||
void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer);
|
||||
|
@ -239,6 +253,8 @@ signals:
|
|||
|
||||
void muteEnvironmentRequested(glm::vec3 position, float radius);
|
||||
|
||||
void outputBufferReceived(const QByteArray _outputBuffer);
|
||||
|
||||
protected:
|
||||
AudioClient();
|
||||
~AudioClient();
|
||||
|
@ -354,9 +370,8 @@ private:
|
|||
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
|
||||
float* _localOutputMixBuffer { NULL };
|
||||
Mutex _localAudioMutex;
|
||||
|
||||
AudioLimiter _audioLimiter;
|
||||
|
||||
|
||||
// Adds Reverb
|
||||
void configureReverb();
|
||||
void updateReverbOptions();
|
||||
|
@ -391,6 +406,8 @@ private:
|
|||
QList<QAudioDeviceInfo> _inputDevices;
|
||||
QList<QAudioDeviceInfo> _outputDevices;
|
||||
|
||||
AudioFileWav _audioFileWav;
|
||||
|
||||
bool _hasReceivedFirstPacket { false };
|
||||
|
||||
QVector<AudioInjectorPointer> _activeLocalAudioInjectors;
|
||||
|
@ -412,6 +429,8 @@ private:
|
|||
|
||||
QTimer* _checkDevicesTimer { nullptr };
|
||||
QTimer* _checkPeakValuesTimer { nullptr };
|
||||
|
||||
bool _isRecording { false };
|
||||
};
|
||||
|
||||
|
||||
|
|
69
libraries/audio-client/src/AudioFileWav.cpp
Normal file
69
libraries/audio-client/src/AudioFileWav.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// AudioWavFile.h
|
||||
// libraries/audio-client/src
|
||||
//
|
||||
// Created by Luis Cuenca on 12/1/2017.
|
||||
// Copyright 2017 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 "AudioFileWav.h"
|
||||
|
||||
bool AudioFileWav::create(const QAudioFormat& audioFormat, const QString& filepath) {
|
||||
if (_file.isOpen()) {
|
||||
_file.close();
|
||||
}
|
||||
_file.setFileName(filepath);
|
||||
if (!_file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
addHeader(audioFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioFileWav::addRawAudioChunk(char* chunk, int size) {
|
||||
if (_file.isOpen()) {
|
||||
QDataStream stream(&_file);
|
||||
stream.writeRawData(chunk, size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioFileWav::close() {
|
||||
QDataStream stream(&_file);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
// fill RIFF and size data on header
|
||||
_file.seek(4);
|
||||
stream << quint32(_file.size() - 8);
|
||||
_file.seek(40);
|
||||
stream << quint32(_file.size() - 44);
|
||||
_file.close();
|
||||
}
|
||||
|
||||
void AudioFileWav::addHeader(const QAudioFormat& audioFormat) {
|
||||
QDataStream stream(&_file);
|
||||
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
// RIFF
|
||||
stream.writeRawData("RIFF", 4);
|
||||
stream << quint32(0);
|
||||
stream.writeRawData("WAVE", 4);
|
||||
|
||||
// Format description PCM = 16
|
||||
stream.writeRawData("fmt ", 4);
|
||||
stream << quint32(16);
|
||||
stream << quint16(1);
|
||||
stream << quint16(audioFormat.channelCount());
|
||||
stream << quint32(audioFormat.sampleRate());
|
||||
stream << quint32(audioFormat.sampleRate() * audioFormat.channelCount() * audioFormat.sampleSize() / 8); // bytes per second
|
||||
stream << quint16(audioFormat.channelCount() * audioFormat.sampleSize() / 8); // block align
|
||||
stream << quint16(audioFormat.sampleSize()); // bits Per Sample
|
||||
// Init data chunck
|
||||
stream.writeRawData("data", 4);
|
||||
stream << quint32(0);
|
||||
}
|
34
libraries/audio-client/src/AudioFileWav.h
Normal file
34
libraries/audio-client/src/AudioFileWav.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// AudioWavFile.h
|
||||
// libraries/audio-client/src
|
||||
//
|
||||
// Created by Luis Cuenca on 12/1/2017.
|
||||
// Copyright 2017 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_AudioFileWav_h
|
||||
#define hifi_AudioFileWav_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QFile>
|
||||
#include <QDataStream>
|
||||
#include <QVector>
|
||||
#include <QAudioFormat>
|
||||
|
||||
class AudioFileWav : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioFileWav() {}
|
||||
bool create(const QAudioFormat& audioFormat, const QString& filepath);
|
||||
bool addRawAudioChunk(char* chunk, int size);
|
||||
void close();
|
||||
|
||||
private:
|
||||
void addHeader(const QAudioFormat& audioFormat);
|
||||
QFile _file;
|
||||
};
|
||||
|
||||
#endif // hifi_AudioFileWav_h
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue