Merge remote-tracking branch 'upstream/master' into docs/deb-rpm-package-instructions

This commit is contained in:
Kalila L 2021-02-22 03:35:28 -05:00
commit 44da6dca36
66 changed files with 579 additions and 188 deletions

1
.gitignore vendored
View file

@ -111,3 +111,4 @@ tools/unity-avatar-exporter
server-console/package-lock.json
vcpkg/
/tools/nitpick/compiledResources
qt/

View file

@ -1,8 +1,8 @@
# Build Android
*Last Updated on December 21, 2019*
*Last Updated on December 15, 2020*
Please read the [general build guide](BUILD.md) for information on building other platforms. Only Android specific instructions are found in this file. **Note that these instructions apply to building for Oculus Quest.**
Please read the [general build guide](BUILD.md) for information on building other platforms. Only Android specific instructions are found in this file. **Note that these instructions apply to building for the Oculus Quest 1.**
## Dependencies
@ -14,7 +14,7 @@ Please install the dependencies for your OS using the [Windows](BUILD_WIN.md), [
### Android Studio
Download the [Android Studio](https://developer.android.com/studio/index.html) installer and run it. Once installed, at the welcome screen, click _Configure_ in the lower right corner and select _SDK Manager_.
Download the [Android Studio](https://developer.android.com/studio/index.html) installer and run it. Once installed, click _File_ then _Settings_, expand _Appearance & Behavior_ then expand _System Settings_ and select _Android SDK_.
From the _SDK Platforms_ tab, select API levels 26 and 28.
@ -27,14 +27,16 @@ From the _SDK Tools_ tab, select the following
* Android SDK Tools
* NDK (even if you have the NDK installed separately)
Still in the _SDK Tools_ tab, click _Show Package Details_. Select CMake 3.6.4. Do this even if you have a separate CMake installation.
Still in the _SDK Tools_ tab, check off _Show Package Details_ at the bottom. Select CMake 3.6.4. Do this even if you have a separate CMake installation. Also, make sure the NDK installed version is 18 (or higher).
Also, make sure the NDK installed version is 18 (or higher).
Now go back to _File_ then _Project Structure_ then under _Project_ set the Android Gradle Plugin Version to `3.2.1` and Gradle Version to `4.10.1`.
If Android Studio pops open the "Plugin Update Recommeded" dialog, do not click update, just click X on the top right to close. Later versions of the Gradle plugin have known issues with cz.malohlava.
## Environment
### Create a keystore in Android Studio
Follow the directions [here](https://developer.android.com/studio/publish/app-signing#generate-key) to create a keystore file. You can save it anywhere (preferably not in the `hifi` folder).
Follow the directions [here](https://developer.android.com/studio/publish/app-signing#generate-key) to create a keystore file. You can save it anywhere (preferably not in the `vircadia` folder).
### Set up machine specific Gradle properties
@ -62,7 +64,11 @@ Add these lines to `gradle.properties`
SUPPRESS_INTERFACE
SUPPRESS_FRAME_PLAYER
The above code to suppress modules is not necessary, but will speed up the build process.
#### The Frame Player for both Android Phone and Oculus Quest is optional, so if you encounter problems with these during your build, you can skip them by adding these lines to `gradle.properties`
SUPPRESS_FRAME_PLAYER
SUPPRESS_QUEST_FRAME_PLAYER
### Clone the repository
@ -74,12 +80,17 @@ The above code to suppress modules is not necessary, but will speed up the build
* Open Android Studio
* Choose _Open an existing Android Studio project_
* Navigate to the `hifi` repository and choose the `android` folder and select _OK_
* Navigate to the `vircadia` repository that had you cloned and choose the `android` folder and select _OK_
* Wait for Gradle to sync (this should take around 20 minutes the first time)
* From the _Build_ menu select _Make Project_
* If a dialog pops open saying "Plugin Update Recommeded" dialog, do not click update, just click X on the top right to close.
* In the _Project_ window click on the project you wish to build (i.e. "questInterface") then click _Build_ in the top menu and choose _Make Module 'questInterface'_
* By default this will build the "debug" apk, you can change this by opening the _Build Variants_ window along the left side and select other build types such as "release".
* Your newly build APK should reside in `vircadia\android\apps\questInterface\release` (if you chose release).
### Running a Module
You are free to use the "adb" command line or other development tools to install (sideload on Quest) your newly built APK, or you can follow the instructions below to load the APK via Android Studio.
* In the toolbar at the top of Android Studio, next to the green hammer icon, you should see a dropdown menu.
* You may already see a configuration for the module you are trying to build. If so, select it.
* Otherwise, select _Edit Configurations_.
@ -112,9 +123,17 @@ To view a more complete debug log,
* Click the icon with the two overlapping squares in the upper left corner of the tab where the sync is running (hover text says _Toggle view_)
* To change verbosity, click _File > Settings_. Under _Build, Execution, Deployment > Compiler_ you can add command-line flags, as per Gradle documentation
If you encounter CMake issues, try adding the following system environment variable:
With your start menu, search for 'Edit the System Environment Variables' and open it.
* Click on 'Advanced' tab, then 'Environment Variables'
* Select 'New' under System variables
* Set "Variable name" to QT_CMAKE_PREFIX_PATH
* Set "Variable value" to the directory that your android build placed the CMake 3.6.4 library CMake directory (i.e. android\qt\lib\cmake).
Some things you can try if you want to do a clean build
* Delete the `build` and `.externalNativeBuild` folders from the folder for each module you're building (for example, `hifi/android/apps/interface`)
* Delete the `build` and `.externalNativeBuild` folders from the folder for each module you're building (for example, `vircadia/android/apps/interface`)
* If you have set your `HIFI_VCPKG_ROOT` environment variable, delete the contents of that directory; otherwise, delete `AppData/Local/Temp/hifi`
* In Android Studio, click _File > Invalidate Caches / Restart_ and select _Invalidate and Restart_

View file

@ -33,6 +33,7 @@
#include <Trace.h>
#include <StatTracker.h>
#include <ThreadHelpers.h>
#include "AssignmentClientLogging.h"
#include "AssignmentFactory.h"
@ -235,10 +236,13 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
qCDebug(assignment_client) << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
// start the deployed assignment
QThread* workerThread = new QThread;
QThread* workerThread = new QThread();
workerThread->setObjectName("ThreadedAssignment Worker");
connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run);
connect(workerThread, &QThread::started, _currentAssignment.data(), [this] {
setThreadName("ThreadedAssignment Worker");
_currentAssignment->run();
});
// Once the ThreadedAssignment says it is finished - we ask it to deleteLater
// This is a queued connection so that it is put into the event loop to be processed by the worker

View file

@ -11,9 +11,13 @@
#include "AudioMixerSlavePool.h"
#include <QObject>
#include <assert.h>
#include <algorithm>
#include <ThreadHelpers.h>
void AudioMixerSlaveThread::run() {
while (true) {
wait();
@ -157,6 +161,7 @@ void AudioMixerSlavePool::resize(int numThreads) {
// start new slaves
for (int i = 0; i < numThreads - _numThreads; ++i) {
auto slave = new AudioMixerSlaveThread(*this, _workerSharedData);
QObject::connect(slave, &QThread::started, [] { setThreadName("AudioMixerSlaveThread"); });
slave->start();
_slaves.emplace_back(slave);
}

View file

@ -34,6 +34,7 @@
#include <QtCore/QDir>
#include <OctreeDataUtils.h>
#include <ThreadHelpers.h>
Q_LOGGING_CATEGORY(octree_server, "hifi.octree-server")
@ -1192,7 +1193,10 @@ void OctreeServer::domainSettingsRequestComplete() {
_persistAsFileType);
_persistManager->moveToThread(&_persistThread);
connect(&_persistThread, &QThread::finished, _persistManager, &QObject::deleteLater);
connect(&_persistThread, &QThread::started, _persistManager, &OctreePersistThread::start);
connect(&_persistThread, &QThread::started, _persistManager, [this] {
setThreadName("OctreePersistThread");
_persistManager->start();
});
connect(_persistManager, &OctreePersistThread::loadCompleted, this, [this]() {
beginRunning();
});

View file

@ -60,6 +60,7 @@
#include <Gzip.h>
#include <OctreeDataUtils.h>
#include <ThreadHelpers.h>
using namespace std::chrono;
@ -830,9 +831,11 @@ void DomainServer::setupNodeListAndAssignments() {
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
_assetClientThread.setObjectName("AssetClient Thread");
QString name = "AssetClient Thread";
_assetClientThread.setObjectName(name);
auto assetClient = DependencyManager::set<AssetClient>();
assetClient->moveToThread(&_assetClientThread);
connect(&_assetClientThread, &QThread::started, [name] { setThreadName(name.toStdString()); });
_assetClientThread.start();
// add whatever static assignments that have been parsed to the queue
addStaticAssignmentsToQueue();

View file

@ -13,6 +13,9 @@ Item {
property alias url: webViewCore.url
property alias canGoBack: webViewCore.canGoBack
property alias webViewCore: webViewCore
// FIXME - This was commented out to allow for manual setting of the userAgent.
//
// property alias webViewCoreProfile: webViewCore.profile
property string webViewCoreUserAgent
property bool useBackground: webViewCore.useBackground

View file

@ -11,6 +11,9 @@ Item {
property alias url: webViewCore.url
property alias canGoBack: webViewCore.canGoBack
property alias webViewCore: webViewCore
// FIXME - This was commented out to allow for manual setting of the userAgent.
//
// property alias webViewCoreProfile: webViewCore.profile
property alias useBackground: webViewCore.useBackground
property alias userAgent: webViewCore.userAgent

View file

@ -27,7 +27,11 @@ Item {
}
*/
property alias viewProfile: webroot.webViewCoreProfile
// FIXME - Reimplement profiles for... why? Was it so that new windows opened share the same profile?
// Are profiles written to by the webengine during the session?
// Removed in PR Feature/web entity user agent #988
//
// property alias viewProfile: webroot.webViewCoreProfile
FlickableWebViewCore {
id: webroot

View file

@ -25,7 +25,11 @@ Item {
property bool isDesktop: false
property alias url: web.url
property alias webView: web.webViewCore
property alias profile: web.webViewCoreProfile
// FIXME - Reimplement profiles for... why? Was it so that new windows opened share the same profile?
// Are profiles written to by the webengine during the session?
// Removed in PR Feature/web entity user agent #988
//
// property alias profile: web.webViewCoreProfile
property bool remove: false
property bool closeButtonVisible: true

View file

@ -38,6 +38,12 @@ Item {
}
*/
// FIXME - Reimplement profiles for... why? Was it so that new windows opened share the same profile?
// Are profiles written to by the webengine during the session?
// Removed in PR Feature/web entity user agent #988
//
// property alias viewProfile: webroot.webViewCoreProfile
FlickableWebViewCore {
id: webroot
width: parent.width

View file

@ -254,6 +254,7 @@
#include "AboutUtil.h"
#include "ExternalResource.h"
#include <ThreadHelpers.h>
#if defined(Q_OS_WIN)
#include <VersionHelpers.h>
@ -1168,6 +1169,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
if (!DISABLE_WATCHDOG) {
auto deadlockWatchdogThread = new DeadlockWatchdogThread();
deadlockWatchdogThread->setMainThreadID(QThread::currentThreadId());
connect(deadlockWatchdogThread, &QThread::started, [] { setThreadName("DeadlockWatchdogThread"); });
deadlockWatchdogThread->start();
// Pause the deadlock watchdog when we sleep, or it might
@ -5206,6 +5208,7 @@ void getCpuUsage(vec3& systemAndUser) {
void setupCpuMonitorThread() {
initCpuUsage();
auto cpuMonitorThread = QThread::currentThread();
setThreadName("CPU Monitor Thread");
QTimer* timer = new QTimer();
timer->setInterval(50);

View file

@ -15,6 +15,7 @@
#include <SharedUtil.h>
#include <shared/QtHelpers.h>
#include <ThreadHelpers.h>
#include "AudioConstants.h"
#include "AudioInjector.h"
@ -54,11 +55,14 @@ AudioInjectorManager::~AudioInjectorManager() {
}
void AudioInjectorManager::createThread() {
_thread = new QThread;
_thread = new QThread();
_thread->setObjectName("Audio Injector Thread");
// when the thread is started, have it call our run to handle injection of audio
connect(_thread, &QThread::started, this, &AudioInjectorManager::run, Qt::DirectConnection);
connect(_thread, &QThread::started, this, [this] {
setThreadName("AudioInjectorManager");
run();
}, Qt::DirectConnection);
moveToThread(_thread);

View file

@ -33,7 +33,6 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
{
// SkeletonModels, and by extention Avatars, use Dual Quaternion skinning.
_useDualQuaternionSkinning = true;
_forceOffset = true;
// Avatars all cast shadow
setCanCastShadow(true);

View file

@ -50,6 +50,7 @@
#include "CompositorHelper.h"
#include "Logging.h"
#include "RefreshRateController.h"
#include <ThreadHelpers.h>
using namespace shader::gpu::program;
@ -285,6 +286,7 @@ bool OpenGLDisplayPlugin::activate() {
widget->context()->doneCurrent();
presentThread->setContext(widget->context());
connect(presentThread.data(), &QThread::started, [] { setThreadName("OpenGL Present Thread"); });
// Start execution
presentThread->start();
}

View file

@ -368,6 +368,11 @@ bool EntityRenderer::needsRenderUpdate() const {
return needsRenderUpdateFromEntity(_entity);
}
Transform EntityRenderer::getTransformToCenterWithMaybeOnlyLocalRotation(const EntityItemPointer& entity, bool& success) const {
return entity->getBillboardMode() == BillboardMode::NONE ? entity->getTransformToCenter(success) :
entity->getTransformToCenterWithOnlyLocalRotation(success);
}
// Returns true if the item in question needs to have updateInScene called because of changes in the entity
bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity) const {
if (entity->needsRenderUpdate()) {
@ -379,12 +384,12 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
}
bool success = false;
auto bound = _entity->getAABox(success);
auto bound = entity->getAABox(success);
if (success && _bound != bound) {
return true;
}
auto newModelTransform = _entity->getTransformToCenter(success);
auto newModelTransform = getTransformToCenterWithMaybeOnlyLocalRotation(entity, success);
// FIXME can we use a stale model transform here?
if (success && newModelTransform != _modelTransform) {
return true;
@ -401,15 +406,15 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
return false;
}
void EntityRenderer::updateModelTransformAndBound() {
void EntityRenderer::updateModelTransformAndBound(const EntityItemPointer& entity) {
bool success = false;
auto newModelTransform = _entity->getTransformToCenter(success);
auto newModelTransform = getTransformToCenterWithMaybeOnlyLocalRotation(entity, success);
if (success) {
_modelTransform = newModelTransform;
}
success = false;
auto bound = _entity->getAABox(success);
auto bound = entity->getAABox(success);
if (success) {
_bound = bound;
}
@ -429,7 +434,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
_prevIsTransparent = transparent;
updateModelTransformAndBound();
updateModelTransformAndBound(entity);
_moving = entity->isMovingRelativeToParent();
_visible = entity->getVisible();

View file

@ -101,7 +101,7 @@ protected:
virtual void doRender(RenderArgs* args) = 0;
virtual bool isFading() const { return _isFading; }
virtual void updateModelTransformAndBound();
virtual void updateModelTransformAndBound(const EntityItemPointer& entity);
virtual bool isTransparent() const { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; }
@ -109,16 +109,14 @@ protected:
virtual void setRenderLayer(RenderLayer value) { _renderLayer = value; }
virtual void setCullWithParent(bool value) { _cullWithParent = value; }
signals:
void requestRenderUpdate();
protected:
template<typename T>
std::shared_ptr<T> asTypedEntity() { return std::static_pointer_cast<T>(_entity); }
static void makeStatusGetters(const EntityItemPointer& entity, Item::Status::Getters& statusGetters);
const Transform& getModelTransform() const;
Transform getTransformToCenterWithMaybeOnlyLocalRotation(const EntityItemPointer& entity, bool& success) const;
Item::Bound _bound;
SharedSoundPointer _collisionSound;
QUuid _changeHandlerId;
@ -154,6 +152,9 @@ protected:
const EntityItemPointer _entity;
QUuid _entityID;
signals:
void requestRenderUpdate();
};
template <typename T>

View file

@ -115,11 +115,12 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const {
}
bool success;
auto transform = getTransform(success);
auto transform = getBillboardMode() == BillboardMode::NONE ? getTransform(success) : getTransformWithOnlyLocalRotation(success);
if (success) {
if (model->getTranslation() != transform.getTranslation()) {
return true;
}
if (model->getRotation() != transform.getRotation()) {
return true;
}
@ -171,7 +172,7 @@ void RenderableModelEntityItem::updateModelBounds() {
}
bool success;
auto transform = getTransform(success);
auto transform = getBillboardMode() == BillboardMode::NONE ? getTransform(success) : getTransformWithOnlyLocalRotation(success);
if (success && (model->getTranslation() != transform.getTranslation() ||
model->getRotation() != transform.getRotation())) {
model->setTransformNoUpdateRenderItems(transform);

View file

@ -41,13 +41,13 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity)
}
}
void PolyLineEntityRenderer::updateModelTransformAndBound() {
void PolyLineEntityRenderer::updateModelTransformAndBound(const EntityItemPointer& entity) {
bool success = false;
auto newModelTransform = _entity->getTransformToCenter(success);
auto newModelTransform = getTransformToCenterWithMaybeOnlyLocalRotation(entity, success);
if (success) {
_modelTransform = newModelTransform;
auto lineEntity = std::static_pointer_cast<PolyLineEntityItem>(_entity);
auto lineEntity = std::static_pointer_cast<PolyLineEntityItem>(entity);
AABox bound;
lineEntity->computeTightLocalBoundingBox(bound);
bound.transform(newModelTransform);

View file

@ -25,7 +25,7 @@ class PolyLineEntityRenderer : public TypedEntityRenderer<PolyLineEntityItem> {
public:
PolyLineEntityRenderer(const EntityItemPointer& entity);
void updateModelTransformAndBound() override;
void updateModelTransformAndBound(const EntityItemPointer& entity) override;
virtual bool isTransparent() const override;

View file

@ -1785,8 +1785,9 @@ void PolyVoxEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& s
void PolyVoxEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
_lastVoxelToLocalMatrix = entity->voxelToLocalMatrix();
_position = entity->getWorldPosition();
_orientation = entity->getWorldOrientation();
bool success;
_position = entity->getCenterPosition(success);
_orientation = entity->getBillboardMode() == BillboardMode::NONE ? entity->getWorldOrientation() : entity->getLocalOrientation();
_lastVoxelVolumeSize = entity->getVoxelVolumeSize();
_params->setSubData(0, vec4(_lastVoxelVolumeSize, 0.0));
graphics::MeshPointer newMesh;

View file

@ -56,15 +56,12 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
withWriteLock([&] {
_shape = entity->getShape();
_position = entity->getWorldPosition();
_dimensions = entity->getUnscaledDimensions(); // get unscaled to avoid scaling twice
_orientation = entity->getWorldOrientation();
_renderTransform = getModelTransform(); // contains parent scale, if this entity scales with its parent
if (_shape == entity::Sphere) {
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
}
_renderTransform.postScale(_dimensions);
_renderTransform.postScale(entity->getUnscaledDimensions());
});
});
}
@ -248,7 +245,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
outColor = procedural->getColor(outColor);
outColor.a *= procedural->isFading() ? Interpolate::calculateFadeRatio(procedural->getFadeStartTime()) : 1.0f;
withReadLock([&] {
procedural->prepare(batch, _position, _dimensions, _orientation, _created, ProceduralProgramKey(outColor.a < 1.0f));
procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f));
});
if (render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES) {

View file

@ -45,10 +45,6 @@ private:
std::shared_ptr<graphics::ProceduralMaterial> _material { std::make_shared<graphics::ProceduralMaterial>() };
glm::vec3 _color { NAN };
float _alpha { NAN };
glm::vec3 _position;
glm::vec3 _dimensions;
glm::quat _orientation;
};
} }

View file

@ -109,6 +109,7 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe
_effect = entity->getTextEffect();
_effectColor = toGlm(entity->getTextEffectColor());
_effectThickness = entity->getTextEffectThickness();
_alignment = entity->getAlignment();
updateTextRenderItem();
}
@ -292,12 +293,12 @@ void entities::TextPayload::render(RenderArgs* args) {
}
auto textRenderable = std::static_pointer_cast<TextEntityRenderer>(renderable);
Transform modelTransform;
Transform transform;
glm::vec3 dimensions;
glm::vec4 textColor;
textRenderable->withReadLock([&] {
modelTransform = textRenderable->_renderTransform;
transform = textRenderable->_renderTransform;
dimensions = textRenderable->_dimensions;
float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f;
@ -313,18 +314,18 @@ void entities::TextPayload::render(RenderArgs* args) {
return;
}
modelTransform.setRotation(BillboardModeHelpers::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), textRenderable->_billboardMode,
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), textRenderable->_billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
float scale = textRenderable->_lineHeight / textRenderer->getFontSize();
modelTransform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z));
modelTransform.setScale(scale);
batch.setModelTransform(modelTransform);
transform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z));
transform.setScale(scale);
batch.setModelTransform(transform);
glm::vec2 bounds = glm::vec2(dimensions.x - (textRenderable->_leftMargin + textRenderable->_rightMargin), dimensions.y - (textRenderable->_topMargin + textRenderable->_bottomMargin));
textRenderer->draw(batch, textRenderable->_leftMargin / scale, -textRenderable->_topMargin / scale, bounds / scale, scale,
textRenderable->_text, textRenderable->_font, textColor, effectColor, textRenderable->_effectThickness, textRenderable->_effect,
textRenderable->_unlit, forward);
textRenderable->_alignment, textRenderable->_unlit, forward);
}
namespace render {

View file

@ -65,6 +65,7 @@ private:
glm::vec3 _dimensions;
QString _font { "" };
TextAlignment _alignment { TextAlignment::LEFT };
TextEffect _effect { TextEffect::NO_EFFECT };
glm::vec3 _effectColor { 0 };
float _effectThickness { 0.0f };

View file

@ -1620,6 +1620,19 @@ const Transform EntityItem::getTransformToCenter(bool& success) const {
return result;
}
const Transform EntityItem::getTransformToCenterWithOnlyLocalRotation(bool& success) const {
Transform result = getTransformWithOnlyLocalRotation(success);
glm::vec3 pivot = getPivot();
if (pivot != ENTITY_ITEM_ZERO_VEC3) {
result.postTranslate(pivot);
}
glm::vec3 registrationPoint = getRegistrationPoint();
if (registrationPoint != ENTITY_ITEM_HALF_VEC3) { // If it is not already centered, translate to center
result.postTranslate((ENTITY_ITEM_HALF_VEC3 - registrationPoint) * getScaledDimensions()); // Position to center
}
return result;
}
/// The maximum bounding cube for the entity, independent of it's rotation.
/// This accounts for the registration point (upon which rotation occurs around).
///

View file

@ -190,6 +190,7 @@ public:
void setCenterPosition(const glm::vec3& position);
const Transform getTransformToCenter(bool& success) const;
const Transform getTransformToCenterWithOnlyLocalRotation(bool& success) const;
void requiresRecalcBoxes();

View file

@ -309,7 +309,6 @@ void EntityItemProperties::setScreenshareFromString(const QString& mode) {
}
}
inline void addTextEffect(QHash<QString, TextEffect>& lookup, TextEffect effect) { lookup[TextEffectHelpers::getNameForTextEffect(effect)] = effect; }
const QHash<QString, TextEffect> stringToTextEffectLookup = [] {
QHash<QString, TextEffect> toReturn;
@ -328,6 +327,23 @@ void EntityItemProperties::setTextEffectFromString(const QString& effect) {
}
}
inline void addTextAlignment(QHash<QString, TextAlignment>& lookup, TextAlignment alignment) { lookup[TextAlignmentHelpers::getNameForTextAlignment(alignment)] = alignment; }
const QHash<QString, TextAlignment> stringToTextAlignmentLookup = [] {
QHash<QString, TextAlignment> toReturn;
addTextAlignment(toReturn, TextAlignment::LEFT);
addTextAlignment(toReturn, TextAlignment::CENTER);
addTextAlignment(toReturn, TextAlignment::RIGHT);
return toReturn;
}();
QString EntityItemProperties::getAlignmentAsString() const { return TextAlignmentHelpers::getNameForTextAlignment(_alignment); }
void EntityItemProperties::setAlignmentFromString(const QString& alignment) {
auto textAlignmentItr = stringToTextAlignmentLookup.find(alignment.toLower());
if (textAlignmentItr != stringToTextAlignmentLookup.end()) {
_alignment = textAlignmentItr.value();
_alignmentChanged = true;
}
}
QString getCollisionGroupAsString(uint16_t group) {
switch (group) {
case USER_COLLISION_GROUP_DYNAMIC:
@ -564,6 +580,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT, textEffect);
CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT_COLOR, textEffectColor);
CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT_THICKNESS, textEffectThickness);
CHECK_PROPERTY_CHANGE(PROP_TEXT_ALIGNMENT, alignment);
// Zone
changedProperties += _keyLight.getChangedProperties();
@ -1339,6 +1356,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* @property {Entities.TextEffect} textEffect="none" - The effect that is applied to the text.
* @property {Color} textEffectColor=255,255,255 - The color of the effect.
* @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range <code>0.0</code> &ndash; <code>0.5</code>.
* @property {Entities.TextAlignment} alignment="left" - How the text is aligned against its background.
* @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, <code>false</code>
* if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to <code>"none"</code>.
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
@ -1791,6 +1809,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TEXT_EFFECT, textEffect, getTextEffectAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_TEXT_EFFECT_COLOR, textEffectColor, u8vec3Color);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_EFFECT_THICKNESS, textEffectThickness);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TEXT_ALIGNMENT, alignment, getAlignmentAsString());
}
// Zones only
@ -2175,6 +2194,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(textEffect, TextEffect);
COPY_PROPERTY_FROM_QSCRIPTVALUE(textEffectColor, u8vec3Color, setTextEffectColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE(textEffectThickness, float, setTextEffectThickness);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(alignment, Alignment);
// Zone
_keyLight.copyFromScriptValue(object, _defaultSettings);
@ -2469,6 +2489,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(textEffect);
COPY_PROPERTY_IF_CHANGED(textEffectColor);
COPY_PROPERTY_IF_CHANGED(textEffectThickness);
COPY_PROPERTY_IF_CHANGED(alignment);
// Zone
_keyLight.merge(other._keyLight);
@ -2838,6 +2859,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect);
ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color);
ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_TEXT_EFFECT_THICKNESS, TextEffectThickness, textEffectThickness, float, 0.0, 0.5);
ADD_PROPERTY_TO_MAP(PROP_TEXT_ALIGNMENT, Alignment, alignment, TextAlignment);
// Zone
{ // Keylight
@ -3280,6 +3302,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT, (uint32_t)properties.getTextEffect());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, properties.getTextEffectColor());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, properties.getTextEffectThickness());
APPEND_ENTITY_PROPERTY(PROP_TEXT_ALIGNMENT, (uint32_t)properties.getAlignment());
}
if (properties.getType() == EntityTypes::Zone) {
@ -3767,6 +3790,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT, TextEffect, setTextEffect);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT_COLOR, u8vec3Color, setTextEffectColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT_THICKNESS, float, setTextEffectThickness);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_ALIGNMENT, TextAlignment, setAlignment);
}
if (properties.getType() == EntityTypes::Zone) {
@ -4168,6 +4192,7 @@ void EntityItemProperties::markAllChanged() {
_textEffectChanged = true;
_textEffectColorChanged = true;
_textEffectThicknessChanged = true;
_alignmentChanged = true;
// Zone
_keyLight.markAllChanged();
@ -4784,6 +4809,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (textEffectThicknessChanged()) {
out += "textEffectThickness";
}
if (alignmentChanged()) {
out += "alignment";
}
// Zone
getKeyLight().listChangedProperties(out);

View file

@ -67,6 +67,7 @@
#include "WebInputMode.h"
#include "GizmoType.h"
#include "TextEffect.h"
#include "TextAlignment.h"
const quint64 UNKNOWN_CREATED_TIME = 0;
@ -328,6 +329,7 @@ public:
DEFINE_PROPERTY_REF_ENUM(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect, TextEffect::NO_EFFECT);
DEFINE_PROPERTY_REF(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR);
DEFINE_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, TextEffectThickness, textEffectThickness, float, TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS);
DEFINE_PROPERTY_REF_ENUM(PROP_TEXT_ALIGNMENT, Alignment, alignment, TextAlignment, TextAlignment::LEFT);
// Zone
DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup);

