From 250baa9c72792c7cab09ee5129ce842052a1eb63 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 11 Dec 2018 15:21:40 -0800 Subject: [PATCH 01/28] add missing properties to text entities --- interface/src/ui/overlays/Text3DOverlay.cpp | 2 +- .../src/RenderableShapeEntityItem.cpp | 15 -- .../src/RenderableShapeEntityItem.h | 1 - .../src/RenderableTextEntityItem.cpp | 79 +++++-- .../src/RenderableTextEntityItem.h | 22 +- .../entities/src/EntityItemProperties.cpp | 78 +++++++ libraries/entities/src/EntityItemProperties.h | 206 ++++++++++-------- libraries/entities/src/EntityPropertyFlags.h | 8 +- libraries/entities/src/TextEntityItem.cpp | 103 +++++++++ libraries/entities/src/TextEntityItem.h | 26 +++ .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/render-utils/src/TextRenderer3D.cpp | 4 +- libraries/render-utils/src/TextRenderer3D.h | 2 +- libraries/render-utils/src/text/Font.cpp | 10 +- libraries/render-utils/src/text/Font.h | 2 +- 16 files changed, 420 insertions(+), 143 deletions(-) diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index c8f8550e8e..b9225f4868 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -123,7 +123,7 @@ void Text3DOverlay::render(RenderArgs* args) { glm::vec4 textColor = { toGlm(_color), getTextAlpha() }; // FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline for a gpu performance increase. - _textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f), true); + _textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f)); } const render::ShapeKey Text3DOverlay::getShapeKey() { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index a705b61cd3..cd43533f17 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -139,21 +139,6 @@ bool ShapeEntityRenderer::isTransparent() const { return Parent::isTransparent(); } -ItemKey ShapeEntityRenderer::getKey() { - ItemKey::Builder builder; - builder.withTypeShape().withTypeMeta().withTagBits(getTagMask()); - - withReadLock([&] { - if (isTransparent()) { - builder.withTransparent(); - } else if (_canCastShadow) { - builder.withShadowCaster(); - } - }); - - return builder.build(); -} - bool ShapeEntityRenderer::useMaterialPipeline() const { bool proceduralReady = resultWithReadLock([&] { return _procedural.isReady(); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 7700aa6ef0..a33f023213 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -25,7 +25,6 @@ public: virtual scriptable::ScriptableModelBase getScriptableModel() override; protected: - ItemKey getKey() override; ShapeKey getShapeKey() override; private: diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 6e281081d2..4ddb398fbf 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -27,7 +27,10 @@ static const int FIXED_FONT_POINT_SIZE = 40; 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() { @@ -37,6 +40,18 @@ TextEntityRenderer::~TextEntityRenderer() { } } +bool TextEntityRenderer::isTransparent() const { + return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f; +} + +ShapeKey TextEntityRenderer::getShapeKey() { + auto builder = render::ShapeKey::Builder().withOwnPipeline(); + if (isTransparent()) { + builder.withTranslucent(); + } + return builder.build(); +} + bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { if (_text != entity->getText()) { return true; @@ -50,10 +65,18 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint return true; } + if (_textAlpha != entity->getTextAlpha()) { + return true; + } + if (_backgroundColor != toGlm(entity->getBackgroundColor())) { return true; } + if (_backgroundAlpha != entity->getBackgroundAlpha()) { + return true; + } + if (_dimensions != entity->getScaledDimensions()) { return true; } @@ -61,6 +84,23 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint if (_billboardMode != entity->getBillboardMode()) { return true; } + + if (_leftMargin != entity->getLeftMargin()) { + return true; + } + + if (_rightMargin != entity->getRightMargin()) { + return true; + } + + if (_topMargin != entity->getTopMargin()) { + return true; + } + + if (_bottomMargin != entity->getBottomMargin()) { + return true; + } + return false; } @@ -76,14 +116,19 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen } void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { - _textColor = toGlm(entity->getTextColor()); - _backgroundColor = toGlm(entity->getBackgroundColor()); - _billboardMode = entity->getBillboardMode(); - _lineHeight = entity->getLineHeight(); _text = entity->getText(); + _lineHeight = entity->getLineHeight(); + _textColor = toGlm(entity->getTextColor()); + _textAlpha = entity->getTextAlpha(); + _backgroundColor = toGlm(entity->getBackgroundColor()); + _backgroundAlpha = entity->getBackgroundAlpha(); + _billboardMode = entity->getBillboardMode(); + _leftMargin = entity->getLeftMargin(); + _rightMargin = entity->getRightMargin(); + _topMargin = entity->getTopMargin(); + _bottomMargin = entity->getBottomMargin(); } - void TextEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableTextEntityItem::render"); @@ -93,17 +138,16 @@ void TextEntityRenderer::doRender(RenderArgs* args) { modelTransform = _renderTransform; dimensions = _dimensions; }); - static const float SLIGHTLY_BEHIND = -0.005f; + float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - bool transparent = fadeRatio < 1.0f; - glm::vec4 textColor = glm::vec4(_textColor, fadeRatio); - glm::vec4 backgroundColor = glm::vec4(_backgroundColor, fadeRatio); + glm::vec4 textColor = glm::vec4(_textColor, fadeRatio * _textAlpha); + glm::vec4 backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha); // 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; @@ -135,18 +179,15 @@ void TextEntityRenderer::doRender(RenderArgs* args) { batch.setModelTransform(transformToTopLeft); auto geometryCache = DependencyManager::get(); - if (!_geometryID) { - _geometryID = geometryCache->allocateID(); - } - geometryCache->bindSimpleProgram(batch, false, transparent, false, false, false); + geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false); geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID); + // FIXME: Factor out textRenderer so that Text3DOverlay overlay 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); - float leftMargin = 0.1f * _lineHeight, topMargin = 0.1f * _lineHeight; - glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, - dimensions.y - 2.0f * topMargin); - _textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale); + glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), + dimensions.y - (_topMargin + _bottomMargin)); + _textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale); } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index a368f280c5..6b20705209 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -25,19 +25,33 @@ class TextEntityRenderer : public TypedEntityRenderer { public: TextEntityRenderer(const EntityItemPointer& entity); ~TextEntityRenderer(); + + bool isTransparent() const 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; virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; virtual void doRender(RenderArgs* args) override; + int _geometryID{ 0 }; std::shared_ptr _textRenderer; - BillboardMode _billboardMode; - glm::vec3 _dimensions; - glm::vec3 _textColor; - glm::vec3 _backgroundColor; + QString _text; float _lineHeight; + glm::vec3 _textColor; + float _textAlpha; + glm::vec3 _backgroundColor; + float _backgroundAlpha; + + float _leftMargin; + float _rightMargin; + float _topMargin; + float _bottomMargin; + + BillboardMode _billboardMode; + glm::vec3 _dimensions; }; } } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 3eb193f1bd..826d180539 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -443,6 +443,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_MAJOR_GRID_EVERY, majorGridEvery); CHECK_PROPERTY_CHANGE(PROP_MINOR_GRID_EVERY, minorGridEvery); + CHECK_PROPERTY_CHANGE(PROP_TEXT_ALPHA, textAlpha); + CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_ALPHA, backgroundAlpha); + CHECK_PROPERTY_CHANGE(PROP_LEFT_MARGIN, leftMargin); + CHECK_PROPERTY_CHANGE(PROP_RIGHT_MARGIN, rightMargin); + CHECK_PROPERTY_CHANGE(PROP_TOP_MARGIN, topMargin); + CHECK_PROPERTY_CHANGE(PROP_BOTTOM_MARGIN, bottomMargin); + // Certifiable Properties CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName); CHECK_PROPERTY_CHANGE(PROP_ITEM_DESCRIPTION, itemDescription); @@ -1141,11 +1148,17 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * created using \n. Overflowing lines are not displayed. * @property {number} lineHeight=0.1 - The height of each line of text (thus determining the font size). * @property {Color} textColor=255,255,255 - The color of the text. + * @property {number} textAlpha=1.0 - The text alpha. * @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. + * @property {number} backgroundAlpha=1.0 - The background alpha. * @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 {number} leftMargin=0.0 - The left margin, in meters. + * @property {number} rightMargin=0.0 - The right margin, in meters. + * @property {number} topMargin=0.0 - The top margin, in meters. + * @property {number} bottomMargin=0.0 - The bottom margin, in meters. * @example Create a text entity. * var text = Entities.addEntity({ * type: "Text", @@ -1503,8 +1516,14 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_TEXT_COLOR, textColor, getTextColor(), u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_ALPHA, textAlpha); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor(), 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); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BOTTOM_MARGIN, bottomMargin); } // Zones only @@ -1858,6 +1877,13 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(majorGridEvery, uint32_t, setMajorGridEvery); COPY_PROPERTY_FROM_QSCRIPTVALUE(minorGridEvery, float, setMinorGridEvery); + COPY_PROPERTY_FROM_QSCRIPTVALUE(textAlpha, float, setTextAlpha); + COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundAlpha, float, setBackgroundAlpha); + COPY_PROPERTY_FROM_QSCRIPTVALUE(leftMargin, float, setLeftMargin); + COPY_PROPERTY_FROM_QSCRIPTVALUE(rightMargin, float, setRightMargin); + COPY_PROPERTY_FROM_QSCRIPTVALUE(topMargin, float, setTopMargin); + COPY_PROPERTY_FROM_QSCRIPTVALUE(bottomMargin, float, setBottomMargin); + if (!honorReadOnly) { // this is used by the json reader to set things that we don't want javascript to able to affect. COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(created, QDateTime, setCreated, [this]() { @@ -2046,6 +2072,13 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(majorGridEvery); COPY_PROPERTY_IF_CHANGED(minorGridEvery); + COPY_PROPERTY_IF_CHANGED(textAlpha); + COPY_PROPERTY_IF_CHANGED(backgroundAlpha); + COPY_PROPERTY_IF_CHANGED(leftMargin); + COPY_PROPERTY_IF_CHANGED(rightMargin); + COPY_PROPERTY_IF_CHANGED(topMargin); + COPY_PROPERTY_IF_CHANGED(bottomMargin); + // Certifiable Properties COPY_PROPERTY_IF_CHANGED(itemName); COPY_PROPERTY_IF_CHANGED(itemDescription); @@ -2410,6 +2443,13 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t); ADD_PROPERTY_TO_MAP(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float); + ADD_PROPERTY_TO_MAP(PROP_TEXT_ALPHA, TextAlpha, textAlpha, float); + ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_ALPHA, BackgroundAlpha, backgroundAlpha, float); + 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); + ADD_PROPERTY_TO_MAP(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float); + // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -2572,8 +2612,14 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText()); APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight()); APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, properties.getTextColor()); + 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()); + APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, properties.getBottomMargin()); } if (properties.getType() == EntityTypes::Model) { @@ -2998,8 +3044,14 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, u8vec3Color, setTextColor); + 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); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BOTTOM_MARGIN, float, setBottomMargin); } if (properties.getType() == EntityTypes::Model) { @@ -3508,6 +3560,13 @@ void EntityItemProperties::markAllChanged() { _followCameraChanged = true; _majorGridEveryChanged = true; _minorGridEveryChanged = true; + + _textAlphaChanged = true; + _backgroundAlphaChanged = true; + _leftMarginChanged = true; + _rightMarginChanged = true; + _topMarginChanged = true; + _bottomMarginChanged = true; } // The minimum bounding box for the entity. @@ -4074,6 +4133,25 @@ QList EntityItemProperties::listChangedProperties() { out += "minorGridEvery"; } + if (textAlphaChanged()) { + out += "textAlpha"; + } + if (backgroundAlphaChanged()) { + out += "backgroundAlpha"; + } + if (leftMarginChanged()) { + out += "leftMargin"; + } + if (rightMarginChanged()) { + out += "rightMargin"; + } + if (topMarginChanged()) { + out += "topMargin"; + } + if (bottomMarginChanged()) { + out += "bottomMargin"; + } + getAnimation().listChangedProperties(out); getKeyLight().listChangedProperties(out); getAmbientLight().listChangedProperties(out); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6fe7907777..e2f483d5b8 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -128,52 +128,60 @@ public: // type _foo { value }; // bool _fooChanged { false }; + // Core Properties DEFINE_PROPERTY(PROP_VISIBLE, Visible, visible, bool, ENTITY_ITEM_DEFAULT_VISIBLE); - DEFINE_PROPERTY(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool, ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW); + DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME); + DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool, ENTITY_ITEM_DEFAULT_LOCKED); + DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString, ENTITY_ITEM_DEFAULT_USER_DATA); + DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); + DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, ""); DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3, ENTITY_ITEM_ZERO_VEC3); DEFINE_PROPERTY_REF(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3, ENTITY_ITEM_DEFAULT_DIMENSIONS); DEFINE_PROPERTY_REF(PROP_ROTATION, Rotation, rotation, glm::quat, ENTITY_ITEM_DEFAULT_ROTATION); + DEFINE_PROPERTY_REF(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT); + DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64, UNKNOWN_CREATED_TIME); + DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY); + DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST_TYPE, EntityHostType, entityHostType, entity::HostType, entity::HostType::DOMAIN); + DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID); + DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID); + DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1); + DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube()); + DEFINE_PROPERTY(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool, ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW); + DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA); + DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); + + // Physics DEFINE_PROPERTY(PROP_DENSITY, Density, density, float, ENTITY_ITEM_DEFAULT_DENSITY); DEFINE_PROPERTY_REF(PROP_VELOCITY, Velocity, velocity, glm::vec3, ENTITY_ITEM_DEFAULT_VELOCITY); + DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY); DEFINE_PROPERTY_REF(PROP_GRAVITY, Gravity, gravity, glm::vec3, ENTITY_ITEM_DEFAULT_GRAVITY); DEFINE_PROPERTY_REF(PROP_ACCELERATION, Acceleration, acceleration, glm::vec3, ENTITY_ITEM_DEFAULT_ACCELERATION); DEFINE_PROPERTY(PROP_DAMPING, Damping, damping, float, ENTITY_ITEM_DEFAULT_DAMPING); + DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING); DEFINE_PROPERTY(PROP_RESTITUTION, Restitution, restitution, float, ENTITY_ITEM_DEFAULT_RESTITUTION); DEFINE_PROPERTY(PROP_FRICTION, Friction, friction, float, ENTITY_ITEM_DEFAULT_FRICTION); DEFINE_PROPERTY(PROP_LIFETIME, Lifetime, lifetime, float, ENTITY_ITEM_DEFAULT_LIFETIME); - DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64, UNKNOWN_CREATED_TIME); - DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString, ENTITY_ITEM_DEFAULT_SCRIPT); - DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP); - DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL); - DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, particle::DEFAULT_COLOR); - DEFINE_PROPERTY_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color, particle::DEFAULT_COLOR_SPREAD); - DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); - DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); - DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, particle::DEFAULT_ALPHA); - DEFINE_PROPERTY(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, particle::DEFAULT_ALPHA_SPREAD); - DEFINE_PROPERTY(PROP_ALPHA_START, AlphaStart, alphaStart, float, particle::DEFAULT_ALPHA_START); - DEFINE_PROPERTY(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, particle::DEFAULT_ALPHA_FINISH); - DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString, ""); - DEFINE_PROPERTY_REF(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT); - DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY); - DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING); DEFINE_PROPERTY(PROP_COLLISIONLESS, Collisionless, collisionless, bool, ENTITY_ITEM_DEFAULT_COLLISIONLESS); DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint16_t, ENTITY_COLLISION_MASK_DEFAULT); DEFINE_PROPERTY(PROP_DYNAMIC, Dynamic, dynamic, bool, ENTITY_ITEM_DEFAULT_DYNAMIC); - DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT); - DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, LightEntityItem::DEFAULT_INTENSITY); - DEFINE_PROPERTY(PROP_FALLOFF_RADIUS, FalloffRadius, falloffRadius, float, LightEntityItem::DEFAULT_FALLOFF_RADIUS); - DEFINE_PROPERTY(PROP_EXPONENT, Exponent, exponent, float, LightEntityItem::DEFAULT_EXPONENT); - DEFINE_PROPERTY(PROP_CUTOFF, Cutoff, cutoff, float, LightEntityItem::DEFAULT_CUTOFF); - DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool, ENTITY_ITEM_DEFAULT_LOCKED); - DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, ""); - DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString, ENTITY_ITEM_DEFAULT_USER_DATA); DEFINE_PROPERTY_REF(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner, SimulationOwner()); - DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString, TextEntityItem::DEFAULT_TEXT); - DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float, TextEntityItem::DEFAULT_LINE_HEIGHT); - DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR); - DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color, TextEntityItem::DEFAULT_BACKGROUND_COLOR); + DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL); + DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray, QByteArray()); + + // Cloning + DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE); + DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); + DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT); + DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); + DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); + DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID); + + // Scripts + DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString, ENTITY_ITEM_DEFAULT_SCRIPT); + DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP); + DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS); + + // Particles DEFINE_PROPERTY_REF_ENUM(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType, SHAPE_TYPE_NONE); DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, particle::DEFAULT_MAX_PARTICLES); DEFINE_PROPERTY(PROP_LIFESPAN, Lifespan, lifespan, float, particle::DEFAULT_LIFESPAN); @@ -184,45 +192,81 @@ public: DEFINE_PROPERTY_REF(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, glm::quat, particle::DEFAULT_EMIT_ORIENTATION); DEFINE_PROPERTY_REF(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, glm::vec3, particle::DEFAULT_EMIT_DIMENSIONS); DEFINE_PROPERTY(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float, particle::DEFAULT_EMIT_RADIUS_START); + DEFINE_PROPERTY_REF(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3, particle::DEFAULT_EMIT_ACCELERATION); + DEFINE_PROPERTY_REF(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3, particle::DEFAULT_ACCELERATION_SPREAD); DEFINE_PROPERTY(PROP_POLAR_START, PolarStart, polarStart, float, particle::DEFAULT_POLAR_START); DEFINE_PROPERTY(PROP_POLAR_FINISH, PolarFinish, polarFinish, float, particle::DEFAULT_POLAR_FINISH); DEFINE_PROPERTY(PROP_AZIMUTH_START, AzimuthStart, azimuthStart, float, particle::DEFAULT_AZIMUTH_START); DEFINE_PROPERTY(PROP_AZIMUTH_FINISH, AzimuthFinish, azimuthFinish, float, particle::DEFAULT_AZIMUTH_FINISH); - DEFINE_PROPERTY_REF(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3, particle::DEFAULT_EMIT_ACCELERATION); - DEFINE_PROPERTY_REF(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3, particle::DEFAULT_ACCELERATION_SPREAD); DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float, particle::DEFAULT_PARTICLE_RADIUS); DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, particle::DEFAULT_RADIUS_SPREAD); DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, particle::DEFAULT_RADIUS_START); DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, particle::DEFAULT_RADIUS_FINISH); + DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, particle::DEFAULT_COLOR); + DEFINE_PROPERTY_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color, particle::DEFAULT_COLOR_SPREAD); + DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); + DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); + DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, particle::DEFAULT_ALPHA); + DEFINE_PROPERTY(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, particle::DEFAULT_ALPHA_SPREAD); + DEFINE_PROPERTY(PROP_ALPHA_START, AlphaStart, alphaStart, float, particle::DEFAULT_ALPHA_START); + DEFINE_PROPERTY(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, particle::DEFAULT_ALPHA_FINISH); + DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, ""); DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, particle::DEFAULT_EMITTER_SHOULD_TRAIL); + DEFINE_PROPERTY(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, particle::DEFAULT_PARTICLE_SPIN); + DEFINE_PROPERTY(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, particle::DEFAULT_SPIN_SPREAD); + DEFINE_PROPERTY(PROP_SPIN_START, SpinStart, spinStart, float, particle::DEFAULT_SPIN_START); + DEFINE_PROPERTY(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::DEFAULT_SPIN_FINISH); + DEFINE_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, bool, particle::DEFAULT_ROTATE_WITH_ENTITY); + + // Model + DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString, ""); + DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString, ""); + DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector, QVector()); + DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector, QVector()); + DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector, QVector()); + DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); + DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); + DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); + + // Light + DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT); + DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, LightEntityItem::DEFAULT_INTENSITY); + DEFINE_PROPERTY(PROP_EXPONENT, Exponent, exponent, float, LightEntityItem::DEFAULT_EXPONENT); + DEFINE_PROPERTY(PROP_CUTOFF, Cutoff, cutoff, float, LightEntityItem::DEFAULT_CUTOFF); + DEFINE_PROPERTY(PROP_FALLOFF_RADIUS, FalloffRadius, falloffRadius, float, LightEntityItem::DEFAULT_FALLOFF_RADIUS); + + // Text + DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString, TextEntityItem::DEFAULT_TEXT); + DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float, TextEntityItem::DEFAULT_LINE_HEIGHT); + DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR); + 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); + DEFINE_PROPERTY_REF(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float, TextEntityItem::DEFAULT_MARGIN); + + // Zone DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_GROUP(AmbientLight, ambientLight, AmbientLightPropertyGroup); - DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); - DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA); - DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE); - DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME); - + DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); + DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); + DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); + DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED); + DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); + DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL); DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); - DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); - DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); - DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); - DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); - DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); - DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); - DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); - DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, ""); - DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE); - DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray, QByteArray()); - DEFINE_PROPERTY(PROP_NORMALS, Normals, normals, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); - DEFINE_PROPERTY(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); - DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector, QVector()); - DEFINE_PROPERTY(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, bool, true); + // Polyvox + DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); + DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA); + DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE); DEFINE_PROPERTY_REF(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString, ""); DEFINE_PROPERTY_REF(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString, ""); DEFINE_PROPERTY_REF(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString, ""); @@ -232,11 +276,25 @@ public: DEFINE_PROPERTY_REF(PROP_X_P_NEIGHBOR_ID, XPNeighborID, xPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID); DEFINE_PROPERTY_REF(PROP_Y_P_NEIGHBOR_ID, YPNeighborID, yPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID); DEFINE_PROPERTY_REF(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID); - DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID); - DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1); - DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube()); + + // Web + DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); + DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI); + + // Line + DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); + DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); + + // Polyline + DEFINE_PROPERTY(PROP_NORMALS, Normals, normals, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); + DEFINE_PROPERTY(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); + DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector, QVector()); + DEFINE_PROPERTY(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, bool, true); + + // Shape DEFINE_PROPERTY_REF(PROP_SHAPE, Shape, shape, QString, "Sphere"); + // Material DEFINE_PROPERTY_REF(PROP_MATERIAL_URL, MaterialURL, materialURL, QString, ""); DEFINE_PROPERTY_REF_ENUM(PROP_MATERIAL_MAPPING_MODE, MaterialMappingMode, materialMappingMode, MaterialMappingMode, UV); DEFINE_PROPERTY_REF(PROP_MATERIAL_PRIORITY, Priority, priority, quint16, 0); @@ -247,19 +305,13 @@ public: DEFINE_PROPERTY_REF(PROP_MATERIAL_DATA, MaterialData, materialData, QString, ""); DEFINE_PROPERTY_REF(PROP_MATERIAL_REPEAT, MaterialRepeat, materialRepeat, bool, true); - DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA); - - DEFINE_PROPERTY(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, particle::DEFAULT_PARTICLE_SPIN); - DEFINE_PROPERTY(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, particle::DEFAULT_SPIN_SPREAD); - DEFINE_PROPERTY(PROP_SPIN_START, SpinStart, spinStart, float, particle::DEFAULT_SPIN_START); - DEFINE_PROPERTY(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::DEFAULT_SPIN_FINISH); - DEFINE_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, bool, particle::DEFAULT_ROTATE_WITH_ENTITY); - + // Image DEFINE_PROPERTY_REF(PROP_IMAGE_URL, ImageURL, imageURL, QString, ""); DEFINE_PROPERTY_REF(PROP_EMISSIVE, Emissive, emissive, bool, false); DEFINE_PROPERTY_REF(PROP_KEEP_ASPECT_RATIO, KeepAspectRatio, keepAspectRatio, bool, true); DEFINE_PROPERTY_REF(PROP_SUB_IMAGE, SubImage, subImage, QRect, QRect()); + // Grid DEFINE_PROPERTY_REF(PROP_GRID_FOLLOW_CAMERA, FollowCamera, followCamera, bool, true); DEFINE_PROPERTY(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t, GridEntityItem::DEFAULT_MAJOR_GRID_EVERY); DEFINE_PROPERTY(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float, GridEntityItem::DEFAULT_MINOR_GRID_EVERY); @@ -284,34 +336,6 @@ public: DEFINE_PROPERTY_REF(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glm::vec3, ENTITY_ITEM_ZERO_VEC3); DEFINE_PROPERTY_REF(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, glm::vec3, ENTITY_ITEM_ZERO_VEC3); - DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector, QVector()); - DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector, QVector()); - DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector, QVector()); - DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); - - DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED); - DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); - DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL); - - DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST_TYPE, EntityHostType, entityHostType, entity::HostType, entity::HostType::DOMAIN); - DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID); - - DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI); - - DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY); - - DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS); - DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); - - DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE); - DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); - DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT); - DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); - DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); - DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID); - - DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); - static QString getComponentModeAsString(uint32_t mode); std::array::const_iterator findComponent(const QString& mode); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index a68c374ce6..864d5efa10 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -280,6 +280,11 @@ enum EntityPropertyList { PROP_EMISSIVE, PROP_SUB_IMAGE, + PROP_LEFT_MARGIN, + PROP_RIGHT_MARGIN, + PROP_TOP_MARGIN, + PROP_BOTTOM_MARGIN, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, @@ -291,10 +296,11 @@ enum EntityPropertyList { // These properties of TextEntity piggy back off of properties of ModelEntities, the type doesn't matter // since the derived class knows how to interpret it's own properties and knows the types it expects PROP_TEXT_COLOR = PROP_COLOR, + PROP_TEXT_ALPHA = PROP_ALPHA, PROP_TEXT = PROP_MODEL_URL, PROP_LINE_HEIGHT = PROP_ANIMATION_URL, PROP_BACKGROUND_COLOR = PROP_ANIMATION_FPS, - PROP_COLLISION_MODEL_URL_OLD_VERSION = PROP_ANIMATION_FPS + 1, + PROP_BACKGROUND_ALPHA = PROP_ALPHA_START, // Aliases/Piggyback properties for Zones. These properties intentionally reuse the enum values for // other properties which will never overlap with each other. We do this so that we don't have to expand diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index b178ce33c3..80c4c896bd 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -26,7 +26,9 @@ const QString TextEntityItem::DEFAULT_TEXT(""); const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f; const glm::u8vec3 TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 }; +const float TextEntityItem::DEFAULT_TEXT_ALPHA = 1.0f; const glm::u8vec3 TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; +const float TextEntityItem::DEFAULT_MARGIN = 0.0f; EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new TextEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); @@ -50,8 +52,14 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColor); + 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); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(bottomMargin, getBottomMargin); return properties; } @@ -62,8 +70,14 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(text, setText); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor); + 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); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(bottomMargin, setBottomMargin); if (somethingChanged) { bool wantDebug = false; @@ -90,8 +104,14 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight); READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, glm::u8vec3, setTextColor); + 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); + READ_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, float, setBottomMargin); return bytesRead; } @@ -101,8 +121,14 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p 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; + requestedProperties += PROP_BOTTOM_MARGIN; return requestedProperties; } @@ -119,8 +145,14 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_TEXT, getText()); APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight()); APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, getTextColor()); + 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()); } @@ -216,6 +248,18 @@ glm::u8vec3 TextEntityItem::getTextColor() const { }); } +void TextEntityItem::setTextAlpha(float value) { + withWriteLock([&] { + _textAlpha = value; + }); +} + +float TextEntityItem::getTextAlpha() const { + return resultWithReadLock([&] { + return _textAlpha; + }); +} + void TextEntityItem::setBackgroundColor(const glm::u8vec3& value) { withWriteLock([&] { _backgroundColor = value; @@ -228,6 +272,18 @@ glm::u8vec3 TextEntityItem::getBackgroundColor() const { }); } +void TextEntityItem::setBackgroundAlpha(float value) { + withWriteLock([&] { + _backgroundAlpha = value; + }); +} + +float TextEntityItem::getBackgroundAlpha() const { + return resultWithReadLock([&] { + return _backgroundAlpha; + }); +} + BillboardMode TextEntityItem::getBillboardMode() const { BillboardMode result; withReadLock([&] { @@ -242,3 +298,50 @@ void TextEntityItem::setBillboardMode(BillboardMode value) { }); } +void TextEntityItem::setLeftMargin(float value) { + withWriteLock([&] { + _leftMargin = value; + }); +} + +float TextEntityItem::getLeftMargin() const { + return resultWithReadLock([&] { + return _leftMargin; + }); +} + +void TextEntityItem::setRightMargin(float value) { + withWriteLock([&] { + _rightMargin = value; + }); +} + +float TextEntityItem::getRightMargin() const { + return resultWithReadLock([&] { + return _rightMargin; + }); +} + +void TextEntityItem::setTopMargin(float value) { + withWriteLock([&] { + _topMargin = value; + }); +} + +float TextEntityItem::getTopMargin() const { + return resultWithReadLock([&] { + return _topMargin; + }); +} + +void TextEntityItem::setBottomMargin(float value) { + withWriteLock([&] { + _bottomMargin = value; + }); +} + +float TextEntityItem::getBottomMargin() const { + return resultWithReadLock([&] { + return _bottomMargin; + }); +} diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 9abef4d198..5c22d8edf0 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -67,19 +67,45 @@ public: glm::u8vec3 getTextColor() const; void setTextColor(const glm::u8vec3& value); + static const float DEFAULT_TEXT_ALPHA; + float getTextAlpha() const; + void setTextAlpha(float value); + static const glm::u8vec3 DEFAULT_BACKGROUND_COLOR; glm::u8vec3 getBackgroundColor() const; void setBackgroundColor(const glm::u8vec3& value); + float getBackgroundAlpha() const; + void setBackgroundAlpha(float value); + BillboardMode getBillboardMode() const; void setBillboardMode(BillboardMode value); + static const float DEFAULT_MARGIN; + float getLeftMargin() const; + void setLeftMargin(float value); + + float getRightMargin() const; + void setRightMargin(float value); + + float getTopMargin() const; + void setTopMargin(float value); + + float getBottomMargin() const; + void setBottomMargin(float value); + private: QString _text; float _lineHeight; glm::u8vec3 _textColor; + float _textAlpha; glm::u8vec3 _backgroundColor; + float _backgroundAlpha; BillboardMode _billboardMode; + float _leftMargin; + float _rightMargin; + float _topMargin; + float _bottomMargin; }; #endif // hifi_TextEntityItem_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 485f8f7de2..f2db155dca 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::GridEntities); + return static_cast(EntityVersion::MissingTextProperties); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3d53efad45..0d4d065f5b 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -249,7 +249,8 @@ enum class EntityVersion : PacketVersion { EntityHostTypes, CleanupProperties, ImageEntities, - GridEntities + GridEntities, + MissingTextProperties }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 8ef0dc0d73..2d507ba21b 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -67,11 +67,11 @@ float TextRenderer3D::getFontSize() const { } void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color, - const glm::vec2& bounds, bool layered) { + const glm::vec2& bounds) { // The font does all the OpenGL work if (_font) { _color = color; - _font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds, layered); + _font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds); } } diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index 6c91411e1d..97b74fcabd 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -39,7 +39,7 @@ public: float getFontSize() const; // Pixel size void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(1.0f), - const glm::vec2& bounds = glm::vec2(-1.0f), bool layered = false); + const glm::vec2& bounds = glm::vec2(-1.0f)); private: TextRenderer3D(const char* family, float pointSize, int weight = -1, bool italic = false, diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 68a1e59dc3..cc451eeedc 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -222,7 +222,6 @@ void Font::setupGPU() { // Setup render pipeline { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D); - gpu::ShaderPointer programTransparent = gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D_transparent); auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); @@ -232,13 +231,14 @@ void Font::setupGPU() { PrepareStencil::testMaskDrawShape(*state); _pipeline = gpu::Pipeline::create(program, state); + gpu::ShaderPointer programTransparent = gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D_transparent); auto transparentState = std::make_shared(); transparentState->setCullMode(gpu::State::CULL_BACK); transparentState->setDepthTest(true, true, gpu::LESS_EQUAL); transparentState->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - PrepareStencil::testMaskDrawShape(*transparentState); + PrepareStencil::testMask(*transparentState); _transparentPipeline = gpu::Pipeline::create(programTransparent, transparentState); } @@ -289,7 +289,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm break; } } - if ((bounds.y != -1) && (advance.y - _fontSize < -origin.y - bounds.y)) { + if ((bounds.y != -1) && (advance.y - _fontSize < origin.y - bounds.y)) { // We are out of the y bound, stop drawing break; } @@ -343,7 +343,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm } void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color, - EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds, bool layered) { + EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds) { if (str == "") { return; } @@ -370,7 +370,7 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString } // need the gamma corrected color here - batch.setPipeline((color.a < 1.0f || layered) ? _transparentPipeline : _pipeline); + batch.setPipeline((color.a < 1.0f) ? _transparentPipeline : _pipeline); batch.setInputFormat(_format); batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture); diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index 9a0a9b4734..85f692f5d8 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -46,7 +46,7 @@ public: // Render string to batch void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str, const glm::vec4& color, EffectType effectType, - const glm::vec2& origin, const glm::vec2& bound, bool layered = false); + const glm::vec2& origin, const glm::vec2& bound); static Pointer load(const QString& family); From 4c1ba8b1aa3883c721edd77651e68f216bc6bb4a Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 18 Dec 2018 16:33:13 -0800 Subject: [PATCH 02/28] Case 20106-AC agents spawned with -n don't come back with the expected amount after a domain restart They were being incorrectly marked as 'unassigned' in the assignment client monitor. --- assignment-client/src/AssignmentClientMonitor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 330023dae0..98b7cbce1b 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -276,6 +276,7 @@ void AssignmentClientMonitor::checkSpares() { // Spawn or kill children, as needed. If --min or --max weren't specified, allow the child count // to drift up or down as far as needed. + if (spareCount < 1 || totalCount < _minAssignmentClientForks) { if (!_maxAssignmentClientForks || totalCount < _maxAssignmentClientForks) { spawnChildClient(); @@ -316,9 +317,9 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer()->addOrUpdateNode(senderID, NodeType::Unassigned, senderSockAddr, senderSockAddr); - auto childData = std::unique_ptr + auto newChildData = std::unique_ptr { new AssignmentClientChildData(Assignment::Type::AllTypes) }; - matchingNode->setLinkedData(std::move(childData)); + matchingNode->setLinkedData(std::move(newChildData)); } else { // tell unknown assignment-client child to exit. qDebug() << "Asking unknown child at" << senderSockAddr << "to exit."; @@ -329,9 +330,8 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer(matchingNode->getLinkedData()); } + childData = dynamic_cast(matchingNode->getLinkedData()); if (childData) { // update our records about how to reach this child From eaeb111bf127b4c8b6d3667f18c117f568c0a99c Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 18 Dec 2018 16:50:30 -0800 Subject: [PATCH 03/28] Fix Typo --- assignment-client/src/AssignmentClientMonitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 98b7cbce1b..fefed6e143 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -308,7 +308,7 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer Date: Thu, 20 Dec 2018 10:15:44 -0700 Subject: [PATCH 04/28] ACs don't need to call into flagtimeForConnecitonStep --- interface/src/Application.cpp | 1 + libraries/networking/src/LimitedNodeList.cpp | 10 +++++++--- libraries/networking/src/LimitedNodeList.h | 12 ++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cce4b14868..5dbc71b472 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1078,6 +1078,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto nodeList = DependencyManager::get(); nodeList->startThread(); + nodeList->setFlagTimeForConnectionStep(true); // move the AddressManager to the NodeList thread so that domain resets due to domain changes always occur // before we tell MyAvatar to go to a new location in the new domain diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 37b914143e..a1137b785a 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -300,10 +300,10 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe } else { HIFI_FCDEBUG(networking(), "Replicated packet of type" << headerType << "received from unknown upstream" << packet.getSenderSockAddr()); - + return false; } - + } else { emit dataReceived(NodeType::Unassigned, packet.getPayloadSize()); return true; @@ -319,7 +319,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe SharedNodePointer matchingNode = nodeWithLocalID(sourceLocalID); sourceNode = matchingNode.data(); } - + QUuid sourceID = sourceNode ? sourceNode->getUUID() : QUuid(); if (!sourceNode && @@ -1261,6 +1261,10 @@ void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep) { } void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp) { + if (!_flagTimeForConnectionStep) { + // this is only true in interface + return; + } if (connectionStep == ConnectionStep::LookupAddress) { QWriteLocker writeLock(&_connectionTimeLock); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index dacefa8e40..33a4a7e0b4 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -122,7 +122,7 @@ public: bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); } bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } bool getThisNodeCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); } - + quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); } Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort); @@ -204,9 +204,9 @@ public: // This allows multiple threads (i.e. a thread pool) to share a lock // without deadlocking when a dying node attempts to acquire a write lock template - void nestedEach(NestedNodeLambda functor, - int* lockWaitOut = nullptr, - int* nodeTransformOut = nullptr, + void nestedEach(NestedNodeLambda functor, + int* lockWaitOut = nullptr, + int* nodeTransformOut = nullptr, int* functorOut = nullptr) { auto start = usecTimestampNow(); { @@ -310,6 +310,9 @@ public: void setAuthenticatePackets(bool useAuthentication) { _useAuthentication = useAuthentication; } bool getAuthenticatePackets() const { return _useAuthentication; } + void setFlagTimeForConnectionStep(bool flag) { _flagTimeForConnectionStep = flag; } + bool isFlagTimeForConnectionStep() { return _flagTimeForConnectionStep; } + static void makeSTUNRequestPacket(char* stunRequestPacket); #if (PR_BUILD || DEV_BUILD) @@ -440,6 +443,7 @@ private: using LocalIDMapping = tbb::concurrent_unordered_map; LocalIDMapping _localIDMap; Node::LocalID _sessionLocalID { 0 }; + bool _flagTimeForConnectionStep { false }; // only keep track in interface }; #endif // hifi_LimitedNodeList_h From 9c60b09c6bd3b547f001d196947a63114339b4db Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Dec 2018 14:50:12 -0800 Subject: [PATCH 05/28] Fix texture MB column not sorting correctly in entity list --- scripts/system/html/js/entityList.js | 35 ++++++++++++++++------------ 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 406f434a65..156006619d 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -24,6 +24,19 @@ const DELTA_X_MOVE_COLUMNS_THRESHOLD = 2; const DELTA_X_COLUMN_SWAP_POSITION = 5; const CERTIFIED_PLACEHOLDER = "** Certified **"; +function decimalMegabytes(number) { + return number ? (number / BYTES_PER_MEGABYTE).toFixed(1) : ""; +} + +function displayIfNonZero(number) { + return number ? number : ""; +} + +function getFilename(url) { + let urlParts = url.split('/'); + return urlParts[urlParts.length - 1]; +} + const COLUMNS = { type: { columnHeader: "Type", @@ -79,6 +92,7 @@ const COLUMNS = { dropdownLabel: "Texture Size", propertyID: "texturesSize", initialWidth: 0.10, + format: decimalMegabytes }, hasTransparent: { columnHeader: "", @@ -605,19 +619,6 @@ function loaded() { })); } - function decimalMegabytes(number) { - return number ? (number / BYTES_PER_MEGABYTE).toFixed(1) : ""; - } - - function displayIfNonZero(number) { - return number ? number : ""; - } - - function getFilename(url) { - let urlParts = url.split('/'); - return urlParts[urlParts.length - 1]; - } - function updateEntityData(entityData) { entities = []; entitiesByID = {}; @@ -639,7 +640,7 @@ function loaded() { certificateID: entity.certificateID, verticesCount: displayIfNonZero(entity.verticesCount), texturesCount: displayIfNonZero(entity.texturesCount), - texturesSize: decimalMegabytes(entity.texturesSize), + texturesSize: entity.texturesSize, hasTransparent: entity.hasTransparent, isBaked: entity.isBaked, drawCalls: displayIfNonZero(entity.drawCalls), @@ -874,7 +875,11 @@ function loaded() { if (column.data.glyph) { elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null; } else { - elCell.innerHTML = itemData[column.data.propertyID]; + let value = itemData[column.data.propertyID]; + if (column.data.format) { + value = column.data.format(value); + } + elCell.innerHTML = value; } elCell.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;"; elCell.className = createColumnClassName(column.columnID); From 78aff6e95ca33e4582b3d5b65fc82b1c1029ee17 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 21 Oct 2018 18:48:38 -0700 Subject: [PATCH 06/28] grab js api; send grab information through trait system --- .../src/avatars/AvatarMixerClientData.cpp | 3 +- interface/src/Application.cpp | 6 + interface/src/avatar/AvatarActionHold.cpp | 26 +- interface/src/avatar/AvatarActionHold.h | 2 + interface/src/avatar/AvatarManager.cpp | 10 + interface/src/avatar/AvatarManager.h | 4 +- interface/src/avatar/GrabManager.cpp | 39 ++ interface/src/avatar/GrabManager.h | 23 + interface/src/avatar/MyAvatar.cpp | 91 +-- interface/src/avatar/MyAvatar.h | 19 + .../src/avatars-renderer/Avatar.cpp | 107 +++- .../src/avatars-renderer/Avatar.h | 5 + libraries/avatars/src/AvatarData.cpp | 158 ++++-- libraries/avatars/src/AvatarData.h | 24 + libraries/avatars/src/AvatarTraits.h | 1 + .../src/EntityTreeRenderer.cpp | 9 + .../src/EntityTreeRenderer.h | 2 + .../entities/src/EntityDynamicInterface.h | 2 + libraries/entities/src/EntityItem.cpp | 86 ++- libraries/entities/src/EntityItem.h | 6 +- .../entities/src/EntityItemProperties.cpp | 1 + libraries/entities/src/EntityItemProperties.h | 2 + .../entities/src/EntityPropertyFlags.cpp | 1 + libraries/entities/src/EntityPropertyFlags.h | 1 + .../entities/src/EntityScriptingInterface.cpp | 123 +++++ .../entities/src/EntityScriptingInterface.h | 103 ++++ libraries/entities/src/EntityTree.cpp | 51 ++ libraries/entities/src/EntityTree.h | 6 + libraries/entities/src/GrabPropertyGroup.cpp | 9 + libraries/entities/src/GrabPropertyGroup.h | 3 + .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/physics/src/ObjectActionTractor.cpp | 80 ++- libraries/physics/src/ObjectDynamic.h | 1 + libraries/shared/src/Grab.cpp | 70 +++ libraries/shared/src/Grab.h | 99 ++++ libraries/shared/src/SpatiallyNestable.cpp | 32 ++ libraries/shared/src/SpatiallyNestable.h | 8 + .../debugging/queryAACubeInspector.js | 5 +- .../controllerModules/farGrabEntity.js | 521 ++++++++++++++++++ .../controllerModules/nearActionGrabEntity.js | 27 +- .../controllerModules/nearGrabEntity.js | 249 +++++++++ .../system/controllers/controllerScripts.js | 14 +- .../libraries/controllerDispatcherUtils.js | 18 +- 44 files changed, 1907 insertions(+), 145 deletions(-) create mode 100644 interface/src/avatar/GrabManager.cpp create mode 100644 interface/src/avatar/GrabManager.h create mode 100644 libraries/shared/src/Grab.cpp create mode 100644 libraries/shared/src/Grab.h create mode 100644 scripts/system/controllers/controllerModules/farGrabEntity.js create mode 100644 scripts/system/controllers/controllerModules/nearGrabEntity.js diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 5e36d8beaf..f215fd4448 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -146,7 +146,8 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, break; } - if (traitType == AvatarTraits::AvatarEntity) { + if (traitType == AvatarTraits::AvatarEntity || + traitType == AvatarTraits::Grab) { auto& instanceVersionRef = _lastReceivedTraitVersions.getInstanceValueRef(traitType, instanceID); if (packetTraitVersion > instanceVersionRef) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 182ff77098..a38e33714b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -208,6 +208,8 @@ #include "InterfaceParentFinder.h" #include "ui/OctreeStatsProvider.h" +#include "avatar/GrabManager.h" + #include #include #include @@ -919,6 +921,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -6085,6 +6088,9 @@ void Application::update(float deltaTime) { updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... updateDialogs(deltaTime); // update various stats dialogs if present + auto grabManager = DependencyManager::get(); + grabManager->simulateGrabs(); + QSharedPointer avatarManager = DependencyManager::get(); { diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 53074ac4ba..dd99907b8e 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -58,6 +58,16 @@ AvatarActionHold::~AvatarActionHold() { #endif } +void AvatarActionHold::removeFromOwner() { + auto avatarManager = DependencyManager::get(); + if (avatarManager) { + auto myAvatar = avatarManager->getMyAvatar(); + if (myAvatar) { + myAvatar->removeHoldAction(this); + } + } +} + bool AvatarActionHold::getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) { auto myAvatar = DependencyManager::get()->getMyAvatar(); MyCharacterController* controller = myAvatar ? myAvatar->getCharacterController() : nullptr; @@ -143,7 +153,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: ownerEntity->setTransitingWithAvatar(_isTransitingWithAvatar); } } - + if (holdingAvatar->isMyAvatar()) { std::shared_ptr myAvatar = avatarManager->getMyAvatar(); @@ -226,7 +236,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: } rotation = palmRotation * _relativeRotation; - position = palmPosition + rotation * _relativePosition; + position = palmPosition + palmRotation * _relativePosition; // update linearVelocity based on offset via _relativePosition; linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); @@ -369,8 +379,12 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { hand = _hand; } - auto myAvatar = DependencyManager::get()->getMyAvatar(); - holderID = myAvatar->getSessionUUID(); + ok = true; + holderID = EntityDynamicInterface::extractStringArgument("hold", arguments, "holderID", ok, false); + if (!ok) { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + holderID = myAvatar->getSessionUUID(); + } ok = true; kinematic = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false); @@ -417,13 +431,13 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { _kinematicSetVelocity = kinematicSetVelocity; _ignoreIK = ignoreIK; _active = true; - + auto myAvatar = DependencyManager::get()->getMyAvatar(); auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { ownerEntity->setDynamicDataDirty(true); - ownerEntity->setDynamicDataNeedsTransmit(true); + ownerEntity->setDynamicDataNeedsTransmit(true); ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isActive()); } }); diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index ddc5808d67..4583300012 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -26,6 +26,8 @@ public: AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity); virtual ~AvatarActionHold(); + virtual void removeFromOwner() override; + virtual bool updateArguments(QVariantMap arguments) override; virtual QVariantMap getArguments() override; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 7ca18ca258..21e59c06d8 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -887,3 +887,13 @@ QVariantMap AvatarManager::getPalData(const QStringList& specificAvatarIdentifie doc.insert("data", palData); return doc.toVariantMap(); } + +void AvatarManager::accumulateGrabPositions(std::map& grabAccumulators) { + auto avatarMap = getHashCopy(); + AvatarHash::iterator itr = avatarMap.begin(); + while (itr != avatarMap.end()) { + const auto& avatar = std::static_pointer_cast(*itr); + avatar->accumulateGrabPositions(grabAccumulators); + itr++; + } +} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 75dbbc7abb..359af8e361 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -198,6 +198,8 @@ public: void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); void removeDeadAvatarEntities(const SetOfEntities& deadEntities); + void accumulateGrabPositions(std::map& grabAccumulators); + public slots: /**jsdoc * @function AvatarManager.updateAvatarRenderStatus @@ -215,7 +217,7 @@ private: void simulateAvatarFades(float deltaTime); AvatarSharedPointer newSharedAvatar() override; - + // called only from the AvatarHashMap thread - cannot be called while this thread holds the // hash lock, since handleRemovedAvatar needs a write lock on the entity tree and the entity tree // frequently grabs a read lock on the hash to get a given avatar by ID diff --git a/interface/src/avatar/GrabManager.cpp b/interface/src/avatar/GrabManager.cpp new file mode 100644 index 0000000000..5312bcc4eb --- /dev/null +++ b/interface/src/avatar/GrabManager.cpp @@ -0,0 +1,39 @@ +// +// GrabManager.cpp +// interface/src/avatar/ +// +// Created by Seth Alves on 2018-12-4. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include "GrabManager.h" + +void GrabManager::simulateGrabs() { + QSharedPointer avatarManager = DependencyManager::get(); + + // Update grabbed objects + auto entityTreeRenderer = DependencyManager::get(); + auto entityTree = entityTreeRenderer->getTree(); + entityTree->withReadLock([&] { + PROFILE_RANGE(simulation, "Grabs"); + + std::map grabAccumulators; + avatarManager->accumulateGrabPositions(grabAccumulators); + + for (auto& accumulatedLocation : grabAccumulators) { + QUuid grabbedThingID = accumulatedLocation.first; + GrabLocationAccumulator& acc = accumulatedLocation.second; + bool success; + SpatiallyNestablePointer grabbedThing = SpatiallyNestable::findByID(grabbedThingID, success); + if (success && grabbedThing) { + glm::vec3 finalPosition = acc.finalizePosition(); + glm::quat finalOrientation = acc.finalizeOrientation(); + grabbedThing->setWorldTransform(finalPosition, finalOrientation); + } + } + }); +} diff --git a/interface/src/avatar/GrabManager.h b/interface/src/avatar/GrabManager.h new file mode 100644 index 0000000000..8834f95aa1 --- /dev/null +++ b/interface/src/avatar/GrabManager.h @@ -0,0 +1,23 @@ +// +// GrabManager.h +// interface/src/avatar/ +// +// Created by Seth Alves on 2018-12-4. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include "AvatarManager.h" + +class GrabManager : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + void simulateGrabs(); + +}; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 46c1f3dda1..1f63a904bb 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -746,7 +746,7 @@ void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object, bool ca void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); animateScaleChanges(deltaTime); - + setFlyingEnabled(getFlyingEnabled()); if (_cauterizationNeedsUpdate) { @@ -820,6 +820,7 @@ void MyAvatar::simulate(float deltaTime) { // and all of its joints, now update our attachements. Avatar::simulateAttachments(deltaTime); relayJointDataToChildren(); + updateGrabs(); if (!_skeletonModel->hasSkeleton()) { // All the simulation that can be done has been done @@ -874,47 +875,12 @@ void MyAvatar::simulate(float deltaTime) { zoneAllowsFlying = zone->getFlyingAllowed(); collisionlessAllowed = zone->getGhostingAllowed(); } - auto now = usecTimestampNow(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); - MovingEntitiesOperator moveOperator; + bool force = false; + bool iShouldTellServer = true; forEachDescendant([&](SpatiallyNestablePointer object) { - // if the queryBox has changed, tell the entity-server - if (object->getNestableType() == NestableType::Entity && object->updateQueryAACube()) { - EntityItemPointer entity = std::static_pointer_cast(object); - bool success; - AACube newCube = entity->getQueryAACube(success); - if (success) { - moveOperator.addEntityToMoveList(entity, newCube); - } - // send an edit packet to update the entity-server about the queryAABox - if (packetSender && entity->isDomainEntity()) { - EntityItemProperties properties = entity->getProperties(); - properties.setQueryAACubeDirty(); - properties.setLastEdited(now); - - packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, - entity->getID(), properties); - entity->setLastBroadcast(usecTimestampNow()); - - entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { - EntityItemPointer entityDescendant = std::dynamic_pointer_cast(descendant); - if (entityDescendant && entityDescendant->isDomainEntity() && descendant->updateQueryAACube()) { - EntityItemProperties descendantProperties; - descendantProperties.setQueryAACube(descendant->getQueryAACube()); - descendantProperties.setLastEdited(now); - packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, - entityDescendant->getID(), descendantProperties); - entityDescendant->setLastBroadcast(now); // for debug/physics status icons - } - }); - } - } + entityTree->updateEntityQueryAACube(object, packetSender, force, iShouldTellServer); }); - // also update the position of children in our local octree - if (moveOperator.hasMovingEntities()) { - PerformanceTimer perfTimer("recurseTreeWithOperator"); - entityTree->recurseTreeWithOperator(&moveOperator); - } }); bool isPhysicsEnabled = qApp->isPhysicsEnabled(); _characterController.setFlyingAllowed((zoneAllowsFlying && _enableFlying) || !isPhysicsEnabled); @@ -4746,3 +4712,50 @@ SpatialParentTree* MyAvatar::getParentTree() const { EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; return entityTree.get(); } + +const QUuid MyAvatar::grab(const QUuid& targetID, int parentJointIndex, + glm::vec3 positionalOffset, glm::quat rotationalOffset) { + auto grabID = QUuid::createUuid(); + // create a temporary grab object to get grabData + + QString hand = "none"; + if (parentJointIndex == CONTROLLER_RIGHTHAND_INDEX || + parentJointIndex == CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX || + parentJointIndex == FARGRAB_RIGHTHAND_INDEX || + parentJointIndex == getJointIndex("RightHand")) { + hand = "right"; + } else if (parentJointIndex == CONTROLLER_LEFTHAND_INDEX || + parentJointIndex == CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX || + parentJointIndex == FARGRAB_LEFTHAND_INDEX || + parentJointIndex == getJointIndex("LeftHand")) { + hand = "left"; + } + + Grab tmpGrab(DependencyManager::get()->getSessionUUID(), + targetID, parentJointIndex, hand, positionalOffset, rotationalOffset); + QByteArray grabData = tmpGrab.toByteArray(); + bool dataChanged = updateAvatarGrabData(grabID, grabData); + + if (dataChanged && _clientTraitsHandler) { + // indicate that the changed data should be sent to the mixer + _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::Grab, grabID); + } + + return grabID; +} + +void MyAvatar::releaseGrab(const QUuid& grabID) { + bool tellHandler { false }; + + _avatarGrabsLock.withWriteLock([&] { + if (_avatarGrabData.remove(grabID)) { + _deletedAvatarGrabs.insert(grabID); + tellHandler = true; + } + }); + + if (tellHandler && _clientTraitsHandler) { + // indicate the deletion of the data to the mixer + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::Grab, grabID); + } +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b2381366bb..92fe6d4f4a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1170,6 +1170,25 @@ public: glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); } + /**jsdoc + * Create a new grab. + * @function MyAvatar.grab + * @param {Uuid} targetID - id of grabbed thing + * @param {number} parentJointIndex - avatar joint being used to grab + * @param {Vec3} offset - target's positional offset from joint + * @param {Quat} rotationalOffset - target's rotational offset from joint + * @returns {Uuid} id of the new grab + */ + Q_INVOKABLE const QUuid grab(const QUuid& targetID, int parentJointIndex, + glm::vec3 positionalOffset, glm::quat rotationalOffset); + + /**jsdoc + * Release (delete) a grab. + * @function MyAvatar.releaseGrab + * @param {Uuid} grabID - id of grabbed thing + */ + Q_INVOKABLE void releaseGrab(const QUuid& grabID); + public slots: /**jsdoc diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 0761cdf597..fdd707c950 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include "ModelEntityItem.h" #include "RenderableModelEntityItem.h" @@ -481,6 +482,103 @@ void Avatar::removeAvatarEntitiesFromTree() { } } +void Avatar::updateGrabs() { + + // update the Grabs according to any changes in _avatarGrabData + _avatarGrabsLock.withWriteLock([&] { + if (_avatarGrabDataChanged) { + foreach (auto grabID, _avatarGrabData.keys()) { + AvatarGrabMap::iterator grabItr = _avatarGrabs.find(grabID); + if (grabItr == _avatarGrabs.end()) { + GrabPointer grab = std::make_shared(); + grab->fromByteArray(_avatarGrabData.value(grabID)); + _avatarGrabs[grabID] = grab; + _changedAvatarGrabs.insert(grabID); + } else { + GrabPointer grab = grabItr.value(); + bool changed = grab->fromByteArray(_avatarGrabData.value(grabID)); + if (changed) { + _changedAvatarGrabs.insert(grabID); + } + } + } + _avatarGrabDataChanged = false; + } + + auto treeRenderer = DependencyManager::get(); + auto entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + EntityEditPacketSender* packetSender = treeRenderer ? treeRenderer->getPacketSender() : nullptr; + auto sessionID = DependencyManager::get()->getSessionUUID(); + + QMutableSetIterator delItr(_deletedAvatarGrabs); + while (delItr.hasNext()) { + QUuid grabID = delItr.next(); + GrabPointer grab = _avatarGrabs[grabID]; + if (!grab) { + delItr.remove(); + continue; + } + + bool success; + SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success); + + // only clear this entry from the _deletedAvatarGrabs if we found the entity. + if (success && target) { + bool iShouldTellServer = target->getEditSenderID() == sessionID; + target->removeGrab(grab); + delItr.remove(); + // in case this is the last grab on an entity, we need to shrink the queryAACube and tell the server + // about the final position. + if (entityTree) { + bool force = true; + entityTree->withWriteLock([&] { + entityTree->updateEntityQueryAACube(target, packetSender, force, iShouldTellServer); + }); + } + } + _avatarGrabs.remove(grabID); + _changedAvatarGrabs.remove(grabID); + } + + QMutableSetIterator changeItr(_changedAvatarGrabs); + while (changeItr.hasNext()) { + QUuid grabID = changeItr.next(); + GrabPointer& grab = _avatarGrabs[grabID]; + + bool success; + SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success); + + if (success && target) { + target->addGrab(grab); + // only clear this entry from the _changedAvatarGrabs if we found the entity. + changeItr.remove(); + } + } + }); +} + +void Avatar::accumulateGrabPositions(std::map& grabAccumulators) { + // relay avatar's joint position to grabbed target in a way that allows for averaging + _avatarGrabsLock.withReadLock([&] { + foreach (auto grabID, _avatarGrabs.keys()) { + const GrabPointer& grab = _avatarGrabs.value(grabID); + + if (!grab->getActionID().isNull()) { + continue; // the accumulated value isn't used, in this case. + } + + glm::vec3 jointTranslation = getAbsoluteJointTranslationInObjectFrame(grab->getParentJointIndex()); + glm::quat jointRotation = getAbsoluteJointRotationInObjectFrame(grab->getParentJointIndex()); + glm::mat4 jointMat = createMatFromQuatAndPos(jointRotation, jointTranslation); + glm::mat4 offsetMat = createMatFromQuatAndPos(grab->getRotationalOffset(), grab->getPositionalOffset()); + glm::mat4 avatarMat = getTransform().getMatrix(); + glm::mat4 worldTransform = avatarMat * jointMat * offsetMat; + GrabLocationAccumulator& grabLocationAccumulator = grabAccumulators[grab->getTargetID()]; + grabLocationAccumulator.accumulate(extractTranslation(worldTransform), extractRotation(worldTransform)); + } + }); +} + void Avatar::relayJointDataToChildren() { forEachChild([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { @@ -551,7 +649,7 @@ void Avatar::simulate(float deltaTime, bool inView) { if (!hasParent()) { setLocalPosition(_globalPosition); } - + _simulationRate.increment(); if (inView) { _simulationInViewRate.increment(); @@ -582,6 +680,7 @@ void Avatar::simulate(float deltaTime, bool inView) { head->setScale(getModelScale()); head->simulate(deltaTime); relayJointDataToChildren(); + updateGrabs(); } else { // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. _skeletonModel->simulate(deltaTime, false); @@ -1295,6 +1394,9 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { } switch (index) { + case NO_JOINT_INDEX: { + return glm::quat(); + } case SENSOR_TO_WORLD_MATRIX_INDEX: { glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); glm::mat4 avatarMatrix = getLocalTransform().getMatrix(); @@ -1344,6 +1446,9 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { } switch (index) { + case NO_JOINT_INDEX: { + return glm::vec3(0.0f); + } case SENSOR_TO_WORLD_MATRIX_INDEX: { glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); glm::mat4 avatarMatrix = getLocalTransform().getMatrix(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 8f70b12122..a54a74fb93 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -440,6 +440,8 @@ public: std::shared_ptr getTransit() { return std::make_shared(_transit); }; AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, float avatarScale, const AvatarTransit::TransitConfig& config); + void accumulateGrabPositions(std::map& grabAccumulators); + signals: void targetScaleChanged(float targetScale); @@ -542,6 +544,7 @@ protected: // protected methods... bool isLookingAtMe(AvatarSharedPointer avatar) const; + void updateGrabs(); void relayJointDataToChildren(); void fade(render::Transaction& transaction, render::Transition::Type type); @@ -628,6 +631,8 @@ protected: static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector& blendshapeOffsets, const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs); + + AvatarGrabMap _avatarGrabs; }; #endif // hifi_Avatar_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index b6c7954f67..ae72725e2e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1893,42 +1893,96 @@ qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice return bytesWritten; } + +qint64 AvatarData::packAvatarEntityTraitInstance(AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID traitInstanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) { + qint64 bytesWritten = 0; + + // grab a read lock on the avatar entities and check for entity data for the given ID + QByteArray entityBinaryData; + + _avatarEntitiesLock.withReadLock([this, &entityBinaryData, &traitInstanceID] { + if (_avatarEntityData.contains(traitInstanceID)) { + entityBinaryData = _avatarEntityData[traitInstanceID]; + } + }); + + if (entityBinaryData.size() > AvatarTraits::MAXIMUM_TRAIT_SIZE) { + qWarning() << "Refusing to pack instanced trait" << traitType << "of size" << entityBinaryData.size() + << "bytes since it exceeds the maximum size " << AvatarTraits::MAXIMUM_TRAIT_SIZE << "bytes"; + return 0; + } + + bytesWritten += destination.writePrimitive(traitType); + + if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { + bytesWritten += destination.writePrimitive(traitVersion); + } + + bytesWritten += destination.write(traitInstanceID.toRfc4122()); + + if (!entityBinaryData.isNull()) { + AvatarTraits::TraitWireSize entityBinarySize = entityBinaryData.size(); + + bytesWritten += destination.writePrimitive(entityBinarySize); + bytesWritten += destination.write(entityBinaryData); + } else { + bytesWritten += destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE); + } + + return bytesWritten; +} + + +qint64 AvatarData::packGrabTraitInstance(AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID traitInstanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) { + qint64 bytesWritten = 0; + + // grab a read lock on the avatar grabs and check for grab data for the given ID + QByteArray grabBinaryData; + + _avatarGrabsLock.withReadLock([this, &grabBinaryData, &traitInstanceID] { + if (_avatarGrabData.contains(traitInstanceID)) { + grabBinaryData = _avatarGrabData[traitInstanceID]; + } + }); + + if (grabBinaryData.size() > AvatarTraits::MAXIMUM_TRAIT_SIZE) { + qWarning() << "Refusing to pack instanced trait" << traitType << "of size" << grabBinaryData.size() + << "bytes since it exceeds the maximum size " << AvatarTraits::MAXIMUM_TRAIT_SIZE << "bytes"; + return 0; + } + + bytesWritten += destination.writePrimitive(traitType); + + if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { + bytesWritten += destination.writePrimitive(traitVersion); + } + + bytesWritten += destination.write(traitInstanceID.toRfc4122()); + + if (!grabBinaryData.isNull()) { + AvatarTraits::TraitWireSize grabBinarySize = grabBinaryData.size(); + + bytesWritten += destination.writePrimitive(grabBinarySize); + bytesWritten += destination.write(grabBinaryData); + } else { + bytesWritten += destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE); + } + + return bytesWritten; +} + qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID, ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) { qint64 bytesWritten = 0; if (traitType == AvatarTraits::AvatarEntity) { - // grab a read lock on the avatar entities and check for entity data for the given ID - QByteArray entityBinaryData; - - _avatarEntitiesLock.withReadLock([this, &entityBinaryData, &traitInstanceID] { - if (_avatarEntityData.contains(traitInstanceID)) { - entityBinaryData = _avatarEntityData[traitInstanceID]; - } - }); - - if (entityBinaryData.size() > AvatarTraits::MAXIMUM_TRAIT_SIZE) { - qWarning() << "Refusing to pack instanced trait" << traitType << "of size" << entityBinaryData.size() - << "bytes since it exceeds the maximum size " << AvatarTraits::MAXIMUM_TRAIT_SIZE << "bytes"; - return 0; - } - - bytesWritten += destination.writePrimitive(traitType); - - if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { - bytesWritten += destination.writePrimitive(traitVersion); - } - - bytesWritten += destination.write(traitInstanceID.toRfc4122()); - - if (!entityBinaryData.isNull()) { - AvatarTraits::TraitWireSize entityBinarySize = entityBinaryData.size(); - - bytesWritten += destination.writePrimitive(entityBinarySize); - bytesWritten += destination.write(entityBinaryData); - } else { - bytesWritten += destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE); - } + packAvatarEntityTraitInstance(traitType, traitInstanceID, destination, traitVersion); + } else if (traitType == AvatarTraits::Grab) { + packGrabTraitInstance(traitType, traitInstanceID, destination, traitVersion); } return bytesWritten; @@ -1940,6 +1994,9 @@ void AvatarData::prepareResetTraitInstances() { foreach (auto entityID, _avatarEntityData.keys()) { _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID); } + foreach (auto grabID, _avatarGrabData.keys()) { + _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::Grab, grabID); + } }); } } @@ -1956,12 +2013,16 @@ void AvatarData::processTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData) { if (traitType == AvatarTraits::AvatarEntity) { updateAvatarEntity(instanceID, traitBinaryData); + } else if (traitType == AvatarTraits::Grab) { + updateAvatarGrabData(instanceID, traitBinaryData); } } void AvatarData::processDeletedTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) { if (traitType == AvatarTraits::AvatarEntity) { clearAvatarEntity(instanceID); + } else if (traitType == AvatarTraits::Grab) { + clearAvatarGrabData(instanceID); } } @@ -2909,3 +2970,38 @@ AABox AvatarData::getDefaultBubbleBox() const { bubbleBox.translate(_globalPosition); return bubbleBox; } + +bool AvatarData::updateAvatarGrabData(const QUuid& grabID, const QByteArray& grabData) { + bool changed { false }; + _avatarGrabsLock.withWriteLock([&] { + AvatarGrabDataMap::iterator itr = _avatarGrabData.find(grabID); + if (itr == _avatarGrabData.end()) { + // create a new one + if (_avatarGrabData.size() < MAX_NUM_AVATAR_GRABS) { + _avatarGrabData.insert(grabID, grabData); + _avatarGrabDataChanged = true; + changed = true; + } else { + qCWarning(avatars) << "Can't create more grabs on avatar, limit reached."; + } + } else { + // update an existing one + if (itr.value() != grabData) { + itr.value() = grabData; + _avatarGrabDataChanged = true; + changed = true; + } + } + }); + + return changed; +} + +void AvatarData::clearAvatarGrabData(const QUuid& grabID) { + _avatarGrabsLock.withWriteLock([&] { + if (_avatarGrabData.remove(grabID)) { + _avatarGrabDataChanged = true; + _deletedAvatarGrabs.insert(grabID); + } + }); +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 71e79191bc..64b8814149 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -54,15 +54,21 @@ #include "AvatarTraits.h" #include "HeadData.h" #include "PathUtils.h" +#include "Grab.h" #include using AvatarSharedPointer = std::shared_ptr; using AvatarWeakPointer = std::weak_ptr; using AvatarHash = QHash; + using AvatarEntityMap = QMap; using AvatarEntityIDs = QSet; +using AvatarGrabDataMap = QMap; +using AvatarGrabIDs = QSet; +using AvatarGrabMap = QMap; + using AvatarDataSequenceNumber = uint16_t; // avatar motion behaviors @@ -1342,6 +1348,13 @@ protected: bool hasParent() const { return !getParentID().isNull(); } bool hasFaceTracker() const { return _headData ? _headData->_isFaceTrackerConnected : false; } + qint64 packAvatarEntityTraitInstance(AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID traitInstanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion); + qint64 packGrabTraitInstance(AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID traitInstanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion); + // isReplicated will be true on downstream Avatar Mixers and their clients, but false on the upstream "master" // Audio Mixer that the replicated avatar is connected to. bool _isReplicated{ false }; @@ -1452,6 +1465,12 @@ protected: AvatarEntityMap _avatarEntityData; bool _avatarEntityDataChanged { false }; + mutable ReadWriteLockable _avatarGrabsLock; + AvatarGrabDataMap _avatarGrabData; + bool _avatarGrabDataChanged { false }; // by network + AvatarGrabIDs _changedAvatarGrabs; // updated grab IDs -- changes needed to entities or physics + AvatarGrabIDs _deletedAvatarGrabs; // deleted grab IDs -- changes needed to entities or physics + // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. ThreadSafeValueCache _sensorToWorldMatrixCache { glm::mat4() }; ThreadSafeValueCache _controllerLeftHandMatrixCache { glm::mat4() }; @@ -1511,6 +1530,9 @@ protected: f(index); } + bool updateAvatarGrabData(const QUuid& grabID, const QByteArray& grabData); + void clearAvatarGrabData(const QUuid& grabID); + private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; @@ -1614,6 +1636,7 @@ QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEnt void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value); // faux joint indexes (-1 means invalid) +const int NO_JOINT_INDEX = 65535; // -1 const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534; // -2 const int CONTROLLER_RIGHTHAND_INDEX = 65533; // -3 const int CONTROLLER_LEFTHAND_INDEX = 65532; // -4 @@ -1626,5 +1649,6 @@ const int FARGRAB_MOUSE_INDEX = 65526; // -10 const int LOWEST_PSEUDO_JOINT_INDEX = 65526; +const int MAX_NUM_AVATAR_GRABS = 6; #endif // hifi_AvatarData_h diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index 5e28515d12..0f4ad0b90f 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -24,6 +24,7 @@ namespace AvatarTraits { SkeletonModelURL, FirstInstancedTrait, AvatarEntity = FirstInstancedTrait, + Grab, TotalTraitTypes }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index a88a8de308..380998321f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include "EntitiesRendererLogging.h" #include "RenderableEntityItem.h" @@ -1250,3 +1251,11 @@ void EntityTreeRenderer::onEntityChanged(const EntityItemID& id) { _changedEntities.insert(id); }); } + +EntityEditPacketSender* EntityTreeRenderer::getPacketSender() { + EntityTreePointer tree = getTree(); + EntitySimulationPointer simulation = tree ? tree->getSimulation() : nullptr; + PhysicalEntitySimulationPointer peSimulation = std::static_pointer_cast(simulation); + EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr; + return packetSender; +} diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index d0bd8a97e3..b4f0bda703 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -120,6 +120,8 @@ public: static void setGetAvatarUpOperator(std::function getAvatarUpOperator) { _getAvatarUpOperator = getAvatarUpOperator; } static glm::vec3 getAvatarUp() { return _getAvatarUpOperator(); } + EntityEditPacketSender* getPacketSender(); + signals: void enterEntity(const EntityItemID& entityItemID); void leaveEntity(const EntityItemID& entityItemID); diff --git a/libraries/entities/src/EntityDynamicInterface.h b/libraries/entities/src/EntityDynamicInterface.h index 6b82e7df73..836dae2057 100644 --- a/libraries/entities/src/EntityDynamicInterface.h +++ b/libraries/entities/src/EntityDynamicInterface.h @@ -50,6 +50,8 @@ public: const QUuid& getID() const { return _id; } EntityDynamicType getType() const { return _type; } + virtual void removeFromOwner() { } + virtual void remapIDs(QHash& map) = 0; virtual bool isAction() const { return false; } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 94d0024fd5..cad00301a6 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -31,6 +31,8 @@ #include // usecTimestampNow() #include #include +#include +#include #include "EntityScriptingInterface.h" #include "EntitiesLogging.h" @@ -38,6 +40,7 @@ #include "EntitySimulation.h" #include "EntityDynamicFactoryInterface.h" + Q_DECLARE_METATYPE(EntityItemPointer); int entityItemPointernMetaTypeId = qRegisterMetaType(); @@ -1647,7 +1650,10 @@ AACube EntityItem::getQueryAACube(bool& success) const { } bool EntityItem::shouldPuffQueryAACube() const { - return hasActions() || isChildOfMyAvatar() || isMovingRelativeToParent(); + bool hasGrabs = _grabsLock.resultWithReadLock([&] { + return _grabs.count() > 0; + }); + return hasActions() || isChildOfMyAvatar() || isMovingRelativeToParent() || hasGrabs; } // TODO: get rid of all users of this function... @@ -2455,13 +2461,22 @@ bool EntityItem::shouldSuppressLocationEdits() const { QList EntityItem::getActionsOfType(EntityDynamicType typeToGet) const { QList result; - QHash::const_iterator i = _objectActions.begin(); - while (i != _objectActions.end()) { + for (QHash::const_iterator i = _objectActions.begin(); + i != _objectActions.end(); + i++) { + EntityDynamicPointer action = i.value(); + if (action->getType() == typeToGet && action->isActive()) { + result += action; + } + } + + for (QHash::const_iterator i = _grabActions.begin(); + i != _grabActions.end(); + i++) { EntityDynamicPointer action = i.value(); if (action->getType() == typeToGet && action->isActive()) { result += action; } - i++; } return result; @@ -3281,4 +3296,65 @@ void EntityItem::prepareForSimulationOwnershipBid(EntityItemProperties& properti bool EntityItem::isWearable() const { return isVisible() && (getParentID() == DependencyManager::get()->getSessionUUID() || getParentID() == AVATAR_SELF_ID); -} \ No newline at end of file +} + +void EntityItem::addGrab(GrabPointer grab) { + SpatiallyNestable::addGrab(grab); + + if (getDynamic()) { + EntityTreePointer entityTree = getTree(); + assert(entityTree); + EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr; + assert(simulation); + + auto actionFactory = DependencyManager::get(); + QUuid actionID = QUuid::createUuid(); + + EntityDynamicType dynamicType; + QVariantMap arguments; + int grabParentJointIndex =grab->getParentJointIndex(); + if (grabParentJointIndex == FARGRAB_RIGHTHAND_INDEX || grabParentJointIndex == FARGRAB_LEFTHAND_INDEX) { + // add a far-grab action + dynamicType = DYNAMIC_TYPE_FAR_GRAB; + arguments["otherID"] = grab->getOwnerID(); + arguments["otherJointIndex"] = grabParentJointIndex; + arguments["targetPosition"] = vec3ToQMap(grab->getPositionalOffset()); + arguments["targetRotation"] = quatToQMap(grab->getRotationalOffset()); + arguments["linearTimeScale"] = 0.05; + arguments["angularTimeScale"] = 0.05; + } else { + // add a near-grab action + dynamicType = DYNAMIC_TYPE_HOLD; + arguments["holderID"] = grab->getOwnerID(); + arguments["hand"] = grab->getHand(); + arguments["timeScale"] = 0.05; + arguments["relativePosition"] = vec3ToQMap(grab->getPositionalOffset()); + arguments["relativeRotation"] = quatToQMap(grab->getRotationalOffset()); + arguments["kinematic"] = _grabProperties.getGrabKinematic(); + arguments["kinematicSetVelocity"] = true; + arguments["ignoreIK"] = _grabProperties.getGrabFollowsController(); + } + EntityDynamicPointer action = actionFactory->factory(dynamicType, actionID, getThisPointer(), arguments); + grab->setActionID(actionID); + _grabActions[actionID] = action; + simulation->addDynamic(action); + } +} + +void EntityItem::removeGrab(GrabPointer grab) { + SpatiallyNestable::removeGrab(grab); + + QUuid actionID = grab->getActionID(); + if (!actionID.isNull()) { + EntityDynamicPointer action = _grabActions.value(actionID); + if (action) { + _grabActions.remove(actionID); + EntityTreePointer entityTree = getTree(); + EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr; + if (simulation) { + action->removeFromSimulation(simulation); + action->removeFromOwner(); + } + } + } +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 5c45c96702..998409f6fa 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -51,7 +51,6 @@ typedef std::shared_ptr EntityDynamicPointer; typedef std::shared_ptr EntityTreeElementPointer; using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr; - #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { }; @@ -554,6 +553,9 @@ public: void prepareForSimulationOwnershipBid(EntityItemProperties& properties, uint64_t now, uint8_t priority); + virtual void addGrab(GrabPointer grab) override; + virtual void removeGrab(GrabPointer grab) override; + signals: void requestRenderUpdate(); void spaceUpdate(std::pair data); @@ -734,6 +736,8 @@ protected: GrabPropertyGroup _grabProperties; + QHash _grabActions; + private: std::unordered_map _materials; std::mutex _materialsLock; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 826d180539..ffa3549955 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2419,6 +2419,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController); ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable); ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_DELEGATE_TO_PARENT, Grab, grab, GrabDelegateToParent, grabDelegateToParent); ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, EquippableLeftPosition, equippableLeftPosition); ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index e2f483d5b8..a025288f95 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -385,6 +385,8 @@ public: void setQueryAACubeDirty() { _queryAACubeChanged = true; } + void setLocationDirty() { _positionChanged = true; _rotationChanged = true; } + void setCreated(QDateTime& v); bool hasTransformOrVelocityChanges() const; diff --git a/libraries/entities/src/EntityPropertyFlags.cpp b/libraries/entities/src/EntityPropertyFlags.cpp index dbb8463141..1f88c03c87 100644 --- a/libraries/entities/src/EntityPropertyFlags.cpp +++ b/libraries/entities/src/EntityPropertyFlags.cpp @@ -190,6 +190,7 @@ QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f) { result = f.getHasProperty(PROP_GRAB_FOLLOWS_CONTROLLER) ? result + "grab.FollowsController " : result; result = f.getHasProperty(PROP_GRAB_TRIGGERABLE) ? result + "grab.Triggerable " : result; result = f.getHasProperty(PROP_GRAB_EQUIPPABLE) ? result + "grab.Equippable " : result; + result = f.getHasProperty(PROP_GRAB_DELEGATE_TO_PARENT) ? result + "grab.GrabDelegateToParent " : result; result = f.getHasProperty(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET) ? result + "grab.LeftEquippablePositionOffset " : result; result = diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 864d5efa10..1e61894a8b 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -267,6 +267,7 @@ enum EntityPropertyList { PROP_GRAB_FOLLOWS_CONTROLLER, PROP_GRAB_TRIGGERABLE, PROP_GRAB_EQUIPPABLE, + PROP_GRAB_DELEGATE_TO_PARENT, PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 303e6d54c4..956ba12560 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -2268,3 +2268,126 @@ bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& en } return result; } + +glm::vec3 EntityScriptingInterface::worldToLocalPosition(glm::vec3 worldPosition, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + bool success; + glm::vec3 localPosition = SpatiallyNestable::worldToLocal(worldPosition, parentID, parentJointIndex, + scalesWithParent, success); + if (success) { + return localPosition; + } else { + return glm::vec3(0.0f); + } +} + +glm::quat EntityScriptingInterface::worldToLocalRotation(glm::quat worldRotation, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + bool success; + glm::quat localRotation = SpatiallyNestable::worldToLocal(worldRotation, parentID, parentJointIndex, + scalesWithParent, success); + if (success) { + return localRotation; + } else { + return glm::quat(); + } +} + +glm::vec3 EntityScriptingInterface::worldToLocalVelocity(glm::vec3 worldVelocity, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + bool success; + glm::vec3 localVelocity = SpatiallyNestable::worldToLocalVelocity(worldVelocity, parentID, parentJointIndex, + scalesWithParent, success); + if (success) { + return localVelocity; + } else { + return glm::vec3(0.0f); + } +} + +glm::vec3 EntityScriptingInterface::worldToLocalAngularVelocity(glm::vec3 worldAngularVelocity, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + bool success; + glm::vec3 localAngularVelocity = SpatiallyNestable::worldToLocalAngularVelocity(worldAngularVelocity, parentID, + parentJointIndex, scalesWithParent, + success); + if (success) { + return localAngularVelocity; + } else { + return glm::vec3(0.0f); + } +} + +glm::vec3 EntityScriptingInterface::worldToLocalDimensions(glm::vec3 worldDimensions, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + + bool success; + glm::vec3 localDimensions = SpatiallyNestable::worldToLocalDimensions(worldDimensions, parentID, parentJointIndex, + scalesWithParent, success); + if (success) { + return localDimensions; + } else { + return glm::vec3(0.0f); + } +} + +glm::vec3 EntityScriptingInterface::localToWorldPosition(glm::vec3 localPosition, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + bool success; + glm::vec3 worldPosition = SpatiallyNestable::localToWorld(localPosition, parentID, parentJointIndex, + scalesWithParent, success); + if (success) { + return worldPosition; + } else { + return glm::vec3(0.0f); + } +} + +glm::quat EntityScriptingInterface::localToWorldRotation(glm::quat localRotation, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + bool success; + glm::quat worldRotation = SpatiallyNestable::localToWorld(localRotation, parentID, parentJointIndex, + scalesWithParent, success); + if (success) { + return worldRotation; + } else { + return glm::quat(); + } +} + +glm::vec3 EntityScriptingInterface::localToWorldVelocity(glm::vec3 localVelocity, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + bool success; + glm::vec3 worldVelocity = SpatiallyNestable::localToWorldVelocity(localVelocity, parentID, parentJointIndex, + scalesWithParent, success); + if (success) { + return worldVelocity; + } else { + return glm::vec3(0.0f); + } +} + +glm::vec3 EntityScriptingInterface::localToWorldAngularVelocity(glm::vec3 localAngularVelocity, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + bool success; + glm::vec3 worldAngularVelocity = SpatiallyNestable::localToWorldAngularVelocity(localAngularVelocity, + parentID, parentJointIndex, + scalesWithParent, success); + if (success) { + return worldAngularVelocity; + } else { + return glm::vec3(0.0f); + } +} + +glm::vec3 EntityScriptingInterface::localToWorldDimensions(glm::vec3 localDimensions, const QUuid& parentID, + int parentJointIndex, bool scalesWithParent) { + bool success; + glm::vec3 worldDimensions = SpatiallyNestable::localToWorldDimensions(localDimensions, parentID, parentJointIndex, + scalesWithParent, success); + if (success) { + return worldDimensions; + } else { + return glm::vec3(0.0f); + } +} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 1398c2ad1c..2c89381c31 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -1573,6 +1573,109 @@ public slots: * print("Scale: " + JSON.stringify(Mat4.extractScale(transform))); // { x: 1, y: 1, z: 1 } */ Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID); + + /**jsdoc + * @function Entities.worldToLocalPosition + * @param {Vec3} worldPosition + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Vec3} + */ + Q_INVOKABLE glm::vec3 worldToLocalPosition(glm::vec3 worldPosition, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + /**jsdoc + * @function Entities.worldToLocalRotation + * @param {Quat} worldRotation + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Quat} + */ + Q_INVOKABLE glm::quat worldToLocalRotation(glm::quat worldRotation, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + /**jsdoc + * @function Entities.worldToLocalVelocity + * @param {Vec3} worldVelocity + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Vec3} + */ + Q_INVOKABLE glm::vec3 worldToLocalVelocity(glm::vec3 worldVelocity, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + /**jsdoc + * @function Entities.worldToLocalAngularVelocity + * @param {Vec3} worldAngularVelocity + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Vec3} + */ + Q_INVOKABLE glm::vec3 worldToLocalAngularVelocity(glm::vec3 worldAngularVelocity, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + /**jsdoc + * @function Entities.worldToLocalDimensions + * @param {Vec3} worldDimensions + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Vec3} + */ + Q_INVOKABLE glm::vec3 worldToLocalDimensions(glm::vec3 worldDimensions, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + /**jsdoc + * @function Entities.localToWorldPosition + * @param {Vec3} localPosition + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Vec3} + */ + Q_INVOKABLE glm::vec3 localToWorldPosition(glm::vec3 localPosition, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + /**jsdoc + * @function Entities.localToWorldRotation + * @param {Quat} localRotation + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Quat} + */ + Q_INVOKABLE glm::quat localToWorldRotation(glm::quat localRotation, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + /**jsdoc + * @function Entities.localToWorldVelocity + * @param {Vec3} localVelocity + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Vec3} + */ + Q_INVOKABLE glm::vec3 localToWorldVelocity(glm::vec3 localVelocity, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + /**jsdoc + * @function Entities.localToWorldAngularVelocity + * @param {Vec3} localAngularVelocity + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Vec3} + */ + Q_INVOKABLE glm::vec3 localToWorldAngularVelocity(glm::vec3 localAngularVelocity, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + /**jsdoc + * @function Entities.localToWorldDimensions + * @param {Vec3} localDimensions + * @param {Uuid} parentID + * @param {number} parentJointIndex + * @param {boolean} scalesWithparent + * @returns {Vec3} + */ + Q_INVOKABLE glm::vec3 localToWorldDimensions(glm::vec3 localDimensions, const QUuid& parentID, + int parentJointIndex = -1, bool scalesWithParent = false); + + /**jsdoc * Get the static certificate for an entity. The static certificate contains static properties of the item which cannot * be altered. diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b23fbca6d1..63bcd70a14 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2586,6 +2586,8 @@ void convertGrabUserDataToProperties(EntityItemProperties& properties) { grabProperties.setEquippable(equippable.toBool()); } + grabProperties.setGrabDelegateToParent(true); + if (grabbableKey["spatialKey"].isObject()) { QJsonObject spatialKey = grabbableKey["spatialKey"].toObject(); grabProperties.setEquippable(true); @@ -2956,3 +2958,52 @@ bool EntityTree::removeMaterialFromOverlay(const QUuid& overlayID, graphics::Mat } return false; } + +void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, + MovingEntitiesOperator& moveOperator, bool force, bool tellServer) { + // if the queryBox has changed, tell the entity-server + if (object->getNestableType() == NestableType::Entity && object->updateQueryAACube()) { + EntityItemPointer entity = std::static_pointer_cast(object); + if (entity->updateQueryAACube() || force) { + bool success; + AACube newCube = entity->getQueryAACube(success); + if (success) { + moveOperator.addEntityToMoveList(entity, newCube); + } + // send an edit packet to update the entity-server about the queryAABox + // unless it is client-only + if (tellServer && packetSender && entity->isDomainEntity()) { + quint64 now = usecTimestampNow(); + EntityItemProperties properties = entity->getProperties(); + properties.setQueryAACubeDirty(); + properties.setLocationDirty(); + properties.setLastEdited(now); + + packetSender->queueEditEntityMessage(PacketType::EntityEdit, getThisPointer(), entity->getID(), properties); + entity->setLastBroadcast(now); // for debug/physics status icons + } + + entity->markDirtyFlags(Simulation::DIRTY_POSITION); + entityChanged(entity); + } + } + + object->forEachDescendant([&](SpatiallyNestablePointer descendant) { + updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer); + }); +} + +void EntityTree::updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, + bool force, bool tellServer) { + // This is used when something other than a script or physics moves an entity. We need to put it in the + // correct place in our local octree, update its and its children's queryAACubes, and send an edit + // packet to the entity-server. + MovingEntitiesOperator moveOperator; + + updateEntityQueryAACubeWorker(object, packetSender, moveOperator, force, tellServer); + + if (moveOperator.hasMovingEntities()) { + PerformanceTimer perfTimer("recurseTreeWithOperator"); + recurseTreeWithOperator(&moveOperator); + } +} diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d08e21e4f0..9181a4851c 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -277,6 +277,9 @@ public: std::map getNamedPaths() const { return _namedPaths; } + void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, + bool force, bool tellServer); + signals: void deletingEntity(const EntityItemID& entityID); void deletingEntityPointer(EntityItem* entityID); @@ -392,6 +395,9 @@ private: bool _serverlessDomain { false }; std::map _namedPaths; + + void updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, + MovingEntitiesOperator& moveOperator, bool force, bool tellServer); }; void convertGrabUserDataToProperties(EntityItemProperties& properties); diff --git a/libraries/entities/src/GrabPropertyGroup.cpp b/libraries/entities/src/GrabPropertyGroup.cpp index 996eed4720..73bc1ed2d0 100644 --- a/libraries/entities/src/GrabPropertyGroup.cpp +++ b/libraries/entities/src/GrabPropertyGroup.cpp @@ -24,6 +24,8 @@ void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProp COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_DELEGATE_TO_PARENT, Grab, grab, + GrabDelegateToParent, grabDelegateToParent); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, EquippableLeftPosition, equippableLeftPosition); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, @@ -141,6 +143,7 @@ bool GrabPropertyGroup::appendToEditPacket(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController()); APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable()); APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, getGrabDelegateToParent()); APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition()); APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation()); APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition()); @@ -164,6 +167,7 @@ bool GrabPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController); READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable); READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable); + READ_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, bool, setGrabDelegateToParent); READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition); READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation); READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition); @@ -177,6 +181,7 @@ bool GrabPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_FOLLOWS_CONTROLLER, GrabFollowsController); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_TRIGGERABLE, Triggerable); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE, Equippable); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_DELEGATE_TO_PARENT, GrabDelegateToParent); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, EquippableRightPosition); @@ -215,6 +220,7 @@ EntityPropertyFlags GrabPropertyGroup::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_GRAB_FOLLOWS_CONTROLLER, grabFollowsController); CHECK_PROPERTY_CHANGE(PROP_GRAB_TRIGGERABLE, triggerable); CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE, equippable); + CHECK_PROPERTY_CHANGE(PROP_GRAB_DELEGATE_TO_PARENT, grabDelegateToParent); CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, equippableLeftPosition); CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, equippableLeftRotation); CHECK_PROPERTY_CHANGE(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, equippableRightPosition); @@ -273,6 +279,7 @@ EntityPropertyFlags GrabPropertyGroup::getEntityProperties(EncodeBitstreamParams requestedProperties += PROP_GRAB_FOLLOWS_CONTROLLER; requestedProperties += PROP_GRAB_TRIGGERABLE; requestedProperties += PROP_GRAB_EQUIPPABLE; + requestedProperties += PROP_GRAB_DELEGATE_TO_PARENT; requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET; requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET; requestedProperties += PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET; @@ -299,6 +306,7 @@ void GrabPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeB APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController()); APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable()); APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, getGrabDelegateToParent()); APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition()); APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation()); APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition()); @@ -321,6 +329,7 @@ int GrabPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* dat READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController); READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable); READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable); + READ_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, bool, setGrabDelegateToParent); READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition); READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation); READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition); diff --git a/libraries/entities/src/GrabPropertyGroup.h b/libraries/entities/src/GrabPropertyGroup.h index 37b157de06..33550fef3d 100644 --- a/libraries/entities/src/GrabPropertyGroup.h +++ b/libraries/entities/src/GrabPropertyGroup.h @@ -31,6 +31,7 @@ static const bool INITIAL_KINEMATIC { true }; static const bool INITIAL_FOLLOWS_CONTROLLER { true }; static const bool INITIAL_TRIGGERABLE { false }; static const bool INITIAL_EQUIPPABLE { false }; +static const bool INITIAL_GRAB_DELEGATE_TO_PARENT { true }; static const glm::vec3 INITIAL_LEFT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; static const glm::quat INITIAL_LEFT_EQUIPPABLE_ROTATION { glm::quat() }; static const glm::vec3 INITIAL_RIGHT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; @@ -123,6 +124,8 @@ public: INITIAL_FOLLOWS_CONTROLLER); DEFINE_PROPERTY(PROP_GRAB_TRIGGERABLE, Triggerable, triggerable, bool, INITIAL_TRIGGERABLE); DEFINE_PROPERTY(PROP_GRAB_EQUIPPABLE, Equippable, equippable, bool, INITIAL_EQUIPPABLE); + DEFINE_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, GrabDelegateToParent, grabDelegateToParent, bool, + INITIAL_GRAB_DELEGATE_TO_PARENT); DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition, equippableLeftPosition, glm::vec3, INITIAL_LEFT_EQUIPPABLE_POSITION); DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation, equippableLeftRotation, diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index f2db155dca..37a1ec5f03 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -40,7 +40,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::JointTransScaled); + return static_cast(AvatarMixerPacketVersion::GrabTraits); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 0d4d065f5b..c353dd6ec7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -303,7 +303,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { MigrateSkeletonURLToTraits, MigrateAvatarEntitiesToTraits, FarGrabJointsRedux, - JointTransScaled + JointTransScaled, + GrabTraits }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp index c5641ad347..87edc858cc 100644 --- a/libraries/physics/src/ObjectActionTractor.cpp +++ b/libraries/physics/src/ObjectActionTractor.cpp @@ -33,36 +33,53 @@ ObjectActionTractor::ObjectActionTractor(const QUuid& id, EntityItemPointer owne _rotationalTargetSet(true), _linearVelocityTarget(0.0f) { - #if WANT_DEBUG +#if WANT_DEBUG qCDebug(physics) << "ObjectActionTractor::ObjectActionTractor"; - #endif +#endif } ObjectActionTractor::~ObjectActionTractor() { - #if WANT_DEBUG +#if WANT_DEBUG qCDebug(physics) << "ObjectActionTractor::~ObjectActionTractor"; - #endif +#endif } bool ObjectActionTractor::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position, - glm::vec3& linearVelocity, glm::vec3& angularVelocity, - float& linearTimeScale, float& angularTimeScale) { - bool success { true }; - EntityItemPointer other = std::dynamic_pointer_cast(getOther()); - withReadLock([&]{ + glm::vec3& linearVelocity, glm::vec3& angularVelocity, + float& linearTimeScale, float& angularTimeScale) { + SpatiallyNestablePointer other = getOther(); + return resultWithReadLock([&]{ linearTimeScale = _linearTimeScale; angularTimeScale = _angularTimeScale; if (!_otherID.isNull()) { - if (other && other->isReadyToComputeShape()) { - rotation = _desiredRotationalTarget * other->getWorldOrientation(); - position = other->getWorldOrientation() * _desiredPositionalTarget + other->getWorldPosition(); + bool otherIsReady { true }; + if (other && other->getNestableType() == NestableType::Entity) { + EntityItemPointer otherEntity = std::static_pointer_cast(other); + otherIsReady = otherEntity->isReadyToComputeShape(); + } + if (other && otherIsReady) { + bool success; + glm::vec3 otherWorldPosition = other->getWorldPosition(_otherJointIndex, success); + if (!success) { + linearTimeScale = FLT_MAX; + angularTimeScale = FLT_MAX; + return false; + } + glm::quat otherWorldOrientation = other->getWorldOrientation(_otherJointIndex, success); + if (!success) { + linearTimeScale = FLT_MAX; + angularTimeScale = FLT_MAX; + return false; + } + rotation = _desiredRotationalTarget * otherWorldOrientation; + position = otherWorldOrientation * _desiredPositionalTarget + otherWorldPosition; } else { // we should have an "other" but can't find it, or its collision shape isn't loaded, // so disable the tractor. linearTimeScale = FLT_MAX; angularTimeScale = FLT_MAX; - success = false; + return false; } } else { rotation = _desiredRotationalTarget; @@ -70,8 +87,8 @@ bool ObjectActionTractor::getTarget(float deltaTimeStep, glm::quat& rotation, gl } linearVelocity = glm::vec3(); angularVelocity = glm::vec3(); + return true; }); - return success; } bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { @@ -108,9 +125,9 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { float linearTimeScale; float angularTimeScale; bool success = tractorAction->getTarget(deltaTimeStep, - rotationForAction, positionForAction, - linearVelocityForAction, angularVelocityForAction, - linearTimeScale, angularTimeScale); + rotationForAction, positionForAction, + linearVelocityForAction, angularVelocityForAction, + linearTimeScale, angularTimeScale); if (success) { if (angularTimeScale < MAX_TRACTOR_TIMESCALE) { angularTractorCount++; @@ -239,7 +256,7 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) { glm::quat rotationalTarget; float angularTimeScale; QUuid otherID; - + int otherJointIndex; bool needUpdate = false; bool somethingChanged = ObjectDynamic::updateArguments(arguments); @@ -274,18 +291,25 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) { } ok = true; - otherID = QUuid(EntityDynamicInterface::extractStringArgument("tractor action", - arguments, "otherID", ok, false)); + otherID = QUuid(EntityDynamicInterface::extractStringArgument("tractor action", arguments, "otherID", ok, false)); if (!ok) { otherID = _otherID; } + ok = true; + otherJointIndex = EntityDynamicInterface::extractIntegerArgument("tractor action", arguments, + "otherJointIndex", ok, false); + if (!ok) { + otherJointIndex = _otherJointIndex; + } + if (somethingChanged || positionalTarget != _desiredPositionalTarget || linearTimeScale != _linearTimeScale || rotationalTarget != _desiredRotationalTarget || angularTimeScale != _angularTimeScale || - otherID != _otherID) { + otherID != _otherID || + otherJointIndex != _otherJointIndex) { // something changed needUpdate = true; } @@ -298,6 +322,7 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) { _desiredRotationalTarget = rotationalTarget; _angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale)); _otherID = otherID; + _otherJointIndex = otherJointIndex; _active = true; auto ownerEntity = _ownerEntity.lock(); @@ -313,20 +338,22 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) { } /**jsdoc - * The "tractor" {@link Entities.ActionType|ActionType} moves and rotates an entity to a target position and + * The "tractor" {@link Entities.ActionType|ActionType} moves and rotates an entity to a target position and * orientation, optionally relative to another entity. * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. * * @typedef {object} Entities.ActionArguments-Tractor * @property {Vec3} targetPosition=0,0,0 - The target position. * @property {Quat} targetRotation=0,0,0,1 - The target rotation. - * @property {Uuid} otherID=null - If an entity ID, the targetPosition and targetRotation are + * @property {Uuid} otherID=null - If an entity ID, the targetPosition and targetRotation are * relative to this entity's position and rotation. + * @property {Uuid} otherJointIndex=null - If an entity JointIndex, the targetPosition and + * targetRotation are relative to this entity's joint's position and rotation. * @property {number} linearTimeScale=3.4e+38 - Controls how long it takes for the entity's position to catch up with the - * target position. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the action + * target position. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the action * is applied using an exponential decay. * @property {number} angularTimeScale=3.4e+38 - Controls how long it takes for the entity's orientation to catch up with the - * target orientation. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the + * target orientation. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the * action is applied using an exponential decay. */ QVariantMap ObjectActionTractor::getArguments() { @@ -339,6 +366,7 @@ QVariantMap ObjectActionTractor::getArguments() { arguments["angularTimeScale"] = _angularTimeScale; arguments["otherID"] = _otherID; + arguments["otherJointIndex"] = _otherJointIndex; }); return arguments; } @@ -354,6 +382,7 @@ void ObjectActionTractor::serializeParameters(QDataStream& dataStream) const { dataStream << localTimeToServerTime(_expires); dataStream << _tag; dataStream << _otherID; + dataStream << _otherJointIndex; }); } @@ -387,6 +416,7 @@ void ObjectActionTractor::deserializeParameters(QByteArray serializedArguments, dataStream >> _tag; dataStream >> _otherID; + dataStream >> _otherJointIndex; _active = true; }); diff --git a/libraries/physics/src/ObjectDynamic.h b/libraries/physics/src/ObjectDynamic.h index bfee79aca9..f41d6cd51d 100644 --- a/libraries/physics/src/ObjectDynamic.h +++ b/libraries/physics/src/ObjectDynamic.h @@ -66,6 +66,7 @@ protected: EntityItemID _otherID; SpatiallyNestableWeakPointer _other; SpatiallyNestablePointer getOther(); + int _otherJointIndex { -1 }; private: qint64 getEntityServerClockSkew() const; diff --git a/libraries/shared/src/Grab.cpp b/libraries/shared/src/Grab.cpp new file mode 100644 index 0000000000..195bc61f66 --- /dev/null +++ b/libraries/shared/src/Grab.cpp @@ -0,0 +1,70 @@ +// +// Grab.cpp +// libraries/avatars/src +// +// Created by Seth Alves on 2018-9-1. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Grab.h" + +QByteArray Grab::toByteArray() { + QByteArray ba; + QDataStream dataStream(&ba, QIODevice::WriteOnly); + const int dataEncodingVersion = 1; + dataStream << dataEncodingVersion << _ownerID << _targetID << _parentJointIndex + << _hand << _positionalOffset << _rotationalOffset; + return ba; +} + +bool Grab::fromByteArray(const QByteArray& grabData) { + QDataStream dataStream(grabData); + + int dataEncodingVersion; + QUuid newOwnerID { QUuid() }; + QUuid newTargetID { QUuid() }; + int newParentJointIndex { -1 }; + QString newHand { "none" }; + glm::vec3 newPositionalOffset { glm::vec3(0.0f) }; + glm::quat newRotationalOffset { glm::quat() }; + + dataStream >> dataEncodingVersion; + assert(dataEncodingVersion == 1); + dataStream >> newOwnerID; + dataStream >> newTargetID; + dataStream >> newParentJointIndex; + dataStream >> newHand; + dataStream >> newPositionalOffset; + dataStream >> newRotationalOffset; + + bool somethingChanged { false }; + if (_ownerID != newOwnerID) { + _ownerID = newOwnerID; + somethingChanged = true; + } + if (_targetID != newTargetID) { + _targetID = newTargetID; + somethingChanged = true; + } + if (_parentJointIndex != newParentJointIndex) { + _parentJointIndex = newParentJointIndex; + somethingChanged = true; + } + if (_hand != newHand) { + _hand = newHand; + somethingChanged = true; + } + if (_positionalOffset != newPositionalOffset) { + _positionalOffset = newPositionalOffset; + somethingChanged = true; + } + if (_rotationalOffset != newRotationalOffset) { + _rotationalOffset = newRotationalOffset; + somethingChanged = true; + } + + return somethingChanged; +} diff --git a/libraries/shared/src/Grab.h b/libraries/shared/src/Grab.h new file mode 100644 index 0000000000..5765d6fd0e --- /dev/null +++ b/libraries/shared/src/Grab.h @@ -0,0 +1,99 @@ +// +// Grab.h +// libraries/avatars/src +// +// Created by Seth Alves on 2018-9-1. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Grab_h +#define hifi_Grab_h + +#include +#include +#include "GLMHelpers.h" +#include "StreamUtils.h" + +class Grab; +using GrabPointer = std::shared_ptr; +using GrabWeakPointer = std::weak_ptr; + +class GrabLocationAccumulator { +public: + void accumulate(glm::vec3 position, glm::quat orientation) { + _position += position; + _orientation = orientation; // XXX + count++; + } + + glm::vec3 finalizePosition() { return count > 0 ? _position * (1.0f / count) : glm::vec3(0.0f); } + glm::quat finalizeOrientation() { return _orientation; } // XXX + +protected: + glm::vec3 _position; + glm::quat _orientation; + int count { 0 }; +}; + +class Grab { +public: + Grab() {}; + Grab(const QUuid& newOwnerID, const QUuid& newTargetID, int newParentJointIndex, const QString& newHand, + glm::vec3 newPositionalOffset, glm::quat newRotationalOffset) : + _ownerID(newOwnerID), + _targetID(newTargetID), + _parentJointIndex(newParentJointIndex), + _hand(newHand), + _positionalOffset(newPositionalOffset), + _rotationalOffset(newRotationalOffset) {} + + QByteArray toByteArray(); + bool fromByteArray(const QByteArray& grabData); + + Grab& operator=(const GrabPointer& other) { + _ownerID = other->_ownerID; + _targetID = other->_targetID; + _parentJointIndex = other->_parentJointIndex; + _hand = other->_hand; + _positionalOffset = other->_positionalOffset; + _rotationalOffset = other->_rotationalOffset; + _actionID = other->_actionID; + return *this; + } + + QUuid getActionID() const { return _actionID; } + void setActionID(const QUuid& actionID) { _actionID = actionID; } + + QUuid getOwnerID() const { return _ownerID; } + void setOwnerID(QUuid ownerID) { _ownerID = ownerID; } + + QUuid getTargetID() const { return _targetID; } + void setTargetID(QUuid targetID) { _targetID = targetID; } + + int getParentJointIndex() const { return _parentJointIndex; } + void setParentJointIndex(int parentJointIndex) { _parentJointIndex = parentJointIndex; } + + QString getHand() const { return _hand; } + void setHand(QString hand) { _hand = hand; } + + glm::vec3 getPositionalOffset() const { return _positionalOffset; } + void setPositionalOffset(glm::vec3 positionalOffset) { _positionalOffset = positionalOffset; } + + glm::quat getRotationalOffset() const { return _rotationalOffset; } + void setRotationalOffset(glm::quat rotationalOffset) { _rotationalOffset = rotationalOffset; } + +protected: + QUuid _actionID; // if an action is created in bullet for this grab, this is the ID + QUuid _ownerID; // avatar ID of grabber + QUuid _targetID; // SpatiallyNestable ID of grabbed + int _parentJointIndex { -1 }; // which avatar joint is being used to grab + QString _hand; // "left" or "right" + glm::vec3 _positionalOffset; // relative to joint + glm::quat _rotationalOffset; // relative to joint +}; + + +#endif // hifi_Grab_h diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index d704498143..9ffbb8fd3d 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1369,3 +1369,35 @@ bool SpatiallyNestable::isParentPathComplete(int depth) const { return parent->isParentPathComplete(depth + 1); } + +void SpatiallyNestable::addGrab(GrabPointer grab) { + _grabsLock.withWriteLock([&] { + _grabs.insert(grab); + }); +} + +void SpatiallyNestable::removeGrab(GrabPointer grab) { + _grabsLock.withWriteLock([&] { + _grabs.remove(grab); + }); +} + +QUuid SpatiallyNestable::getEditSenderID() { + // if more than one avatar is grabbing something, decide which one should tell the enity-server about it + QUuid editSenderID; + bool editSenderIDSet { false }; + _grabsLock.withReadLock([&] { + foreach (const GrabPointer &grab, _grabs) { + QUuid ownerID = grab->getOwnerID(); + if (!editSenderIDSet) { + editSenderID = ownerID; + editSenderIDSet = true; + } else { + if (ownerID < editSenderID) { + editSenderID = ownerID; + } + } + } + }); + return editSenderID; +} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index cf2e304d19..0c6bada85b 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -18,6 +18,7 @@ #include "AACube.h" #include "SpatialParentFinder.h" #include "shared/ReadWriteLockable.h" +#include "Grab.h" class SpatiallyNestable; using SpatiallyNestableWeakPointer = std::weak_ptr; @@ -213,6 +214,10 @@ public: virtual void dimensionsChanged() { _queryAACubeSet = false; } // called when a this object's dimensions have changed virtual void parentDeleted() { } // called on children of a deleted parent + virtual void addGrab(GrabPointer grab); + virtual void removeGrab(GrabPointer grab); + virtual QUuid getEditSenderID(); + protected: QUuid _id; mutable SpatiallyNestableWeakPointer _parent; @@ -232,6 +237,9 @@ protected: quint64 _translationChanged { 0 }; quint64 _rotationChanged { 0 }; + mutable ReadWriteLockable _grabsLock; + QSet _grabs; + private: SpatiallyNestable() = delete; const NestableType _nestableType; // EntityItem or an AvatarData diff --git a/scripts/developer/debugging/queryAACubeInspector.js b/scripts/developer/debugging/queryAACubeInspector.js index d8a87c3cf5..d6adcf02b7 100644 --- a/scripts/developer/debugging/queryAACubeInspector.js +++ b/scripts/developer/debugging/queryAACubeInspector.js @@ -40,7 +40,8 @@ function updateOverlay(entityID, queryAACube) { blue: 255 }, alpha: 1, - solid: false + solid: false, + grabbable: false }); } } @@ -60,4 +61,4 @@ function cleanup() { } } -Script.scriptEnding.connect(cleanup); \ No newline at end of file +Script.scriptEnding.connect(cleanup); diff --git a/scripts/system/controllers/controllerModules/farGrabEntity.js b/scripts/system/controllers/controllerModules/farGrabEntity.js new file mode 100644 index 0000000000..39c65aa022 --- /dev/null +++ b/scripts/system/controllers/controllerModules/farGrabEntity.js @@ -0,0 +1,521 @@ +"use strict"; + +// farGrabEntity.js +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +/* jslint bitwise: true */ + +/* global Script, Controller, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Quat, getEnabledModuleByName, makeRunningValues, + Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, + HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, + projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, makeLaserParams, AddressManager, + getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, findGroupParent, + worldPositionToRegistrationFrameMatrix, DISPATCHER_PROPERTIES +*/ + +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + var MARGIN = 25; + + function TargetObject(entityID, entityProps) { + this.entityID = entityID; + this.entityProps = entityProps; + this.targetEntityID = null; + this.targetEntityProps = null; + + this.getTargetEntity = function() { + var parentPropsLength = this.parentProps.length; + if (parentPropsLength !== 0) { + var targetEntity = { + id: this.parentProps[parentPropsLength - 1].id, + props: this.parentProps[parentPropsLength - 1]}; + this.targetEntityID = targetEntity.id; + this.targetEntityProps = targetEntity.props; + return targetEntity; + } + this.targetEntityID = this.entityID; + this.targetEntityProps = this.entityProps; + return { + id: this.entityID, + props: this.entityProps}; + }; + } + + function FarGrabEntity(hand) { + this.hand = hand; + this.targetEntityID = null; + this.targetObject = null; + this.previouslyUnhooked = {}; + this.potentialEntityWithContextOverlay = false; + this.entityWithContextOverlay = false; + this.contextOverlayTimer = false; + this.highlightedEntity = null; + this.reticleMinX = MARGIN; + this.reticleMaxX = 0; + this.reticleMinY = MARGIN; + this.reticleMaxY = 0; + this.lastUnexpectedChildrenCheckTime = 0; + + var FAR_GRAB_JOINTS = [65527, 65528]; // FARGRAB_LEFTHAND_INDEX, FARGRAB_RIGHTHAND_INDEX + + var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object + var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position + var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified + var DISTANCE_HOLDING_UNITY_DISTANCE = 6; // The distance at which the distance holding action timeframe is unmodified + + this.parameters = makeDispatcherModuleParameters( + 540, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100, + makeLaserParams(this.hand, false)); + + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.distanceGrabTimescale = function(mass, distance) { + var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass / + DISTANCE_HOLDING_UNITY_MASS * distance / + DISTANCE_HOLDING_UNITY_DISTANCE; + if (timeScale < DISTANCE_HOLDING_ACTION_TIMEFRAME) { + timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME; + } + return timeScale; + }; + + this.getMass = function(dimensions, density) { + return (dimensions.x * dimensions.y * dimensions.z) * density; + }; + + this.startFarGrabEntity = function (controllerData, targetProps) { + var controllerLocation = controllerData.controllerLocations[this.hand]; + var worldControllerPosition = controllerLocation.position; + var worldControllerRotation = controllerLocation.orientation; + // transform the position into room space + var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); + var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); + + var now = Date.now(); + + // add the action and initialize some variables + this.currentObjectPosition = targetProps.position; + this.currentObjectRotation = targetProps.rotation; + this.currentObjectTime = now; + + this.grabRadius = this.grabbedDistance; + this.grabRadialVelocity = 0.0; + + // offset between controller vector at the grab radius and the entity position + var targetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); + targetPosition = Vec3.sum(targetPosition, worldControllerPosition); + this.offsetPosition = Vec3.subtract(this.currentObjectPosition, targetPosition); + + // compute a constant based on the initial conditions which we use below to exaggerate hand motion + // onto the held object + this.radiusScalar = Math.log(this.grabRadius + 1.0); + if (this.radiusScalar < 1.0) { + this.radiusScalar = 1.0; + } + + // compute the mass for the purpose of energy and how quickly to move object + this.mass = this.getMass(targetProps.dimensions, targetProps.density); + + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + unhighlightTargetEntity(this.targetEntityID); + var message = { + hand: this.hand, + entityID: this.targetEntityID + }; + + Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); + + var newTargetPosLocal = MyAvatar.worldToJointPoint(targetProps.position); + MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], newTargetPosLocal); + MyAvatar.setJointRotation(FAR_GRAB_JOINTS[this.hand], { x: 0, y: 0, z: 0, w: 1 }); + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(targetProps.id, "startNearGrab", args); + + this.targetEntityID = targetProps.id; + + + if (this.grabID) { + MyAvatar.releaseGrab(this.grabID); + } + var farJointIndex = FAR_GRAB_JOINTS[this.hand]; + this.grabID = MyAvatar.grab(targetProps.id, farJointIndex, + Entities.worldToLocalPosition(targetProps.position, MyAvatar.SELF_ID, farJointIndex), + Entities.worldToLocalRotation(targetProps.rotation, MyAvatar.SELF_ID, farJointIndex)); + + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'grab', + grabbedEntity: targetProps.id, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + this.grabbing = true; + + this.previousRoomControllerPosition = roomControllerPosition; + }; + + this.continueDistanceHolding = function(controllerData) { + var controllerLocation = controllerData.controllerLocations[this.hand]; + var worldControllerPosition = controllerLocation.position; + var worldControllerRotation = controllerLocation.orientation; + + // also transform the position into room space + var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); + var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); + + var targetProps = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); + var now = Date.now(); + var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds + this.currentObjectTime = now; + + // the action was set up when this.distanceHolding was called. update the targets. + var radius = Vec3.distance(this.currentObjectPosition, worldControllerPosition) * + this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; + if (radius < 1.0) { + radius = 1.0; + } + + var roomHandDelta = Vec3.subtract(roomControllerPosition, this.previousRoomControllerPosition); + var worldHandDelta = Mat4.transformVector(MyAvatar.getSensorToWorldMatrix(), roomHandDelta); + var handMoved = Vec3.multiply(worldHandDelta, radius); + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved); + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "continueDistanceGrab", args); + + // Update radialVelocity + var lastVelocity = Vec3.multiply(worldHandDelta, 1.0 / deltaObjectTime); + var delta = Vec3.normalize(Vec3.subtract(targetProps.position, worldControllerPosition)); + var newRadialVelocity = Vec3.dot(lastVelocity, delta); + + var VELOCITY_AVERAGING_TIME = 0.016; + var blendFactor = deltaObjectTime / VELOCITY_AVERAGING_TIME; + if (blendFactor < 0.0) { + blendFactor = 0.0; + } else if (blendFactor > 1.0) { + blendFactor = 1.0; + } + this.grabRadialVelocity = blendFactor * newRadialVelocity + (1.0 - blendFactor) * this.grabRadialVelocity; + + var RADIAL_GRAB_AMPLIFIER = 10.0; + if (Math.abs(this.grabRadialVelocity) > 0.0) { + this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaObjectTime * + this.grabRadius * RADIAL_GRAB_AMPLIFIER); + } + + // don't let grabRadius go all the way to zero, because it can't come back from that + var MINIMUM_GRAB_RADIUS = 0.1; + if (this.grabRadius < MINIMUM_GRAB_RADIUS) { + this.grabRadius = MINIMUM_GRAB_RADIUS; + } + var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); + newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition); + newTargetPosition = Vec3.sum(newTargetPosition, this.offsetPosition); + + // MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], MyAvatar.worldToJointPoint(newTargetPosition)); + + // var newTargetPosLocal = Mat4.transformPoint(MyAvatar.getSensorToWorldMatrix(), newTargetPosition); + var newTargetPosLocal = MyAvatar.worldToJointPoint(newTargetPosition); + MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], newTargetPosLocal); + MyAvatar.setJointRotation(FAR_GRAB_JOINTS[this.hand], { x: 0, y: 0, z: 0, w: 1 }); + + this.previousRoomControllerPosition = roomControllerPosition; + }; + + this.endFarGrabEntity = function (controllerData) { + if (this.grabID) { + MyAvatar.releaseGrab(this.grabID); + this.grabID = null; + } + + this.hapticTargetID = null; + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'release', + grabbedEntity: this.targetEntityID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + unhighlightTargetEntity(this.targetEntityID); + this.grabbing = false; + this.targetEntityID = null; + this.potentialEntityWithContextOverlay = false; + MyAvatar.clearJointData(FAR_GRAB_JOINTS[this.hand]); + }; + + this.updateRecommendedArea = function() { + var dims = Controller.getViewportDimensions(); + this.reticleMaxX = dims.x - MARGIN; + this.reticleMaxY = dims.y - MARGIN; + }; + + this.calculateNewReticlePosition = function(intersection) { + this.updateRecommendedArea(); + var point2d = HMD.overlayFromWorldPoint(intersection); + point2d.x = Math.max(this.reticleMinX, Math.min(point2d.x, this.reticleMaxX)); + point2d.y = Math.max(this.reticleMinY, Math.min(point2d.y, this.reticleMaxY)); + return point2d; + }; + + this.notPointingAtEntity = function(controllerData) { + var intersection = controllerData.rayPicks[this.hand]; + var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); + var entityType = entityProperty.type; + var hudRayPick = controllerData.hudRayPicks[this.hand]; + var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); + if ((intersection.type === Picks.INTERSECTED_ENTITY && entityType === "Web") || + intersection.type === Picks.INTERSECTED_OVERLAY || Window.isPointOnDesktopWindow(point2d)) { + return true; + } + return false; + }; + + this.destroyContextOverlay = function(controllerData) { + if (this.entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(this.entityWithContextOverlay); + this.entityWithContextOverlay = false; + this.potentialEntityWithContextOverlay = false; + } + }; + + this.targetIsNull = function() { + var properties = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); + if (Object.keys(properties).length === 0 && this.distanceHolding) { + return true; + } + return false; + }; + + this.getTargetProps = function (controllerData) { + var targetEntity = controllerData.rayPicks[this.hand].objectID; + if (targetEntity) { + var gtProps = Entities.getEntityProperties(targetEntity, DISPATCHER_PROPERTIES); + if (entityIsGrabbable(gtProps)) { + // give haptic feedback + if (gtProps.id !== this.hapticTargetID) { + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + this.hapticTargetID = gtProps.id; + } + // if we've attempted to grab a child, roll up to the root of the tree + var groupRootProps = findGroupParent(controllerData, gtProps); + if (entityIsGrabbable(groupRootProps)) { + return groupRootProps; + } + return gtProps; + } + } + return null; + }; + + this.isReady = function (controllerData) { + if (HMD.active) { + if (this.notPointingAtEntity(controllerData)) { + return makeRunningValues(false, [], []); + } + + this.distanceHolding = false; + + if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + return makeRunningValues(true, [], []); + } else { + this.destroyContextOverlay(); + } + } + return makeRunningValues(false, [], []); + }; + + this.run = function (controllerData) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || + this.notPointingAtEntity(controllerData) || this.targetIsNull()) { + this.endFarGrabEntity(controllerData); + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); + this.highlightedEntity = null; + return makeRunningValues(false, [], []); + } + this.intersectionDistance = controllerData.rayPicks[this.hand].distance; + + // gather up the readiness of the near-grab modules + var nearGrabNames = [ + this.hand === RIGHT_HAND ? "RightScaleAvatar" : "LeftScaleAvatar", + this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", + this.hand === RIGHT_HAND ? "RightNearGrabEntity" : "LeftNearGrabEntity", + this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay", + this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight" + ]; + + var nearGrabReadiness = []; + for (var i = 0; i < nearGrabNames.length; i++) { + var nearGrabModule = getEnabledModuleByName(nearGrabNames[i]); + var ready = nearGrabModule ? nearGrabModule.isReady(controllerData) : makeRunningValues(false, [], []); + nearGrabReadiness.push(ready); + } + + if (this.targetEntityID) { + // if we are doing a distance grab and the object or tablet gets close enough to the controller, + // stop the far-grab so the near-grab or equip can take over. + for (var k = 0; k < nearGrabReadiness.length; k++) { + if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.targetEntityID || + HMD.tabletID && nearGrabReadiness[k].targets[0] === HMD.tabletID)) { + this.endFarGrabEntity(controllerData); + return makeRunningValues(false, [], []); + } + } + + this.continueDistanceHolding(controllerData); + } else { + // if we are doing a distance search and this controller moves into a position + // where it could near-grab something, stop searching. + for (var j = 0; j < nearGrabReadiness.length; j++) { + if (nearGrabReadiness[j].active) { + this.endFarGrabEntity(controllerData); + return makeRunningValues(false, [], []); + } + } + + var rayPickInfo = controllerData.rayPicks[this.hand]; + if (rayPickInfo.type === Picks.INTERSECTED_ENTITY) { + if (controllerData.triggerClicks[this.hand]) { + var entityID = rayPickInfo.objectID; + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); + this.highlightedEntity = null; + var targetProps = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); + if (targetProps.href !== "") { + AddressManager.handleLookupString(targetProps.href); + return makeRunningValues(false, [], []); + } + + this.targetObject = new TargetObject(entityID, targetProps); + this.targetObject.parentProps = getEntityParents(targetProps); + + if (this.contextOverlayTimer) { + Script.clearTimeout(this.contextOverlayTimer); + } + this.contextOverlayTimer = false; + if (entityID === this.entityWithContextOverlay) { + this.destroyContextOverlay(); + } else { + Selection.removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityID); + } + + var targetEntity = this.targetObject.getTargetEntity(); + entityID = targetEntity.id; + targetProps = targetEntity.props; + + if (entityIsGrabbable(targetProps) || entityIsGrabbable(this.targetObject.entityProps)) { + + this.targetEntityID = entityID; + this.grabbedDistance = rayPickInfo.distance; + this.distanceHolding = true; + this.startFarGrabEntity(controllerData, targetProps); + } + } else { + var targetEntityID = rayPickInfo.objectID; + if (this.highlightedEntity !== targetEntityID) { + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); + var selectionTargetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); + + var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); + selectionTargetObject.parentProps = getEntityParents(selectionTargetProps); + var selectionTargetEntity = selectionTargetObject.getTargetEntity(); + + if (entityIsGrabbable(selectionTargetEntity.props) || + entityIsGrabbable(selectionTargetObject.entityProps)) { + + Selection.addToSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", rayPickInfo.objectID); + } + this.highlightedEntity = rayPickInfo.objectID; + } + + if (!this.entityWithContextOverlay) { + var _this = this; + + if (_this.potentialEntityWithContextOverlay !== rayPickInfo.objectID) { + if (_this.contextOverlayTimer) { + Script.clearTimeout(_this.contextOverlayTimer); + } + _this.contextOverlayTimer = false; + _this.potentialEntityWithContextOverlay = rayPickInfo.objectID; + } + + if (!_this.contextOverlayTimer) { + _this.contextOverlayTimer = Script.setTimeout(function () { + if (!_this.entityWithContextOverlay && + _this.contextOverlayTimer && + _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { + var cotProps = Entities.getEntityProperties(rayPickInfo.objectID, + DISPATCHER_PROPERTIES); + var pointerEvent = { + type: "Move", + id: _this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, + rayPickInfo.intersection, cotProps), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.surfaceNormal, + direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), + button: "Secondary" + }; + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.objectID, pointerEvent)) { + _this.entityWithContextOverlay = rayPickInfo.objectID; + } + } + _this.contextOverlayTimer = false; + }, 500); + } + } + } + } else if (this.highlightedEntity) { + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); + this.highlightedEntity = null; + } + } + return this.exitIfDisabled(controllerData); + }; + + this.exitIfDisabled = function(controllerData) { + var moduleName = this.hand === RIGHT_HAND ? "RightDisableModules" : "LeftDisableModules"; + var disableModule = getEnabledModuleByName(moduleName); + if (disableModule) { + if (disableModule.disableModules) { + this.endFarGrabEntity(controllerData); + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); + this.highlightedEntity = null; + return makeRunningValues(false, [], []); + } + } + var grabbedThing = this.distanceHolding ? this.targetObject.entityID : null; + var offset = this.calculateOffset(controllerData); + var laserLockInfo = makeLaserLockInfo(grabbedThing, false, this.hand, offset); + return makeRunningValues(true, [], [], laserLockInfo); + }; + + this.calculateOffset = function(controllerData) { + if (this.distanceHolding) { + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); + } + return undefined; + }; + } + + var leftFarGrabEntity = new FarGrabEntity(LEFT_HAND); + var rightFarGrabEntity = new FarGrabEntity(RIGHT_HAND); + + enableDispatcherModule("LeftFarGrabEntity", leftFarGrabEntity); + enableDispatcherModule("RightFarGrabEntity", rightFarGrabEntity); + + function cleanup() { + disableDispatcherModule("LeftFarGrabEntity"); + disableDispatcherModule("RightFarGrabEntity"); + } + Script.scriptEnding.connect(cleanup); +}()); diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index bb563a269c..ddff35b9e7 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -8,10 +8,10 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, getGrabbableData, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, - Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, + MSECS_PER_SEC, makeDispatcherModuleParameters, makeRunningValues, TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity, Uuid, - DISPATCHER_PROPERTIES + DISPATCHER_PROPERTIES, HMD */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -65,25 +65,14 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.grabFollowsController = grabbableData.grabFollowsController; this.kinematicGrab = grabbableData.grabKinematic; - var handRotation; - var handPosition; - if (this.grabFollowsController) { - var controllerID = - (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var controllerLocation = getControllerWorldLocation(controllerID, false); - handRotation = controllerLocation.orientation; - handPosition = controllerLocation.position; + var handJointIndex; + if (HMD.mounted && HMD.isHandControllerAvailable() && grabbableData.grabFollowsController) { + handJointIndex = getControllerJointIndex(this.hand); } else { - handRotation = this.getHandRotation(); - handPosition = this.getHandPosition(); + handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); } - - var objectRotation = targetProps.rotation; - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var currentObjectPosition = targetProps.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + this.offsetPosition = Entities.worldToLocalPosition(targetProps.position, MyAvatar.SELF_ID, handJointIndex); + this.offsetRotation = Entities.worldToLocalRotation(targetProps.rotation, MyAvatar.SELF_ID, handJointIndex); var now = Date.now(); this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC); diff --git a/scripts/system/controllers/controllerModules/nearGrabEntity.js b/scripts/system/controllers/controllerModules/nearGrabEntity.js new file mode 100644 index 0000000000..78e15f51e4 --- /dev/null +++ b/scripts/system/controllers/controllerModules/nearGrabEntity.js @@ -0,0 +1,249 @@ +"use strict"; + +// nearGrabEntity.js +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, + enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, + findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, + HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME, + TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, Uuid, highlightTargetEntity, unhighlightTargetEntity, + distanceBetweenEntityLocalPositionAndBoundingBox, getGrabbableData, getGrabPointSphereOffset, DISPATCHER_PROPERTIES +*/ + +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/cloneEntityUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + + // XXX this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; + // XXX this.kinematicGrab = (grabbableData.kinematic !== undefined) ? grabbableData.kinematic : NEAR_GRABBING_KINEMATIC; + + // this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378 + var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral + + function getGrabOffset(handController) { + var offset = getGrabPointSphereOffset(handController, true); + offset.y = -offset.y; + return Vec3.multiply(MyAvatar.sensorToWorldScale, offset); + } + + function NearGrabEntity(hand) { + this.hand = hand; + this.targetEntityID = null; + this.grabbing = false; + this.hapticTargetID = null; + this.highlightedEntity = null; + this.cloneAllowed = true; + this.grabID = null; + + this.parameters = makeDispatcherModuleParameters( + 500, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.startNearGrabEntity = function (controllerData, targetProps) { + var grabData = getGrabbableData(targetProps); + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + unhighlightTargetEntity(this.targetEntityID); + this.highlightedEntity = null; + var message = { + hand: this.hand, + entityID: this.targetEntityID + }; + + Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); + var handJointIndex; + if (HMD.mounted && HMD.isHandControllerAvailable() && grabData.grabFollowsController) { + handJointIndex = getControllerJointIndex(this.hand); + } else { + handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + } + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(targetProps.id, "startNearGrab", args); + + this.targetEntityID = targetProps.id; + + if (this.grabID) { + MyAvatar.releaseGrab(this.grabID); + } + + var relativePosition = Entities.worldToLocalPosition(targetProps.position, MyAvatar.SELF_ID, handJointIndex); + var relativeRotation = Entities.worldToLocalRotation(targetProps.rotation, MyAvatar.SELF_ID, handJointIndex); + + this.grabID = MyAvatar.grab(targetProps.id, handJointIndex, relativePosition, relativeRotation); + + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'grab', + grabbedEntity: targetProps.id, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + this.grabbing = true; + }; + + this.endNearGrabEntity = function (controllerData) { + if (this.grabID) { + MyAvatar.releaseGrab(this.grabID); + this.grabID = null; + } + + this.hapticTargetID = null; + var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'release', + grabbedEntity: this.targetEntityID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + unhighlightTargetEntity(this.targetEntityID); + this.highlightedEntity = null; + this.grabbing = false; + this.targetEntityID = null; + }; + + this.getTargetProps = function (controllerData) { + // nearbyEntityProperties is already sorted by length from controller + var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + var sensorScaleFactor = MyAvatar.sensorToWorldScale; + for (var i = 0; i < nearbyEntityProperties.length; i++) { + var props = nearbyEntityProperties[i]; + var handPosition = controllerData.controllerLocations[this.hand].position; + var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props); + var distance = Vec3.distance(handPosition, props.position); + if ((dist > TEAR_AWAY_DISTANCE) || + (distance > NEAR_GRAB_RADIUS * sensorScaleFactor)) { + continue; + } + if (entityIsGrabbable(props) || entityIsCloneable(props)) { + // give haptic feedback + if (props.id !== this.hapticTargetID) { + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + this.hapticTargetID = props.id; + } + if (!entityIsCloneable(props)) { + // if we've attempted to grab a non-cloneable child, roll up to the root of the tree + var groupRootProps = findGroupParent(controllerData, props); + if (entityIsGrabbable(groupRootProps)) { + return groupRootProps; + } + } + return props; + } + } + return null; + }; + + this.isReady = function (controllerData, deltaTime) { + this.targetEntityID = null; + this.grabbing = false; + + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && + controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { + this.cloneAllowed = true; + return makeRunningValues(false, [], []); + } + + var targetProps = this.getTargetProps(controllerData); + if (targetProps) { + this.targetEntityID = targetProps.id; + this.highlightedEntity = this.targetEntityID; + highlightTargetEntity(this.targetEntityID); + return makeRunningValues(true, [this.targetEntityID], []); + } else { + if (this.highlightedEntity) { + unhighlightTargetEntity(this.highlightedEntity); + this.highlightedEntity = null; + } + this.hapticTargetID = null; + return makeRunningValues(false, [], []); + } + }; + + this.run = function (controllerData, deltaTime) { + if (this.grabbing) { + if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE && + controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { + this.endNearGrabEntity(controllerData); + return makeRunningValues(false, [], []); + } + + var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; + if (!props) { + props = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); + if (!props) { + // entity was deleted + unhighlightTargetEntity(this.targetEntityID); + this.highlightedEntity = null; + this.grabbing = false; + this.targetEntityID = null; + this.hapticTargetID = null; + return makeRunningValues(false, [], []); + } + } + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args); + } else { + // still searching / highlighting + var readiness = this.isReady(controllerData); + if (!readiness.active) { + unhighlightTargetEntity(this.highlightedEntity); + this.highlightedEntity = null; + return readiness; + } + if (controllerData.triggerClicks[this.hand] || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { + // switch to grab + var targetProps = this.getTargetProps(controllerData); + var targetCloneable = entityIsCloneable(targetProps); + + if (targetCloneable) { + if (this.cloneAllowed) { + var cloneID = cloneEntity(targetProps); + if (cloneID !== null) { + var cloneProps = Entities.getEntityProperties(cloneID, DISPATCHER_PROPERTIES); + this.grabbing = true; + this.targetEntityID = cloneID; + this.startNearGrabEntity(controllerData, cloneProps); + this.cloneAllowed = false; // prevent another clone call until inputs released + } + } + } else if (targetProps) { + this.grabbing = true; + this.startNearGrabEntity(controllerData, targetProps); + } + } + } + + return makeRunningValues(true, [this.targetEntityID], []); + }; + + this.cleanup = function () { + if (this.targetEntityID) { + this.endNearGrabEntity(); + } + }; + } + + var leftNearGrabEntity = new NearGrabEntity(LEFT_HAND); + var rightNearGrabEntity = new NearGrabEntity(RIGHT_HAND); + + enableDispatcherModule("LeftNearGrabEntity", leftNearGrabEntity); + enableDispatcherModule("RightNearGrabEntity", rightNearGrabEntity); + + function cleanup() { + leftNearGrabEntity.cleanup(); + rightNearGrabEntity.cleanup(); + disableDispatcherModule("LeftNearGrabEntity"); + disableDispatcherModule("RightNearGrabEntity"); + } + Script.scriptEnding.connect(cleanup); +}()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 83fa455519..7ab824f2c5 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -18,11 +18,7 @@ var CONTOLLER_SCRIPTS = [ "toggleAdvancedMovementForHandControllers.js", "handTouch.js", "controllerDispatcher.js", - "controllerModules/nearParentGrabEntity.js", "controllerModules/nearParentGrabOverlay.js", - "controllerModules/nearActionGrabEntity.js", - "controllerModules/farActionGrabEntityDynOnly.js", - "controllerModules/farParentGrabEntity.js", "controllerModules/stylusInput.js", "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", @@ -39,6 +35,16 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearTabletHighlight.js" ]; +if (Settings.getValue("useTraitsGrab", false)) { + CONTOLLER_SCRIPTS.push("controllerModules/nearGrabEntity.js"); + CONTOLLER_SCRIPTS.push("controllerModules/farGrabEntity.js"); +} else { + CONTOLLER_SCRIPTS.push("controllerModules/nearParentGrabEntity.js"); + CONTOLLER_SCRIPTS.push("controllerModules/nearActionGrabEntity.js"); + CONTOLLER_SCRIPTS.push("controllerModules/farActionGrabEntityDynOnly.js"); + CONTOLLER_SCRIPTS.push("controllerModules/farParentGrabEntity.js"); +} + var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 070997d479..fbaa61e9c9 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -104,7 +104,7 @@ TEAR_AWAY_CHECK_TIME = 0.15; // seconds, duration between checks NEAR_GRAB_DISTANCE = 0.14; // Grab an entity if its bounding box is within this distance. // Smaller than TEAR_AWAY_DISTANCE for hysteresis. -DISPATCHER_HOVERING_LIST = "dispactherHoveringList"; +DISPATCHER_HOVERING_LIST = "dispatcherHoveringList"; DISPATCHER_HOVERING_STYLE = { isOutlineSmooth: true, outlineWidth: 0, @@ -144,6 +144,7 @@ DISPATCHER_PROPERTIES = [ "grab.grabFollowsController", "grab.triggerable", "grab.equippable", + "grab.grabDelegateToParent", "grab.equippableLeftPosition", "grab.equippableLeftRotation", "grab.equippableRightPosition", @@ -332,15 +333,15 @@ getControllerJointIndex = function (hand) { if (now - getControllerJointIndexCacheTime[hand] > GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME) { if (HMD.isHandControllerAvailable()) { var controllerJointIndex = -1; - if (Camera.mode === "first person") { - controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? - "_CONTROLLER_RIGHTHAND" : - "_CONTROLLER_LEFTHAND"); - } else if (Camera.mode === "third person") { + // if (Camera.mode === "first person") { + // controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + // "_CONTROLLER_RIGHTHAND" : + // "_CONTROLLER_LEFTHAND"); + // } else if (Camera.mode === "third person") { controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); - } + // } getControllerJointIndexCacheTime[hand] = now; getControllerJointIndexCache[hand] = controllerJointIndex; @@ -409,7 +410,8 @@ ensureDynamic = function (entityID) { }; findGroupParent = function (controllerData, targetProps) { - while (targetProps.parentID && + while (targetProps.grab.grabDelegateToParent && + targetProps.parentID && targetProps.parentID !== Uuid.NULL && Entities.getNestableType(targetProps.parentID) == "entity") { var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES); From fc128b49deabba4be0c819c5b9451d4f6ae6d576 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 10 Dec 2018 15:17:06 -0800 Subject: [PATCH 07/28] don't crash when releasing an entity that has expired --- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index fdd707c950..8c340b7fbb 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -563,7 +563,7 @@ void Avatar::accumulateGrabPositions(std::map& g foreach (auto grabID, _avatarGrabs.keys()) { const GrabPointer& grab = _avatarGrabs.value(grabID); - if (!grab->getActionID().isNull()) { + if (!grab || !grab->getActionID().isNull()) { continue; // the accumulated value isn't used, in this case. } From 6f25c59a0cb1133827647ab5b1bb1a6bd127b409 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 10 Dec 2018 15:42:33 -0800 Subject: [PATCH 08/28] turn-off nearEntityGrab when scaleEntity is active --- .../controllerModules/nearGrabEntity.js | 33 ++++++++----------- .../controllerModules/scaleEntity.js | 2 +- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/scripts/system/controllers/controllerModules/nearGrabEntity.js b/scripts/system/controllers/controllerModules/nearGrabEntity.js index 78e15f51e4..a56c28900e 100644 --- a/scripts/system/controllers/controllerModules/nearGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearGrabEntity.js @@ -6,13 +6,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, - enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, - TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, - findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, - HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME, - TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, Uuid, highlightTargetEntity, unhighlightTargetEntity, - distanceBetweenEntityLocalPositionAndBoundingBox, getGrabbableData, getGrabPointSphereOffset, DISPATCHER_PROPERTIES +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, enableDispatcherModule, + disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, + makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, Vec3, + cloneEntity, entityIsCloneable, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, TEAR_AWAY_DISTANCE, + distanceBetweenPointAndEntityBoundingBox, highlightTargetEntity, unhighlightTargetEntity, getGrabbableData, + DISPATCHER_PROPERTIES, HMD */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -21,18 +20,6 @@ Script.include("/~/system/libraries/controllers.js"); (function() { - // XXX this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; - // XXX this.kinematicGrab = (grabbableData.kinematic !== undefined) ? grabbableData.kinematic : NEAR_GRABBING_KINEMATIC; - - // this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378 - var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral - - function getGrabOffset(handController) { - var offset = getGrabPointSphereOffset(handController, true); - offset.y = -offset.y; - return Vec3.multiply(MyAvatar.sensorToWorldScale, offset); - } - function NearGrabEntity(hand) { this.hand = hand; this.targetEntityID = null; @@ -95,7 +82,6 @@ Script.include("/~/system/libraries/controllers.js"); } this.hapticTargetID = null; - var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); @@ -176,6 +162,13 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(false, [], []); } + if (controllerData.secondaryValues[LEFT_HAND] >= TRIGGER_ON_VALUE && + controllerData.secondaryValues[RIGHT_HAND] >= TRIGGER_ON_VALUE) { + // let scaleEntity module take over + this.endNearGrabEntity(controllerData); + return makeRunningValues(false, [], []); + } + var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; if (!props) { props = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); diff --git a/scripts/system/controllers/controllerModules/scaleEntity.js b/scripts/system/controllers/controllerModules/scaleEntity.js index 24c05a1394..0f981d88f8 100644 --- a/scripts/system/controllers/controllerModules/scaleEntity.js +++ b/scripts/system/controllers/controllerModules/scaleEntity.js @@ -19,7 +19,7 @@ this.parameters = dispatcherUtils.makeDispatcherModuleParameters( 120, - this.hand === RIGHT_HAND ? ["rightHandTrigger"] : ["leftHandTrigger"], + this.hand === RIGHT_HAND ? ["rightHandTrigger", "rightHand"] : ["leftHandTrigger", "leftHand"], [], 100 ); From 70b68294fff67d96a8ca1f4a98a82791972a69a4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 10 Dec 2018 18:08:30 -0800 Subject: [PATCH 09/28] port over PR-14464 --- .../controllerModules/farGrabEntity.js | 18 +++++++------ .../controllerModules/nearGrabEntity.js | 26 +++++++------------ 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farGrabEntity.js b/scripts/system/controllers/controllerModules/farGrabEntity.js index 39c65aa022..e09a15c851 100644 --- a/scripts/system/controllers/controllerModules/farGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farGrabEntity.js @@ -58,7 +58,8 @@ Script.include("/~/system/libraries/controllers.js"); this.reticleMaxX = 0; this.reticleMinY = MARGIN; this.reticleMaxY = 0; - this.lastUnexpectedChildrenCheckTime = 0; + this.endedGrab = 0; + this.MIN_HAPTIC_PULSE_INTERVAL = 500; // ms var FAR_GRAB_JOINTS = [65527, 65528]; // FARGRAB_LEFTHAND_INDEX, FARGRAB_RIGHTHAND_INDEX @@ -126,7 +127,12 @@ Script.include("/~/system/libraries/controllers.js"); // compute the mass for the purpose of energy and how quickly to move object this.mass = this.getMass(targetProps.dimensions, targetProps.density); - Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + // Debounce haptic pules. Can occur as near grab controller module vacillates between being ready or not due to + // changing positions and floating point rounding. + if (Date.now() - this.endedGrab > this.MIN_HAPTIC_PULSE_INTERVAL) { + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + } + unhighlightTargetEntity(this.targetEntityID); var message = { hand: this.hand, @@ -237,7 +243,8 @@ Script.include("/~/system/libraries/controllers.js"); this.grabID = null; } - this.hapticTargetID = null; + this.endedGrab = Date.now(); + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ @@ -300,11 +307,6 @@ Script.include("/~/system/libraries/controllers.js"); if (targetEntity) { var gtProps = Entities.getEntityProperties(targetEntity, DISPATCHER_PROPERTIES); if (entityIsGrabbable(gtProps)) { - // give haptic feedback - if (gtProps.id !== this.hapticTargetID) { - Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); - this.hapticTargetID = gtProps.id; - } // if we've attempted to grab a child, roll up to the root of the tree var groupRootProps = findGroupParent(controllerData, gtProps); if (entityIsGrabbable(groupRootProps)) { diff --git a/scripts/system/controllers/controllerModules/nearGrabEntity.js b/scripts/system/controllers/controllerModules/nearGrabEntity.js index a56c28900e..6b8a4a7eb2 100644 --- a/scripts/system/controllers/controllerModules/nearGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearGrabEntity.js @@ -9,9 +9,9 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, Vec3, - cloneEntity, entityIsCloneable, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, TEAR_AWAY_DISTANCE, + cloneEntity, entityIsCloneable, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, distanceBetweenPointAndEntityBoundingBox, highlightTargetEntity, unhighlightTargetEntity, getGrabbableData, - DISPATCHER_PROPERTIES, HMD + DISPATCHER_PROPERTIES, HMD, NEAR_GRAB_DISTANCE */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -24,7 +24,6 @@ Script.include("/~/system/libraries/controllers.js"); this.hand = hand; this.targetEntityID = null; this.grabbing = false; - this.hapticTargetID = null; this.highlightedEntity = null; this.cloneAllowed = true; this.grabID = null; @@ -81,8 +80,6 @@ Script.include("/~/system/libraries/controllers.js"); this.grabID = null; } - this.hapticTargetID = null; - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ @@ -100,21 +97,18 @@ Script.include("/~/system/libraries/controllers.js"); // nearbyEntityProperties is already sorted by length from controller var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; var sensorScaleFactor = MyAvatar.sensorToWorldScale; + var nearGrabDistance = NEAR_GRAB_DISTANCE * sensorScaleFactor; + var nearGrabRadius = NEAR_GRAB_RADIUS * sensorScaleFactor; for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; - var handPosition = controllerData.controllerLocations[this.hand].position; - var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props); - var distance = Vec3.distance(handPosition, props.position); - if ((dist > TEAR_AWAY_DISTANCE) || - (distance > NEAR_GRAB_RADIUS * sensorScaleFactor)) { + var grabPosition = controllerData.controllerLocations[this.hand].position; // Is offset from hand position. + var dist = distanceBetweenPointAndEntityBoundingBox(grabPosition, props); + var distance = Vec3.distance(grabPosition, props.position); + if ((dist > nearGrabDistance) || + (distance > nearGrabRadius)) { // Only smallish entities can be near grabbed. continue; } if (entityIsGrabbable(props) || entityIsCloneable(props)) { - // give haptic feedback - if (props.id !== this.hapticTargetID) { - Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); - this.hapticTargetID = props.id; - } if (!entityIsCloneable(props)) { // if we've attempted to grab a non-cloneable child, roll up to the root of the tree var groupRootProps = findGroupParent(controllerData, props); @@ -149,7 +143,6 @@ Script.include("/~/system/libraries/controllers.js"); unhighlightTargetEntity(this.highlightedEntity); this.highlightedEntity = null; } - this.hapticTargetID = null; return makeRunningValues(false, [], []); } }; @@ -178,7 +171,6 @@ Script.include("/~/system/libraries/controllers.js"); this.highlightedEntity = null; this.grabbing = false; this.targetEntityID = null; - this.hapticTargetID = null; return makeRunningValues(false, [], []); } } From 9900040ece46349819ef0a5686c5ca35e509119e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 10 Dec 2018 18:09:02 -0800 Subject: [PATCH 10/28] enable trait-based grabbing by default --- scripts/system/controllers/controllerScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 7ab824f2c5..2114f2c0b2 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -35,7 +35,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearTabletHighlight.js" ]; -if (Settings.getValue("useTraitsGrab", false)) { +if (Settings.getValue("useTraitsGrab", true)) { CONTOLLER_SCRIPTS.push("controllerModules/nearGrabEntity.js"); CONTOLLER_SCRIPTS.push("controllerModules/farGrabEntity.js"); } else { From 1c1e648b8b2f0db3f963b89493350a605c9fcb95 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 10 Dec 2018 18:36:11 -0800 Subject: [PATCH 11/28] port over PR-14429 --- .../controllerModules/farGrabEntity.js | 83 +++++++------------ .../controllerModules/nearGrabEntity.js | 25 +----- 2 files changed, 31 insertions(+), 77 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farGrabEntity.js b/scripts/system/controllers/controllerModules/farGrabEntity.js index e09a15c851..406066d137 100644 --- a/scripts/system/controllers/controllerModules/farGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farGrabEntity.js @@ -53,7 +53,6 @@ Script.include("/~/system/libraries/controllers.js"); this.potentialEntityWithContextOverlay = false; this.entityWithContextOverlay = false; this.contextOverlayTimer = false; - this.highlightedEntity = null; this.reticleMinX = MARGIN; this.reticleMaxX = 0; this.reticleMinY = MARGIN; @@ -339,8 +338,6 @@ Script.include("/~/system/libraries/controllers.js"); if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.notPointingAtEntity(controllerData) || this.targetIsNull()) { this.endFarGrabEntity(controllerData); - Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - this.highlightedEntity = null; return makeRunningValues(false, [], []); } this.intersectionDistance = controllerData.rayPicks[this.hand].distance; @@ -387,8 +384,6 @@ Script.include("/~/system/libraries/controllers.js"); if (rayPickInfo.type === Picks.INTERSECTED_ENTITY) { if (controllerData.triggerClicks[this.hand]) { var entityID = rayPickInfo.objectID; - Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - this.highlightedEntity = null; var targetProps = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); if (targetProps.href !== "") { AddressManager.handleLookupString(targetProps.href); @@ -419,64 +414,42 @@ Script.include("/~/system/libraries/controllers.js"); this.distanceHolding = true; this.startFarGrabEntity(controllerData, targetProps); } - } else { - var targetEntityID = rayPickInfo.objectID; - if (this.highlightedEntity !== targetEntityID) { - Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - var selectionTargetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); + } else if (!this.entityWithContextOverlay) { + var _this = this; - var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); - selectionTargetObject.parentProps = getEntityParents(selectionTargetProps); - var selectionTargetEntity = selectionTargetObject.getTargetEntity(); - - if (entityIsGrabbable(selectionTargetEntity.props) || - entityIsGrabbable(selectionTargetObject.entityProps)) { - - Selection.addToSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", rayPickInfo.objectID); + if (_this.potentialEntityWithContextOverlay !== rayPickInfo.objectID) { + if (_this.contextOverlayTimer) { + Script.clearTimeout(_this.contextOverlayTimer); } - this.highlightedEntity = rayPickInfo.objectID; + _this.contextOverlayTimer = false; + _this.potentialEntityWithContextOverlay = rayPickInfo.objectID; } - if (!this.entityWithContextOverlay) { - var _this = this; - - if (_this.potentialEntityWithContextOverlay !== rayPickInfo.objectID) { - if (_this.contextOverlayTimer) { - Script.clearTimeout(_this.contextOverlayTimer); + if (!_this.contextOverlayTimer) { + _this.contextOverlayTimer = Script.setTimeout(function () { + if (!_this.entityWithContextOverlay && + _this.contextOverlayTimer && + _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { + var cotProps = Entities.getEntityProperties(rayPickInfo.objectID, + DISPATCHER_PROPERTIES); + var pointerEvent = { + type: "Move", + id: _this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, + rayPickInfo.intersection, cotProps), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.surfaceNormal, + direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), + button: "Secondary" + }; + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.objectID, pointerEvent)) { + _this.entityWithContextOverlay = rayPickInfo.objectID; + } } _this.contextOverlayTimer = false; - _this.potentialEntityWithContextOverlay = rayPickInfo.objectID; - } - - if (!_this.contextOverlayTimer) { - _this.contextOverlayTimer = Script.setTimeout(function () { - if (!_this.entityWithContextOverlay && - _this.contextOverlayTimer && - _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { - var cotProps = Entities.getEntityProperties(rayPickInfo.objectID, - DISPATCHER_PROPERTIES); - var pointerEvent = { - type: "Move", - id: _this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, - rayPickInfo.intersection, cotProps), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.surfaceNormal, - direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), - button: "Secondary" - }; - if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.objectID, pointerEvent)) { - _this.entityWithContextOverlay = rayPickInfo.objectID; - } - } - _this.contextOverlayTimer = false; - }, 500); - } + }, 500); } } - } else if (this.highlightedEntity) { - Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - this.highlightedEntity = null; } } return this.exitIfDisabled(controllerData); diff --git a/scripts/system/controllers/controllerModules/nearGrabEntity.js b/scripts/system/controllers/controllerModules/nearGrabEntity.js index 6b8a4a7eb2..020f65a5de 100644 --- a/scripts/system/controllers/controllerModules/nearGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearGrabEntity.js @@ -10,7 +10,7 @@ disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, Vec3, cloneEntity, entityIsCloneable, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, - distanceBetweenPointAndEntityBoundingBox, highlightTargetEntity, unhighlightTargetEntity, getGrabbableData, + distanceBetweenPointAndEntityBoundingBox, getGrabbableData, DISPATCHER_PROPERTIES, HMD, NEAR_GRAB_DISTANCE */ @@ -24,7 +24,6 @@ Script.include("/~/system/libraries/controllers.js"); this.hand = hand; this.targetEntityID = null; this.grabbing = false; - this.highlightedEntity = null; this.cloneAllowed = true; this.grabID = null; @@ -37,14 +36,7 @@ Script.include("/~/system/libraries/controllers.js"); this.startNearGrabEntity = function (controllerData, targetProps) { var grabData = getGrabbableData(targetProps); Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); - unhighlightTargetEntity(this.targetEntityID); - this.highlightedEntity = null; - var message = { - hand: this.hand, - entityID: this.targetEntityID - }; - Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); var handJointIndex; if (HMD.mounted && HMD.isHandControllerAvailable() && grabData.grabFollowsController) { handJointIndex = getControllerJointIndex(this.hand); @@ -87,8 +79,7 @@ Script.include("/~/system/libraries/controllers.js"); grabbedEntity: this.targetEntityID, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); - unhighlightTargetEntity(this.targetEntityID); - this.highlightedEntity = null; + this.grabbing = false; this.targetEntityID = null; }; @@ -135,14 +126,8 @@ Script.include("/~/system/libraries/controllers.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { this.targetEntityID = targetProps.id; - this.highlightedEntity = this.targetEntityID; - highlightTargetEntity(this.targetEntityID); return makeRunningValues(true, [this.targetEntityID], []); } else { - if (this.highlightedEntity) { - unhighlightTargetEntity(this.highlightedEntity); - this.highlightedEntity = null; - } return makeRunningValues(false, [], []); } }; @@ -167,8 +152,6 @@ Script.include("/~/system/libraries/controllers.js"); props = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); if (!props) { // entity was deleted - unhighlightTargetEntity(this.targetEntityID); - this.highlightedEntity = null; this.grabbing = false; this.targetEntityID = null; return makeRunningValues(false, [], []); @@ -178,11 +161,9 @@ Script.include("/~/system/libraries/controllers.js"); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args); } else { - // still searching / highlighting + // still searching var readiness = this.isReady(controllerData); if (!readiness.active) { - unhighlightTargetEntity(this.highlightedEntity); - this.highlightedEntity = null; return readiness; } if (controllerData.triggerClicks[this.hand] || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { From 6a547d042cc96ecb0c76618ec2a1f7c766e1208b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 10 Dec 2018 18:43:21 -0800 Subject: [PATCH 12/28] port over PR-14276 --- .../controllerModules/farGrabEntity.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farGrabEntity.js b/scripts/system/controllers/controllerModules/farGrabEntity.js index 406066d137..da1f3893dc 100644 --- a/scripts/system/controllers/controllerModules/farGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farGrabEntity.js @@ -47,6 +47,7 @@ Script.include("/~/system/libraries/controllers.js"); function FarGrabEntity(hand) { this.hand = hand; + this.grabbing = false; this.targetEntityID = null; this.targetObject = null; this.previouslyUnhooked = {}; @@ -335,8 +336,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || - this.notPointingAtEntity(controllerData) || this.targetIsNull()) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.targetIsNull()) { this.endFarGrabEntity(controllerData); return makeRunningValues(false, [], []); } @@ -346,10 +346,12 @@ Script.include("/~/system/libraries/controllers.js"); var nearGrabNames = [ this.hand === RIGHT_HAND ? "RightScaleAvatar" : "LeftScaleAvatar", this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", - this.hand === RIGHT_HAND ? "RightNearGrabEntity" : "LeftNearGrabEntity", - this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay", - this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight" + this.hand === RIGHT_HAND ? "RightNearGrabEntity" : "LeftNearGrabEntity" ]; + if (!this.grabbing) { + nearGrabNames.push(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"); + nearGrabNames.push(this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight"); + } var nearGrabReadiness = []; for (var i = 0; i < nearGrabNames.length; i++) { @@ -359,11 +361,10 @@ Script.include("/~/system/libraries/controllers.js"); } if (this.targetEntityID) { - // if we are doing a distance grab and the object or tablet gets close enough to the controller, + // if we are doing a distance grab and the object gets close enough to the controller, // stop the far-grab so the near-grab or equip can take over. for (var k = 0; k < nearGrabReadiness.length; k++) { - if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.targetEntityID || - HMD.tabletID && nearGrabReadiness[k].targets[0] === HMD.tabletID)) { + if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.targetEntityID)) { this.endFarGrabEntity(controllerData); return makeRunningValues(false, [], []); } From 905af34564639ee533592147e0d54a09a5ee7ac1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 12 Dec 2018 12:00:06 -0800 Subject: [PATCH 13/28] disable traits-based grabbing by default --- scripts/system/controllers/controllerScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 2114f2c0b2..7ab824f2c5 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -35,7 +35,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearTabletHighlight.js" ]; -if (Settings.getValue("useTraitsGrab", true)) { +if (Settings.getValue("useTraitsGrab", false)) { CONTOLLER_SCRIPTS.push("controllerModules/nearGrabEntity.js"); CONTOLLER_SCRIPTS.push("controllerModules/farGrabEntity.js"); } else { From 0967a94557af001c860601ed895687a51bd583c8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 13 Dec 2018 12:08:01 -0800 Subject: [PATCH 14/28] get grabbing and scaling to get along better --- .../controllerModules/nearGrabEntity.js | 67 +++++++++++-------- .../controllerModules/scaleEntity.js | 11 +-- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/scripts/system/controllers/controllerModules/nearGrabEntity.js b/scripts/system/controllers/controllerModules/nearGrabEntity.js index 020f65a5de..bb74c59691 100644 --- a/scripts/system/controllers/controllerModules/nearGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearGrabEntity.js @@ -7,11 +7,10 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, enableDispatcherModule, - disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, - makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, Vec3, - cloneEntity, entityIsCloneable, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, - distanceBetweenPointAndEntityBoundingBox, getGrabbableData, - DISPATCHER_PROPERTIES, HMD, NEAR_GRAB_DISTANCE + disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, + makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, Vec3, cloneEntity, + entityIsCloneable, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, distanceBetweenPointAndEntityBoundingBox, + getGrabbableData, getEnabledModuleByName, DISPATCHER_PROPERTIES, HMD, NEAR_GRAB_DISTANCE */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -33,9 +32,12 @@ Script.include("/~/system/libraries/controllers.js"); [], 100); - this.startNearGrabEntity = function (controllerData, targetProps) { + this.startGrab = function (targetProps) { + if (this.grabID) { + MyAvatar.releaseGrab(this.grabID); + } + var grabData = getGrabbableData(targetProps); - Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); var handJointIndex; if (HMD.mounted && HMD.isHandControllerAvailable() && grabData.grabFollowsController) { @@ -44,33 +46,42 @@ Script.include("/~/system/libraries/controllers.js"); handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); } - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(targetProps.id, "startNearGrab", args); - this.targetEntityID = targetProps.id; - if (this.grabID) { - MyAvatar.releaseGrab(this.grabID); - } - var relativePosition = Entities.worldToLocalPosition(targetProps.position, MyAvatar.SELF_ID, handJointIndex); var relativeRotation = Entities.worldToLocalRotation(targetProps.rotation, MyAvatar.SELF_ID, handJointIndex); - this.grabID = MyAvatar.grab(targetProps.id, handJointIndex, relativePosition, relativeRotation); + }; + + this.startNearGrabEntity = function (targetProps) { + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + + this.startGrab(targetProps); + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(targetProps.id, "startNearGrab", args); Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'grab', grabbedEntity: targetProps.id, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); + this.grabbing = true; }; - this.endNearGrabEntity = function (controllerData) { + this.endGrab = function () { if (this.grabID) { MyAvatar.releaseGrab(this.grabID); this.grabID = null; } + }; + + this.endNearGrabEntity = function () { + this.endGrab(); + + this.grabbing = false; + this.targetEntityID = null; var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); @@ -79,9 +90,6 @@ Script.include("/~/system/libraries/controllers.js"); grabbedEntity: this.targetEntityID, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); - - this.grabbing = false; - this.targetEntityID = null; }; this.getTargetProps = function (controllerData) { @@ -123,6 +131,13 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(false, [], []); } + var scaleModuleName = this.hand === RIGHT_HAND ? "RightScaleEntity" : "LeftScaleEntity"; + var scaleModule = getEnabledModuleByName(scaleModuleName); + if (scaleModule.grabbedThingID || scaleModule.isReady(controllerData).active) { + // we're rescaling -- don't start a grab. + return makeRunningValues(false, [], []); + } + var targetProps = this.getTargetProps(controllerData); if (targetProps) { this.targetEntityID = targetProps.id; @@ -133,17 +148,11 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData, deltaTime) { + if (this.grabbing) { if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { - this.endNearGrabEntity(controllerData); - return makeRunningValues(false, [], []); - } - - if (controllerData.secondaryValues[LEFT_HAND] >= TRIGGER_ON_VALUE && - controllerData.secondaryValues[RIGHT_HAND] >= TRIGGER_ON_VALUE) { - // let scaleEntity module take over - this.endNearGrabEntity(controllerData); + this.endNearGrabEntity(); return makeRunningValues(false, [], []); } @@ -178,13 +187,13 @@ Script.include("/~/system/libraries/controllers.js"); var cloneProps = Entities.getEntityProperties(cloneID, DISPATCHER_PROPERTIES); this.grabbing = true; this.targetEntityID = cloneID; - this.startNearGrabEntity(controllerData, cloneProps); + this.startNearGrabEntity(cloneProps); this.cloneAllowed = false; // prevent another clone call until inputs released } } } else if (targetProps) { this.grabbing = true; - this.startNearGrabEntity(controllerData, targetProps); + this.startNearGrabEntity(targetProps); } } } diff --git a/scripts/system/controllers/controllerModules/scaleEntity.js b/scripts/system/controllers/controllerModules/scaleEntity.js index 0f981d88f8..51ed0ff94b 100644 --- a/scripts/system/controllers/controllerModules/scaleEntity.js +++ b/scripts/system/controllers/controllerModules/scaleEntity.js @@ -19,7 +19,7 @@ this.parameters = dispatcherUtils.makeDispatcherModuleParameters( 120, - this.hand === RIGHT_HAND ? ["rightHandTrigger", "rightHand"] : ["leftHandTrigger", "leftHand"], + this.hand === RIGHT_HAND ? ["rightHandTrigger"] : ["leftHandTrigger"], [], 100 ); @@ -63,13 +63,15 @@ if (thisHandTargetProps && otherHandTargetProps) { if (thisHandTargetProps.id === otherHandTargetProps.id) { this.grabbedThingID = thisHandTargetProps.id; - this.scalingStartDistance = Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position, - controllerData.controllerLocations[this.otherHand()].position)); + this.scalingStartDistance = + Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position, + controllerData.controllerLocations[this.otherHand()].position)); this.scalingStartDimensions = thisHandTargetProps.dimensions; return dispatcherUtils.makeRunningValues(true, [], []); } } } + this.grabbedThingID = false; return dispatcherUtils.makeRunningValues(false, [], []); }; @@ -82,10 +84,11 @@ controllerData.controllerLocations[this.otherHand()].position)); var currentRescale = scalingCurrentDistance / this.scalingStartDistance; var newDimensions = Vec3.multiply(currentRescale, this.scalingStartDimensions); - Entities.editEntity(this.grabbedThingID, { dimensions: newDimensions }); + Entities.editEntity(this.grabbedThingID, { localDimensions: newDimensions }); } return dispatcherUtils.makeRunningValues(true, [], []); } + this.grabbedThingID = false; return dispatcherUtils.makeRunningValues(false, [], []); }; } From 38cc2205ec03c0554ba744162b24f78ef97a9c1e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 13 Dec 2018 14:20:29 -0800 Subject: [PATCH 15/28] dimensions is a simulation restricted change --- libraries/entities/src/EntityItemProperties.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index ffa3549955..7eb2e1c765 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -3605,6 +3605,7 @@ bool EntityItemProperties::hasSimulationRestrictedChanges() const { return _positionChanged || _localPositionChanged || _rotationChanged || _localRotationChanged || _velocityChanged || _localVelocityChanged + || _localDimensionsChanged || _dimensionsChanged || _angularVelocityChanged || _localAngularVelocityChanged || _accelerationChanged || _parentIDChanged || _parentJointIndexChanged; From bc5079e5d75f814f7011dde15f7541cd220a9a48 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 14 Dec 2018 10:06:55 -0800 Subject: [PATCH 16/28] fix flawed logic in updateEntityQueryAACubeWorker --- libraries/entities/src/EntityTree.cpp | 44 +++++++++++++-------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 63bcd70a14..506df95007 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2962,30 +2962,28 @@ bool EntityTree::removeMaterialFromOverlay(const QUuid& overlayID, graphics::Mat void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, MovingEntitiesOperator& moveOperator, bool force, bool tellServer) { // if the queryBox has changed, tell the entity-server - if (object->getNestableType() == NestableType::Entity && object->updateQueryAACube()) { - EntityItemPointer entity = std::static_pointer_cast(object); - if (entity->updateQueryAACube() || force) { - bool success; - AACube newCube = entity->getQueryAACube(success); - if (success) { - moveOperator.addEntityToMoveList(entity, newCube); - } - // send an edit packet to update the entity-server about the queryAABox - // unless it is client-only - if (tellServer && packetSender && entity->isDomainEntity()) { - quint64 now = usecTimestampNow(); - EntityItemProperties properties = entity->getProperties(); - properties.setQueryAACubeDirty(); - properties.setLocationDirty(); - properties.setLastEdited(now); - - packetSender->queueEditEntityMessage(PacketType::EntityEdit, getThisPointer(), entity->getID(), properties); - entity->setLastBroadcast(now); // for debug/physics status icons - } - - entity->markDirtyFlags(Simulation::DIRTY_POSITION); - entityChanged(entity); + EntityItemPointer entity = std::dynamic_pointer_cast(object); + if (entity && (entity->updateQueryAACube() || force)) { + bool success; + AACube newCube = entity->getQueryAACube(success); + if (success) { + moveOperator.addEntityToMoveList(entity, newCube); } + // send an edit packet to update the entity-server about the queryAABox + // unless it is client-only + if (tellServer && packetSender && entity->isDomainEntity()) { + quint64 now = usecTimestampNow(); + EntityItemProperties properties = entity->getProperties(); + properties.setQueryAACubeDirty(); + properties.setLocationDirty(); + properties.setLastEdited(now); + + packetSender->queueEditEntityMessage(PacketType::EntityEdit, getThisPointer(), entity->getID(), properties); + entity->setLastBroadcast(now); // for debug/physics status icons + } + + entity->markDirtyFlags(Simulation::DIRTY_POSITION); + entityChanged(entity); } object->forEachDescendant([&](SpatiallyNestablePointer descendant) { From 895e8cdba18c9c226ec2b707628df6a43c43d340 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 14 Dec 2018 11:47:57 -0800 Subject: [PATCH 17/28] fix off-hand adjustment of equipped entities. make traits-based grabbing be enabled by default, again --- interface/src/avatar/GrabManager.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 6 +- libraries/entities/src/EntityItem.cpp | 2 +- libraries/shared/src/SpatiallyNestable.cpp | 60 ++++++++++--------- .../system/controllers/controllerScripts.js | 2 +- .../libraries/controllerDispatcherUtils.js | 14 +---- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/interface/src/avatar/GrabManager.cpp b/interface/src/avatar/GrabManager.cpp index 5312bcc4eb..c41435d67e 100644 --- a/interface/src/avatar/GrabManager.cpp +++ b/interface/src/avatar/GrabManager.cpp @@ -32,7 +32,7 @@ void GrabManager::simulateGrabs() { if (success && grabbedThing) { glm::vec3 finalPosition = acc.finalizePosition(); glm::quat finalOrientation = acc.finalizeOrientation(); - grabbedThing->setWorldTransform(finalPosition, finalOrientation); + grabbedThing->setTransform(createMatFromQuatAndPos(finalOrientation, finalPosition)); } } }); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 8c340b7fbb..1bbf348a9e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -680,7 +680,6 @@ void Avatar::simulate(float deltaTime, bool inView) { head->setScale(getModelScale()); head->simulate(deltaTime); relayJointDataToChildren(); - updateGrabs(); } else { // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. _skeletonModel->simulate(deltaTime, false); @@ -716,6 +715,11 @@ void Avatar::simulate(float deltaTime, bool inView) { updateAvatarEntities(); } + { + PROFILE_RANGE(simulation, "grabs"); + updateGrabs(); + } + updateFadingStatus(); } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cad00301a6..2c05c8910b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -3301,7 +3301,7 @@ bool EntityItem::isWearable() const { void EntityItem::addGrab(GrabPointer grab) { SpatiallyNestable::addGrab(grab); - if (getDynamic()) { + if (getDynamic() && getParentID().isNull()) { EntityTreePointer entityTree = getTree(); assert(entityTree); EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 9ffbb8fd3d..c100e46ccc 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -481,27 +481,29 @@ void SpatiallyNestable::setWorldTransform(const glm::vec3& position, const glm:: return; } - bool changed = false; bool success = true; Transform parentTransform = getParentTransform(success); - _transformLock.withWriteLock([&] { - Transform myWorldTransform; - Transform::mult(myWorldTransform, parentTransform, _transform); - if (myWorldTransform.getRotation() != orientation) { - changed = true; - myWorldTransform.setRotation(orientation); - } - if (myWorldTransform.getTranslation() != position) { - changed = true; - myWorldTransform.setTranslation(position); - } + if (success) { + bool changed = false; + _transformLock.withWriteLock([&] { + Transform myWorldTransform; + Transform::mult(myWorldTransform, parentTransform, _transform); + if (myWorldTransform.getRotation() != orientation) { + changed = true; + myWorldTransform.setRotation(orientation); + } + if (myWorldTransform.getTranslation() != position) { + changed = true; + myWorldTransform.setTranslation(position); + } + if (changed) { + Transform::inverseMult(_transform, parentTransform, myWorldTransform); + _translationChanged = usecTimestampNow(); + } + }); if (changed) { - Transform::inverseMult(_transform, parentTransform, myWorldTransform); - _translationChanged = usecTimestampNow(); + locationChanged(false); } - }); - if (success && changed) { - locationChanged(false); } } @@ -788,19 +790,21 @@ void SpatiallyNestable::setTransform(const Transform& transform, bool& success) return; } - bool changed = false; Transform parentTransform = getParentTransform(success); - _transformLock.withWriteLock([&] { - Transform beforeTransform = _transform; - Transform::inverseMult(_transform, parentTransform, transform); - if (_transform != beforeTransform) { - changed = true; - _translationChanged = usecTimestampNow(); - _rotationChanged = usecTimestampNow(); + if (success) { + bool changed = false; + _transformLock.withWriteLock([&] { + Transform beforeTransform = _transform; + Transform::inverseMult(_transform, parentTransform, transform); + if (_transform != beforeTransform) { + changed = true; + _translationChanged = usecTimestampNow(); + _rotationChanged = usecTimestampNow(); + } + }); + if (changed) { + locationChanged(); } - }); - if (success && changed) { - locationChanged(); } } diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 7ab824f2c5..2114f2c0b2 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -35,7 +35,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearTabletHighlight.js" ]; -if (Settings.getValue("useTraitsGrab", false)) { +if (Settings.getValue("useTraitsGrab", true)) { CONTOLLER_SCRIPTS.push("controllerModules/nearGrabEntity.js"); CONTOLLER_SCRIPTS.push("controllerModules/farGrabEntity.js"); } else { diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index fbaa61e9c9..e6df09fff9 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -332,17 +332,9 @@ getControllerJointIndex = function (hand) { var now = Date.now(); if (now - getControllerJointIndexCacheTime[hand] > GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME) { if (HMD.isHandControllerAvailable()) { - var controllerJointIndex = -1; - // if (Camera.mode === "first person") { - // controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? - // "_CONTROLLER_RIGHTHAND" : - // "_CONTROLLER_LEFTHAND"); - // } else if (Camera.mode === "third person") { - controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? - "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : - "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); - // } - + var controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CONTROLLER_RIGHTHAND" : + "_CONTROLLER_LEFTHAND"); getControllerJointIndexCacheTime[hand] = now; getControllerJointIndexCache[hand] = controllerJointIndex; return controllerJointIndex; From 613f2902261e622dd707c07e6dbc7d523865fe10 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 14 Dec 2018 14:22:57 -0800 Subject: [PATCH 18/28] don't attempt to grab other's avatar-entities --- .../libraries/controllerDispatcherUtils.js | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index e6df09fff9..d25450ed18 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -5,7 +5,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4, +/* global module, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4, Selection, Uuid, MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true, HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, @@ -31,6 +31,7 @@ disableDispatcherModule:true, getEnabledModuleByName:true, getGrabbableData:true, + isAnothersAvatarEntity:true, entityIsGrabbable:true, entityIsDistanceGrabbable:true, getControllerJointIndexCacheTime:true, @@ -152,7 +153,9 @@ DISPATCHER_PROPERTIES = [ "grab.equippableIndicatorURL", "grab.equippableIndicatorScale", "grab.equippableIndicatorOffset", - "userData" + "userData", + "entityHostType", + "owningAvatarID" ]; // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step @@ -287,10 +290,24 @@ getGrabbableData = function (ggdProps) { return grabbableData; }; +isAnothersAvatarEntity = function (iaaeProps) { + if (iaaeProps.entityHostType !== "avatar") { + return false; + } + if (iaaeProps.owningAvatarID === MyAvatar.sessionUUID) { + return false; + } + if (iaaeProps.owningAvatarID === MyAvatar.SELF_ID) { + return false; + } + return true; +}; + entityIsGrabbable = function (eigProps) { var grabbable = getGrabbableData(eigProps).grabbable; if (!grabbable || eigProps.locked || + isAnothersAvatarEntity(eigProps) || FORBIDDEN_GRAB_TYPES.indexOf(eigProps.type) >= 0) { return false; } From 9bf9e379a3be439c3a06020adeb929aba5e82215 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 14 Dec 2018 15:20:32 -0800 Subject: [PATCH 19/28] fix far-grab initial rotation and entity-method call --- libraries/physics/src/ObjectActionTractor.cpp | 2 +- scripts/system/controllers/controllerModules/farGrabEntity.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp index 87edc858cc..a46aac3f29 100644 --- a/libraries/physics/src/ObjectActionTractor.cpp +++ b/libraries/physics/src/ObjectActionTractor.cpp @@ -72,7 +72,7 @@ bool ObjectActionTractor::getTarget(float deltaTimeStep, glm::quat& rotation, gl angularTimeScale = FLT_MAX; return false; } - rotation = _desiredRotationalTarget * otherWorldOrientation; + rotation = otherWorldOrientation * _desiredRotationalTarget; position = otherWorldOrientation * _desiredPositionalTarget + otherWorldPosition; } else { // we should have an "other" but can't find it, or its collision shape isn't loaded, diff --git a/scripts/system/controllers/controllerModules/farGrabEntity.js b/scripts/system/controllers/controllerModules/farGrabEntity.js index da1f3893dc..dab1aa97af 100644 --- a/scripts/system/controllers/controllerModules/farGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farGrabEntity.js @@ -146,7 +146,7 @@ Script.include("/~/system/libraries/controllers.js"); MyAvatar.setJointRotation(FAR_GRAB_JOINTS[this.hand], { x: 0, y: 0, z: 0, w: 1 }); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(targetProps.id, "startNearGrab", args); + Entities.callEntityMethod(targetProps.id, "startDistanceGrab", args); this.targetEntityID = targetProps.id; From 05a80ae0e4f9536387347526141685ffb660dbd6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 14 Dec 2018 17:07:07 -0800 Subject: [PATCH 20/28] don't bootstrap with a traits-based grab --- libraries/entities/src/EntityItem.cpp | 55 +++++++++++++++++---------- libraries/entities/src/EntityItem.h | 3 ++ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 2c05c8910b..cddf961dbd 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2096,6 +2096,35 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPoin return result; } +void EntityItem::enableNoBootstrap() { + if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { + _flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + } + }); + } +} + +void EntityItem::disableNoBootstrap() { + if (!stillHasGrabActions()) { + _flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; + _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); + } + }); + } +} + + bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action) { assert(action); assert(simulation); @@ -2117,17 +2146,7 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn auto actionType = action->getType(); if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) { - if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { - _flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; - _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar - forEachDescendant([&](SpatiallyNestablePointer child) { - if (child->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); - } - }); - } + enableNoBootstrap(); } } else { qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed"; @@ -2208,16 +2227,8 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi action->setIsMine(false); _objectActions.remove(actionID); - if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) { - _flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING; - _flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar - forEachDescendant([&](SpatiallyNestablePointer child) { - if (child->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING); - } - }); + if (removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) { + disableNoBootstrap(); } else { // NO-OP: we assume SPECIAL_FLAGS_NO_BOOTSTRAPPING bits and collision group are correct // because they should have been set correctly when the action was added @@ -3299,6 +3310,7 @@ bool EntityItem::isWearable() const { } void EntityItem::addGrab(GrabPointer grab) { + enableNoBootstrap(); SpatiallyNestable::addGrab(grab); if (getDynamic() && getParentID().isNull()) { @@ -3357,4 +3369,5 @@ void EntityItem::removeGrab(GrabPointer grab) { } } } + disableNoBootstrap(); } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 998409f6fa..09cd0b5d26 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -670,6 +670,9 @@ protected: bool _simulated { false }; // set by EntitySimulation bool _visuallyReady { true }; + void enableNoBootstrap(); + void disableNoBootstrap(); + bool addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action); bool removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation = nullptr); void deserializeActionsInternal(); From 54a5805b5c84f63613b16ef2f1756f4f27957476 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 18 Dec 2018 10:43:11 -0800 Subject: [PATCH 21/28] add final edit when dropping an avatar-entity. don't allow grabbing of other's avatar entities --- libraries/entities/src/EntityItem.cpp | 8 ++++++++ libraries/entities/src/EntityTree.cpp | 6 +++--- scripts/system/libraries/controllerDispatcherUtils.js | 3 +-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cddf961dbd..8d46af0013 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2465,6 +2465,14 @@ bool EntityItem::shouldSuppressLocationEdits() const { i++; } + i = _grabActions.begin(); + while (i != _grabActions.end()) { + if (i.value()->shouldSuppressLocationEdits()) { + return true; + } + i++; + } + // if any of the ancestors are MyAvatar, suppress return isChildOfMyAvatar(); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 506df95007..c9e855f606 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2969,9 +2969,9 @@ void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, if (success) { moveOperator.addEntityToMoveList(entity, newCube); } - // send an edit packet to update the entity-server about the queryAABox - // unless it is client-only - if (tellServer && packetSender && entity->isDomainEntity()) { + // send an edit packet to update the entity-server about the queryAABox. We do this for domain-hosted + // entities as well as for avatar-entities; the packet-sender will route the update accordingly + if (tellServer && packetSender && (entity->isDomainEntity() || entity->isAvatarEntity())) { quint64 now = usecTimestampNow(); EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index d25450ed18..0b1d17c266 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -154,7 +154,6 @@ DISPATCHER_PROPERTIES = [ "grab.equippableIndicatorScale", "grab.equippableIndicatorOffset", "userData", - "entityHostType", "owningAvatarID" ]; @@ -291,7 +290,7 @@ getGrabbableData = function (ggdProps) { }; isAnothersAvatarEntity = function (iaaeProps) { - if (iaaeProps.entityHostType !== "avatar") { + if (!iaaeProps.avatarEntity) { return false; } if (iaaeProps.owningAvatarID === MyAvatar.sessionUUID) { From 3c0243419b69c1d767564762680aa94566cb0e77 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 18 Dec 2018 11:01:49 -0800 Subject: [PATCH 22/28] trying again on avatar-entity grab releases --- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 1bbf348a9e..50d3c568d9 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -525,6 +525,14 @@ void Avatar::updateGrabs() { // only clear this entry from the _deletedAvatarGrabs if we found the entity. if (success && target) { bool iShouldTellServer = target->getEditSenderID() == sessionID; + + EntityItemPointer entity = std::dynamic_pointer_cast(target); + if (entity && entity->isAvatarEntity() && (entity->getOwningAvatarID() == sessionID || + entity->getOwningAvatarID() == AVATAR_SELF_ID)) { + // this is our own avatar-entity, so we always tell the server about the release + iShouldTellServer = true; + } + target->removeGrab(grab); delItr.remove(); // in case this is the last grab on an entity, we need to shrink the queryAACube and tell the server From bf46960dc344b5bb82f5468f2a3c62b6ac911a33 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 18 Dec 2018 12:03:21 -0800 Subject: [PATCH 23/28] fix timing on startNearGrab entity-method call for cloned entities. Don't allow grabbing of other's equipped or worn entities --- .../controllerModules/nearGrabEntity.js | 1 + .../libraries/controllerDispatcherUtils.js | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/scripts/system/controllers/controllerModules/nearGrabEntity.js b/scripts/system/controllers/controllerModules/nearGrabEntity.js index bb74c59691..60a5781ca4 100644 --- a/scripts/system/controllers/controllerModules/nearGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearGrabEntity.js @@ -185,6 +185,7 @@ Script.include("/~/system/libraries/controllers.js"); var cloneID = cloneEntity(targetProps); if (cloneID !== null) { var cloneProps = Entities.getEntityProperties(cloneID, DISPATCHER_PROPERTIES); + cloneProps.id = cloneID; this.grabbing = true; this.targetEntityID = cloneID; this.startNearGrabEntity(cloneProps); diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 0b1d17c266..e8d15c9b59 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -32,6 +32,7 @@ getEnabledModuleByName:true, getGrabbableData:true, isAnothersAvatarEntity:true, + isAnothersChildEntity:true, entityIsGrabbable:true, entityIsDistanceGrabbable:true, getControllerJointIndexCacheTime:true, @@ -302,11 +303,31 @@ isAnothersAvatarEntity = function (iaaeProps) { return true; }; +isAnothersChildEntity = function (iaceProps) { + while (iaceProps.parentID && iaceProps.parentID !== Uuid.NULL) { + if (Entities.getNestableType(iaceProps.parentID) == "avatar") { + if (iaceProps.parentID == MyAvatar.SELF_ID || iaceProps.parentID == MyAvatar.sessionUUID) { + return false; // not another's, it's mine. + } + return true; + } + // Entities.getNestableType(iaceProps.parentID) == "entity") { + var parentProps = Entities.getEntityProperties(iaceProps.parentID, DISPATCHER_PROPERTIES); + if (!parentProps) { + break; + } + parentProps.id = iaceProps.parentID; + iaceProps = parentProps; + } + return false; +}; + entityIsGrabbable = function (eigProps) { var grabbable = getGrabbableData(eigProps).grabbable; if (!grabbable || eigProps.locked || isAnothersAvatarEntity(eigProps) || + isAnothersChildEntity(eigProps) || FORBIDDEN_GRAB_TYPES.indexOf(eigProps.type) >= 0) { return false; } From 3ccbd0bc7b7f77f45e8cd7c98a78acd05d838eeb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 18 Dec 2018 14:09:20 -0800 Subject: [PATCH 24/28] disable positionand velocity script edits on grabbed entities --- libraries/entities/src/EntityItemProperties.cpp | 12 ++++++++++++ libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityScriptingInterface.cpp | 4 ++++ libraries/shared/src/SpatiallyNestable.cpp | 8 ++++++++ libraries/shared/src/SpatiallyNestable.h | 1 + 5 files changed, 26 insertions(+) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7eb2e1c765..2963187d75 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -3595,6 +3595,18 @@ bool EntityItemProperties::hasTransformOrVelocityChanges() const { || _accelerationChanged; } +void EntityItemProperties::clearTransformOrVelocityChanges() { + _positionChanged = false; + _localPositionChanged = false; + _rotationChanged = false; + _localRotationChanged = false; + _velocityChanged = false; + _localVelocityChanged = false; + _angularVelocityChanged = false; + _localAngularVelocityChanged = false; + _accelerationChanged = false; +} + bool EntityItemProperties::hasMiscPhysicsChanges() const { return _gravityChanged || _dimensionsChanged || _densityChanged || _frictionChanged || _restitutionChanged || _dampingChanged || _angularDampingChanged || _registrationPointChanged || diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index a025288f95..05235f20ef 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -390,6 +390,7 @@ public: void setCreated(QDateTime& v); bool hasTransformOrVelocityChanges() const; + void clearTransformOrVelocityChanges(); bool hasMiscPhysicsChanges() const; bool hasSimulationRestrictedChanges() const; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 956ba12560..cdeaf120ce 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -816,6 +816,10 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& QString previousUserdata; if (entity) { + if (properties.hasTransformOrVelocityChanges() && entity->hasGrabs()) { + // if an entity is grabbed, the grab will override any position changes + properties.clearTransformOrVelocityChanges(); + } if (properties.hasSimulationRestrictedChanges()) { if (_bidOnSimulationOwnership) { // flag for simulation ownership, or upgrade existing ownership priority diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index c100e46ccc..8805205361 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1386,6 +1386,14 @@ void SpatiallyNestable::removeGrab(GrabPointer grab) { }); } +bool SpatiallyNestable::hasGrabs() { + bool result { false }; + _grabsLock.withReadLock([&] { + result = !_grabs.isEmpty(); + }); + return result; +} + QUuid SpatiallyNestable::getEditSenderID() { // if more than one avatar is grabbing something, decide which one should tell the enity-server about it QUuid editSenderID; diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 0c6bada85b..a55f32896b 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -216,6 +216,7 @@ public: virtual void addGrab(GrabPointer grab); virtual void removeGrab(GrabPointer grab); + bool hasGrabs(); virtual QUuid getEditSenderID(); protected: From 306c54c0605baa318ff0c6ab07c509113f5eac5b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Dec 2018 12:28:23 -0800 Subject: [PATCH 25/28] fix code that allows adjusting of own avatar entities while forbidding adjusting those of others --- scripts/system/libraries/controllerDispatcherUtils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index e8d15c9b59..221af07474 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -155,6 +155,7 @@ DISPATCHER_PROPERTIES = [ "grab.equippableIndicatorScale", "grab.equippableIndicatorOffset", "userData", + "avatarEntity", "owningAvatarID" ]; From a7515a4bdef17d3deef123d1f6f9af815700f208 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Dec 2018 15:06:01 -0800 Subject: [PATCH 26/28] include owningAvatarID in entityPropertyFlagsFromScriptValue --- libraries/entities/src/EntityItemProperties.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 2963187d75..1c384ad16c 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2451,6 +2451,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_TOP_MARGIN, TopMargin, topMargin, float); ADD_PROPERTY_TO_MAP(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float); + ADD_PROPERTY_TO_MAP(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid); + // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -4363,4 +4365,4 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT); setCloneDynamic(ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); setCloneAvatarEntity(ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); -} \ No newline at end of file +} From ad1235ce9404ef68cb0abb6b0044bc0bcccf252f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 2 Jan 2019 13:14:18 -0800 Subject: [PATCH 27/28] also bump entity protocol version --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 37a1ec5f03..2cc5804d10 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::MissingTextProperties); + return static_cast(EntityVersion::GrabTraits); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index c353dd6ec7..62549a7562 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -250,7 +250,8 @@ enum class EntityVersion : PacketVersion { CleanupProperties, ImageEntities, GridEntities, - MissingTextProperties + MissingTextProperties, + GrabTraits }; enum class EntityScriptCallMethodVersion : PacketVersion { From b2f25629eb8ed1208c814babdc8a1dd62445ce63 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Jan 2019 08:15:19 -0800 Subject: [PATCH 28/28] don't allow scaling of others' equipped entities --- scripts/system/controllers/controllerModules/scaleEntity.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/scaleEntity.js b/scripts/system/controllers/controllerModules/scaleEntity.js index 51ed0ff94b..50b6c5b853 100644 --- a/scripts/system/controllers/controllerModules/scaleEntity.js +++ b/scripts/system/controllers/controllerModules/scaleEntity.js @@ -7,7 +7,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Vec3, MyAvatar, Entities, RIGHT_HAND */ +/* global Script, Vec3, MyAvatar, Entities, RIGHT_HAND, entityIsGrabbable */ (function() { var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); @@ -62,6 +62,9 @@ var otherHandTargetProps = otherModule.getTargetProps(controllerData); if (thisHandTargetProps && otherHandTargetProps) { if (thisHandTargetProps.id === otherHandTargetProps.id) { + if (!entityIsGrabbable(thisHandTargetProps)) { + return dispatcherUtils.makeRunningValues(false, [], []); + } this.grabbedThingID = thisHandTargetProps.id; this.scalingStartDistance = Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position,