merge with master

This commit is contained in:
HifiExperiments 2021-02-25 19:06:40 -08:00
commit c47351c4f2
74 changed files with 758 additions and 273 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

@ -72,9 +72,11 @@ Where `/path/to/directory` is the path to a directory where you wish the build f
// The identifying tag of the release.
CMAKE_BACKTRACE_TOKEN
// The release version.
// The release version, e.g., 2021.3.2.
RELEASE_NUMBER
// The build commit.
// The release name, e.g., Eos.
RELEASE_NAME
// The build commit, e.g., use a Git hash for the most recent commit in the branch - fd6973b.
BUILD_NUMBER
// The type of release.

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

@ -4,6 +4,7 @@
#
# Created by Leonardo Murillo on 12/16/2015.
# Copyright 2015 High Fidelity, Inc.
# 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
@ -31,7 +32,7 @@ macro(GENERATE_INSTALLERS)
set(CPACK_PACKAGE_NAME ${_DISPLAY_NAME})
set(CPACK_PACKAGE_VENDOR "Vircadia")
set(CPACK_PACKAGE_VERSION ${BUILD_VERSION})
set(CPACK_PACKAGE_FILE_NAME "Vircadia${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}")
set(CPACK_PACKAGE_FILE_NAME "Vircadia${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}-${RELEASE_NAME}")
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME})
if (PR_BUILD)

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

@ -112,8 +112,7 @@ MessageBox {
popup.button1text = 'CANCEL'
popup.titleText = 'Get Avatars'
popup.bodyText = 'Get avatars from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
'Wear avatars in <b><a href="app://purchases">Inventory.</a></b>'
popup.bodyText = 'Get avatars from the Community Bazaar. (Coming soon!)'
popup.onLinkClicked = function(link) {
popup.close();

View file

@ -4,6 +4,56 @@
"/": "/0.155245,-0.941538,23.9289/0,0.791589,0,0.611053"
},
"Entities": [
{
"id": "{0a199807-4a83-4286-b09c-f21124627c3e}",
"type": "Box",
"name": "Config Wizard Loader",
"lastEdited": 1613737207915514,
"visible": false,
"position": {
"x": -1.2722,
"y": 0.4266,
"z": 24.2307
},
"dimensions": {
"x": 0.20000000298023224,
"y": 0.20000000298023224,
"z": 0.20000000298023224
},
"rotation": {
"x": 0,
"y": -0.7660443782806396,
"z": 0,
"w": -0.6427876949310303
},
"created": 1613736996738696,
"lastEditedBy": "{ff9b500e-e450-4127-b41f-1c42be16f71b}",
"queryAACube": {
"x": -0.17320507764816284,
"y": -0.17320507764816284,
"z": -0.17320507764816284,
"scale": 0.3464101552963257
},
"grab": {
"grabbable": false
},
"damping": 0,
"angularDamping": 0,
"collisionless": true,
"ignoreForCollisions": true,
"script": "https://cdn-1.vircadia.com/us-e-1/DomainContent/Tutorial/Apps/configWizard/dist/wizardLoader.js",
"color": {
"red": 0,
"green": 180,
"blue": 239
},
"shape": "Cube",
"clientOnly": false,
"avatarEntity": false,
"localEntity": false,
"faceCamera": false,
"isFacingAvatar": false
},
{
"id": "{eb485a2d-2040-42f6-a960-51c88e2434b9}",
"type": "Box",

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

@ -372,6 +372,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()) {
@ -383,12 +388,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;
@ -405,15 +410,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;
}
@ -433,7 +438,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
_prevIsTransparent = transparent;
updateModelTransformAndBound();
updateModelTransformAndBound(entity);
_moving = entity->isMovingRelativeToParent();
_visible = entity->getVisible();

View file

@ -107,7 +107,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; }
@ -115,16 +115,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;
// Shared methods for entities that support materials
using MaterialMap = std::unordered_map<std::string, graphics::MultiMaterial>;
bool needsRenderUpdateFromMaterials() const;
@ -168,6 +166,9 @@ protected:
const EntityItemPointer _entity;
QUuid _entityID;
signals:
void requestRenderUpdate();
};
template <typename T>

View file

@ -264,8 +264,6 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) {
bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES;
bool forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD;
//geometryCache->bindSimpleProgram(batch, false, transparent, wireframe, true, true, forward, materials.getCullFaceMode());
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition(), true));
batch.setModelTransform(transform);

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