View file

@ -253,6 +253,7 @@ enum EntityPropertyList {
PROP_TEXT_EFFECT = PROP_DERIVED_12,
PROP_TEXT_EFFECT_COLOR = PROP_DERIVED_13,
PROP_TEXT_EFFECT_THICKNESS = PROP_DERIVED_14,
PROP_TEXT_ALIGNMENT = PROP_DERIVED_15,
// Zone
// Keylight

View file

@ -218,8 +218,9 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori
// extents is the entity relative, scaled, centered extents of the entity
glm::vec3 position = entity->getWorldPosition();
glm::mat4 translation = glm::translate(position);
glm::quat orientation = entity->getWorldOrientation();
glm::mat4 rotation = glm::mat4_cast(BillboardModeHelpers::getBillboardRotation(position, orientation, entity->getBillboardMode(),
BillboardMode billboardMode = entity->getBillboardMode();
glm::quat orientation = billboardMode == BillboardMode::NONE ? entity->getWorldOrientation() : entity->getLocalOrientation();
glm::mat4 rotation = glm::mat4_cast(BillboardModeHelpers::getBillboardRotation(position, orientation, billboardMode,
viewFrustumPos, entity->getRotateForPicking()));
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
@ -368,8 +369,9 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3
// extents is the entity relative, scaled, centered extents of the entity
glm::vec3 position = entity->getWorldPosition();
glm::mat4 translation = glm::translate(position);
glm::quat orientation = entity->getWorldOrientation();
glm::mat4 rotation = glm::mat4_cast(BillboardModeHelpers::getBillboardRotation(position, orientation, entity->getBillboardMode(),
BillboardMode billboardMode = entity->getBillboardMode();
glm::quat orientation = billboardMode == BillboardMode::NONE ? entity->getWorldOrientation() : entity->getLocalOrientation();
glm::mat4 rotation = glm::mat4_cast(BillboardModeHelpers::getBillboardRotation(position, orientation, billboardMode,
viewFrustumPos, entity->getRotateForPicking()));
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);

View file

@ -108,10 +108,11 @@ bool GizmoEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
QVariantMap& extraInfo, bool precisionPicking) const {
glm::vec3 dimensions = getScaledDimensions();
glm::vec2 xyDimensions(dimensions.x, dimensions.z);
glm::quat rotation = getWorldOrientation();
BillboardMode billboardMode = getBillboardMode();
glm::quat rotation = billboardMode == BillboardMode::NONE ? getWorldOrientation() : getLocalOrientation();
rotation *= glm::angleAxis(-(float)M_PI_2, Vectors::RIGHT);
glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()));
rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, getBillboardMode(), viewFrustumPos);
rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, billboardMode, viewFrustumPos);
if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) {
glm::vec3 hitPosition = origin + (distance * direction);
@ -143,10 +144,11 @@ bool GizmoEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin,
//// Scale the dimensions by the diameter
glm::vec3 dimensions = getScaledDimensions();
glm::vec2 xyDimensions(dimensions.x, dimensions.z);
glm::quat rotation = getWorldOrientation();
BillboardMode billboardMode = getBillboardMode();
glm::quat rotation = billboardMode == BillboardMode::NONE ? getWorldOrientation() : getLocalOrientation();
rotation *= glm::angleAxis(-(float)M_PI_2, Vectors::RIGHT);
glm::vec3 position = getWorldPosition();
rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, getBillboardMode(), viewFrustumPos);
rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, billboardMode, viewFrustumPos);
glm::quat inverseRot = glm::inverse(rotation);
glm::vec3 localOrigin = inverseRot * (origin - position);

