diff --git a/.gitignore b/.gitignore index 82c9535c5d..195def9544 100644 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,4 @@ tools/unity-avatar-exporter server-console/package-lock.json vcpkg/ /tools/nitpick/compiledResources +qt/ \ No newline at end of file diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index fc73d7905e..c52f6a1d8f 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -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_ diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 50eee258ab..adc7f5e3c5 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -33,6 +33,7 @@ #include #include +#include #include "AssignmentClientLogging.h" #include "AssignmentFactory.h" @@ -235,10 +236,13 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointergetDomainHandler().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 diff --git a/assignment-client/src/audio/AudioMixerSlavePool.cpp b/assignment-client/src/audio/AudioMixerSlavePool.cpp index 5f6936cb2d..e8a2909acb 100644 --- a/assignment-client/src/audio/AudioMixerSlavePool.cpp +++ b/assignment-client/src/audio/AudioMixerSlavePool.cpp @@ -11,9 +11,13 @@ #include "AudioMixerSlavePool.h" +#include + #include #include +#include + 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); } diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index f72ab0ac05..4ef220df29 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -34,6 +34,7 @@ #include #include +#include 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(); }); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 1d7ef7fcfc..bb79bb367a 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -60,6 +60,7 @@ #include #include +#include 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->moveToThread(&_assetClientThread); + connect(&_assetClientThread, &QThread::started, [name] { setThreadName(name.toStdString()); }); _assetClientThread.start(); // add whatever static assignments that have been parsed to the queue addStaticAssignmentsToQueue(); diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml index e7d4f645aa..931233622a 100644 --- a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml @@ -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 diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index 3e2e74b3d5..8fdaf5feb4 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -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 diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index 8aaafbd0fe..78292b8bcd 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -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 diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 9cbbd48a22..0984f09141 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -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 diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index ebef5f85c5..a90824f3ad 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -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 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 07fecc13dc..06ce6c3d6c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -254,6 +254,7 @@ #include "AboutUtil.h" #include "ExternalResource.h" +#include #if defined(Q_OS_WIN) #include @@ -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); diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 04e5666106..9494a84fab 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -15,6 +15,7 @@ #include #include +#include #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); diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 15ff09d13d..5ac808d3fb 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -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); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index f601b99779..ba83ddff55 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -50,6 +50,7 @@ #include "CompositorHelper.h" #include "Logging.h" #include "RefreshRateController.h" +#include 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(); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index b2670e3bce..3e13b301df 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -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(); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 7f0e1e16ee..9392d61e75 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -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 std::shared_ptr asTypedEntity() { return std::static_pointer_cast(_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 diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 4d3a79c9af..3c245e5d4f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -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); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 65e9f57b02..e4bb6952a7 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -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(_entity); + auto lineEntity = std::static_pointer_cast(entity); AABox bound; lineEntity->computeTightLocalBoundingBox(bound); bound.transform(newModelTransform); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index c4fbb9a776..6e5068c24f 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -25,7 +25,7 @@ class PolyLineEntityRenderer : public TypedEntityRenderer { public: PolyLineEntityRenderer(const EntityItemPointer& entity); - void updateModelTransformAndBound() override; + void updateModelTransformAndBound(const EntityItemPointer& entity) override; virtual bool isTransparent() const override; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 859accf01a..5c8374b937 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -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; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 674d7c297d..35739c2430 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -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) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index fe62ad48b9..403d389378 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -45,10 +45,6 @@ private: std::shared_ptr _material { std::make_shared() }; glm::vec3 _color { NAN }; float _alpha { NAN }; - - glm::vec3 _position; - glm::vec3 _dimensions; - glm::quat _orientation; }; } } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 16d9afb913..4248d3f2cd 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -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(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 { diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 0f736d1229..c4cfaab9a2 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -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 }; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9d3947ab56..6e761698c9 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -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). /// diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 152e003913..b08817044a 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -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(); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5f925c4b19..5c52cb2708 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -309,7 +309,6 @@ void EntityItemProperties::setScreenshareFromString(const QString& mode) { } } - inline void addTextEffect(QHash& lookup, TextEffect effect) { lookup[TextEffectHelpers::getNameForTextEffect(effect)] = effect; } const QHash stringToTextEffectLookup = [] { QHash toReturn; @@ -328,6 +327,23 @@ void EntityItemProperties::setTextEffectFromString(const QString& effect) { } } +inline void addTextAlignment(QHash& lookup, TextAlignment alignment) { lookup[TextAlignmentHelpers::getNameForTextAlignment(alignment)] = alignment; } +const QHash stringToTextAlignmentLookup = [] { + QHash 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 0.00.5. + * @property {Entities.TextAlignment} alignment="left" - How the text is aligned against its background. * @property {boolean} faceCamera - true if billboardMode is "yaw", false * if it isn't. Setting this property to false sets the billboardMode to "none". *

Deprecated: This property is deprecated and will be removed.

