diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index a281c1d097..381edd8731 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -25,28 +25,22 @@ using namespace render; using namespace render::entities; static const int FIXED_FONT_POINT_SIZE = 40; -const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 92.0f; // Determined through experimentation to fit font to line - // height. +const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 92.0f; // Determined through experimentation to fit font to line height. const float LINE_SCALE_RATIO = 1.2f; TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) : Parent(entity), _textRenderer(TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f)) { - auto geometryCache = DependencyManager::get(); - if (geometryCache) { - _geometryID = geometryCache->allocateID(); - } -} - -TextEntityRenderer::~TextEntityRenderer() { - auto geometryCache = DependencyManager::get(); - if (_geometryID && geometryCache) { - geometryCache->releaseID(_geometryID); - } } bool TextEntityRenderer::isTransparent() const { - return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE; + return Parent::isTransparent() || _backgroundAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE; +} + +bool TextEntityRenderer::isTextTransparent() const { + return resultWithReadLock([&] { + return Parent::isTransparent() || _textAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE; + }); } Item::Bound TextEntityRenderer::getBound() { @@ -61,7 +55,7 @@ Item::Bound TextEntityRenderer::getBound() { } ShapeKey TextEntityRenderer::getShapeKey() { - auto builder = render::ShapeKey::Builder().withOwnPipeline(); + auto builder = render::ShapeKey::Builder(); if (isTransparent()) { builder.withTranslucent(); } @@ -134,6 +128,7 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen _dimensions = entity->getScaledDimensions(); updateModelTransformAndBound(); _renderTransform = getModelTransform(); + _renderTransform.postScale(_dimensions); }); }); } @@ -152,65 +147,49 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe _rightMargin = entity->getRightMargin(); _topMargin = entity->getTopMargin(); _bottomMargin = entity->getBottomMargin(); + updateTextRenderItem(); }); } void TextEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableTextEntityItem::render"); - - glm::vec4 textColor; - glm::vec4 backgroundColor; - Transform modelTransform; - glm::vec3 dimensions; - BillboardMode billboardMode; - bool forward; - withReadLock([&] { - modelTransform = _renderTransform; - dimensions = _dimensions; - billboardMode = _billboardMode; - - float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - textColor = glm::vec4(_textColor, fadeRatio * _textAlpha); - textColor = EntityRenderer::calculatePulseColor(textColor, _pulseProperties, _created); - backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha); - backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created); - forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD; - }); - - // Render background - static const float SLIGHTLY_BEHIND = -0.005f; - glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND); - glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND); - - // Batch render calls Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - // FIXME: we need to find a better way of rendering text so we don't have to do this - if (forward) { - DependencyManager::get()->setupKeyLightBatch(args, batch); + glm::vec4 backgroundColor; + Transform modelTransform; + BillboardMode billboardMode; + PrimitiveMode primitiveMode; + RenderLayer renderLayer; + withReadLock([&] { + modelTransform = _renderTransform; + billboardMode = _billboardMode; + primitiveMode = _primitiveMode; + renderLayer = _renderLayer; + + float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; + backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha); + backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created); + }); + + if (backgroundColor.a <= 0.0f) { + return; } - auto transformToTopLeft = modelTransform; - transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), billboardMode, args->getViewFrustum().getPosition())); - transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left - transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed + modelTransform.setRotation(EntityItem::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), billboardMode, args->getViewFrustum().getPosition())); + batch.setModelTransform(modelTransform); - if (backgroundColor.a > 0.0f) { - batch.setModelTransform(transformToTopLeft); - auto geometryCache = DependencyManager::get(); - geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false, true, forward); - geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID); + auto geometryCache = DependencyManager::get(); + render::ShapePipelinePointer pipeline; + if (renderLayer == RenderLayer::WORLD && args->_renderMethod != Args::RenderMethod::FORWARD) { + pipeline = backgroundColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); + } else { + pipeline = backgroundColor.a < 1.0f ? geometryCache->getForwardTransparentShapePipeline() : geometryCache->getForwardOpaqueShapePipeline(); } - - if (textColor.a > 0.0f) { - // FIXME: Factor out textRenderer so that text parts can be grouped by pipeline for a gpu performance increase. - float scale = _lineHeight / _textRenderer->getFontSize(); - transformToTopLeft.setScale(scale); // Scale to have the correct line height - batch.setModelTransform(transformToTopLeft); - - glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), dimensions.y - (_topMargin + _bottomMargin)); - _textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale, forward); + if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) { + geometryCache->renderWireShapeInstance(args, batch, GeometryCache::Quad, backgroundColor, pipeline); + } else { + geometryCache->renderSolidShapeInstance(args, batch, GeometryCache::Quad, backgroundColor, pipeline); } } @@ -222,4 +201,176 @@ QSizeF TextEntityRenderer::textSize(const QString& text) const { float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; return QSizeF(extents.x, extents.y) * pointToWorldScale; +} + +void TextEntityRenderer::onAddToSceneTyped(const TypedEntityPointer& entity) { + Parent::onAddToSceneTyped(entity); + _textPayload = std::make_shared(entity->getID(), _textRenderer); + _textRenderID = AbstractViewStateInterface::instance()->getMain3DScene()->allocateID(); + auto renderPayload = std::make_shared(_textPayload); + render::Transaction transaction; + transaction.resetItem(_textRenderID, renderPayload); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); + updateTextRenderItem(); +} + +void TextEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) { + Parent::onRemoveFromSceneTyped(entity); + render::Transaction transaction; + transaction.removeItem(_textRenderID); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); + _textPayload.reset(); +} + +void TextEntityRenderer::updateTextRenderItem() const { + render::Transaction transaction; + transaction.updateItem(_textRenderID); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); +} + +entities::TextPayload::TextPayload(const QUuid& entityID, const std::weak_ptr& textRenderer) : + _entityID(entityID), _textRenderer(textRenderer) { + auto geometryCache = DependencyManager::get(); + if (geometryCache) { + _geometryID = geometryCache->allocateID(); + } +} + +entities::TextPayload::~TextPayload() { + auto geometryCache = DependencyManager::get(); + if (_geometryID && geometryCache) { + geometryCache->releaseID(_geometryID); + } +} + +ItemKey entities::TextPayload::getKey() const { + auto renderable = DependencyManager::get()->renderableForEntityId(_entityID); + if (renderable) { + auto textRenderable = std::static_pointer_cast(renderable); + ItemKey::Builder key; + // Similar to EntityRenderer::getKey() + // FIXME: should be withSubMetaCulled and not meta, but there's a bug in the layer rendering that won't render it in that case + // (the TextEntityRenderer should also be withMetaCullGroup) + if (textRenderable->isTextTransparent()) { + key = ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(textRenderable->getTagMask()).withLayer(textRenderable->getHifiRenderLayer()); + } else if (textRenderable->_canCastShadow) { + key = ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(textRenderable->getTagMask()).withShadowCaster().withLayer(textRenderable->getHifiRenderLayer()); + } else { + key = ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(textRenderable->getTagMask()).withLayer(textRenderable->getHifiRenderLayer()); + } + + if (!textRenderable->_visible) { + key.withInvisible(); + } + return key; + } + return ItemKey::Builder::opaqueShape(); +} + +Item::Bound entities::TextPayload::getBound() const { + auto renderable = DependencyManager::get()->renderableForEntityId(_entityID); + if (renderable) { + return std::static_pointer_cast(renderable)->getBound(); + } + return Item::Bound(); +} + +ShapeKey entities::TextPayload::getShapeKey() const { + auto renderable = DependencyManager::get()->renderableForEntityId(_entityID); + if (renderable) { + auto textRenderable = std::static_pointer_cast(renderable); + + auto builder = render::ShapeKey::Builder().withOwnPipeline(); + if (textRenderable->isTextTransparent()) { + builder.withTranslucent(); + } + if (textRenderable->_primitiveMode == PrimitiveMode::LINES) { + builder.withWireframe(); + } + return builder.build(); + } + return ShapeKey::Builder::invalid(); +} + +void entities::TextPayload::render(RenderArgs* args) { + PerformanceTimer perfTimer("TextPayload::render"); + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + + auto textRenderer = _textRenderer.lock(); + if (!textRenderer) { + return; + } + + auto renderable = DependencyManager::get()->renderableForEntityId(_entityID); + if (!renderable) { + return; + } + auto textRenderable = std::static_pointer_cast(renderable); + + glm::vec4 textColor; + Transform modelTransform; + BillboardMode billboardMode; + float lineHeight, leftMargin, rightMargin, topMargin, bottomMargin; + QString text; + glm::vec3 dimensions; + bool forward; + textRenderable->withReadLock([&] { + modelTransform = textRenderable->_renderTransform; + billboardMode = textRenderable->_billboardMode; + lineHeight = textRenderable->_lineHeight; + leftMargin = textRenderable->_leftMargin; + rightMargin = textRenderable->_rightMargin; + topMargin = textRenderable->_topMargin; + bottomMargin = textRenderable->_bottomMargin; + text = textRenderable->_text; + dimensions = textRenderable->_dimensions; + + float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f; + textColor = glm::vec4(textRenderable->_textColor, fadeRatio * textRenderable->_textAlpha); + textColor = EntityRenderer::calculatePulseColor(textColor, textRenderable->_pulseProperties, textRenderable->_created); + forward = textRenderable->_renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD; + }); + + if (textColor.a <= 0.0f) { + return; + } + + modelTransform.setRotation(EntityItem::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), billboardMode, args->getViewFrustum().getPosition())); + + float scale = lineHeight / textRenderer->getFontSize(); + const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f; + modelTransform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z)); + modelTransform.setScale(scale); + batch.setModelTransform(modelTransform); + + glm::vec2 bounds = glm::vec2(dimensions.x - (leftMargin + rightMargin), dimensions.y - (topMargin + bottomMargin)); + textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, text, textColor, bounds / scale, forward); +} + +namespace render { +template <> const ItemKey payloadGetKey(const TextPayload::Pointer& payload) { + if (payload) { + return payload->getKey(); + } + return ItemKey::Builder::opaqueShape(); +} + +template <> const Item::Bound payloadGetBound(const TextPayload::Pointer& payload) { + if (payload) { + return payload->getBound(); + } + return Item::Bound(); +} + +template <> const ShapeKey shapeGetShapeKey(const TextPayload::Pointer& payload) { + if (payload) { + return payload->getShapeKey(); + } + return ShapeKey::Builder::invalid(); +} + +template <> void payloadRender(const TextPayload::Pointer& payload, RenderArgs* args) { + return payload->render(args); +} } \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index e0306375a0..04b1c7a851 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -19,27 +19,31 @@ class TextRenderer3D; namespace render { namespace entities { +class TextPayload; + class TextEntityRenderer : public TypedEntityRenderer { using Parent = TypedEntityRenderer; using Pointer = std::shared_ptr; public: TextEntityRenderer(const EntityItemPointer& entity); - ~TextEntityRenderer(); QSizeF textSize(const QString& text) const; protected: bool isTransparent() const override; + bool isTextTransparent() const; Item::Bound getBound() override; ShapeKey getShapeKey() override; + void onAddToSceneTyped(const TypedEntityPointer& entity) override; + void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override; + private: virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; virtual void doRender(RenderArgs* args) override; - int _geometryID{ 0 }; std::shared_ptr _textRenderer; PulsePropertyGroup _pulseProperties; @@ -58,8 +62,42 @@ private: BillboardMode _billboardMode; glm::vec3 _dimensions; + + std::shared_ptr _textPayload; + render::ItemID _textRenderID; + void updateTextRenderItem() const; + + friend class render::entities::TextPayload; +}; + +class TextPayload { +public: + TextPayload() = default; + TextPayload(const QUuid& entityID, const std::weak_ptr& textRenderer); + ~TextPayload(); + + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + ItemKey getKey() const; + Item::Bound getBound() const; + ShapeKey getShapeKey() const; + void render(RenderArgs* args); + +protected: + QUuid _entityID; + std::weak_ptr _textRenderer; + + int _geometryID { 0 }; }; } } +namespace render { + template <> const ItemKey payloadGetKey(const entities::TextPayload::Pointer& payload); + template <> const Item::Bound payloadGetBound(const entities::TextPayload::Pointer& payload); + template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload); + template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args); +} + #endif // hifi_RenderableTextEntityItem_h