View file

@ -330,6 +330,19 @@ const Transform ModelEntityItem::getTransform(bool& success, int depth) const {
return worldTransform;
}
const Transform ModelEntityItem::getTransformWithOnlyLocalRotation(bool& success, int depth) const {
const Transform parentTransform = getParentTransform(success, depth);
Transform localTransform = getLocalTransform();
localTransform.postScale(getModelScale());
Transform worldTransform;
Transform::mult(worldTransform, parentTransform, localTransform);
worldTransform.setRotation(localTransform.getRotation());
return worldTransform;
}
void ModelEntityItem::setCompoundShapeURL(const QString& url) {
withWriteLock([&] {
if (_compoundShapeURL.get() != url) {

View file

@ -71,6 +71,7 @@ public:
virtual void setScaledDimensions(const glm::vec3& value) override;
virtual const Transform getTransform(bool& success, int depth = 0) const override;
virtual const Transform getTransformWithOnlyLocalRotation(bool& success, int depth = 0) const override;
virtual const Transform getTransform() const override;
static const QString DEFAULT_COMPOUND_SHAPE_URL;

View file

@ -378,14 +378,15 @@ glm::mat4 PolyVoxEntityItem::localToVoxelMatrix() const {
}
glm::mat4 PolyVoxEntityItem::voxelToWorldMatrix(bool includeBillboard) const {
glm::quat orientation = getWorldOrientation();
glm::vec3 position = getWorldPosition();
glm::mat4 translation = glm::translate(position);
glm::mat4 rotation;
if (includeBillboard) {
rotation = glm::mat4_cast(BillboardModeHelpers::getBillboardRotation(position, orientation, getBillboardMode(), BillboardModeHelpers::getPrimaryViewFrustumPosition()));
BillboardMode billboardMode = getBillboardMode();
glm::quat orientation = billboardMode == BillboardMode::NONE ? getWorldOrientation() : getLocalOrientation();
rotation = glm::mat4_cast(BillboardModeHelpers::getBillboardRotation(position, orientation, billboardMode, BillboardModeHelpers::getPrimaryViewFrustumPosition()));
} else {
rotation = glm::mat4_cast(orientation);
rotation = glm::mat4_cast(getWorldOrientation());
}
return translation * rotation * voxelToLocalMatrix();
}

View file

@ -277,9 +277,10 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
float& distance, BoxFace& face, glm::vec3& surfaceNormal,
QVariantMap& extraInfo, bool precisionPicking) const {
glm::vec3 dimensions = getScaledDimensions();
glm::quat rotation = getWorldOrientation();
BillboardMode billboardMode = getBillboardMode();
glm::quat rotation = billboardMode == BillboardMode::NONE ? getWorldOrientation() : getLocalOrientation();
glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()));
rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, getBillboardMode(), viewFrustumPos);
rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, billboardMode, viewFrustumPos);
// determine the ray in the frame of the entity transformed from a unit sphere
glm::mat4 entityToWorldMatrix = glm::translate(position) * glm::mat4_cast(rotation) * glm::scale(dimensions);
@ -309,9 +310,10 @@ bool ShapeEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin,
BoxFace& face, glm::vec3& surfaceNormal,
QVariantMap& extraInfo, bool precisionPicking) const {
glm::vec3 dimensions = getScaledDimensions();
glm::quat rotation = getWorldOrientation();
BillboardMode billboardMode = getBillboardMode();
glm::quat rotation = billboardMode == BillboardMode::NONE ? getWorldOrientation() : getLocalOrientation();
glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()));
rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, getBillboardMode(), viewFrustumPos);
rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, billboardMode, viewFrustumPos);
// determine the parabola in the frame of the entity transformed from a unit sphere
glm::mat4 entityToWorldMatrix = glm::translate(position) * glm::mat4_cast(rotation) * glm::scale(dimensions);