@ -37,15 +37,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());
});
});
}
@ -132,7 +129,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 (wireframe) {

View file

@ -42,10 +42,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

@ -76,6 +76,7 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe
_effect = entity->getTextEffect();
_effectColor = toGlm(entity->getTextEffectColor());
_effectThickness = entity->getTextEffectThickness();
_alignment = entity->getAlignment();
bool materialChanged = false;
glm::vec3 color = toGlm(entity->getBackgroundColor());
@ -326,12 +327,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;
@ -347,18 +348,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

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

@ -1,60 +0,0 @@
Collection of scripts to create server distribution packages. Most of these scripts assume
use of the build script at https://github.com/vircadia/vircadia-builder, specifically that
the following directory structure exists
base folder/
source/ git checkout
build/ result of cmake build
qt5-install/ installed or built Qt5 installation
These scripts assume that the current directory is the pkg-scripts folder inside of the source directory
and that the base folder can be reached by going to "../..". This may not work if pkg-scripts is a symlink;
adding an VIRCADIA=~/Vircadia to the beginning of the commandline will override where it looks for the base folder
Ubuntu:
DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server
This script will retrieve the current git commit date and hash and assemble a version from it.
It will attempt construct a .deb file in the pkg-scripts folder
Amazon Linux 2:
./make-rpm-server
This script will retrieve the current git commit date and hash and assemble a version from it.
It will attempt construct an .rpm file in the pkg-scripts folder
Docker:
./make-docker-server
This script will attempt to create a docker container
Results:
The following directory structure is created for binaries:
/opt/vircadia - executables
/opt/vircadia/lib - private shared libraries required for executables
/opt/vircadia/resources - files required by domain-server administrative website
/opt/vircadia/plugins - files required by assignment-client, mainly for audio codecs
The following systemd services are installed in /usr/lib/systemd/system:
vircadia-assignment-client.service
vircadia-domain-server.service
vircadia-server.target - used to launch/shutdown the two prior services
vircadia-assignment-client@.service
vircadia-domain-server@.service
vircadia-server@.target - used to launch/shutdown the two prior services
The top three services in this list are the "normal" services that launch Vircadia
in the typical fashion. The bottom three services are "template" services designed
to permit multiple services to be installed and running on a single machine.
The script "/opt/vircadia/new-server serverName basePort" will do the necessary
setup for a new domain with the specified server name and port. Upon installation
the package will create and launch a domain named "default" at base port 40100.
The domain name here has nothing to do with the name people will use to find your
domain and has nothing to do with "place names", it is only used to locate the files
used to configure and run the domain on your server.
The server stores its files in the following locations:
/var/lib/vircadia/.local - "unnamed" services (the default location for Vircadia servers)
/var/lib/vircadia/serverName - "named" (template) domains
/etc/opt/vircadia - environment variables when launching named domains

88
pkg-scripts/README.md Normal file
View file

@ -0,0 +1,88 @@
# Vircadia Server Packaging Scripts
Collection of scripts to create server distribution packages. Most of these scripts assume
use of the build script at https://github.com/vircadia/vircadia-builder, specifically that
the following directory structure exists:
```
base folder/
source/ git checkout
build/ result of cmake build
qt5-install/ installed or built Qt5 installation
```
These scripts assume that the current directory is the pkg-scripts folder inside of the source directory
and that the base folder can be reached by going to `../..`. This may not work if pkg-scripts is a symlink; adding an VIRCADIA=~/Vircadia to the beginning of the commandline will override where it looks for the base folder.
## Ubuntu
```
DEBVERSION="Semver e.g. 2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server
```
This script will retrieve the current git commit date and hash and append it to your specified version.
It will attempt construct a .deb file in the pkg-scripts folder
## Amazon Linux 2
You will need to install `rpm-build` if you have not already.
```
sudo yum install rpm-build
```
Then, run the build script.
```
RPMVERSION="Semver e.g. 2021.1.0" ./make-rpm-server
```
This script will retrieve the current git commit date and hash and append it to your specified version.
It will attempt construct an .rpm file in the pkg-scripts folder
## Docker
```
./make-docker-server
```
This script will attempt to create a docker container.
## Results
### Binaries
The following directory structure is created for binaries:
```
/opt/vircadia - executables
/opt/vircadia/lib - private shared libraries required for executables
/opt/vircadia/resources - files required by domain-server administrative website
/opt/vircadia/plugins - files required by assignment-client, mainly for audio codecs
```
### Services
The following systemd services are installed in `/usr/lib/systemd/system`:
```
vircadia-assignment-client.service
vircadia-domain-server.service
vircadia-server.target - used to launch/shutdown the two prior services
vircadia-assignment-client@.service
vircadia-domain-server@.service
vircadia-server@.target - used to launch/shutdown the two prior services
```
The top three services in this list are the "normal" services that launch Vircadia
in the typical fashion. The bottom three services are "template" services designed
to permit multiple services to be installed and running on a single machine.
The script `/opt/vircadia/new-server serverName basePort` will do the necessary
setup for a new domain with the specified server name and port. Upon installation
the package will create and launch a domain named "default" at base port 40100.
The domain name here has nothing to do with the name people will use to find your
domain and has nothing to do with "place names", it is only used to locate the files
used to configure and run the domain on your server.
### Stored Files
The server stores its files in the following locations:
```
/var/lib/vircadia/.local - "unnamed" services (the default location for Vircadia servers)
/var/lib/vircadia/serverName - "named" (template) domains
/etc/opt/vircadia - environment variables when launching named domains
```

View file

@ -6,11 +6,11 @@ fi
GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"`
GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7`
VERSION=2020.2.0-asteria-$GITDATE-$GITCOMMIT
VERSION=$DEBVERSION-$GITDATE-$GITCOMMIT
sudo apt-get install chrpath binutils dh-make
DEB_BUILD_ROOT=temp-make-deb/vircadia-server-$VERSION-0ubuntu1
DEB_BUILD_ROOT=temp-make-deb/vircadia-server_$VERSION-0ubuntu1
rm -r temp-make-deb
mkdir -p $DEB_BUILD_ROOT

View file

@ -5,7 +5,7 @@ if [ "$VIRCADIA" = "" ]; then
fi
GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"`
GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7`
VERSION=2020.2.0_ASTERIA_${GITDATE}_${GITCOMMIT}
VERSION=${RPMVERSION}_${GITDATE}_${GITCOMMIT}
SOFILES=`ls \
$VIRCADIA/build/libraries/*/*.so \

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

@ -36,6 +36,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
"system/inspect.js",
"system/keyboardShortcuts/keyboardShortcuts.js",
"system/checkForUpdates.js",
"system/onEscape.js",
"system/onFirstRun.js",
"system/appreciate/appreciate_app.js"
];

View file

@ -3,10 +3,9 @@
//
// away.js
//
// examples
//
// Created by Howard Stearns 11/3/15
// Copyright 2015 High Fidelity, Inc.
// 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
@ -255,17 +254,6 @@ function setActiveProperties() {
Script.clearInterval(avatarMovedInterval);
}
function maybeGoActive(event) {
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
return;
}
if (!isAway && (event.text === 'ESC')) {
goAway();
} else {
goActive();
}
}
var wasHmdActive = HMD.active;
var wasMouseCaptured = Reticle.mouseCaptured;
@ -329,12 +317,24 @@ var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable";
var handleMessage = function(channel, message, sender) {
if (channel === CHANNEL_AWAY_ENABLE && sender === MyAvatar.sessionUUID) {
print("away.js | Got message on Hifi-Away-Enable: ", message);
setEnabled(message === 'enable');
if (message === 'enable') {
setEnabled(true);
} else if (message === 'toggle') {
toggleAway();
}
}
};
Messages.subscribe(CHANNEL_AWAY_ENABLE);
Messages.messageReceived.connect(handleMessage);
function toggleAway() {
if (!isAway) {
goAway();
} else {
goActive();
}
}
var maybeIntervalTimer = Script.setInterval(function() {
maybeMoveOverlay();
maybeGoAway();
@ -343,7 +343,6 @@ var maybeIntervalTimer = Script.setInterval(function() {
Controller.mousePressEvent.connect(goActive);
Controller.keyPressEvent.connect(maybeGoActive);
// Note peek() so as to not interfere with other mappings.
eventMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(goActive);
@ -371,7 +370,6 @@ Script.scriptEnding.connect(function () {
HMD.awayStateWhenFocusLostInVRChanged.disconnect(awayStateWhenFocusLostInVRChanged);
Controller.disableMapping(eventMappingName);
Controller.mousePressEvent.disconnect(goActive);
Controller.keyPressEvent.disconnect(maybeGoActive);
Messages.messageReceived.disconnect(handleMessage);
Messages.unsubscribe(CHANNEL_AWAY_ENABLE);
});

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."
},
@ -584,7 +587,7 @@
"tooltip": "The mode in which to draw an entity, either \"Solid\" or \"Wireframe\"."
},
"billboardMode": {
"tooltip": "Determines if and how the entity will face the camera.",
"tooltip": "Determines if and how the entity will face the camera."
},
"renderWithZones": {
"tooltip": "If set, this entity will only render when your avatar is inside of a zone in this list."

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;

View file

@ -0,0 +1,34 @@
'use strict';
//
// onEscape.js
//
// Created by Kalila L. on Feb 3 2021.
// Copyright 2021 Vircadia contributors.
//
// This script manages actions when the user triggers an "escape" key or action.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() { // BEGIN LOCAL_SCOPE
function maybeEscapeKeyPressed (event) {
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
return;
}
if (event.text === 'ESC') {
var CHANNEL_AWAY_ENABLE = 'Hifi-Away-Enable';
Messages.sendMessage(CHANNEL_AWAY_ENABLE, 'toggle', true);
}
}
Controller.keyPressEvent.connect(maybeEscapeKeyPressed);
Script.scriptEnding.connect(function () {
Controller.keyPressEvent.disconnect(maybeEscapeKeyPressed);
});
}());

View file

@ -13,6 +13,7 @@
//
(function() { // BEGIN LOCAL_SCOPE
// Check to see if we should run this script or bail...
var SETTING_TO_CHECK = 'firstRun';
var DEFAULT_DISPLAY_NAME = '';
@ -20,6 +21,8 @@
return;
}
// If this is our first run, then proceed...
if (MyAvatar.displayName === '') {
var selectedDisplayName = Window.prompt('Enter a display name.', MyAvatar.displayName);