From 0955512a80ab81d72ebc27d5bedbc5a13a2e9943 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 31 Jan 2019 15:22:09 -0800 Subject: [PATCH 01/10] add billboard mode to web entities and fix picking/culling --- interface/src/Application.cpp | 26 ++++++++++++- .../src/EntityTreeRenderer.cpp | 1 - .../src/EntityTreeRenderer.h | 4 -- .../src/RenderableImageEntityItem.cpp | 35 +++++++----------- .../src/RenderableImageEntityItem.h | 3 +- .../src/RenderableTextEntityItem.cpp | 33 ++++++----------- .../src/RenderableTextEntityItem.h | 8 ++-- .../src/RenderableWebEntityItem.cpp | 22 ++++++++++- .../src/RenderableWebEntityItem.h | 2 + libraries/entities/src/EntityItem.cpp | 2 + libraries/entities/src/EntityItem.h | 5 +++ .../entities/src/EntityItemProperties.cpp | 37 +++++++++++-------- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntityPropertyFlags.h | 10 ++--- libraries/entities/src/ImageEntityItem.cpp | 21 ++++++++--- libraries/entities/src/ImageEntityItem.h | 13 ++++--- libraries/entities/src/TextEntityItem.cpp | 23 +++++++++--- libraries/entities/src/TextEntityItem.h | 4 +- libraries/entities/src/WebEntityItem.cpp | 28 ++++++++++++++ libraries/entities/src/WebEntityItem.h | 5 +++ libraries/networking/src/udt/PacketHeaders.h | 1 + 21 files changed, 191 insertions(+), 94 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6b71c51a6e..c0b90899d6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2288,8 +2288,30 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->setPrecisionPicking(rayPickID, value); }); - EntityTreeRenderer::setGetAvatarUpOperator([] { - return DependencyManager::get()->getMyAvatar()->getWorldOrientation() * Vectors::UP; + EntityItem::setBillboardRotationOperator([this](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode) { + if (billboardMode == BillboardMode::YAW) { + //rotate about vertical to face the camera + ViewFrustum frustum; + copyViewFrustum(frustum); + glm::vec3 dPosition = frustum.getPosition() - position; + // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees + float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z); + return glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); + } else if (billboardMode == BillboardMode::FULL) { + ViewFrustum frustum; + copyViewFrustum(frustum); + glm::vec3 cameraPos = frustum.getPosition(); + // use the referencial from the avatar, y isn't always up + glm::vec3 avatarUP = DependencyManager::get()->getMyAvatar()->getWorldOrientation() * Vectors::UP; + // check to see if glm::lookAt will work / using glm::lookAt variable name + glm::highp_vec3 s(glm::cross(position - cameraPos, avatarUP)); + + // make sure s is not NaN for any component + if (glm::length2(s) > 0.0f) { + return glm::conjugate(glm::toQuat(glm::lookAt(cameraPos, position, avatarUP))); + } + } + return rotation; }); render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 61344e3193..3da3693997 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -42,7 +42,6 @@ #include std::function EntityTreeRenderer::_entitiesShouldFadeFunction = []() { return true; }; -std::function EntityTreeRenderer::_getAvatarUpOperator = []() { return Vectors::UP; }; QString resolveScriptURL(const QString& scriptUrl) { auto normalizedScriptUrl = DependencyManager::get()->normalizeURL(scriptUrl); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index d9f594a20b..4a3c1c4b86 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -118,9 +118,6 @@ public: // Access the workload Space workload::SpacePointer getWorkloadSpace() const { return _space; } - static void setGetAvatarUpOperator(std::function getAvatarUpOperator) { _getAvatarUpOperator = getAvatarUpOperator; } - static glm::vec3 getAvatarUp() { return _getAvatarUpOperator(); } - EntityEditPacketSender* getPacketSender(); signals: @@ -258,7 +255,6 @@ private: workload::SpacePointer _space{ new workload::Space() }; workload::Transaction::Updates _spaceUpdates; - static std::function _getAvatarUpOperator; }; diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index c565eb1f0a..96dd1733e7 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -96,12 +96,12 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce _emissive = entity->getEmissive(); _keepAspectRatio = entity->getKeepAspectRatio(); - _billboardMode = entity->getBillboardMode(); _subImage = entity->getSubImage(); _color = entity->getColor(); _alpha = entity->getAlpha(); _pulseProperties = entity->getPulseProperties(); + _billboardMode = entity->getBillboardMode(); if (!_textureIsLoaded && _texture && _texture->isLoaded()) { _textureIsLoaded = true; @@ -118,6 +118,17 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce }); } +Item::Bound ImageEntityRenderer::getBound() { + auto bound = Parent::getBound(); + if (_billboardMode != BillboardMode::NONE) { + glm::vec3 dimensions = bound.getScale(); + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + bound.setScaleStayCentered(glm::vec3(SQRT_2 * max)); + } + return bound; +} + ShapeKey ImageEntityRenderer::getShapeKey() { auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias(); if (isTransparent()) { @@ -159,27 +170,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch* batch = args->_batch; - if (_billboardMode == BillboardMode::YAW) { - //rotate about vertical to face the camera - glm::vec3 dPosition = args->getViewFrustum().getPosition() - transform.getTranslation(); - // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees - float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z); - glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); - transform.setRotation(orientation); - } else if (_billboardMode == BillboardMode::FULL) { - glm::vec3 billboardPos = transform.getTranslation(); - glm::vec3 cameraPos = args->getViewFrustum().getPosition(); - // use the referencial from the avatar, y isn't always up - glm::vec3 avatarUP = EntityTreeRenderer::getAvatarUp(); - // check to see if glm::lookAt will work / using glm::lookAt variable name - glm::highp_vec3 s(glm::cross(billboardPos - cameraPos, avatarUP)); - - // make sure s is not NaN for any component - if (glm::length2(s) > 0.0f) { - glm::quat rotation(conjugate(toQuat(glm::lookAt(cameraPos, billboardPos, avatarUP)))); - transform.setRotation(rotation); - } - } + transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode)); transform.postScale(dimensions); batch->setModelTransform(transform); diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.h b/libraries/entities-renderer/src/RenderableImageEntityItem.h index a20533cc8c..d60b38fe65 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.h +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.h @@ -23,6 +23,7 @@ public: ~ImageEntityRenderer(); protected: + Item::Bound getBound() override; ShapeKey getShapeKey() override; bool isTransparent() const override; @@ -39,12 +40,12 @@ private: bool _emissive; bool _keepAspectRatio; - BillboardMode _billboardMode; QRect _subImage; glm::u8vec3 _color; float _alpha; PulsePropertyGroup _pulseProperties; + BillboardMode _billboardMode; glm::vec3 _dimensions; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 28879613f5..48d39963dd 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -47,6 +47,17 @@ bool TextEntityRenderer::isTransparent() const { return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE; } +Item::Bound TextEntityRenderer::getBound() { + auto bound = Parent::getBound(); + if (_billboardMode != BillboardMode::NONE) { + glm::vec3 dimensions = bound.getScale(); + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + bound.setScaleStayCentered(glm::vec3(SQRT_2 * max)); + } + return bound; +} + ShapeKey TextEntityRenderer::getShapeKey() { auto builder = render::ShapeKey::Builder().withOwnPipeline(); if (isTransparent()) { @@ -170,27 +181,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; auto transformToTopLeft = modelTransform; - if (_billboardMode == BillboardMode::YAW) { - //rotate about vertical to face the camera - glm::vec3 dPosition = args->getViewFrustum().getPosition() - modelTransform.getTranslation(); - // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees - float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z); - glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); - transformToTopLeft.setRotation(orientation); - } else if (_billboardMode == BillboardMode::FULL) { - glm::vec3 billboardPos = transformToTopLeft.getTranslation(); - glm::vec3 cameraPos = args->getViewFrustum().getPosition(); - // use the referencial from the avatar, y isn't always up - glm::vec3 avatarUP = EntityTreeRenderer::getAvatarUp(); - // check to see if glm::lookAt will work / using glm::lookAt variable name - glm::highp_vec3 s(glm::cross(billboardPos - cameraPos, avatarUP)); - - // make sure s is not NaN for any component - if (glm::length2(s) > 0.0f) { - glm::quat rotation(conjugate(toQuat(glm::lookAt(cameraPos, billboardPos, avatarUP)))); - transformToTopLeft.setRotation(rotation); - } - } + transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), _billboardMode)); 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 diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index a7c0b77974..e0306375a0 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -26,11 +26,13 @@ public: TextEntityRenderer(const EntityItemPointer& entity); ~TextEntityRenderer(); - bool isTransparent() const override; - ShapeKey getShapeKey() override; - QSizeF textSize(const QString& text) const; +protected: + bool isTransparent() const override; + Item::Bound getBound() override; + ShapeKey getShapeKey() override; + private: virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index ba136256cb..51f7be4cab 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -124,6 +124,10 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe return true; } + if (_billboardMode != entity->getBillboardMode()) { + return true; + } + if (_sourceURL != entity->getSourceUrl()) { return true; } @@ -209,6 +213,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _color = entity->getColor(); _alpha = entity->getAlpha(); _pulseProperties = entity->getPulseProperties(); + _billboardMode = entity->getBillboardMode(); if (_contentType == ContentType::NoContent) { return; @@ -271,6 +276,17 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene }); } +Item::Bound WebEntityRenderer::getBound() { + auto bound = Parent::getBound(); + if (_billboardMode != BillboardMode::NONE) { + glm::vec3 dimensions = bound.getScale(); + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + bound.setScaleStayCentered(glm::vec3(SQRT_2 * max)); + } + return bound; +} + void WebEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("WebEntityRenderer::render"); withWriteLock([&] { @@ -298,14 +314,18 @@ void WebEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; glm::vec4 color; + Transform transform; withReadLock([&] { float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; color = glm::vec4(toGlm(_color), _alpha * fadeRatio); color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created); - batch.setModelTransform(_renderTransform); + transform = _renderTransform; }); batch.setResourceTexture(0, _texture); + transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode)); + batch.setModelTransform(transform); + // Turn off jitter for these entities batch.pushProjectionJitter(); DependencyManager::get()->bindWebBrowserProgram(batch, color.a < OPAQUE_ALPHA_THRESHOLD); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index e5eff5818b..30b63a72df 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -56,6 +56,7 @@ protected: virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doRender(RenderArgs* args) override; virtual bool isTransparent() const override; + Item::Bound getBound() override; virtual bool wantsHandControllerPointerEvents() const override { return true; } virtual bool wantsKeyboardFocus() const override { return true; } @@ -85,6 +86,7 @@ private: glm::u8vec3 _color; float _alpha { 1.0f }; PulsePropertyGroup _pulseProperties; + BillboardMode _billboardMode; QString _sourceURL; uint16_t _dpi; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 41e4f43a5d..b7d9355cd1 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -49,6 +49,8 @@ int EntityItem::_maxActionsDataSize = 800; quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND; QString EntityItem::_marketplacePublicKey; +std::function EntityItem::_getBillboardRotationOperator = [](const glm::vec3&, const glm::quat& rotation, BillboardMode) { return rotation; }; + EntityItem::EntityItem(const EntityItemID& entityItemID) : SpatiallyNestable(NestableType::Entity, entityItemID) { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ec7ad78313..2351b5fbfb 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -562,6 +562,9 @@ public: virtual void addGrab(GrabPointer grab) override; virtual void removeGrab(GrabPointer grab) override; + static void setBillboardRotationOperator(std::function getBillboardRotationOperator) { _getBillboardRotationOperator = getBillboardRotationOperator; } + static glm::quat getBillboardRotation(const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode) { return _getBillboardRotationOperator(position, rotation, billboardMode); } + signals: void requestRenderUpdate(); void spaceUpdate(std::pair data); @@ -753,6 +756,8 @@ private: std::unordered_map _materials; std::mutex _materialsLock; + static std::function _getBillboardRotationOperator; + }; #endif // hifi_EntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 9e087c3ac2..ed2a43c513 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -543,6 +543,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ALPHA, alpha); changedProperties += _pulse.getChangedProperties(); CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures); + CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); // Particles CHECK_PROPERTY_CHANGE(PROP_MAX_PARTICLES, maxParticles); @@ -601,7 +602,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_TEXT_ALPHA, textAlpha); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_COLOR, backgroundColor); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_ALPHA, backgroundAlpha); - CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); CHECK_PROPERTY_CHANGE(PROP_LEFT_MARGIN, leftMargin); CHECK_PROPERTY_CHANGE(PROP_RIGHT_MARGIN, rightMargin); CHECK_PROPERTY_CHANGE(PROP_TOP_MARGIN, topMargin); @@ -1337,6 +1337,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. * @property {Color} color=255,255,255 - The color of the web surface. * @property {number} alpha=1 - The alpha of the web surface. + * @property {BillboardMode} billboardMode="none" - If "none", the entity is not billboarded. If "yaw", the entity will be + * oriented to follow your camera around the y-axis. If "full" the entity will be oriented to face your camera. The following deprecated + * behavior is also supported: you can also set "faceCamera" to true to set billboardMode to "yaw", and you can set + * "isFacingAvatar" to true to set billboardMode to "full". Setting either to false sets the mode to "none" * @property {string} sourceUrl="" - The URL of the Web page to display. This value does not change as you or others navigate * on the Web entity. * @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter @@ -1717,6 +1721,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Text only if (_type == EntityTypes::Text) { _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); @@ -1724,7 +1729,6 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_ALPHA, textAlpha); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BACKGROUND_ALPHA, backgroundAlpha); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LEFT_MARGIN, leftMargin); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RIGHT_MARGIN, rightMargin); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TOP_MARGIN, topMargin); @@ -1760,6 +1764,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi); @@ -1826,11 +1831,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMISSIVE, emissive); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEEP_ASPECT_RATIO, keepAspectRatio); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SUB_IMAGE, subImage); // Handle conversions to old 'textures' property from "imageURL" @@ -2035,6 +2040,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha); _pulse.copyFromScriptValue(object, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(textures, QString, setTextures); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode); // Particles COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, quint32, setMaxParticles); @@ -2093,7 +2099,6 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(textAlpha, float, setTextAlpha); COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, u8vec3Color, setBackgroundColor); COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundAlpha, float, setBackgroundAlpha); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(leftMargin, float, setLeftMargin); COPY_PROPERTY_FROM_QSCRIPTVALUE(rightMargin, float, setRightMargin); COPY_PROPERTY_FROM_QSCRIPTVALUE(topMargin, float, setTopMargin); @@ -2314,6 +2319,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(alpha); _pulse.merge(other._pulse); COPY_PROPERTY_IF_CHANGED(textures); + COPY_PROPERTY_IF_CHANGED(billboardMode); // Particles COPY_PROPERTY_IF_CHANGED(maxParticles); @@ -2372,7 +2378,6 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(textAlpha); COPY_PROPERTY_IF_CHANGED(backgroundColor); COPY_PROPERTY_IF_CHANGED(backgroundAlpha); - COPY_PROPERTY_IF_CHANGED(billboardMode); COPY_PROPERTY_IF_CHANGED(leftMargin); COPY_PROPERTY_IF_CHANGED(rightMargin); COPY_PROPERTY_IF_CHANGED(topMargin); @@ -2633,6 +2638,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_ALPHA_MODE, Pulse, pulse, AlphaMode, alphaMode); } ADD_PROPERTY_TO_MAP(PROP_TEXTURES, Textures, textures, QString); + ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); // Particles ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, @@ -2726,7 +2732,6 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_TEXT_ALPHA, TextAlpha, textAlpha, float); ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color); ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_ALPHA, BackgroundAlpha, backgroundAlpha, float); - ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); ADD_PROPERTY_TO_MAP(PROP_LEFT_MARGIN, LeftMargin, leftMargin, float); ADD_PROPERTY_TO_MAP(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float); ADD_PROPERTY_TO_MAP(PROP_TOP_MARGIN, TopMargin, topMargin, float); @@ -3136,6 +3141,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticPulse.setProperties(properties); _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText()); APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight()); @@ -3143,7 +3149,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_TEXT_ALPHA, properties.getTextAlpha()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, properties.getBackgroundColor()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, properties.getBackgroundAlpha()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_LEFT_MARGIN, properties.getLeftMargin()); APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, properties.getRightMargin()); APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, properties.getTopMargin()); @@ -3201,6 +3206,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticPulse.setProperties(properties); _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl()); APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI()); @@ -3262,11 +3268,11 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticPulse.setProperties(properties); _staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, properties.getImageURL()); APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, properties.getEmissive()); APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, properties.getKeepAspectRatio()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, properties.getSubImage()); } @@ -3608,6 +3614,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::Text) { properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); @@ -3615,7 +3622,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_ALPHA, float, setTextAlpha); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, u8vec3Color, setBackgroundColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_ALPHA, float, setBackgroundAlpha); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LEFT_MARGIN, float, setLeftMargin); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RIGHT_MARGIN, float, setRightMargin); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TOP_MARGIN, float, setTopMargin); @@ -3662,6 +3668,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI); @@ -3720,11 +3727,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IMAGE_URL, QString, setImageURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMISSIVE, bool, setEmissive); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SUB_IMAGE, QRect, setSubImage); } @@ -3939,11 +3946,12 @@ void EntityItemProperties::markAllChanged() { // Common _shapeTypeChanged = true; + _compoundShapeURLChanged = true; _colorChanged = true; _alphaChanged = true; _pulse.markAllChanged(); _texturesChanged = true; - _compoundShapeURLChanged = true; + _billboardModeChanged = true; // Particles _maxParticlesChanged = true; @@ -4002,7 +4010,6 @@ void EntityItemProperties::markAllChanged() { _textAlphaChanged = true; _backgroundColorChanged = true; _backgroundAlphaChanged = true; - _billboardModeChanged = true; _leftMarginChanged = true; _rightMarginChanged = true; _topMarginChanged = true; @@ -4415,6 +4422,9 @@ QList EntityItemProperties::listChangedProperties() { if (texturesChanged()) { out += "textures"; } + if (billboardModeChanged()) { + out += "billboardMode"; + } // Particles if (maxParticlesChanged()) { @@ -4571,9 +4581,6 @@ QList EntityItemProperties::listChangedProperties() { if (backgroundAlphaChanged()) { out += "backgroundAlpha"; } - if (billboardModeChanged()) { - out += "billboardMode"; - } if (leftMarginChanged()) { out += "leftMargin"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 58fe63cf40..712f2d120f 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -242,6 +242,7 @@ public: DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, ENTITY_ITEM_DEFAULT_ALPHA); DEFINE_PROPERTY_GROUP(Pulse, pulse, PulsePropertyGroup); DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, ""); + DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE); // Particles DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, particle::DEFAULT_MAX_PARTICLES); @@ -300,7 +301,6 @@ public: DEFINE_PROPERTY_REF(PROP_TEXT_ALPHA, TextAlpha, textAlpha, float, TextEntityItem::DEFAULT_TEXT_ALPHA); DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color, TextEntityItem::DEFAULT_BACKGROUND_COLOR); DEFINE_PROPERTY_REF(PROP_BACKGROUND_ALPHA, BackgroundAlpha, backgroundAlpha, float, TextEntityItem::DEFAULT_TEXT_ALPHA); - DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE); DEFINE_PROPERTY_REF(PROP_LEFT_MARGIN, LeftMargin, leftMargin, float, TextEntityItem::DEFAULT_MARGIN); DEFINE_PROPERTY_REF(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float, TextEntityItem::DEFAULT_MARGIN); DEFINE_PROPERTY_REF(PROP_TOP_MARGIN, TopMargin, topMargin, float, TextEntityItem::DEFAULT_MARGIN); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 5cf809e38e..093df92dc1 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -118,6 +118,7 @@ enum EntityPropertyList { PROP_PULSE_COLOR_MODE, PROP_PULSE_ALPHA_MODE, PROP_TEXTURES, + PROP_BILLBOARD_MODE, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new shared EntityItem properties to the list ABOVE this line @@ -232,11 +233,10 @@ enum EntityPropertyList { PROP_TEXT_ALPHA = PROP_DERIVED_3, PROP_BACKGROUND_COLOR = PROP_DERIVED_4, PROP_BACKGROUND_ALPHA = PROP_DERIVED_5, - PROP_BILLBOARD_MODE = PROP_DERIVED_6, - PROP_LEFT_MARGIN = PROP_DERIVED_7, - PROP_RIGHT_MARGIN = PROP_DERIVED_8, - PROP_TOP_MARGIN = PROP_DERIVED_9, - PROP_BOTTOM_MARGIN = PROP_DERIVED_10, + PROP_LEFT_MARGIN = PROP_DERIVED_6, + PROP_RIGHT_MARGIN = PROP_DERIVED_7, + PROP_TOP_MARGIN = PROP_DERIVED_8, + PROP_BOTTOM_MARGIN = PROP_DERIVED_9, // Zone // Keylight diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index 1e8e4511cf..837e824f4a 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -35,11 +35,11 @@ EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& d withReadLock([&] { _pulseProperties.getProperties(properties); }); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emissive, getEmissive); COPY_ENTITY_PROPERTY_TO_PROPERTIES(keepAspectRatio, getKeepAspectRatio); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(subImage, getSubImage); return properties; @@ -54,11 +54,11 @@ bool ImageEntityItem::setProperties(const EntityItemProperties& properties) { bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); somethingChanged |= pulsePropertiesChanged; }); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setImageURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emissive, setEmissive); SET_ENTITY_PROPERTY_FROM_PROPERTIES(keepAspectRatio, setKeepAspectRatio); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(subImage, setSubImage); if (somethingChanged) { @@ -91,11 +91,11 @@ int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, bytesRead += bytesFromPulse; dataAt += bytesFromPulse; }); + READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_IMAGE_URL, QString, setImageURL); READ_ENTITY_PROPERTY(PROP_EMISSIVE, bool, setEmissive); READ_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); - READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_SUB_IMAGE, QRect, setSubImage); return bytesRead; @@ -107,11 +107,11 @@ EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams& requestedProperties += PROP_COLOR; requestedProperties += PROP_ALPHA; requestedProperties += _pulseProperties.getEntityProperties(params); + requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += PROP_IMAGE_URL; requestedProperties += PROP_EMISSIVE; requestedProperties += PROP_KEEP_ASPECT_RATIO; - requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += PROP_SUB_IMAGE; return requestedProperties; @@ -133,14 +133,24 @@ void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); }); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, getImageURL()); APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, getEmissive()); APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, getKeepAspectRatio()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, getSubImage()); } +glm::vec3 ImageEntityItem::getRaycastDimensions() const { + glm::vec3 dimensions = getScaledDimensions(); + if (getBillboardMode() != BillboardMode::NONE) { + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + return glm::vec3(SQRT_2 * max); + } + return dimensions; +} + bool ImageEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, @@ -149,6 +159,7 @@ bool ImageEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h index 05218016b3..a1be5a0554 100644 --- a/libraries/entities/src/ImageEntityItem.h +++ b/libraries/entities/src/ImageEntityItem.h @@ -43,6 +43,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + glm::vec3 getRaycastDimensions() const override; virtual bool supportsDetailedIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, @@ -77,15 +78,15 @@ public: PulsePropertyGroup getPulseProperties() const; protected: - QString _imageURL; - bool _emissive { false }; - bool _keepAspectRatio { true }; - BillboardMode _billboardMode; - QRect _subImage; - glm::u8vec3 _color; float _alpha; PulsePropertyGroup _pulseProperties; + BillboardMode _billboardMode; + + QString _imageURL; + bool _emissive { false }; + bool _keepAspectRatio { true }; + QRect _subImage; }; #endif // hifi_ImageEntityItem_h diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index a743d0a7a9..bc98c61ff7 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -52,6 +52,7 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de withReadLock([&] { _pulseProperties.getProperties(properties); }); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight); @@ -59,7 +60,6 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(textAlpha, getTextAlpha); COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundAlpha, getBackgroundAlpha); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(leftMargin, getLeftMargin); COPY_ENTITY_PROPERTY_TO_PROPERTIES(rightMargin, getRightMargin); COPY_ENTITY_PROPERTY_TO_PROPERTIES(topMargin, getTopMargin); @@ -75,6 +75,7 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) { bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); somethingChanged |= pulsePropertiesChanged; }); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(text, setText); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight); @@ -82,7 +83,6 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(textAlpha, setTextAlpha); SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundColor, setBackgroundColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundAlpha, setBackgroundAlpha); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(leftMargin, setLeftMargin); SET_ENTITY_PROPERTY_FROM_PROPERTIES(rightMargin, setRightMargin); SET_ENTITY_PROPERTY_FROM_PROPERTIES(topMargin, setTopMargin); @@ -117,6 +117,7 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, bytesRead += bytesFromPulse; dataAt += bytesFromPulse; }); + READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight); @@ -124,7 +125,6 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_TEXT_ALPHA, float, setTextAlpha); READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, glm::u8vec3, setBackgroundColor); READ_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, float, setBackgroundAlpha); - READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_LEFT_MARGIN, float, setLeftMargin); READ_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, float, setRightMargin); READ_ENTITY_PROPERTY(PROP_TOP_MARGIN, float, setTopMargin); @@ -137,13 +137,14 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += _pulseProperties.getEntityProperties(params); + requestedProperties += PROP_BILLBOARD_MODE; + requestedProperties += PROP_TEXT; requestedProperties += PROP_LINE_HEIGHT; requestedProperties += PROP_TEXT_COLOR; requestedProperties += PROP_TEXT_ALPHA; requestedProperties += PROP_BACKGROUND_COLOR; requestedProperties += PROP_BACKGROUND_ALPHA; - requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += PROP_LEFT_MARGIN; requestedProperties += PROP_RIGHT_MARGIN; requestedProperties += PROP_TOP_MARGIN; @@ -166,6 +167,7 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); }); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_TEXT, getText()); APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight()); @@ -173,12 +175,20 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_TEXT_ALPHA, getTextAlpha()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, getBackgroundColor()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, getBackgroundAlpha()); - APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_LEFT_MARGIN, getLeftMargin()); APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, getRightMargin()); APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, getTopMargin()); APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, getBottomMargin()); - +} + +glm::vec3 TextEntityItem::getRaycastDimensions() const { + glm::vec3 dimensions = getScaledDimensions(); + if (getBillboardMode() != BillboardMode::NONE) { + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + return glm::vec3(SQRT_2 * max); + } + return dimensions; } bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -189,6 +199,7 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 5ca6823052..1ead9d3e15 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -47,6 +47,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + glm::vec3 getRaycastDimensions() const override; virtual bool supportsDetailedIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, @@ -99,6 +100,8 @@ public: PulsePropertyGroup getPulseProperties() const; private: + BillboardMode _billboardMode; + QString _text; float _lineHeight; glm::u8vec3 _textColor; @@ -106,7 +109,6 @@ private: glm::u8vec3 _backgroundColor; float _backgroundAlpha; PulsePropertyGroup _pulseProperties; - BillboardMode _billboardMode; float _leftMargin; float _rightMargin; float _topMargin; diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 51b78aaec7..2e5c3f1a6a 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -48,6 +48,7 @@ EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& des withReadLock([&] { _pulseProperties.getProperties(properties); }); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl); COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI); @@ -68,6 +69,7 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { bool pulsePropertiesChanged = _pulseProperties.setProperties(properties); somethingChanged |= pulsePropertiesChanged; }); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl); SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI); @@ -107,6 +109,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i bytesRead += bytesFromPulse; dataAt += bytesFromPulse; }); + READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl); READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI); @@ -123,6 +126,7 @@ EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& pa requestedProperties += PROP_COLOR; requestedProperties += PROP_ALPHA; requestedProperties += _pulseProperties.getEntityProperties(params); + requestedProperties += PROP_BILLBOARD_MODE; requestedProperties += PROP_SOURCE_URL; requestedProperties += PROP_DPI; @@ -148,6 +152,7 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); }); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, getSourceUrl()); APPEND_ENTITY_PROPERTY(PROP_DPI, getDPI()); @@ -157,6 +162,16 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, getShowKeyboardFocusHighlight()); } +glm::vec3 WebEntityItem::getRaycastDimensions() const { + glm::vec3 dimensions = getScaledDimensions(); + if (getBillboardMode() != BillboardMode::NONE) { + float max = glm::max(dimensions.x, glm::max(dimensions.y, dimensions.z)); + const float SQRT_2 = 1.41421356237f; + return glm::vec3(SQRT_2 * max); + } + return dimensions; +} + bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, @@ -165,6 +180,7 @@ bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const g glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; @@ -235,6 +251,18 @@ float WebEntityItem::getAlpha() const { }); } +BillboardMode WebEntityItem::getBillboardMode() const { + return resultWithReadLock([&] { + return _billboardMode; + }); +} + +void WebEntityItem::setBillboardMode(BillboardMode value) { + withWriteLock([&] { + _billboardMode = value; + }); +} + void WebEntityItem::setSourceUrl(const QString& value) { withWriteLock([&] { _sourceUrl = value; diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 86b2377c90..bb1e527712 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -44,6 +44,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + glm::vec3 getRaycastDimensions() const override; virtual bool supportsDetailedIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, @@ -60,6 +61,9 @@ public: float getAlpha() const; void setAlpha(float alpha); + void setBillboardMode(BillboardMode value); + BillboardMode getBillboardMode() const; + static const QString DEFAULT_SOURCE_URL; void setSourceUrl(const QString& value); QString getSourceUrl() const; @@ -86,6 +90,7 @@ protected: glm::u8vec3 _color; float _alpha { 1.0f }; PulsePropertyGroup _pulseProperties; + BillboardMode _billboardMode; QString _sourceUrl; uint16_t _dpi; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 0697fe8885..5f55c189ce 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -261,6 +261,7 @@ enum class EntityVersion : PacketVersion { PulseProperties, RingGizmoEntities, ShowKeyboardFocusHighlight, + WebBillboardMode, // Add new versions above here NUM_PACKET_TYPE, From 17ab0cb92e2c2aedf35e5b5d363acd779c01c9f8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 8 Feb 2019 13:12:10 -0800 Subject: [PATCH 02/10] more robust ShapeManager garbage collector --- libraries/physics/src/ShapeManager.cpp | 11 +++++++++-- libraries/physics/src/ShapeManager.h | 2 +- libraries/shared/src/HashKey.h | 3 +++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index ef7a4a1749..cb37b0f96d 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -57,7 +57,14 @@ bool ShapeManager::releaseShapeByKey(const HashKey& key) { if (shapeRef->refCount > 0) { shapeRef->refCount--; if (shapeRef->refCount == 0) { - _pendingGarbage.push_back(key); + // look for existing entry in _pendingGarbage, starting from the back + for (int32_t i = _pendingGarbage.size() - 1; i > -1; --i) { + if (_pendingGarbage[i] == key.getHash64()) { + // already on the list, don't add it again + return true; + } + } + _pendingGarbage.push_back(key.getHash64()); const int MAX_SHAPE_GARBAGE_CAPACITY = 255; if (_pendingGarbage.size() > MAX_SHAPE_GARBAGE_CAPACITY) { collectGarbage(); @@ -89,7 +96,7 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) { void ShapeManager::collectGarbage() { int numShapes = _pendingGarbage.size(); for (int i = 0; i < numShapes; ++i) { - HashKey& key = _pendingGarbage[i]; + HashKey key(_pendingGarbage[i]); ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef && shapeRef->refCount == 0) { ShapeFactory::deleteShape(shapeRef->shape); diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index d75bb1dc4a..58f1b53c18 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -75,7 +75,7 @@ private: // btHashMap is required because it supports memory alignment of the btCollisionShapes btHashMap _shapeMap; - btAlignedObjectArray _pendingGarbage; + btAlignedObjectArray _pendingGarbage; }; #endif // hifi_ShapeManager_h diff --git a/libraries/shared/src/HashKey.h b/libraries/shared/src/HashKey.h index 5fce182084..a32a8664a7 100644 --- a/libraries/shared/src/HashKey.h +++ b/libraries/shared/src/HashKey.h @@ -32,6 +32,9 @@ class HashKey { public: static float getNumQuantizedValuesPerMeter(); + HashKey() {} + HashKey(uint64_t hash) : _hash(hash) {} + // These two methods are required by btHashMap. bool equals(const HashKey& other) const { return _hash == other._hash; } int32_t getHash() const { return (int32_t)((uint32_t)_hash); } From a3567dea3a2db620262f503a770fe750812dbf47 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 8 Feb 2019 14:18:33 -0800 Subject: [PATCH 03/10] clean HashKey API, reduce dependency tree --- libraries/physics/src/ShapeManager.cpp | 21 +++++------ libraries/physics/src/ShapeManager.h | 4 +-- libraries/shared/src/HashKey.h | 5 ++- libraries/shared/src/ShapeInfo.cpp | 49 ++++++++++++++------------ libraries/shared/src/ShapeInfo.h | 6 ++-- 5 files changed, 43 insertions(+), 42 deletions(-) diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index cb37b0f96d..991cd0efc9 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -33,8 +33,8 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { if (info.getType() == SHAPE_TYPE_NONE) { return nullptr; } - HashKey key = info.getHash(); - ShapeReference* shapeRef = _shapeMap.find(key); + HashKey hashKey(info.getHash()); + ShapeReference* shapeRef = _shapeMap.find(hashKey); if (shapeRef) { shapeRef->refCount++; return shapeRef->shape; @@ -44,27 +44,28 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { ShapeReference newRef; newRef.refCount = 1; newRef.shape = shape; - newRef.key = key; - _shapeMap.insert(key, newRef); + newRef.key = info.getHash(); + _shapeMap.insert(hashKey, newRef); } return shape; } // private helper method -bool ShapeManager::releaseShapeByKey(const HashKey& key) { - ShapeReference* shapeRef = _shapeMap.find(key); +bool ShapeManager::releaseShapeByKey(uint64_t key) { + HashKey hashKey(key); + ShapeReference* shapeRef = _shapeMap.find(hashKey); if (shapeRef) { if (shapeRef->refCount > 0) { shapeRef->refCount--; if (shapeRef->refCount == 0) { // look for existing entry in _pendingGarbage, starting from the back for (int32_t i = _pendingGarbage.size() - 1; i > -1; --i) { - if (_pendingGarbage[i] == key.getHash64()) { + if (_pendingGarbage[i] == key) { // already on the list, don't add it again return true; } } - _pendingGarbage.push_back(key.getHash64()); + _pendingGarbage.push_back(key); const int MAX_SHAPE_GARBAGE_CAPACITY = 255; if (_pendingGarbage.size() > MAX_SHAPE_GARBAGE_CAPACITY) { collectGarbage(); @@ -107,8 +108,8 @@ void ShapeManager::collectGarbage() { } int ShapeManager::getNumReferences(const ShapeInfo& info) const { - HashKey key = info.getHash(); - const ShapeReference* shapeRef = _shapeMap.find(key); + HashKey hashKey(info.getHash()); + const ShapeReference* shapeRef = _shapeMap.find(hashKey); if (shapeRef) { return shapeRef->refCount; } diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index 58f1b53c18..47b6c5340a 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -63,13 +63,13 @@ public: bool hasShape(const btCollisionShape* shape) const; private: - bool releaseShapeByKey(const HashKey& key); + bool releaseShapeByKey(uint64_t key); class ShapeReference { public: int refCount; const btCollisionShape* shape; - HashKey key; + uint64_t key { 0 }; ShapeReference() : refCount(0), shape(nullptr) {} }; diff --git a/libraries/shared/src/HashKey.h b/libraries/shared/src/HashKey.h index a32a8664a7..446eb4c25f 100644 --- a/libraries/shared/src/HashKey.h +++ b/libraries/shared/src/HashKey.h @@ -39,13 +39,12 @@ public: bool equals(const HashKey& other) const { return _hash == other._hash; } int32_t getHash() const { return (int32_t)((uint32_t)_hash); } - void clear() { _hash = _hashCount = 0; } - bool isNull() const { return _hash == 0 && _hashCount == 0; } + // These methods for accumulating a hash. void hashUint64(uint64_t data); void hashFloat(float data); void hashVec3(const glm::vec3& data); - uint64_t getHash64() const { return _hash; } // for debug/test purposes + uint64_t getHash64() const { return _hash; } private: uint64_t _hash { 0 }; diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index c256cf2b76..bf51e455c5 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -13,6 +13,7 @@ #include +#include "HashKey.h" #include "NumericalConstants.h" // for MILLIMETERS_PER_METER /**jsdoc @@ -96,7 +97,7 @@ void ShapeInfo::clear() { _sphereCollection.clear(); _halfExtents = glm::vec3(0.0f); _offset = glm::vec3(0.0f); - _hashKey.clear(); + _hash64 = 0; _type = SHAPE_TYPE_NONE; } @@ -131,14 +132,14 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString default: break; } - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setBox(const glm::vec3& halfExtents) { _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setSphere(float radius) { @@ -146,7 +147,7 @@ void ShapeInfo::setSphere(float radius) { _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); _halfExtents = glm::vec3(radius); - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setMultiSphere(const std::vector& centers, const std::vector& radiuses) { @@ -158,12 +159,12 @@ void ShapeInfo::setMultiSphere(const std::vector& centers, const std: SphereData sphere = SphereData(centers[i], radiuses[i]); _sphereCollection.push_back(sphere); } - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { _pointCollection = pointCollection; - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) { @@ -172,12 +173,12 @@ void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) { radius = glm::max(radius, MIN_HALF_EXTENT); cylinderHalfHeight = glm::max(cylinderHalfHeight, 0.0f); _halfExtents = glm::vec3(radius, cylinderHalfHeight + radius, radius); - _hashKey.clear(); + _hash64 = 0; } void ShapeInfo::setOffset(const glm::vec3& offset) { _offset = offset; - _hashKey.clear(); + _hash64 = 0; } uint32_t ShapeInfo::getNumSubShapes() const { @@ -269,20 +270,21 @@ float ShapeInfo::computeVolume() const { return volume; } -const HashKey& ShapeInfo::getHash() const { +uint64_t ShapeInfo::getHash() const { // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. - if (_hashKey.isNull() && _type != SHAPE_TYPE_NONE) { + if (_hash64 == 0 && _type != SHAPE_TYPE_NONE) { + HashKey hashKey; // The key is not yet cached therefore we must compute it. - _hashKey.hashUint64((uint64_t)_type); + hashKey.hashUint64((uint64_t)_type); if (_type == SHAPE_TYPE_MULTISPHERE) { for (auto &sphereData : _sphereCollection) { - _hashKey.hashVec3(glm::vec3(sphereData)); - _hashKey.hashFloat(sphereData.w); + hashKey.hashVec3(glm::vec3(sphereData)); + hashKey.hashFloat(sphereData.w); } } else if (_type != SHAPE_TYPE_SIMPLE_HULL) { - _hashKey.hashVec3(_halfExtents); - _hashKey.hashVec3(_offset); + hashKey.hashVec3(_halfExtents); + hashKey.hashVec3(_offset); } else { // TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique // descriptive string. Shapes that are uniquely described by their type and URL could just put their @@ -292,7 +294,7 @@ const HashKey& ShapeInfo::getHash() const { const int numPoints = (int)points.size(); for (int i = 0; i < numPoints; ++i) { - _hashKey.hashVec3(points[i]); + hashKey.hashVec3(points[i]); } } @@ -300,23 +302,24 @@ const HashKey& ShapeInfo::getHash() const { if (!url.isEmpty()) { QByteArray baUrl = url.toLocal8Bit(); uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size()); - _hashKey.hashUint64((uint64_t)urlHash); + hashKey.hashUint64((uint64_t)urlHash); } if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) { uint64_t numHulls = (uint64_t)_pointCollection.size(); - _hashKey.hashUint64(numHulls); + hashKey.hashUint64(numHulls); } else if (_type == SHAPE_TYPE_MULTISPHERE) { uint64_t numSpheres = (uint64_t)_sphereCollection.size(); - _hashKey.hashUint64(numSpheres); + hashKey.hashUint64(numSpheres); } else if (_type == SHAPE_TYPE_SIMPLE_HULL) { - _hashKey.hashUint64(1); - } + hashKey.hashUint64(1); + } + _hash64 = hashKey.getHash64(); } - return _hashKey; + return _hash64; } void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) { _halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT)); - _hashKey.clear(); + _hash64 = 0; } diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index d838d7b214..6b0f981b24 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -18,8 +18,6 @@ #include #include -#include "HashKey.h" - const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored // Bullet has a mesh generation util for convex shapes that we used to @@ -91,7 +89,7 @@ public: float computeVolume() const; - const HashKey& getHash() const; + uint64_t getHash() const; protected: void setHalfExtents(const glm::vec3& halfExtents); @@ -102,7 +100,7 @@ protected: TriangleIndices _triangleIndices; glm::vec3 _halfExtents = glm::vec3(0.0f); glm::vec3 _offset = glm::vec3(0.0f); - mutable HashKey _hashKey; + mutable uint64_t _hash64; ShapeType _type = SHAPE_TYPE_NONE; }; From d7f38bac17c9945e3eda395c366cf037fbf5ba27 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 8 Feb 2019 14:50:30 -0800 Subject: [PATCH 04/10] fix ShapeInfoTests to use reduced HashKey API --- tests/physics/src/ShapeInfoTests.cpp | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/physics/src/ShapeInfoTests.cpp b/tests/physics/src/ShapeInfoTests.cpp index efc88a4032..0116eb027f 100644 --- a/tests/physics/src/ShapeInfoTests.cpp +++ b/tests/physics/src/ShapeInfoTests.cpp @@ -55,20 +55,20 @@ void ShapeInfoTests::testHashFunctions() { // test sphere info.setSphere(radiusX); ++testCount; - HashKey key = info.getHash(); - hashPtr = hashes.find(key); + HashKey hashKey(info.getHash()); + hashPtr = hashes.find(hashKey); if (hashPtr) { std::cout << testCount << " hash collision sphere radius = " << radiusX - << " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr + << " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr << std::dec << std::endl; ++numCollisions; assert(false); } else { - hashes.insert(key, key.getHash()); + hashes.insert(hashKey, hashKey.getHash()); } // track bit distribution counts to evaluate hash function randomness for (int j = 0; j < NUM_HASH_BITS; ++j) { - if (masks[j] & key.getHash()) { + if (masks[j] & hashKey.getHash()) { ++bits[j]; } } @@ -80,21 +80,21 @@ void ShapeInfoTests::testHashFunctions() { // test box info.setBox(glm::vec3(radiusX, radiusY, radiusZ)); ++testCount; - HashKey key = info.getHash(); - hashPtr = hashes.find(key); + HashKey hashKey(info.getHash()); + hashPtr = hashes.find(hashKey); if (hashPtr) { std::cout << testCount << " hash collision box dimensions = < " << radiusX << ", " << radiusY << ", " << radiusZ << " >" - << " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr << " : 0x" << key.getHash64() + << " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr << " : 0x" << hashKey.getHash64() << std::dec << std::endl; ++numCollisions; assert(false); } else { - hashes.insert(key, key.getHash()); + hashes.insert(hashKey, hashKey.getHash()); } // track bit distribution counts to evaluate hash function randomness for (int k = 0; k < NUM_HASH_BITS; ++k) { - if (masks[k] & key.getHash()) { + if (masks[k] & hashKey.getHash()) { ++bits[k]; } } @@ -117,14 +117,14 @@ void ShapeInfoTests::testBoxShape() { ShapeInfo info; glm::vec3 halfExtents(1.23f, 4.56f, 7.89f); info.setBox(halfExtents); - HashKey key = info.getHash(); + HashKey hashKey(info.getHash()); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); - QCOMPARE(key.getHash(), otherKey.getHash()); + QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; } @@ -133,14 +133,14 @@ void ShapeInfoTests::testSphereShape() { ShapeInfo info; float radius = 1.23f; info.setSphere(radius); - HashKey key = info.getHash(); + HashKey hashKey = info.getHash(); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); - QCOMPARE(key.getHash(), otherKey.getHash()); + QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; } @@ -151,14 +151,14 @@ void ShapeInfoTests::testCylinderShape() { float radius = 1.23f; float height = 4.56f; info.setCylinder(radius, height); - HashKey key = info.getHash(); + HashKey hashKey(info.getHash()); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); - QCOMPARE(key.getHash(), otherKey.getHash()); + QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; */ @@ -170,14 +170,14 @@ void ShapeInfoTests::testCapsuleShape() { float radius = 1.23f; float height = 4.56f; info.setCapsule(radius, height); - HashKey key = info.getHash(); + HashKey hashKey(info.getHash()); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); - QCOMPARE(key.getHash(), otherKey.getHash()); + QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; */ From c9b142531bbdbcc8b63669d70ee19d4e10eaf087 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sat, 9 Feb 2019 08:21:24 -0800 Subject: [PATCH 05/10] add more tracing details in PrePhysics --- libraries/physics/src/ShapeManager.cpp | 36 +++++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 991cd0efc9..ad937d2bcc 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -17,7 +17,10 @@ #include "ShapeFactory.h" +const int MAX_RING_SIZE = 256; + ShapeManager::ShapeManager() { + _garbageRing.reserve(MAX_RING_SIZE); } ShapeManager::~ShapeManager() { @@ -58,17 +61,29 @@ bool ShapeManager::releaseShapeByKey(uint64_t key) { if (shapeRef->refCount > 0) { shapeRef->refCount--; if (shapeRef->refCount == 0) { - // look for existing entry in _pendingGarbage, starting from the back - for (int32_t i = _pendingGarbage.size() - 1; i > -1; --i) { - if (_pendingGarbage[i] == key) { + // look for existing entry in _garbageRing + int32_t ringSize = _garbageRing.size(); + for (int32_t i = 0; i < ringSize; ++i) { + int32_t j = (_ringIndex + ringSize) % ringSize; + if (_garbageRing[j] == key) { // already on the list, don't add it again return true; } } - _pendingGarbage.push_back(key); - const int MAX_SHAPE_GARBAGE_CAPACITY = 255; - if (_pendingGarbage.size() > MAX_SHAPE_GARBAGE_CAPACITY) { - collectGarbage(); + if (ringSize == MAX_RING_SIZE) { + // remove one + HashKey hashKeyToRemove(_garbageRing[_ringIndex]); + ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove); + if (shapeRef && shapeRef->refCount == 0) { + ShapeFactory::deleteShape(shapeRef->shape); + _shapeMap.remove(hashKeyToRemove); + } + // replace at _ringIndex and advance + _garbageRing[_ringIndex] = key; + _ringIndex = (_ringIndex + 1) % ringSize; + } else { + // add one + _garbageRing.push_back(key); } } return true; @@ -95,16 +110,17 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) { } void ShapeManager::collectGarbage() { - int numShapes = _pendingGarbage.size(); + int numShapes = _garbageRing.size(); for (int i = 0; i < numShapes; ++i) { - HashKey key(_pendingGarbage[i]); + HashKey key(_garbageRing[i]); ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef && shapeRef->refCount == 0) { ShapeFactory::deleteShape(shapeRef->shape); _shapeMap.remove(key); } } - _pendingGarbage.clear(); + _ringIndex = 0; + _garbageRing.clear(); } int ShapeManager::getNumReferences(const ShapeInfo& info) const { From 8bdac589b88f3339bf7f4a4f9f4c1017a4cd296f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sat, 9 Feb 2019 08:23:09 -0800 Subject: [PATCH 06/10] use RingBuffer for gradual garbage collection --- interface/src/Application.cpp | 59 +++++++++++++++++----------- libraries/physics/src/ShapeManager.h | 8 +++- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1029398794..b3c978df9a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6309,40 +6309,53 @@ void Application::update(float deltaTime) { PROFILE_RANGE(simulation_physics, "PrePhysics"); PerformanceTimer perfTimer("prePhysics)"); { + PROFILE_RANGE(simulation_physics, "RemoveEntities"); const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics(); _physicsEngine->removeObjects(motionStates); _entitySimulation->deleteObjectsRemovedFromPhysics(); } - VectorOfMotionStates motionStates; - getEntities()->getTree()->withReadLock([&] { - _entitySimulation->getObjectsToAddToPhysics(motionStates); - _physicsEngine->addObjects(motionStates); - - }); - getEntities()->getTree()->withReadLock([&] { - _entitySimulation->getObjectsToChange(motionStates); - VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates); - _entitySimulation->setObjectsToChange(stillNeedChange); - }); + { + PROFILE_RANGE(simulation_physics, "AddEntities"); + VectorOfMotionStates motionStates; + getEntities()->getTree()->withReadLock([&] { + _entitySimulation->getObjectsToAddToPhysics(motionStates); + _physicsEngine->addObjects(motionStates); + }); + } + { + VectorOfMotionStates motionStates; + PROFILE_RANGE(simulation_physics, "ChangeEntities"); + getEntities()->getTree()->withReadLock([&] { + _entitySimulation->getObjectsToChange(motionStates); + VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates); + _entitySimulation->setObjectsToChange(stillNeedChange); + }); + } _entitySimulation->applyDynamicChanges(); t1 = std::chrono::high_resolution_clock::now(); - PhysicsEngine::Transaction transaction; - avatarManager->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - avatarManager->handleProcessedPhysicsTransaction(transaction); - myAvatar->getCharacterController()->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction); - myAvatar->prepareForPhysicsSimulation(); - _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); + { + PROFILE_RANGE(simulation_physics, "Avatars"); + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); + myAvatar->getCharacterController()->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction); + myAvatar->prepareForPhysicsSimulation(); + _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); + } - _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { - dynamic->prepareForPhysicsSimulation(); - }); + { + PROFILE_RANGE(simulation_physics, "PrepareActions"); + _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { + dynamic->prepareForPhysicsSimulation(); + }); + } } auto t2 = std::chrono::high_resolution_clock::now(); { diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index 47b6c5340a..7583b7d919 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -12,6 +12,8 @@ #ifndef hifi_ShapeManager_h #define hifi_ShapeManager_h +#include + #include #include @@ -41,6 +43,7 @@ // later. When that list grows big enough the ShapeManager will remove any matching // entries that still have zero ref-count. + class ShapeManager { public: @@ -75,7 +78,10 @@ private: // btHashMap is required because it supports memory alignment of the btCollisionShapes btHashMap _shapeMap; - btAlignedObjectArray _pendingGarbage; + //btAlignedObjectArray _pendingGarbage; + + std::vector _garbageRing; + uint32_t _ringIndex { 0 }; }; #endif // hifi_ShapeManager_h From d4c52e4b1c2fbef3b31664383a665510ebd9fd68 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 11 Feb 2019 09:16:31 -0800 Subject: [PATCH 07/10] fix implicit cast warning for Visual Studio --- libraries/physics/src/ShapeManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index ad937d2bcc..8acbe51540 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -62,7 +62,7 @@ bool ShapeManager::releaseShapeByKey(uint64_t key) { shapeRef->refCount--; if (shapeRef->refCount == 0) { // look for existing entry in _garbageRing - int32_t ringSize = _garbageRing.size(); + int32_t ringSize = (int32_t)(_garbageRing.size()); for (int32_t i = 0; i < ringSize; ++i) { int32_t j = (_ringIndex + ringSize) % ringSize; if (_garbageRing[j] == key) { @@ -110,7 +110,7 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) { } void ShapeManager::collectGarbage() { - int numShapes = _garbageRing.size(); + int numShapes = (int32_t)(_garbageRing.size()); for (int i = 0; i < numShapes; ++i) { HashKey key(_garbageRing[i]); ShapeReference* shapeRef = _shapeMap.find(key); From 152edd477c3952aab14d458ef55d466fa7798c75 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 13 Feb 2019 10:00:51 -0800 Subject: [PATCH 08/10] change trace context to rendering --- interface/src/ui/ApplicationOverlay.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 3579776213..8270b8d307 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -90,7 +90,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { } void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { - PROFILE_RANGE(app, __FUNCTION__); + PROFILE_RANGE(render, __FUNCTION__); if (!_uiTexture) { _uiTexture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()); @@ -119,7 +119,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { } void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { - PROFILE_RANGE(app, __FUNCTION__); + PROFILE_RANGE(render, __FUNCTION__); gpu::Batch& batch = *renderArgs->_batch; auto geometryCache = DependencyManager::get(); @@ -178,7 +178,7 @@ static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LI static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); void ApplicationOverlay::buildFramebufferObject() { - PROFILE_RANGE(app, __FUNCTION__); + PROFILE_RANGE(render, __FUNCTION__); auto uiSize = glm::uvec2(qApp->getUiSize()); if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) { From c79e3e5a6d7eab0c0b5e374ec691c7c812b6d2ad Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 13 Feb 2019 10:02:58 -0800 Subject: [PATCH 09/10] add some trace contexts for easier debugging --- interface/src/Application.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b3c978df9a..615e9f1440 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6311,7 +6311,10 @@ void Application::update(float deltaTime) { { PROFILE_RANGE(simulation_physics, "RemoveEntities"); const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics(); - _physicsEngine->removeObjects(motionStates); + { + PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size()); + _physicsEngine->removeObjects(motionStates); + } _entitySimulation->deleteObjectsRemovedFromPhysics(); } @@ -6320,6 +6323,7 @@ void Application::update(float deltaTime) { VectorOfMotionStates motionStates; getEntities()->getTree()->withReadLock([&] { _entitySimulation->getObjectsToAddToPhysics(motionStates); + PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size()); _physicsEngine->addObjects(motionStates); }); } From ba91bab224a7fdab673bb36f91143a17d007c14f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 13 Feb 2019 10:05:32 -0800 Subject: [PATCH 10/10] remove cruft --- libraries/physics/src/ShapeManager.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index 7583b7d919..c1fb57e017 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -78,8 +78,6 @@ private: // btHashMap is required because it supports memory alignment of the btCollisionShapes btHashMap _shapeMap; - //btAlignedObjectArray _pendingGarbage; - std::vector _garbageRing; uint32_t _ringIndex { 0 }; };