View file

@ -69,6 +69,7 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffect, getTextEffect);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffectColor, getTextEffectColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffectThickness, getTextEffectThickness);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alignment, getAlignment);
return properties;
}
@ -96,7 +97,8 @@ bool TextEntityItem::setSubClassProperties(const EntityItemProperties& propertie
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffect, setTextEffect);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffectColor, setTextEffectColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffectThickness, setTextEffectThickness);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alignment, setAlignment);
return somethingChanged;
}
@ -131,6 +133,7 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT, TextEffect, setTextEffect);
READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, glm::u8vec3, setTextEffectColor);
READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, float, setTextEffectThickness);
READ_ENTITY_PROPERTY(PROP_TEXT_ALIGNMENT, TextAlignment, setAlignment);
return bytesRead;
}
@ -155,6 +158,7 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p
requestedProperties += PROP_TEXT_EFFECT;
requestedProperties += PROP_TEXT_EFFECT_COLOR;
requestedProperties += PROP_TEXT_EFFECT_THICKNESS;
requestedProperties += PROP_TEXT_ALIGNMENT;
return requestedProperties;
}
@ -189,6 +193,7 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT, (uint32_t)getTextEffect());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, getTextEffectColor());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, getTextEffectThickness());
APPEND_ENTITY_PROPERTY(PROP_TEXT_ALIGNMENT, (uint32_t)getAlignment());
}
void TextEntityItem::setText(const QString& value) {
@ -388,8 +393,21 @@ float TextEntityItem::getTextEffectThickness() const {
});
}
void TextEntityItem::setAlignment(TextAlignment value) {
withWriteLock([&] {
_needsRenderUpdate |= _alignment != value;
_alignment = value;
});
}
TextAlignment TextEntityItem::getAlignment() const {
return resultWithReadLock<TextAlignment>([&] {
return _alignment;
});
}
PulsePropertyGroup TextEntityItem::getPulseProperties() const {
return resultWithReadLock<PulsePropertyGroup>([&] {
return _pulseProperties;
});
}
}