@@ -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 EntityItemProperties::listChangedProperties() { if (textEffectThicknessChanged()) { out += "textEffectThickness"; } + if (alignmentChanged()) { + out += "alignment"; + } // Zone getKeyLight().listChangedProperties(out); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 0de2998a13..91de540062 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -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); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index d9337d70b5..e0b5a04094 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -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 diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 90c185be8b..f330058a78 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -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); diff --git a/libraries/entities/src/GizmoEntityItem.cpp b/libraries/entities/src/GizmoEntityItem.cpp index 694371d739..b6c5713c1a 100644 --- a/libraries/entities/src/GizmoEntityItem.cpp +++ b/libraries/entities/src/GizmoEntityItem.cpp @@ -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); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 61362896ee..321e92b9f0 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -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) { diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 6e92b225a1..a00327251c 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -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; diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 5ed9f69b5a..49dc0d7c90 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -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(); } diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 7858e62f7d..4349eefd2d 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -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); diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index ebe536cae8..021e753710 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -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([&] { + return _alignment; + }); +} + PulsePropertyGroup TextEntityItem::getPulseProperties() const { return resultWithReadLock([&] { return _pulseProperties; }); -} \ No newline at end of file +} diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 3e58831a72..fff7f57bfb 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -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; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp index af865b3ad7..0712f68541 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp @@ -8,8 +8,10 @@ #include "GLTexture.h" +#include #include #include +#include #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 diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 0b9d5a2d60..f4e4cca482 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -19,6 +19,7 @@ #include #include +#include #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->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(); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index f54b004538..1d96969035 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -286,6 +286,7 @@ enum class EntityVersion : PacketVersion { UseOriginalPivot, UserAgent, AllBillboardMode, + TextAlignment, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 2997c272f9..63447f6e70 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -31,6 +31,7 @@ #include "Socket.h" #include #include +#include #include "../NetworkLogging.h" @@ -67,24 +68,26 @@ const microseconds SendQueue::MINIMUM_ESTIMATED_TIMEOUT = milliseconds(10); std::unique_ptr 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(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; } diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 2050dd1487..583f090942 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -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); diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp index 55788c8a02..c5411a6195 100644 --- a/libraries/qml/src/qml/impl/SharedObject.cpp +++ b/libraries/qml/src/qml/impl/SharedObject.cpp @@ -29,6 +29,7 @@ #include "RenderControl.h" #include "RenderEventHandler.h" #include "TextureCache.h" +#include // 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 diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 1c9fdc50d6..9fa822994c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -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() { diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 264ff409a6..76d8374fb7 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -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); } } \ No newline at end of file diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index 3a88ed555c..edccf1429c 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -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); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 425e4f2da5..7147c10872 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -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(); drawInfo.indicesBuffer = std::make_shared(); 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> 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 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(); diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index c75f0f746f..322e96439e 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -14,6 +14,7 @@ #include "Glyph.h" #include "TextEffect.h" +#include "TextAlignment.h" #include #include @@ -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 }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f42178b023..e553672b02 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -87,7 +87,7 @@ #include "SettingHandle.h" #include #include - +#include 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); diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index e35c74e68a..4e9dba8893 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -14,6 +14,8 @@ #include #include +#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); diff --git a/libraries/shared/src/SettingInterface.cpp b/libraries/shared/src/SettingInterface.cpp index 04da35656e..ab5b3e380a 100644 --- a/libraries/shared/src/SettingInterface.cpp +++ b/libraries/shared/src/SettingInterface.cpp @@ -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 diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index c1c1fd38d9..670b03611b 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -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); diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 01e3b045ad..29f23afdfb 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -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); diff --git a/libraries/shared/src/TextAlignment.cpp b/libraries/shared/src/TextAlignment.cpp new file mode 100644 index 0000000000..533234f1da --- /dev/null +++ b/libraries/shared/src/TextAlignment.cpp @@ -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]; +} \ No newline at end of file diff --git a/libraries/shared/src/TextAlignment.h b/libraries/shared/src/TextAlignment.h new file mode 100644 index 0000000000..829b07dc36 --- /dev/null +++ b/libraries/shared/src/TextAlignment.h @@ -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 + *

A {@link Entities.EntityProperties-Text|Text} entity may use one of the following alignments:

+ * + * + * + * + * + * + * + * + * + *
ValueDescription
"left"Text is aligned to the left side.
"center"Text is centered.
"right"Text is aligned to the right side.
+ * @typedef {string} Entities.TextAlignment + */ + +enum class TextAlignment { + LEFT = 0, + CENTER, + RIGHT +}; + +class TextAlignmentHelpers { +public: + static QString getNameForTextAlignment(TextAlignment alignment); +}; + +#endif // hifi_TextAlignment_h \ No newline at end of file diff --git a/libraries/shared/src/TextEffect.h b/libraries/shared/src/TextEffect.h index 91bd5ec60c..3e205f72fe 100644 --- a/libraries/shared/src/TextEffect.h +++ b/libraries/shared/src/TextEffect.h @@ -20,7 +20,7 @@ * * "none"No effect. * "outline"An outline effect. - * "outlineFill"An outline effect, with fill. + * "outline fill"An outline effect, with fill. * "shadow"A shadow effect. * * diff --git a/libraries/shared/src/ThreadHelpers.h b/libraries/shared/src/ThreadHelpers.h index d236344dc5..42de117e67 100644 --- a/libraries/shared/src/ThreadHelpers.h +++ b/libraries/shared/src/ThreadHelpers.h @@ -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 preStartCallback, std::function startCallback, diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 61c74dc17c..e80b11915a 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -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; } diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index c5eb740325..b5c1f713e7 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #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()); } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 3629698e11..ac41502a38 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -403,6 +404,7 @@ bool ViveControllerManager::activate() { if (_viveProEye) { _viveProEyeReadThread = std::make_shared(); + connect(_viveProEyeReadThread.get(), &QThread::started, [] { setThreadName("ViveProEyeReadThread"); }); _viveProEyeReadThread->start(QThread::HighPriority); } } diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 2455b9155e..21b41bd354 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -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." }, diff --git a/scripts/system/create/entityList/html/entityList.html b/scripts/system/create/entityList/html/entityList.html index 93585c7338..e054ca121b 100644 --- a/scripts/system/create/entityList/html/entityList.html +++ b/scripts/system/create/entityList/html/entityList.html @@ -25,9 +25,10 @@
- - - + + + +
@@ -146,35 +147,22 @@
- - - - - - @@ -242,6 +230,40 @@ +
+ + + + + + + +
- - - -