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/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/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/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/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/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/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 @@ +
+ + + + + + + +
- - - -