View file

@ -15,7 +15,6 @@
#include "EntityItem.h"
#include "PulsePropertyGroup.h"
#include "TextEffect.h"
class TextEntityItem : public EntityItem {
public:
@ -100,6 +99,9 @@ public:
float getTextEffectThickness() const;
void setTextEffectThickness(float value);
TextAlignment getAlignment() const;
void setAlignment(TextAlignment value);
PulsePropertyGroup getPulseProperties() const;
private:
@ -117,6 +119,7 @@ private:
bool _unlit;
QString _font;
TextAlignment _alignment;
TextEffect _effect;
glm::u8vec3 _effectColor;
float _effectThickness;

View file

@ -8,8 +8,10 @@
#include "GLTexture.h"
#include <QObject>
#include <QtCore/QThread>
#include <NumericalConstants.h>
#include <ThreadHelpers.h>
#include "GLBackend.h"
@ -64,7 +66,7 @@ public:
protected:
class TextureBufferThread : public QThread {
public:
TextureBufferThread(GLTextureTransferEngineDefault& parent) : _parent(parent) { start(); }
TextureBufferThread(GLTextureTransferEngineDefault& parent) : _parent(parent) {}
protected:
void run() override {
@ -302,6 +304,10 @@ void GLTextureTransferEngineDefault::processTransferQueues() {
#if THREADED_TEXTURE_BUFFERING
if (!_transferThread) {
_transferThread = new TextureBufferThread(*this);
QString name = "TextureBufferThread";
_transferThread->setObjectName(name);
QObject::connect(_transferThread, &QThread::started, [name] { setThreadName(name.toStdString()); });
_transferThread->start();
}
#endif

View file

@ -19,6 +19,7 @@
#include <QFileInfo>
#include <SharedUtil.h>
#include <ThreadHelpers.h>
#include "AssetResourceRequest.h"
#include "FileResourceRequest.h"
@ -28,12 +29,16 @@
#include "NetworkingConstants.h"
ResourceManager::ResourceManager(bool atpSupportEnabled) : _atpSupportEnabled(atpSupportEnabled) {
_thread.setObjectName("Resource Manager Thread");
QString name = "Resource Manager Thread";
_thread.setObjectName(name);
if (_atpSupportEnabled) {
auto assetClient = DependencyManager::set<AssetClient>();
assetClient->moveToThread(&_thread);
QObject::connect(&_thread, &QThread::started, assetClient.data(), &AssetClient::initCaching);
QObject::connect(&_thread, &QThread::started, assetClient.data(), [assetClient, name] {
setThreadName(name.toStdString());
assetClient->initCaching();
});
}
_thread.start();

View file

@ -286,6 +286,7 @@ enum class EntityVersion : PacketVersion {
UseOriginalPivot,
UserAgent,
AllBillboardMode,
TextAlignment,
// Add new versions above here
NUM_PACKET_TYPE,

View file

@ -31,6 +31,7 @@
#include "Socket.h"
#include <Trace.h>
#include <Profile.h>
#include <ThreadHelpers.h>
#include "../NetworkLogging.h"
@ -67,24 +68,26 @@ const microseconds SendQueue::MINIMUM_ESTIMATED_TIMEOUT = milliseconds(10);
std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destination, SequenceNumber currentSequenceNumber,
MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK) {
Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*");
auto queue = std::unique_ptr<SendQueue>(new SendQueue(socket, destination, currentSequenceNumber,
currentMessageNumber, hasReceivedHandshakeACK));
// Setup queue private thread
QThread* thread = new QThread;
thread->setObjectName("Networking: SendQueue " + destination.objectName()); // Name thread for easier debug
QThread* thread = new QThread();
QString name = "Networking: SendQueue " + destination.objectName();
thread->setObjectName(name); // Name thread for easier debug
connect(thread, &QThread::started, [name] { setThreadName(name.toStdString()); });
connect(thread, &QThread::started, queue.get(), &SendQueue::run);
connect(queue.get(), &QObject::destroyed, thread, &QThread::quit); // Thread auto cleanup
connect(thread, &QThread::finished, thread, &QThread::deleteLater); // Thread auto cleanup
// Move queue to private thread and start it
queue->moveToThread(thread);
thread->start();
return queue;
}

View file

@ -41,6 +41,7 @@
#include "PulseMode.h"
#include "GizmoType.h"
#include "TextEffect.h"
#include "TextAlignment.h"
#include "OctreeConstants.h"
#include "OctreeElement.h"
@ -278,6 +279,7 @@ public:
static int unpackDataFromBytes(const unsigned char* dataBytes, PulseMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, GizmoType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, TextEffect& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, TextAlignment& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result);

View file

@ -29,6 +29,7 @@
#include "RenderControl.h"
#include "RenderEventHandler.h"
#include "TextureCache.h"
#include <ThreadHelpers.h>
// Time between receiving a request to render the offscreen UI actually triggering
// the render. Could possibly be increased depending on the framerate we expect to
@ -162,7 +163,9 @@ void SharedObject::setRootItem(QQuickItem* rootItem) {
// Create the render thread
_renderThread = new QThread();
_renderThread->setObjectName(objectName());
QString name = objectName();
_renderThread->setObjectName(name);
QObject::connect(_renderThread, &QThread::started, [name] { setThreadName("QML SharedObject " + name.toStdString()); });
_renderThread->start();
// Create event handler for the render thread

View file

@ -114,7 +114,8 @@ Transform Model::getTransform() const {
return transform;
} else if (_spatiallyNestableOverride) {
bool success;
Transform transform = _spatiallyNestableOverride->getTransform(success);
Transform transform = _billboardMode == BillboardMode::NONE ? _spatiallyNestableOverride->getTransform(success) :
_spatiallyNestableOverride->getTransformWithOnlyLocalRotation(success);
if (success) {
transform.setScale(getScale());
return transform;
@ -151,6 +152,7 @@ void Model::setOffset(const glm::vec3& offset) {
// if someone manually sets our offset, then we are no longer snapped to center
_snapModelToRegistrationPoint = false;
_snappedToRegistrationPoint = false;
_forceOffset = true;
}
void Model::calculateTextureInfo() {

View file

@ -43,18 +43,18 @@ float TextRenderer3D::getFontSize() const {
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds,
const QString& str, const glm::vec4& color, bool unlit, bool forward) {
if (_font) {
_font->drawString(batch, _drawInfo, str, color, glm::vec3(0.0f), 0, TextEffect::NO_EFFECT, { x, y }, bounds, 1.0f, unlit, forward);
_font->drawString(batch, _drawInfo, str, color, glm::vec3(0.0f), 0, TextEffect::NO_EFFECT, TextAlignment::LEFT, { x, y }, bounds, 1.0f, unlit, forward);
}
}
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, float scale,
const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor,
float effectThickness, TextEffect effect, bool unlit, bool forward) {
float effectThickness, TextEffect effect, TextAlignment alignment, bool unlit, bool forward) {
if (font != _family) {
_family = font;
_font = Font::load(_family);
}
if (_font) {
_font->drawString(batch, _drawInfo, str, color, effectColor, effectThickness, effect, { x, y }, bounds, scale, unlit, forward);
_font->drawString(batch, _drawInfo, str, color, effectColor, effectThickness, effect, alignment, { x, y }, bounds, scale, unlit, forward);
}
}

View file

@ -17,6 +17,7 @@
#include "text/Font.h"
#include "TextEffect.h"
#include "TextAlignment.h"
#include "FontFamilies.h"
class TextRenderer3D {
@ -30,7 +31,7 @@ public:
const QString& str, const glm::vec4& color, bool unlit, bool forward);
void draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, float scale,
const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor,
float effectThickness, TextEffect effect, bool unlit, bool forward);
float effectThickness, TextEffect effect, TextAlignment alignment, bool unlit, bool forward);
private:
TextRenderer3D(const char* family);

View file

@ -65,7 +65,6 @@ struct QuadBuilder {
vertices[3] = TextureVertex(min + size,
texMin + glm::vec2(texSize.x, 0.0f), bounds);
}
};
Font::Pointer Font::load(QIODevice& fontFile) {
@ -303,7 +302,18 @@ void Font::setupGPU() {
}
}
void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows) {
inline QuadBuilder adjustedQuadBuilderForAlignmentMode(const Glyph& glyph, glm::vec2 advance, float scale, float enlargeForShadows,
TextAlignment alignment, float rightSpacing) {
if (alignment == TextAlignment::RIGHT) {
advance.x += rightSpacing;
} else if (alignment == TextAlignment::CENTER) {
advance.x += 0.5f * rightSpacing;
}
return QuadBuilder(glyph, advance, scale, enlargeForShadows);
}
void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows,
TextAlignment alignment) {
drawInfo.verticesBuffer = std::make_shared<gpu::Buffer>();
drawInfo.indicesBuffer = std::make_shared<gpu::Buffer>();
drawInfo.indexCount = 0;
@ -314,15 +324,17 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
drawInfo.origin = origin;
float enlargedBoundsX = bounds.x - 0.5f * DOUBLE_MAX_OFFSET_PIXELS * float(enlargeForShadows);
float rightEdge = origin.x + enlargedBoundsX;
// Top left of text
glm::vec2 advance = origin;
std::vector<std::pair<Glyph, vec2>> glyphsAndCorners;
foreach(const QString& token, tokenizeForWrapping(str)) {
bool isNewLine = (token == QString('\n'));
bool forceNewLine = false;
// Handle wrapping
if (!isNewLine && (bounds.x != -1) && (advance.x + computeExtent(token).x > origin.x + enlargedBoundsX)) {
if (!isNewLine && (bounds.x != -1) && (advance.x + computeExtent(token).x > rightEdge)) {
// We are out of the x bound, force new line
forceNewLine = true;
}
@ -347,38 +359,8 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
if (!isNewLine) {
for (auto c : token) {
auto glyph = _glyphs[c];
quint16 verticesOffset = numVertices;
QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent), scale, enlargeForShadows);
drawInfo.verticesBuffer->append(qd);
numVertices += VERTICES_PER_QUAD;
// Sam's recommended triangle slices
// Triangle tri1 = { v0, v1, v3 };
// Triangle tri2 = { v1, v2, v3 };
// NOTE: Random guy on the internet's recommended triangle slices
// Triangle tri1 = { v0, v1, v2 };
// Triangle tri2 = { v2, v3, v0 };
// The problem here being that the 4 vertices are { ll, lr, ul, ur }, a Z pattern
// Additionally, you want to ensure that the shared side vertices are used sequentially
// to improve cache locality
//
// 2 -- 3
// | |
// | |
// 0 -- 1
//
// { 0, 1, 2 } -> { 2, 1, 3 }
quint16 indices[NUMBER_OF_INDICES_PER_QUAD];
indices[0] = verticesOffset + 0;
indices[1] = verticesOffset + 1;
indices[2] = verticesOffset + 2;
indices[3] = verticesOffset + 2;
indices[4] = verticesOffset + 1;
indices[5] = verticesOffset + 3;
drawInfo.indicesBuffer->append(sizeof(indices), (const gpu::Byte*)indices);
drawInfo.indexCount += NUMBER_OF_INDICES_PER_QUAD;
glyphsAndCorners.emplace_back(glyph, advance - glm::vec2(0.0f, _ascent));
// Advance by glyph size
advance.x += glyph.d;
@ -388,10 +370,71 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
advance.x += _spaceWidth;
}
}
std::vector<QuadBuilder> quadBuilders;
quadBuilders.reserve(glyphsAndCorners.size());
{
int i = glyphsAndCorners.size() - 1;
while (i >= 0) {
auto nextGlyphAndCorner = glyphsAndCorners[i];
float rightSpacing = rightEdge - (nextGlyphAndCorner.second.x + nextGlyphAndCorner.first.d);
quadBuilders.push_back(adjustedQuadBuilderForAlignmentMode(nextGlyphAndCorner.first, nextGlyphAndCorner.second, scale, enlargeForShadows,
alignment, rightSpacing));
i--;
while (i >= 0) {
auto prevGlyphAndCorner = glyphsAndCorners[i];
// We're to the right of the last character we checked, which means we're on a previous line, so we need to
// recalculate the spacing, so we exit this loop
if (prevGlyphAndCorner.second.x >= nextGlyphAndCorner.second.x) {
break;
}
quadBuilders.push_back(adjustedQuadBuilderForAlignmentMode(prevGlyphAndCorner.first, prevGlyphAndCorner.second, scale, enlargeForShadows,
alignment, rightSpacing));
nextGlyphAndCorner = prevGlyphAndCorner;
i--;
}
}
}
// The quadBuilders is backwards now because we looped over the glyphs backwards to adjust their alignment
for (int i = quadBuilders.size() - 1; i >= 0; i--) {
quint16 verticesOffset = numVertices;
drawInfo.verticesBuffer->append(quadBuilders[i]);
numVertices += VERTICES_PER_QUAD;
// Sam's recommended triangle slices
// Triangle tri1 = { v0, v1, v3 };
// Triangle tri2 = { v1, v2, v3 };
// NOTE: Random guy on the internet's recommended triangle slices
// Triangle tri1 = { v0, v1, v2 };
// Triangle tri2 = { v2, v3, v0 };
// The problem here being that the 4 vertices are { ll, lr, ul, ur }, a Z pattern
// Additionally, you want to ensure that the shared side vertices are used sequentially
// to improve cache locality
//
// 2 -- 3
// | |
// | |
// 0 -- 1
//
// { 0, 1, 2 } -> { 2, 1, 3 }
quint16 indices[NUMBER_OF_INDICES_PER_QUAD];
indices[0] = verticesOffset + 0;
indices[1] = verticesOffset + 1;
indices[2] = verticesOffset + 2;
indices[3] = verticesOffset + 2;
indices[4] = verticesOffset + 1;
indices[5] = verticesOffset + 3;
drawInfo.indicesBuffer->append(sizeof(indices), (const gpu::Byte*)indices);
drawInfo.indexCount += NUMBER_OF_INDICES_PER_QUAD;
}
}
void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color,
const glm::vec3& effectColor, float effectThickness, TextEffect effect,
const glm::vec3& effectColor, float effectThickness, TextEffect effect, TextAlignment alignment,
const glm::vec2& origin, const glm::vec2& bounds, float scale, bool unlit, bool forward) {
if (!_loaded || str == "") {
return;
@ -401,11 +444,12 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString
const int SHADOW_EFFECT = (int)TextEffect::SHADOW_EFFECT;
// If we're switching to or from shadow effect mode, we need to rebuild the vertices
if (str != drawInfo.string || bounds != drawInfo.bounds || origin != drawInfo.origin ||
if (str != drawInfo.string || bounds != drawInfo.bounds || origin != drawInfo.origin || alignment != _alignment ||
(drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) ||
(textEffect == SHADOW_EFFECT && scale != _scale)) {
_scale = scale;
buildVertices(drawInfo, str, origin, bounds, scale, textEffect == SHADOW_EFFECT);
_alignment = alignment;
buildVertices(drawInfo, str, origin, bounds, scale, textEffect == SHADOW_EFFECT, alignment);
}
setupGPU();

View file

@ -14,6 +14,7 @@
#include "Glyph.h"
#include "TextEffect.h"
#include "TextAlignment.h"
#include <gpu/Batch.h>
#include <gpu/Pipeline.h>
@ -58,7 +59,7 @@ public:
// Render string to batch
void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str, const glm::vec4& color,
const glm::vec3& effectColor, float effectThickness, TextEffect effect,
const glm::vec3& effectColor, float effectThickness, TextEffect effect, TextAlignment alignment,
const glm::vec2& origin, const glm::vec2& bound, float scale, bool unlit, bool forward);
static Pointer load(const QString& family);
@ -76,7 +77,8 @@ private:
glm::vec2 computeTokenExtent(const QString& str) const;
const Glyph& getGlyph(const QChar& c) const;
void buildVertices(DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows);
void buildVertices(DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows,
TextAlignment alignment);
void setupGPU();
@ -96,6 +98,7 @@ private:
float _spaceWidth { 0.0f };
float _scale { 0.0f };
TextAlignment _alignment;
bool _loaded { true };

View file

@ -87,7 +87,7 @@
#include "SettingHandle.h"
#include <AddressManager.h>
#include <NetworkingConstants.h>
#include <ThreadHelpers.h>
const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS {
"com.highfidelity.experimental.enableExtendedJSExceptions"
@ -429,13 +429,17 @@ void ScriptEngine::runInThread() {
// The thread interface cannot live on itself, and we want to move this into the thread, so
// the thread cannot have this as a parent.
QThread* workerThread = new QThread();
workerThread->setObjectName(QString("js:") + getFilename().replace("about:",""));
QString name = QString("js:") + getFilename().replace("about:","");
workerThread->setObjectName(name);
moveToThread(workerThread);
// NOTE: If you connect any essential signals for proper shutdown or cleanup of
// the script engine, make sure to add code to "reconnect" them to the
// disconnectNonEssentialSignals() method
connect(workerThread, &QThread::started, this, &ScriptEngine::run);
connect(workerThread, &QThread::started, this, [this, name] {
setThreadName(name.toStdString());
run();
});
connect(this, &QObject::destroyed, workerThread, &QThread::quit);
connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater);

View file

@ -14,6 +14,8 @@
#include <QDebug>
#include <QtCore/QCoreApplication>
#include "ThreadHelpers.h"
GenericThread::GenericThread() :
_stopThread(false),
_isThreaded(false) // assume non-threaded, must call initialize()
@ -36,8 +38,11 @@ void GenericThread::initialize(bool isThreaded, QThread::Priority priority) {
// match the thread name to our object name
_thread->setObjectName(objectName());
connect(_thread, &QThread::started, this, &GenericThread::started);
connect(_thread, &QThread::started, this, &GenericThread::threadRoutine);
connect(_thread, &QThread::started, this, [this] {
setThreadName("Generic thread " + objectName().toStdString());
started();
threadRoutine();
});
connect(_thread, &QThread::finished, this, &GenericThread::finished);
moveToThread(_thread);

View file

@ -21,6 +21,7 @@
#include "SettingManager.h"
#include "SharedLogging.h"
#include "SharedUtil.h"
#include "ThreadHelpers.h"
namespace Setting {
// This should only run as a post-routine in the QCoreApplication destructor
@ -53,7 +54,10 @@ namespace Setting {
thread->setObjectName("Settings Thread");
// Setup setting periodical save timer
QObject::connect(thread, &QThread::started, globalManager.data(), &Manager::startTimer);
QObject::connect(thread, &QThread::started, globalManager.data(), [globalManager] {
setThreadName("Settings Save Thread");
globalManager->startTimer();
});
QObject::connect(thread, &QThread::finished, globalManager.data(), &Manager::stopTimer);
// Setup manager threading affinity

View file

@ -754,6 +754,17 @@ const Transform SpatiallyNestable::getTransform(bool& success, int depth) const
return result;
}
const Transform SpatiallyNestable::getTransformWithOnlyLocalRotation(bool& success, int depth) const {
Transform result;
// return a world-space transform for this object's location
Transform parentTransform = getParentTransform(success, depth);
_transformLock.withReadLock([&] {
Transform::mult(result, parentTransform, _transform);
result.setRotation(_transform.getRotation());
});
return result;
}
const Transform SpatiallyNestable::getTransform() const {
bool success;
Transform result = getTransform(success);

View file

@ -82,6 +82,7 @@ public:
// world frame
virtual const Transform getTransform(bool& success, int depth = 0) const;
virtual const Transform getTransformWithOnlyLocalRotation(bool& success, int depth = 0) const;
virtual const Transform getTransform() const;
virtual void setTransform(const Transform& transform, bool& success);
virtual bool setTransform(const Transform& transform);

View file

@ -0,0 +1,25 @@
//
// Created by HifiExperiments on 2/9/21
// Copyright 2021 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "TextAlignment.h"
const char* textAlignmentNames[] = {
"left",
"center",
"right"
};
static const size_t TEXT_ALIGNMENT_NAMES = (sizeof(textAlignmentNames) / sizeof(textAlignmentNames[0]));
QString TextAlignmentHelpers::getNameForTextAlignment(TextAlignment alignment) {
if (((int)alignment <= 0) || ((int)alignment >= (int)TEXT_ALIGNMENT_NAMES)) {
alignment = (TextAlignment)0;
}
return textAlignmentNames[(int)alignment];
}

View file

@ -0,0 +1,40 @@
//
// Created by HifiExperiments on 2/9/21
// Copyright 2021 Vircadia contributors.
//
// 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_TextAlignment_h
#define hifi_TextAlignment_h
#include "QString"
/**jsdoc
* <p>A {@link Entities.EntityProperties-Text|Text} entity may use one of the following alignments:</p>
* <table>
* <thead>
* <tr><th>Value</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>"left"</code></td><td>Text is aligned to the left side.</td></tr>
* <tr><td><code>"center"</code></td><td>Text is centered.</td></tr>
* <tr><td><code>"right"</code></td><td>Text is aligned to the right side.</td></tr>
* </tbody>
* </table>
* @typedef {string} Entities.TextAlignment
*/
enum class TextAlignment {
LEFT = 0,
CENTER,
RIGHT
};
class TextAlignmentHelpers {
public:
static QString getNameForTextAlignment(TextAlignment alignment);
};
#endif // hifi_TextAlignment_h

View file

@ -20,7 +20,7 @@
* <tbody>
* <tr><td><code>"none"</code></td><td>No effect.</td></tr>
* <tr><td><code>"outline"</code></td><td>An outline effect.</td></tr>
* <tr><td><code>"outlineFill"</code></td><td>An outline effect, with fill.</td></tr>
* <tr><td><code>"outline fill"</code></td><td>An outline effect, with fill.</td></tr>
* <tr><td><code>"shadow"</code></td><td>A shadow effect.</td></tr>
* </tbody>
* </table>

View file

@ -32,6 +32,8 @@ void withLock(QMutex& lock, F function) {
function();
}
void setThreadName(const std::string& name);
void moveToNewNamedThread(QObject* object, const QString& name,
std::function<void(QThread*)> preStartCallback,
std::function<void()> startCallback,

View file

@ -595,7 +595,7 @@ void TabletProxy::gotoMenuScreen(const QString& submenu) {
}
void TabletProxy::loadQMLOnTopImpl(const QVariant& path, bool localSafeContext) {
if (QThread::currentThread() != thread()) {
if (QThread::currentThread() != thread()) {
qCWarning(uiLogging) << __FUNCTION__ << "may not be called directly by scripts";
return;
}

View file

@ -31,6 +31,7 @@
#include <display-plugins/CompositorHelper.h>
#include <ui-plugins/PluginContainer.h>
#include <gl/OffscreenGLCanvas.h>
#include <ThreadHelpers.h>
#include "OpenVrHelpers.h"
@ -494,6 +495,7 @@ bool OpenVrDisplayPlugin::internalActivate() {
_submitCanvas->doneCurrent();
});
}
connect(_submitThread.get(), &QThread::started, [] { setThreadName("OpenVR Submit Thread"); });
_submitCanvas->moveToThread(_submitThread.get());
}

View file

@ -49,6 +49,7 @@
#include <glm/gtc/quaternion.hpp>
#include <ui-plugins/PluginContainer.h>
#include <plugins/DisplayPlugin.h>
#include <ThreadHelpers.h>
#include <controllers/UserInputMapper.h>
#include <plugins/InputConfiguration.h>
@ -403,6 +404,7 @@ bool ViveControllerManager::activate() {
if (_viveProEye) {
_viveProEyeReadThread = std::make_shared<ViveProEyeReadThread>();
connect(_viveProEyeReadThread.get(), &QThread::started, [] { setThreadName("ViveProEyeReadThread"); });
_viveProEyeReadThread->start(QThread::HighPriority);
}
}

View file

@ -38,6 +38,9 @@
"textEffectThickness": {
"tooltip": "The magnitude of the text effect."
},
"textAlignment": {
"tooltip": "How the text is aligned within its left and right bounds."
},
"topMargin": {
"tooltip": "The top margin, in meters."
},

View file

@ -25,9 +25,10 @@
</head>
<body onload='loaded();' id="entity-list-body">
<div id="entity-list-menubar">
<input type="button" class="normal" id="selection" value="Select&#9662;" />
<input type="button" class="normal" id="actions" value="Edit&#9662;" />
<input type="button" class="normal" id="tools" value="Tools&#9662;" />
<input type="button" class="entity-list-menutitle" id="actions" value="Edit&#9662;" />
<input type="button" class="entity-list-menutitle" id="selection" value="Select&#9662;" />
<input type="button" class="entity-list-menutitle" id="transform" value="Transform&#9662;" />
<input type="button" class="entity-list-menutitle" id="tools" value="Tools&#9662;" />
</div>
<div id="entity-list-header">
<input type="button" class="glyph" id="refresh" value="F" />
@ -146,35 +147,22 @@
</div>
</button>
<div class="menu-separator"></div>
<button class="menu-button" id="moveEntitySelectionToAvatar" >
<button class="menu-button" id="exportSelectedEntities" >
<div class = "menu-item">
<div class = "menu-item-caption">Move Selected Entities to Avatar</div>
<div class = "menu-item-caption">Export Selected Entities</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
<div class="menu-separator"></div>
<button class="menu-button" id="rotateAsTheNextClickedSurface" >
<button class="menu-button" id="importEntitiesFromFile" >
<div class = "menu-item">
<div class = "menu-item-caption">Snap To Next Clicked Surface</div>
<div class = "menu-item-shortcut">0</div>
<div class = "menu-item-caption">Import Entities (.json) From a File</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
<button class="menu-button" id="quickRotate90x" >
<button class="menu-button" id="importEntitiesFromUrl" >
<div class = "menu-item">
<div class = "menu-item-caption">Rotate 90&deg; on X axis</div>
<div class = "menu-item-shortcut">7</div>
</div>
</button>
<button class="menu-button" id="quickRotate90y" >
<div class = "menu-item">
<div class = "menu-item-caption">Rotate 90&deg; on Y axis</div>
<div class = "menu-item-shortcut">8</div>
</div>
</button>
<button class="menu-button" id="quickRotate90z" >
<div class = "menu-item">
<div class = "menu-item-caption">Rotate 90&deg; on Z axis</div>
<div class = "menu-item-shortcut">9</div>
<div class = "menu-item-caption">Import Entities (.json) From a URL</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
</div>
@ -242,6 +230,40 @@
</div>
</button>
</div>
<div class="entity-list-menu" id="transform-menu" >
<button class="menu-button" id="moveEntitySelectionToAvatar" >
<div class = "menu-item">
<div class = "menu-item-caption">Move Selected Entities to Avatar</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
<div class="menu-separator"></div>
<button class="menu-button" id="rotateAsTheNextClickedSurface" >
<div class = "menu-item">
<div class = "menu-item-caption">Snap To Next Clicked Surface</div>
<div class = "menu-item-shortcut">0</div>
</div>
</button>
<div class="menu-separator"></div>
<button class="menu-button" id="quickRotate90x" >
<div class = "menu-item">
<div class = "menu-item-caption">Rotate 90&deg; on X axis</div>
<div class = "menu-item-shortcut">7</div>
</div>
</button>
<button class="menu-button" id="quickRotate90y" >
<div class = "menu-item">
<div class = "menu-item-caption">Rotate 90&deg; on Y axis</div>
<div class = "menu-item-shortcut">8</div>
</div>
</button>
<button class="menu-button" id="quickRotate90z" >
<div class = "menu-item">
<div class = "menu-item-caption">Rotate 90&deg; on Z axis</div>
<div class = "menu-item-shortcut">9</div>
</div>
</button>
</div>
<div class="entity-list-menu" id="tools-menu" >
<button class="menu-button" id="setCameraFocusToSelection" >
<div class = "menu-item">
@ -261,25 +283,6 @@
<div class = "menu-item-caption">Toggle Local/World Mode</div>
<div class = "menu-item-shortcut">T</div>
</div>
</button>
<div class="menu-separator"></div>
<button class="menu-button" id="exportSelectedEntities" >
<div class = "menu-item">
<div class = "menu-item-caption">Export Selected Entities</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
<button class="menu-button" id="importEntitiesFromFile" >
<div class = "menu-item">
<div class = "menu-item-caption">Import Entities (.json) From a File</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
<button class="menu-button" id="importEntitiesFromUrl" >
<div class = "menu-item">
<div class = "menu-item-caption">Import Entities (.json) From a URL</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
<div class="menu-separator"></div>
<button class="menu-button" id="gridActivator" >

View file

@ -155,7 +155,15 @@ const COLUMNS = {
propertyID: "lastEdited",
initialWidth: 0.38,
defaultSortOrder: DESCENDING_SORT,
},
},
urlWithPath: {
columnHeader: "URL",
dropdownLabel: "URL",
propertyID: "urlWithPath",
initialWidth: 0.54,
initiallyShown: false,
defaultSortOrder: ASCENDING_SORT,
},
};
const FILTER_TYPES = [
@ -226,9 +234,10 @@ let elEntityTable,
elToggleVisible,
elActionsMenu,
elSelectionMenu,
elToolsMenu,
elTransformMenu,
elToolsMenu,
elMenuBackgroundOverlay,
elHmdMultiSelect,
elHmdMultiSelect,
elHmdCopy,
elHmdCut,
elHmdPaste,
@ -313,6 +322,7 @@ function loaded() {
elHmdMultiSelect = document.getElementById("hmdmultiselect");
elActionsMenu = document.getElementById("actions");
elSelectionMenu = document.getElementById("selection");
elTransformMenu = document.getElementById("transform");
elToolsMenu = document.getElementById("tools");
elMenuBackgroundOverlay = document.getElementById("menuBackgroundOverlay");
elHmdCopy = document.getElementById("hmdcopy");
@ -395,6 +405,10 @@ function loaded() {
document.getElementById("menuBackgroundOverlay").style.display = "block";
document.getElementById("selection-menu").style.display = "block";
};
elTransformMenu.onclick = function() {
document.getElementById("menuBackgroundOverlay").style.display = "block";
document.getElementById("transform-menu").style.display = "block";
};
elToolsMenu.onclick = function() {
document.getElementById("menuBackgroundOverlay").style.display = "block";
document.getElementById("tools-menu").style.display = "block";
@ -919,7 +933,7 @@ function loaded() {
entityIds: selection,
}));
}
function updateEntityData(entityData) {
entities = [];
entitiesByID = {};
@ -929,13 +943,14 @@ function loaded() {
entityData.forEach(function(entity) {
let type = entity.type;
let filename = getFilename(entity.url);
let entityData = {
id: entity.id,
name: entity.name,
type: type,
url: entity.certificateID === "" ? filename : "<i>" + CERTIFIED_PLACEHOLDER + "</i>",
fullUrl: entity.certificateID === "" ? filename : CERTIFIED_PLACEHOLDER,
urlWithPath: entity.certificateID === "" ? entity.url : "<i>" + CERTIFIED_PLACEHOLDER + "</i>",
locked: entity.locked,
visible: entity.visible,
certificateID: entity.certificateID,
@ -975,11 +990,13 @@ function loaded() {
let searchFilter = searchTerm === '' || (e.name.toLowerCase().indexOf(searchTerm) > -1 ||
e.type.toLowerCase().indexOf(searchTerm) > -1 ||
e.fullUrl.toLowerCase().indexOf(searchTerm) > -1 ||
(e.urlWithPath.toLowerCase().indexOf(searchTerm) > -1 &&
columnsByID["urlWithPath"].elTh.style.visibility === "visible") ||
e.id.toLowerCase().indexOf(searchTerm) > -1);
return typeFilter && searchFilter;
});
});
PROFILE("sort", function() {
let isAscendingSort = currentSortOrder === ASCENDING_SORT;
let isDefaultSort = currentSortOrder === COLUMNS[currentSortColumnID].defaultSortOrder;
@ -1850,6 +1867,7 @@ function loaded() {
document.getElementById("menuBackgroundOverlay").style.display = "none";
document.getElementById("selection-menu").style.display = "none";
document.getElementById("actions-menu").style.display = "none";
document.getElementById("transform-menu").style.display = "none";
document.getElementById("tools-menu").style.display = "none";
}

View file

@ -239,6 +239,17 @@ const GROUPS = [
decimals: 2,
propertyID: "textEffectThickness",
},
{
label: "Alignment",
type: "dropdown",
options: {
left: "Left",
center: "Center",
right: "Right"
},
propertyID: "textAlignment",
propertyName: "alignment", // actual entity property name
},
{
label: "Top Margin",
type: "number-draggable",

View file

@ -1364,10 +1364,46 @@ div#grid-section, body#entity-list-body {
}
#entity-list-menubar {
margin: 0px -8px 6px -8px;
padding: 0px 8px 0px 8px;
padding: 0px 9px 0px 9px;
border: none;
background-color: #000;
background: linear-gradient(#343434 20%, #000 100%);
background: linear-gradient(#343434 30%, #000 100%);
}
input[type=button].entity-list-menutitle {
font-family: FiraSans-SemiBold;
font-size: 15px;
text-transform: none;
vertical-align: top;
height: 28px;
min-width: 30px;
padding: 0px 8px;
margin-right: 6px;
border-radius: 5px;
border: none;
color: #fff;
background-color: #000;
background: linear-gradient(#343434 30%, #000 100%);
cursor: pointer;
}
input[type=button].entity-list-menutitle:enabled:hover {
background: linear-gradient(#000, #000);
border: none;
}
input[type=button].entity-list-menutitle:active {
background: linear-gradient(#343434, #343434);
}
input[type=button].entity-list-menutitle:disabled {
color: #252525;
background: linear-gradient(#575757 30%, #252525 100%);
}
input[type=button].entity-list-menutitle[pressed=pressed] {
color: #00b4ef;
}
input[type=button].entity-list-menutitle:focus {
outline: none;
color: #000000;
background: linear-gradient(#c0c0c0, #c0c0c0);
border-radius: 5px 5px 0px 0px;
}
.icon-input-radius input {
position: relative;