diff --git a/assignment-client/src/entities/AssignmentParentFinder.cpp b/assignment-client/src/entities/AssignmentParentFinder.cpp index 5fe4742b90..cc5c7557dc 100644 --- a/assignment-client/src/entities/AssignmentParentFinder.cpp +++ b/assignment-client/src/entities/AssignmentParentFinder.cpp @@ -11,9 +11,14 @@ #include "AssignmentParentFinder.h" -SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID) const { +SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success) const { SpatiallyNestableWeakPointer parent; // search entities parent = _tree->findEntityByEntityItemID(parentID); + if (parent.expired()) { + success = false; + } else { + success = true; + } return parent; } diff --git a/assignment-client/src/entities/AssignmentParentFinder.h b/assignment-client/src/entities/AssignmentParentFinder.h index 99fa58ffed..9a776bc7dd 100644 --- a/assignment-client/src/entities/AssignmentParentFinder.h +++ b/assignment-client/src/entities/AssignmentParentFinder.h @@ -25,7 +25,7 @@ class AssignmentParentFinder : public SpatialParentFinder { public: AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { } virtual ~AssignmentParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID) const; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const; protected: EntityTreePointer _tree; diff --git a/examples/actionInspector.js b/examples/debugging/actionInspector.js similarity index 98% rename from examples/actionInspector.js rename to examples/debugging/actionInspector.js index 934120ddf6..303dcbebbd 100644 --- a/examples/actionInspector.js +++ b/examples/debugging/actionInspector.js @@ -1,6 +1,6 @@ // // actionInspector.js -// examples +// examples/debugging/ // // Created by Seth Alves on 2015-9-30. // Copyright 2015 High Fidelity, Inc. @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("libraries/utils.js"); +Script.include("../libraries/utils.js"); var INSPECT_RADIUS = 10; diff --git a/examples/grabInspector.js b/examples/debugging/grabInspector.js similarity index 98% rename from examples/grabInspector.js rename to examples/debugging/grabInspector.js index 8a027f819a..aeea2c20b4 100644 --- a/examples/grabInspector.js +++ b/examples/debugging/grabInspector.js @@ -1,6 +1,6 @@ // // grabInspector.js -// examples +// examples/debugging/ // // Created by Seth Alves on 2015-9-30. // Copyright 2015 High Fidelity, Inc. @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("libraries/utils.js"); +Script.include("../libraries/utils.js"); var INSPECT_RADIUS = 10; var overlays = {}; diff --git a/examples/debugging/queryAACubeInspector.js b/examples/debugging/queryAACubeInspector.js new file mode 100644 index 0000000000..2735617bc1 --- /dev/null +++ b/examples/debugging/queryAACubeInspector.js @@ -0,0 +1,58 @@ +// +// grabInspector.js +// examples/debugging/ +// +// Created by Seth Alves on 2015-12-19. +// Copyright 2015 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 +// + +// This script draws an overlay cube around nearby entities to show their queryAABoxes. + + +Script.include("../libraries/utils.js"); + +var INSPECT_RADIUS = 10; +var overlays = {}; + +function updateOverlay(entityID, queryAACube) { + var cubeCenter = {x: queryAACube.x + queryAACube.scale / 2.0, + y: queryAACube.y + queryAACube.scale / 2.0, + z: queryAACube.z + queryAACube.scale / 2.0}; + + if (entityID in overlays) { + var overlay = overlays[entityID]; + Overlays.editOverlay(overlay, { + position: cubeCenter, + size: queryAACube.scale + }); + } else { + overlays[entityID] = Overlays.addOverlay("cube", { + position: cubeCenter, + size: queryAACube.scale, + color: { red: 0, green: 0, blue: 255}, + alpha: 1, + // borderSize: ..., + solid: false + }); + } +} + +Script.setInterval(function() { + var nearbyEntities = Entities.findEntities(MyAvatar.position, INSPECT_RADIUS); + for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) { + var entityID = nearbyEntities[entityIndex]; + var queryAACube = Entities.getEntityProperties(entityID, ["queryAACube"]).queryAACube; + updateOverlay(entityID, queryAACube); + } +}, 100); + +function cleanup() { + for (var entityID in overlays) { + Overlays.deleteOverlay(overlays[entityID]); + } +} + +Script.scriptEnding.connect(cleanup); diff --git a/interface/resources/controllers/standard_navigation.json b/interface/resources/controllers/standard_navigation.json index c3b30e8607..208970f030 100644 --- a/interface/resources/controllers/standard_navigation.json +++ b/interface/resources/controllers/standard_navigation.json @@ -11,7 +11,7 @@ { "from": "Standard.DR", "to": "Actions.UiNavLateral" }, { "from": "Standard.LB", "to": "Actions.UiNavGroup","filters": "invert" }, { "from": "Standard.RB", "to": "Actions.UiNavGroup" }, - { "from": [ "Standard.A", "Standard.X", "Standard.RT", "Standard.LT" ], "to": "Actions.UiNavSelect" }, + { "from": [ "Standard.A", "Standard.X" ], "to": "Actions.UiNavSelect" }, { "from": [ "Standard.B", "Standard.Y", "Standard.RightPrimaryThumb", "Standard.LeftPrimaryThumb" ], "to": "Actions.UiNavBack" }, { "from": [ "Standard.RT", "Standard.LT" ], diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 3852ea3819..d84ea306b8 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -169,8 +169,10 @@ DialogContainer { switch (event.key) { case Qt.Key_Escape: case Qt.Key_Back: - enabled = false - event.accepted = true + if (enabled) { + enabled = false + event.accepted = true + } break case Qt.Key_Enter: case Qt.Key_Return: diff --git a/interface/resources/qml/ErrorDialog.qml b/interface/resources/qml/ErrorDialog.qml index 76d9111d97..4ace671c52 100644 --- a/interface/resources/qml/ErrorDialog.qml +++ b/interface/resources/qml/ErrorDialog.qml @@ -96,6 +96,9 @@ DialogContainer { } Keys.onPressed: { + if (!enabled) { + return + } switch (event.key) { case Qt.Key_Escape: case Qt.Key_Back: diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 29264fa608..3ce5251771 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -343,8 +343,10 @@ DialogContainer { switch (event.key) { case Qt.Key_Escape: case Qt.Key_Back: - enabled = false - event.accepted = true + if (enabled) { + enabled = false + event.accepted = true + } break case Qt.Key_Enter: case Qt.Key_Return: diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index e8b01df9d0..73fb135ac4 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -300,6 +300,10 @@ VrDialog { Keys.onPressed: { + if (!enabled) { + return + } + if (event.modifiers === Qt.ControlModifier) switch (event.key) { case Qt.Key_A: diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index 951aa24471..2d8bf08dc5 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -10,7 +10,6 @@ import "styles" VrDialog { id: root - objectName: "topLevelWindow" HifiConstants { id: hifi } title: "QmlWindow" resizable: true @@ -43,10 +42,6 @@ VrDialog { focus: true property var dialog: root - onLoaded: { - forceActiveFocus() - } - Keys.onPressed: { console.log("QmlWindow pageLoader keypress") } diff --git a/interface/resources/qml/Root.qml b/interface/resources/qml/Root.qml index 1b0f09558f..14799f78c0 100644 --- a/interface/resources/qml/Root.qml +++ b/interface/resources/qml/Root.qml @@ -1,11 +1,16 @@ import Hifi 1.0 -import QtQuick 2.3 +import QtQuick 2.5 +import QtQuick.Controls 1.4 // This is our primary 'window' object to which all dialogs and controls will // be childed. Root { id: root + objectName: "desktopRoot" anchors.fill: parent + property var rootMenu: Menu { + objectName: "rootMenu" + } onParentChanged: { forceActiveFocus(); diff --git a/interface/resources/qml/RootMenu.qml b/interface/resources/qml/RootMenu.qml deleted file mode 100644 index b8c81a6589..0000000000 --- a/interface/resources/qml/RootMenu.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 1.3 - -Item { - Menu { - id: root - objectName: "rootMenu" - } -} diff --git a/interface/resources/qml/controls/DialogBase.qml b/interface/resources/qml/controls/DialogBase.qml index a6616fc731..9fb2a47907 100644 --- a/interface/resources/qml/controls/DialogBase.qml +++ b/interface/resources/qml/controls/DialogBase.qml @@ -5,6 +5,7 @@ import "../styles" Item { id: root + objectName: "topLevelWindow" HifiConstants { id: hifi } implicitHeight: contentImplicitHeight + titleBorder.height + hifi.styles.borderWidth implicitWidth: contentImplicitWidth + hifi.styles.borderWidth * 2 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e1e531a354..a57f2c2260 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1177,7 +1177,6 @@ void Application::initializeUi() { offscreenUi->setProxyWindow(_window->windowHandle()); offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); offscreenUi->load("Root.qml"); - offscreenUi->load("RootMenu.qml"); // FIXME either expose so that dialogs can set this themselves or // do better detection in the offscreen UI of what has focus offscreenUi->setNavigationFocused(false); diff --git a/interface/src/InterfaceParentFinder.cpp b/interface/src/InterfaceParentFinder.cpp index 112bae5bb8..de4b0ce38c 100644 --- a/interface/src/InterfaceParentFinder.cpp +++ b/interface/src/InterfaceParentFinder.cpp @@ -16,22 +16,31 @@ #include "InterfaceParentFinder.h" -SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID) const { +SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& success) const { SpatiallyNestableWeakPointer parent; if (parentID.isNull()) { + success = true; return parent; } // search entities EntityTreeRenderer* treeRenderer = qApp->getEntities(); - EntityTreePointer tree = treeRenderer->getTree(); - parent = tree->findEntityByEntityItemID(parentID); + EntityTreePointer tree = treeRenderer ? treeRenderer->getTree() : nullptr; + parent = tree ? tree->findEntityByEntityItemID(parentID) : nullptr; if (!parent.expired()) { + success = true; return parent; } // search avatars QSharedPointer avatarManager = DependencyManager::get(); - return avatarManager->getAvatarBySessionID(parentID); + parent = avatarManager->getAvatarBySessionID(parentID); + if (!parent.expired()) { + success = true; + return parent; + } + + success = false; + return parent; } diff --git a/interface/src/InterfaceParentFinder.h b/interface/src/InterfaceParentFinder.h index c8e8d4ed9f..db579c2144 100644 --- a/interface/src/InterfaceParentFinder.h +++ b/interface/src/InterfaceParentFinder.h @@ -21,7 +21,7 @@ class InterfaceParentFinder : public SpatialParentFinder { public: InterfaceParentFinder() { } virtual ~InterfaceParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID) const; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const; }; #endif // hifi_InterfaceParentFinder_h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index d9e2159df7..d0eaea8f23 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1021,6 +1021,9 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) { if (_moving && _motionState) { _motionState->addDirtyFlags(Simulation::DIRTY_POSITION); } + if (_moving || _hasNewJointRotations || _hasNewJointTranslations) { + locationChanged(); + } endUpdate(); return bytesRead; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 6d4477e93f..8f89027d2f 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -159,7 +159,9 @@ public: AvatarMotionState* getMotionState() { return _motionState; } + using SpatiallyNestable::setPosition; virtual void setPosition(const glm::vec3& position) override; + using SpatiallyNestable::setOrientation; virtual void setOrientation(const glm::quat& orientation) override; public slots: diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d5e756d6db..1848845a07 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -90,10 +90,18 @@ const QUrl& AvatarData::defaultFullAvatarModelUrl() { // There are a number of possible strategies for this set of tools through endRender, below. void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) { avatarLock.lock(); - Transform trans = getTransform(); + bool success; + Transform trans = getTransform(success); + if (!success) { + qDebug() << "Warning -- AvatarData::nextAttitude failed"; + return; + } trans.setTranslation(position); trans.setRotation(orientation); - SpatiallyNestable::setTransform(trans); + SpatiallyNestable::setTransform(trans, success); + if (!success) { + qDebug() << "Warning -- AvatarData::nextAttitude failed"; + } avatarLock.unlock(); updateAttitude(); } @@ -208,8 +216,9 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { setAtBit(bitItems, IS_EYE_TRACKER_CONNECTED); } // referential state - SpatiallyNestablePointer parent = getParentPointer(); - if (parent) { + bool success; + SpatiallyNestablePointer parent = getParentPointer(success); + if (parent && success) { setAtBit(bitItems, HAS_REFERENTIAL); } *destinationBuffer++ = bitItems; @@ -1446,7 +1455,11 @@ QJsonObject AvatarData::toJson() const { } auto recordingBasis = getRecordingBasis(); - Transform avatarTransform = getTransform(); + bool success; + Transform avatarTransform = getTransform(success); + if (!success) { + qDebug() << "Warning -- AvatarData::toJson couldn't get avatar transform"; + } avatarTransform.setScale(getTargetScale()); if (recordingBasis) { root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); @@ -1635,3 +1648,13 @@ void AvatarData::setPosition(const glm::vec3& position) { void AvatarData::setOrientation(const glm::quat& orientation) { SpatiallyNestable::setOrientation(orientation); } + +glm::quat AvatarData::getAbsoluteJointRotationInObjectFrame(int index) const { + assert(false); + return glm::quat(); +} + +glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const { + assert(false); + return glm::vec3(); +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index bb35a6ea29..c0fe62c7c4 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -201,7 +201,9 @@ public: float getBodyRoll() const; void setBodyRoll(float bodyRoll); + using SpatiallyNestable::setPosition; virtual void setPosition(const glm::vec3& position) override; + using SpatiallyNestable::setOrientation; virtual void setOrientation(const glm::quat& orientation) override; void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. @@ -354,6 +356,9 @@ public slots: void setJointMappingsFromNetworkReply(); void setSessionUUID(const QUuid& sessionUUID) { setID(sessionUUID); } + virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; + virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; + protected: glm::vec3 _handPosition; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index f517c49b00..0bb0de1ce0 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -767,7 +767,12 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT // Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2) const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f; - const float stretchFactor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2); + bool success; + auto minAACube = entity->getMinimumAACube(success); + if (!success) { + return; + } + const float stretchFactor = log(1.0f + (minAACube.getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2); AudioInjector::playSound(collisionSoundURL, volume, stretchFactor, position); } diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index a1cdbbaf51..4de363016b 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -56,14 +56,20 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { gpu::Batch& batch = *args->_batch; glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha()); + bool success; + auto transToCenter = getTransformToCenter(success); + if (!success) { + return; + } + if (_procedural->ready()) { - batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well + batch.setModelTransform(transToCenter); // we want to include the scale as well _procedural->prepare(batch, getPosition(), getDimensions()); auto color = _procedural->getColor(cubeColor); batch._glColor4f(color.r, color.g, color.b, color.a); DependencyManager::get()->renderCube(batch); } else { - DependencyManager::get()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor); + DependencyManager::get()->renderSolidCubeInstance(batch, transToCenter, cubeColor); } static const auto triCount = DependencyManager::get()->getCubeTriangleCount(); args->_details._trianglesRendered += (int)triCount; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 5504268dce..25f4052c75 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -28,7 +28,12 @@ namespace render { template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload) { if (payload && payload->entity) { - return payload->entity->getAABox(); + bool success; + auto result = payload->entity->getAABox(success); + if (!success) { + return render::Item::Bound(); + } + return result; } return render::Item::Bound(); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 9127e4ca22..10f6df84a4 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -196,7 +196,12 @@ namespace render { template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) { if (payload && payload->entity) { - return payload->entity->getAABox(); + bool success; + auto result = payload->entity->getAABox(success); + if (!success) { + return render::Item::Bound(); + } + return result; } return render::Item::Bound(); } @@ -338,9 +343,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } else { static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; - auto shapeTransform = getTransformToCenter(); - batch.setModelTransform(Transform()); // we want to include the scale as well - DependencyManager::get()->renderWireCubeInstance(batch, shapeTransform, greenColor); + bool success; + auto shapeTransform = getTransformToCenter(success); + if (success) { + batch.setModelTransform(Transform()); // we want to include the scale as well + DependencyManager::get()->renderWireCubeInstance(batch, shapeTransform, greenColor); + } } } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 567d2bc24a..115eba0a05 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -235,10 +235,15 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { for (auto& particle : _particles) { particlePrimitives->emplace_back(particle.position, glm::vec2(particle.lifetime, particle.seed)); } - - auto bounds = getAABox(); - auto position = getPosition(); - auto rotation = getRotation(); + + bool successb, successp, successr; + auto bounds = getAABox(successb); + auto position = getPosition(successp); + auto rotation = getOrientation(successr); + bool success = successb && successp && successr; + if (!success) { + return; + } Transform transform; if (!getEmitterShouldTrail()) { transform.setTranslation(position); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 25530fe41a..1048f594f4 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -142,9 +142,11 @@ glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const { glm::mat4 RenderablePolyVoxEntityItem::voxelToLocalMatrix() const { glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units - glm::vec3 center = getCenterPosition(); - glm::vec3 position = getPosition(); + bool success; // TODO -- Does this actually have to happen in world space? + glm::vec3 center = getCenterPosition(success); + glm::vec3 position = getPosition(success); glm::vec3 positionToCenter = center - position; + positionToCenter -= getDimensions() * Vectors::HALF - getSurfacePositionAdjustment(); glm::mat4 centerToCorner = glm::translate(glm::mat4(), positionToCenter); glm::mat4 scaled = glm::scale(centerToCorner, scale); @@ -581,7 +583,12 @@ namespace render { template <> const Item::Bound payloadGetBound(const PolyVoxPayload::Pointer& payload) { if (payload && payload->_owner) { auto polyVoxEntity = std::dynamic_pointer_cast(payload->_owner); - return polyVoxEntity->getAABox(); + bool success; + auto result = polyVoxEntity->getAABox(success); + if (!success) { + return render::Item::Bound(); + } + return result; } return render::Item::Bound(); } diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp index 6c72dc488c..9bed5b3bb0 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp @@ -59,7 +59,11 @@ void RenderableSphereEntityItem::render(RenderArgs* args) { gpu::Batch& batch = *args->_batch; glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha()); - Transform modelTransform = getTransformToCenter(); + bool success; + Transform modelTransform = getTransformToCenter(success); + if (!success) { + return; + } modelTransform.postScale(SPHERE_ENTITY_SCALE); if (_procedural->ready()) { batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index bdf3b5b97c..d88b4d5f0a 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -44,8 +44,12 @@ void RenderableTextEntityItem::render(RenderArgs* args) { // Batch render calls Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - - Transform transformToTopLeft = getTransformToCenter(); + + bool success; + Transform transformToTopLeft = getTransformToCenter(success); + if (!success) { + return; + } if (getFaceCamera()) { //rotate about vertical to face the camera glm::vec3 dPosition = args->_viewFrustum->getPosition() - getPosition(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 44a0740a4d..8aae853f5e 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -185,7 +185,11 @@ void RenderableWebEntityItem::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransformToCenter()); + bool success; + batch.setModelTransform(getTransformToCenter(success)); + if (!success) { + return; + } bool textured = false, culled = false, emissive = false; if (_texture) { batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index ca16b3aca1..eb01bd551e 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -135,7 +135,11 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { gpu::Batch& batch = *args->_batch; batch.setModelTransform(Transform()); - auto shapeTransform = getTransformToCenter(); + bool success; + auto shapeTransform = getTransformToCenter(success); + if (!success) { + break; + } auto deferredLightingEffect = DependencyManager::get(); if (getShapeType() == SHAPE_TYPE_SPHERE) { shapeTransform.postScale(SPHERE_ENTITY_SCALE); @@ -190,7 +194,12 @@ namespace render { template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) { if (payload && payload->entity) { - return payload->entity->getAABox(); + bool success; + auto result = payload->entity->getAABox(success); + if (!success) { + return render::Item::Bound(); + } + return result; } return render::Item::Bound(); } diff --git a/libraries/entities/src/AddEntityOperator.cpp b/libraries/entities/src/AddEntityOperator.cpp index 2bfbbe2fd9..1c39b40495 100644 --- a/libraries/entities/src/AddEntityOperator.cpp +++ b/libraries/entities/src/AddEntityOperator.cpp @@ -25,7 +25,13 @@ AddEntityOperator::AddEntityOperator(EntityTreePointer tree, EntityItemPointer n // caller must have verified existence of newEntity assert(_newEntity); - _newEntityBox = _newEntity->getMaximumAACube().clamp((float)(-HALF_TREE_SCALE), (float)HALF_TREE_SCALE); + bool success; + auto queryCube = _newEntity->getQueryAACube(success); + if (!success) { + _newEntity->markAncestorMissing(true); + } + + _newEntityBox = queryCube.clamp((float)(-HALF_TREE_SCALE), (float)HALF_TREE_SCALE); } bool AddEntityOperator::preRecursion(OctreeElementPointer element) { diff --git a/libraries/entities/src/BoundingBoxRelatedProperties.cpp b/libraries/entities/src/BoundingBoxRelatedProperties.cpp deleted file mode 100644 index e9ee302300..0000000000 --- a/libraries/entities/src/BoundingBoxRelatedProperties.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// -// BoundingBoxRelatedProperties.cpp -// libraries/entities/src -// -// Created by Seth Alves on 2015-9-24 -// Copyright 2013 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 "EntityItemProperties.h" -#include "BoundingBoxRelatedProperties.h" -#include "EntityTree.h" - -BoundingBoxRelatedProperties::BoundingBoxRelatedProperties(EntityItemPointer entity) : - position(entity->getPosition()), - rotation(entity->getRotation()), - registrationPoint(entity->getRegistrationPoint()), - dimensions(entity->getDimensions()), - parentID(entity->getParentID()) { -} - -BoundingBoxRelatedProperties::BoundingBoxRelatedProperties(EntityItemPointer entity, - const EntityItemProperties& propertiesWithUpdates) : - BoundingBoxRelatedProperties(entity) { - - if (propertiesWithUpdates.parentIDChanged()) { - parentID = propertiesWithUpdates.getParentID(); - } - - bool parentFound = false; - if (parentID != UNKNOWN_ENTITY_ID) { - EntityTreePointer tree = entity->getTree(); - EntityItemPointer parentZone = tree->findEntityByID(parentID); - if (parentZone) { - parentFound = true; - glm::vec3 localPosition = propertiesWithUpdates.containsPositionChange() ? - propertiesWithUpdates.getPosition() : - entity->getLocalPosition(); - - glm::quat localRotation = propertiesWithUpdates.rotationChanged() ? - propertiesWithUpdates.getRotation() : - entity->getLocalOrientation(); - - const Transform parentTransform = parentZone->getTransformToCenter(); - Transform parentDescaled(parentTransform.getRotation(), glm::vec3(1.0f), parentTransform.getTranslation()); - - Transform localTransform(localRotation, glm::vec3(1.0f), localPosition); - Transform result; - Transform::mult(result, parentDescaled, localTransform); - position = result.getTranslation(); - rotation = result.getRotation(); - } - } - - if (!parentFound) { - if (propertiesWithUpdates.containsPositionChange()) { - position = propertiesWithUpdates.getPosition(); - } - if (propertiesWithUpdates.rotationChanged()) { - rotation = propertiesWithUpdates.getRotation(); - } - } - - if (propertiesWithUpdates.registrationPointChanged()) { - registrationPoint = propertiesWithUpdates.getRegistrationPoint(); - } - - if (propertiesWithUpdates.dimensionsChanged()) { - dimensions = propertiesWithUpdates.getDimensions(); - } -} - -AACube BoundingBoxRelatedProperties::getMaximumAACube() const { - // see EntityItem::getMaximumAACube for comments which explain the following. - glm::vec3 scaledRegistrationPoint = (dimensions * registrationPoint); - glm::vec3 registrationRemainder = (dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - registrationPoint)); - glm::vec3 furthestExtentFromRegistration = glm::max(scaledRegistrationPoint, registrationRemainder); - float radius = glm::length(furthestExtentFromRegistration); - glm::vec3 minimumCorner = position - glm::vec3(radius, radius, radius); - return AACube(minimumCorner, radius * 2.0f); -} diff --git a/libraries/entities/src/BoundingBoxRelatedProperties.h b/libraries/entities/src/BoundingBoxRelatedProperties.h deleted file mode 100644 index 811c885fd2..0000000000 --- a/libraries/entities/src/BoundingBoxRelatedProperties.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// BoundingBoxRelatedProperties.h -// libraries/entities/src -// -// Created by Seth Alves on 2015-9-24 -// Copyright 2013 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 "EntityItem.h" - -#ifndef hifi_BoundingBoxRelatedProperties_h -#define hifi_BoundingBoxRelatedProperties_h - -class BoundingBoxRelatedProperties { - public: - BoundingBoxRelatedProperties(EntityItemPointer entity); - BoundingBoxRelatedProperties(EntityItemPointer entity, const EntityItemProperties& propertiesWithUpdates); - AACube getMaximumAACube() const; - - glm::vec3 position; - glm::quat rotation; - glm::vec3 registrationPoint; - glm::vec3 dimensions; - EntityItemID parentID; -}; - -#endif // hifi_BoundingBoxRelatedProperties_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 13d5c2836f..2f15fa7293 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -133,6 +133,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_ACTION_DATA; requestedProperties += PROP_PARENT_ID; requestedProperties += PROP_PARENT_JOINT_INDEX; + requestedProperties += PROP_QUERY_AA_CUBE; return requestedProperties; } @@ -269,6 +270,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getActionData()); APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, getParentID()); APPEND_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, getParentJointIndex()); + APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube()); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -693,6 +695,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, setParentID); READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex); + READ_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, AACube, setQueryAACube); + bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged); @@ -1047,6 +1051,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getActionData); COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentID, getParentID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentJointIndex, getParentJointIndex); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube); properties._defaultSettings = false; @@ -1111,6 +1116,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, setParentID); SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube); if (somethingChanged) { uint64_t now = usecTimestampNow(); @@ -1152,8 +1158,8 @@ void EntityItem::recordCreationTime() { _lastSimulated = now; } -const Transform EntityItem::getTransformToCenter() const { - Transform result = getTransform(); +const Transform EntityItem::getTransformToCenter(bool& success) const { + Transform result = getTransform(success); if (getRegistrationPoint() != ENTITY_ITEM_HALF_VEC3) { // If it is not already centered, translate to center result.postTranslate(ENTITY_ITEM_HALF_VEC3 - getRegistrationPoint()); // Position to center } @@ -1171,28 +1177,31 @@ void EntityItem::setDimensions(const glm::vec3& value) { /// The maximum bounding cube for the entity, independent of it's rotation. /// This accounts for the registration point (upon which rotation occurs around). /// -const AACube& EntityItem::getMaximumAACube() const { +AACube EntityItem::getMaximumAACube(bool& success) const { if (_recalcMaxAACube) { // * we know that the position is the center of rotation - glm::vec3 centerOfRotation = getPosition(); // also where _registration point is + glm::vec3 centerOfRotation = getPosition(success); // also where _registration point is + if (success) { + // * we know that the registration point is the center of rotation + // * we can calculate the length of the furthest extent from the registration point + // as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint) + glm::vec3 registrationPoint = (getDimensions() * getRegistrationPoint()); + glm::vec3 registrationRemainder = (getDimensions() * (glm::vec3(1.0f, 1.0f, 1.0f) - getRegistrationPoint())); + glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder); - // * we know that the registration point is the center of rotation - // * we can calculate the length of the furthest extent from the registration point - // as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint) - glm::vec3 registrationPoint = (getDimensions() * getRegistrationPoint()); - glm::vec3 registrationRemainder = (getDimensions() * (glm::vec3(1.0f, 1.0f, 1.0f) - getRegistrationPoint())); - glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder); + // * we know that if you rotate in any direction you would create a sphere + // that has a radius of the length of furthest extent from registration point + float radius = glm::length(furthestExtentFromRegistration); - // * we know that if you rotate in any direction you would create a sphere - // that has a radius of the length of furthest extent from registration point - float radius = glm::length(furthestExtentFromRegistration); + // * we know that the minimum bounding cube of this maximum possible sphere is + // (center - radius) to (center + radius) + glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius); - // * we know that the minimum bounding cube of this maximum possible sphere is - // (center - radius) to (center + radius) - glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius); - - _maxAACube = AACube(minimumCorner, radius * 2.0f); - _recalcMaxAACube = false; + _maxAACube = AACube(minimumCorner, radius * 2.0f); + _recalcMaxAACube = false; + } + } else { + success = true; } return _maxAACube; } @@ -1200,7 +1209,7 @@ const AACube& EntityItem::getMaximumAACube() const { /// The minimum bounding cube for the entity accounting for it's rotation. /// This accounts for the registration point (upon which rotation occurs around). /// -const AACube& EntityItem::getMinimumAACube() const { +AACube EntityItem::getMinimumAACube(bool& success) const { if (_recalcMinAACube) { // _position represents the position of the registration point. glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint; @@ -1208,25 +1217,30 @@ const AACube& EntityItem::getMinimumAACube() const { glm::vec3 unrotatedMinRelativeToEntity = - (getDimensions() * getRegistrationPoint()); glm::vec3 unrotatedMaxRelativeToEntity = getDimensions() * registrationRemainder; Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity }; - Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation()); + Extents rotatedExtentsRelativeToRegistrationPoint = + unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation()); // shift the extents to be relative to the position/registration point - rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition()); + rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition(success)); - // the cube that best encompasses extents is... - AABox box(rotatedExtentsRelativeToRegistrationPoint); - glm::vec3 centerOfBox = box.calcCenter(); - float longestSide = box.getLargestDimension(); - float halfLongestSide = longestSide / 2.0f; - glm::vec3 cornerOfCube = centerOfBox - glm::vec3(halfLongestSide, halfLongestSide, halfLongestSide); + if (success) { + // the cube that best encompasses extents is... + AABox box(rotatedExtentsRelativeToRegistrationPoint); + glm::vec3 centerOfBox = box.calcCenter(); + float longestSide = box.getLargestDimension(); + float halfLongestSide = longestSide / 2.0f; + glm::vec3 cornerOfCube = centerOfBox - glm::vec3(halfLongestSide, halfLongestSide, halfLongestSide); - _minAACube = AACube(cornerOfCube, longestSide); - _recalcMinAACube = false; + _minAACube = AACube(cornerOfCube, longestSide); + _recalcMinAACube = false; + } + } else { + success = true; } return _minAACube; } -const AABox& EntityItem::getAABox() const { +AABox EntityItem::getAABox(bool& success) const { if (_recalcAABox) { // _position represents the position of the registration point. glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint; @@ -1234,17 +1248,37 @@ const AABox& EntityItem::getAABox() const { glm::vec3 unrotatedMinRelativeToEntity = - (getDimensions() * _registrationPoint); glm::vec3 unrotatedMaxRelativeToEntity = getDimensions() * registrationRemainder; Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity }; - Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation()); + Extents rotatedExtentsRelativeToRegistrationPoint = + unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation()); // shift the extents to be relative to the position/registration point - rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition()); + rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition(success)); - _cachedAABox = AABox(rotatedExtentsRelativeToRegistrationPoint); - _recalcAABox = false; + if (success) { + _cachedAABox = AABox(rotatedExtentsRelativeToRegistrationPoint); + _recalcAABox = false; + } + } else { + success = true; } return _cachedAABox; } +AACube EntityItem::getQueryAACube(bool& success) const { + AACube result = SpatiallyNestable::getQueryAACube(success); + if (success) { + return result; + } + // this is for when we've loaded an older json file that didn't have queryAACube properties. + result = getMaximumAACube(success); + if (success) { + _queryAACube = result; + _queryAACubeSet = true; + } + return result; +} + + // NOTE: This should only be used in cases of old bitstreams which only contain radius data // 0,0,0 --> maxDimension,maxDimension,maxDimension // ... has a corner to corner distance of glm::length(maxDimension,maxDimension,maxDimension) @@ -1274,7 +1308,9 @@ float EntityItem::getRadius() const { bool EntityItem::contains(const glm::vec3& point) const { if (getShapeType() == SHAPE_TYPE_COMPOUND) { - return getAABox().contains(point); + bool success; + bool result = getAABox(success).contains(point); + return result && success; } else { ShapeInfo info; info.setParams(getShapeType(), glm::vec3(0.5f)); @@ -1817,6 +1853,14 @@ QList EntityItem::getActionsOfType(EntityActionType typeToG return result; } +glm::quat EntityItem::getAbsoluteJointRotationInObjectFrame(int index) const { + return glm::quat(); +} + +glm::vec3 EntityItem::getAbsoluteJointTranslationInObjectFrame(int index) const { + return glm::vec3(0.0f); +} + void EntityItem::locationChanged() { requiresRecalcBoxes(); SpatiallyNestable::locationChanged(); // tell all the children, also diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 244198f7b8..06434a3ea4 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -161,11 +161,10 @@ public: // attributes applicable to all entity types EntityTypes::EntityType getType() const { return _type; } - inline glm::vec3 getCenterPosition() const { return getTransformToCenter().getTranslation(); } + inline glm::vec3 getCenterPosition(bool& success) const { return getTransformToCenter(success).getTranslation(); } void setCenterPosition(const glm::vec3& position); - const Transform getTransformToCenter() const; - void setTranformToCenter(const Transform& transform); + const Transform getTransformToCenter(bool& success) const; inline void requiresRecalcBoxes() { _recalcAABox = true; _recalcMinAACube = true; _recalcMaxAACube = true; } @@ -232,9 +231,12 @@ public: quint64 getExpiry() const; // position, size, and bounds related helpers - const AACube& getMaximumAACube() const; - const AACube& getMinimumAACube() const; - const AABox& getAABox() const; /// axis aligned bounding box in world-frame (meters) + virtual AACube getMaximumAACube(bool& success) const override; + AACube getMinimumAACube(bool& success) const; + AABox getAABox(bool& success) const; /// axis aligned bounding box in world-frame (meters) + + using SpatiallyNestable::getQueryAACube; + virtual AACube getQueryAACube(bool& success) const override; const QString& getScript() const { return _script; } void setScript(const QString& value) { _script = value; } @@ -379,8 +381,8 @@ public: QList getActionsOfType(EntityActionType typeToGet); // these are in the frame of this object - virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); } - virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override { return glm::vec3(0.0f); } + virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; + virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; virtual void loader() {} // called indirectly when urls for geometry are updated diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 3a2fdf55d4..e069e53244 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -262,6 +262,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_Z_P_NEIGHBOR_ID, zPNeighborID); CHECK_PROPERTY_CHANGE(PROP_PARENT_ID, parentID); CHECK_PROPERTY_CHANGE(PROP_PARENT_JOINT_INDEX, parentJointIndex); + CHECK_PROPERTY_CHANGE(PROP_QUERY_AA_CUBE, queryAACube); changedProperties += _animation.getChangedProperties(); changedProperties += _keyLight.getChangedProperties(); @@ -473,10 +474,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_ID, parentID); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_JOINT_INDEX, parentJointIndex); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_QUERY_AA_CUBE, queryAACube); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_QUERY_AA_CUBE, queryAACube); + // FIXME - I don't think these properties are supported any more //COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel); //COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha); @@ -601,6 +605,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(parentID, QUuid, setParentID); COPY_PROPERTY_FROM_QSCRIPTVALUE(parentJointIndex, quint16, setParentJointIndex); + COPY_PROPERTY_FROM_QSCRIPTVALUE(queryAACube, AACube, setQueryAACube); COPY_PROPERTY_FROM_QSCRIPTVALUE(localPosition, glmVec3, setLocalPosition); COPY_PROPERTY_FROM_QSCRIPTVALUE(localRotation, glmQuat, setLocalRotation); @@ -922,6 +927,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription()); APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, properties.getParentID()); APPEND_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, properties.getParentJointIndex()); + APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, properties.getQueryAACube()); if (properties.getType() == EntityTypes::Web) { APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl()); @@ -1208,6 +1214,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_ID, QUuid, setParentID); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_QUERY_AA_CUBE, AACube, setQueryAACube); if (properties.getType() == EntityTypes::Web) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl); @@ -1460,32 +1467,8 @@ void EntityItemProperties::markAllChanged() { _parentIDChanged = true; _parentJointIndexChanged = true; -} -/// The maximum bounding cube for the entity, independent of it's rotation. -/// This accounts for the registration point (upon which rotation occurs around). -/// -AACube EntityItemProperties::getMaximumAACube() const { - // * we know that the position is the center of rotation - glm::vec3 centerOfRotation = _position; // also where _registration point is - - // * we know that the registration point is the center of rotation - // * we can calculate the length of the furthest extent from the registration point - // as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint) - glm::vec3 registrationPoint = (_dimensions * _registrationPoint); - glm::vec3 registrationRemainder = (_dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint)); - glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder); - - // * we know that if you rotate in any direction you would create a sphere - // that has a radius of the length of furthest extent from registration point - float radius = glm::length(furthestExtentFromRegistration); - - // * we know that the minimum bounding cube of this maximum possible sphere is - // (center - radius) to (center + radius) - glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius); - float diameter = radius * 2.0f; - - return AACube(minimumCorner, diameter); + _queryAACubeChanged = true; } // The minimum bounding box for the entity. @@ -1785,6 +1768,9 @@ QList EntityItemProperties::listChangedProperties() { if (parentJointIndexChanged()) { out += "parentJointIndex"; } + if (queryAACubeChanged()) { + out += "queryAACube"; + } getAnimation().listChangedProperties(out); getKeyLight().listChangedProperties(out); @@ -1798,3 +1784,7 @@ QList EntityItemProperties::listChangedProperties() { bool EntityItemProperties::parentDependentPropertyChanged() const { return localPositionChanged() || positionChanged() || localRotationChanged() || rotationChanged(); } + +bool EntityItemProperties::parentRelatedPropertyChanged() const { + return parentDependentPropertyChanged() || parentIDChanged() || parentJointIndexChanged(); +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 5420e75aed..f03b88034c 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -84,8 +84,8 @@ public: EntityPropertyFlags getChangedProperties() const; bool parentDependentPropertyChanged() const; // was there a changed in a property that requires parent info to interpret? + bool parentRelatedPropertyChanged() const; // parentDependentPropertyChanged or parentID or parentJointIndex - AACube getMaximumAACube() const; AABox getAABox() const; void debugDump() const; @@ -192,7 +192,8 @@ public: 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, 0); + DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1); + DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube()); // these are used when bouncing location data into and out of scripts DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3); @@ -397,6 +398,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParentID, parentID, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParentJointIndex, parentJointIndex, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, QueryAACube, queryAACube, ""); properties.getAnimation().debugDump(); properties.getAtmosphere().debugDump(); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 304e6f672c..01d043d070 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -122,6 +122,9 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) { inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) { return QScriptValue(QUuid(v).toString()); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { return aaCubeToScriptValue(e, v); } + + #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(X,G,g,P,p) \ if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ @@ -225,6 +228,13 @@ inline glmVec3 glmVec3_convertFromScriptValue(const QScriptValue& v, bool& isVal return glm::vec3(0); } +inline AACube AACube_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + AACube result; + aaCubeFromScriptValue(v, result); + return result; +} + inline qVectorFloat qVectorFloat_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return qVectorFloatFromScriptValue(v); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 2ba86a491e..9e8b21cb5f 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -157,6 +157,8 @@ enum EntityPropertyList { PROP_LOCAL_POSITION, // only used to convert values to and from scripts PROP_LOCAL_ROTATION, // only used to convert values to and from scripts + PROP_QUERY_AA_CUBE, // how the EntityTree considers the size and position on an entity + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 0746c2a824..cfb8e0b5a9 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -72,12 +72,15 @@ EntityItemProperties convertLocationToScriptSemantics(const EntityItemProperties scriptSideProperties.setLocalPosition(entitySideProperties.getPosition()); scriptSideProperties.setLocalRotation(entitySideProperties.getRotation()); + bool success; glm::vec3 worldPosition = SpatiallyNestable::localToWorld(entitySideProperties.getPosition(), entitySideProperties.getParentID(), - entitySideProperties.getParentJointIndex()); + entitySideProperties.getParentJointIndex(), + success); glm::quat worldRotation = SpatiallyNestable::localToWorld(entitySideProperties.getRotation(), entitySideProperties.getParentID(), - entitySideProperties.getParentJointIndex()); + entitySideProperties.getParentJointIndex(), + success); scriptSideProperties.setPosition(worldPosition); scriptSideProperties.setRotation(worldRotation); @@ -89,13 +92,15 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti // convert position and rotation properties from world-space to local, unless localPosition and localRotation // are set. If they are set, they overwrite position and rotation. EntityItemProperties entitySideProperties = scriptSideProperties; + bool success; if (scriptSideProperties.localPositionChanged()) { entitySideProperties.setPosition(scriptSideProperties.getLocalPosition()); } else if (scriptSideProperties.positionChanged()) { glm::vec3 localPosition = SpatiallyNestable::worldToLocal(entitySideProperties.getPosition(), entitySideProperties.getParentID(), - entitySideProperties.getParentJointIndex()); + entitySideProperties.getParentJointIndex(), + success); entitySideProperties.setPosition(localPosition); } @@ -104,7 +109,8 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti } else if (scriptSideProperties.rotationChanged()) { glm::quat localRotation = SpatiallyNestable::worldToLocal(entitySideProperties.getRotation(), entitySideProperties.getParentID(), - entitySideProperties.getParentJointIndex()); + entitySideProperties.getParentJointIndex(), + success); entitySideProperties.setRotation(localRotation); } @@ -128,6 +134,10 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY); + if (propertiesWithSimID.parentRelatedPropertyChanged()) { + // due to parenting, the server may not know where something is in world-space, so include the bounding cube. + propertiesWithSimID.setQueryAACube(entity->getQueryAACube()); + } // and make note of it now, so we can act on it right away. entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY); @@ -193,16 +203,15 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) { EntityItemProperties properties = scriptSideProperties; EntityItemID entityID(id); - // If we have a local entity tree set, then also update it. if (!_entityTree) { queueEntityMessage(PacketType::EntityEdit, entityID, properties); return id; } + // If we have a local entity tree set, then also update it. bool updatedEntity = false; _entityTree->withWriteLock([&] { - if (scriptSideProperties.parentDependentPropertyChanged() || - scriptSideProperties.parentIDChanged() || scriptSideProperties.parentJointIndexChanged()) { + if (scriptSideProperties.parentRelatedPropertyChanged()) { // All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them. // If any of these changed, pull any missing properties from the entity. EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); @@ -265,7 +274,24 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& entity->flagForOwnership(); } } + if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) { + properties.setQueryAACube(entity->getQueryAACube()); + } entity->setLastBroadcast(usecTimestampNow()); + + // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server + // if they've changed. + entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { + if (descendant->getNestableType() == NestableType::Entity) { + if (descendant->computePuffedQueryAACube()) { + EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); + EntityItemProperties newQueryCubeProperties; + newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); + queueEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties); + entityDescendant->setLastBroadcast(usecTimestampNow()); + } + } + }); } }); queueEntityMessage(PacketType::EntityEdit, entityID, properties); diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 70b973e672..ad354465d0 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -119,8 +119,9 @@ void EntitySimulation::sortEntitiesThatMoved() { while (itemItr != _entitiesToSort.end()) { EntityItemPointer entity = *itemItr; // check to see if this movement has sent the entity outside of the domain. - AACube newCube = entity->getMaximumAACube(); - if (!domainBounds.touches(newCube)) { + bool success; + AACube newCube = entity->getQueryAACube(success); + if (success && !domainBounds.touches(newCube)) { qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds."; _entitiesToDelete.insert(entity); _mortalEntities.remove(entity); @@ -200,8 +201,9 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) { uint32_t dirtyFlags = entity->getDirtyFlags(); if (dirtyFlags & Simulation::DIRTY_POSITION) { AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE); - AACube newCube = entity->getMaximumAACube(); - if (!domainBounds.touches(newCube)) { + bool success; + AACube newCube = entity->getQueryAACube(success); + if (success && !domainBounds.touches(newCube)) { qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds."; _entitiesToDelete.insert(entity); _mortalEntities.remove(entity); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f0a03623c2..fa9b6032b2 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -89,6 +89,9 @@ void EntityTree::postAddEntity(EntityItemPointer entity) { _isDirty = true; maybeNotifyNewCollisionSoundURL("", entity->getCollisionSoundURL()); emit addingEntity(entity->getEntityItemID()); + + // find and hook up any entities with this entity as a (previously) missing parent + fixupMissingParents(); } bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) { @@ -142,8 +145,12 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI EntityItemProperties tempProperties; tempProperties.setLocked(wantsLocked); - BoundingBoxRelatedProperties newBBRelProperties(entity, tempProperties); - UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, newBBRelProperties); + bool success; + AACube queryCube = entity->getQueryAACube(success); + if (!success) { + qCDebug(entities) << "Warning -- failed to get query-cube for" << entity->getID(); + } + UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, queryCube); recurseTreeWithOperator(&theOperator); entity->setProperties(tempProperties); _isDirty = true; @@ -211,8 +218,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI QString collisionSoundURLBefore = entity->getCollisionSoundURL(); uint32_t preFlags = entity->getDirtyFlags(); - BoundingBoxRelatedProperties newBBRelProperties(entity, properties); - UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, newBBRelProperties); + UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, properties.getQueryAACube()); recurseTreeWithOperator(&theOperator); entity->setProperties(properties); @@ -229,14 +235,19 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI if (!childEntity) { continue; } - BoundingBoxRelatedProperties newChildBBRelProperties(childEntity); EntityTreeElementPointer containingElement = childEntity->getElement(); if (!containingElement) { continue; } - UpdateEntityOperator theChildOperator(getThisPointer(), - containingElement, - childEntity, newChildBBRelProperties); + + bool success; + AACube queryCube = childEntity->getQueryAACube(success); + if (!success) { + _missingParent.append(childEntity); + continue; + } + + UpdateEntityOperator theChildOperator(getThisPointer(), containingElement, childEntity, queryCube); recurseTreeWithOperator(&theChildOperator); foreach (SpatiallyNestablePointer childChild, childEntity->getChildren()) { if (childChild && childChild->getNestableType() == NestableType::Entity) { @@ -316,6 +327,11 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti // Recurse the tree and store the entity in the correct tree element AddEntityOperator theOperator(getThisPointer(), result); recurseTreeWithOperator(&theOperator); + if (result->getAncestorMissing()) { + // we added the entity, but didn't know about all its ancestors, so it went into the wrong place. + // add it to a list of entities needing to be fixed once their parents are known. + _missingParent.append(result); + } postAddEntity(result); } @@ -757,6 +773,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList= 0) { + QUuid value = properties.getParentID(); + changedProperties[index] = QString("parentID:") + value.toString(); + } + } } int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, @@ -918,7 +942,38 @@ void EntityTree::entityChanged(EntityItemPointer entity) { } } +void EntityTree::fixupMissingParents() { + MovingEntitiesOperator moveOperator(getThisPointer()); + + QMutableVectorIterator iter(_missingParent); + while (iter.hasNext()) { + EntityItemWeakPointer entityWP = iter.next(); + EntityItemPointer entity = entityWP.lock(); + if (entity) { + bool success; + AACube newCube = entity->getQueryAACube(success); + if (success) { + // this entity's parent (or ancestry) was previously not fully known, and now is. Update its + // location in the EntityTree. + moveOperator.addEntityToMoveList(entity, newCube); + iter.remove(); + entity->markAncestorMissing(false); + } + } else { + // entity was deleted before we found its parent. + iter.remove(); + } + } + + if (moveOperator.hasMovingEntities()) { + PerformanceTimer perfTimer("recurseTreeWithOperator"); + recurseTreeWithOperator(&moveOperator); + } + +} + void EntityTree::update() { + fixupMissingParents(); if (_simulation) { withWriteLock([&] { _simulation->updateEntities(); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index f68e2d59e9..0f77c4af9a 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -307,6 +307,9 @@ protected: quint64 _totalEditDeltas = 0; quint64 _maxEditDelta = 0; quint64 _treeResetTime = 0; + + void fixupMissingParents(); + QVector _missingParent; }; #endif // hifi_EntityTree_h diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 8944c95cbc..2320004be8 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -302,8 +302,9 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData // simulation changing what's visible. consider the case where the entity contains an angular velocity // the entity may not be in view and then in view a frame later, let the client side handle it's view // frustum culling on rendering. - AACube entityCube = entity->getMaximumAACube(); - if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) { + bool success; + AACube entityCube = entity->getQueryAACube(success); + if (!success || params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) { includeThisEntity = false; // out of view, don't include it } } @@ -413,19 +414,29 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData } bool EntityTreeElement::containsEntityBounds(EntityItemPointer entity) const { - return containsBounds(entity->getMaximumAACube()); + bool success; + auto queryCube = entity->getQueryAACube(success); + if (!success) { + return false; + } + return containsBounds(queryCube); } bool EntityTreeElement::bestFitEntityBounds(EntityItemPointer entity) const { - return bestFitBounds(entity->getMaximumAACube()); + bool success; + auto queryCube = entity->getQueryAACube(success); + if (!success) { + return false; + } + return bestFitBounds(queryCube); } bool EntityTreeElement::containsBounds(const EntityItemProperties& properties) const { - return containsBounds(properties.getMaximumAACube()); + return containsBounds(properties.getQueryAACube()); } bool EntityTreeElement::bestFitBounds(const EntityItemProperties& properties) const { - return bestFitBounds(properties.getMaximumAACube()); + return bestFitBounds(properties.getQueryAACube()); } bool EntityTreeElement::containsBounds(const AACube& bounds) const { @@ -526,7 +537,12 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con return; } - AABox entityBox = entity->getAABox(); + bool success; + AABox entityBox = entity->getAABox(success); + if (!success) { + return; + } + float localDistance; BoxFace localFace; glm::vec3 localSurfaceNormal; @@ -631,11 +647,12 @@ EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector& foundEntities) const { forEachEntity([&](EntityItemPointer entity) { - AABox entityBox = entity->getAABox(); + bool success; + AABox entityBox = entity->getAABox(success); // if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case glm::vec3 penetration; - if (entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) { + if (success && entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) { glm::vec3 dimensions = entity->getDimensions(); @@ -651,9 +668,12 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc // maximum bounding sphere, which is actually larger than our actual radius float entityTrueRadius = dimensions.x / 2.0f; - if (findSphereSpherePenetration(searchPosition, searchRadius, - entity->getCenterPosition(), entityTrueRadius, penetration)) { - foundEntities.push_back(entity); + bool success; + if (findSphereSpherePenetration(searchPosition, searchRadius, + entity->getCenterPosition(success), entityTrueRadius, penetration)) { + if (success) { + foundEntities.push_back(entity); + } } } else { // determine the worldToEntityMatrix that doesn't include scale because @@ -679,7 +699,8 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc void EntityTreeElement::getEntities(const AACube& cube, QVector& foundEntities) { forEachEntity([&](EntityItemPointer entity) { - AABox entityBox = entity->getAABox(); + bool success; + AABox entityBox = entity->getAABox(success); // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better // FIXME - consider allowing the entity to determine penetration so that // entities could presumably dull actuall hull testing if they wanted to @@ -693,10 +714,10 @@ void EntityTreeElement::getEntities(const AACube& cube, QVector& foundEntities) { forEachEntity([&](EntityItemPointer entity) { - AABox entityBox = entity->getAABox(); + bool success; + AABox entityBox = entity->getAABox(success); // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better // FIXME - consider allowing the entity to determine penetration so that // entities could presumably dull actuall hull testing if they wanted to @@ -718,10 +740,10 @@ void EntityTreeElement::getEntities(const AABox& box, QVector // test the triangles of the face against the box? // if translated search face triangle intersect target box // add to result - // + // // If the entities AABox touches the search cube then consider it to be found - if (entityBox.touches(box)) { + if (success && entityBox.touches(box)) { foundEntities.push_back(entity); } }); @@ -940,7 +962,11 @@ bool EntityTreeElement::pruneChildren() { void EntityTreeElement::expandExtentsToContents(Extents& extents) { withReadLock([&] { foreach(EntityItemPointer entity, _entityItems) { - extents.add(entity->getAABox()); + bool success; + AABox aaBox = entity->getAABox(success); + if (success) { + extents.add(aaBox); + } } }); } diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 841b70aa56..7ad7b39f20 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -110,7 +110,11 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons // then translate back to work coordinates glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f)); distance = glm::distance(origin, hitAt); - surfaceNormal = glm::normalize(hitAt - getCenterPosition()); + bool success; + surfaceNormal = glm::normalize(hitAt - getCenterPosition(success)); + if (!success) { + return false; + } return true; } return false; diff --git a/libraries/entities/src/UpdateEntityOperator.cpp b/libraries/entities/src/UpdateEntityOperator.cpp index 4acc386333..94599496b0 100644 --- a/libraries/entities/src/UpdateEntityOperator.cpp +++ b/libraries/entities/src/UpdateEntityOperator.cpp @@ -14,12 +14,11 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTreePointer tree, EntityTreeElementPointer containingElement, EntityItemPointer existingEntity, - const BoundingBoxRelatedProperties& newProperties) : + const AACube newQueryAACube) : _tree(tree), _existingEntity(existingEntity), _containingElement(containingElement), _containingElementCube(containingElement->getAACube()), - _newProperties(newProperties), _entityItemID(existingEntity->getEntityItemID()), _foundOld(false), _foundNew(false), @@ -41,13 +40,13 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTreePointer tree, // entity into the the element, or do we want to use the entities "relaxed" bounds // which can handle all potential rotations? // the getMaximumAACube is the relaxed form. - _oldEntityCube = _existingEntity->getMaximumAACube(); + _oldEntityCube = _existingEntity->getQueryAACube(); _oldEntityBox = _oldEntityCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); // clamp to domain bounds // If our new properties don't have bounds details (no change to position, etc) or if this containing element would // be the best fit for our new properties, then just do the new portion of the store pass, since the change path will // be the same for both parts of the update - bool oldElementBestFit = _containingElement->bestFitBounds(newProperties.getMaximumAACube()); + bool oldElementBestFit = _containingElement->bestFitBounds(newQueryAACube); // For some reason we've seen a case where the original containing element isn't a best fit for the old properties // in this case we want to move it, even if the properties haven't changed. diff --git a/libraries/entities/src/UpdateEntityOperator.h b/libraries/entities/src/UpdateEntityOperator.h index aac442d415..ea6faabe3e 100644 --- a/libraries/entities/src/UpdateEntityOperator.h +++ b/libraries/entities/src/UpdateEntityOperator.h @@ -12,7 +12,6 @@ #ifndef hifi_UpdateEntityOperator_h #define hifi_UpdateEntityOperator_h -#include "BoundingBoxRelatedProperties.h" #include "EntitiesLogging.h" #include "EntityItem.h" #include "EntityItemProperties.h" @@ -22,7 +21,7 @@ class UpdateEntityOperator : public RecurseOctreeOperator { public: UpdateEntityOperator(EntityTreePointer tree, EntityTreeElementPointer containingElement, - EntityItemPointer existingEntity, const BoundingBoxRelatedProperties& newProperties); + EntityItemPointer existingEntity, const AACube newQueryAACube); ~UpdateEntityOperator(); @@ -34,7 +33,6 @@ private: EntityItemPointer _existingEntity; EntityTreeElementPointer _containingElement; AACube _containingElementCube; // we temporarily store our cube here in case we need to delete the containing element - BoundingBoxRelatedProperties _newProperties; EntityItemID _entityItemID; bool _foundOld; bool _foundNew; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index ca869ec29f..42ee77b02b 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -41,7 +41,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP; + return VERSION_ENTITITES_HAVE_QUERY_BOX; case PacketType::AvatarData: case PacketType::BulkAvatarData: return static_cast(AvatarMixerPacketVersion::SoftAttachmentSupport); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3b4b358daf..81cc80e9d5 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -161,7 +161,8 @@ const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; const PacketVersion VERSION_ENTITIES_POLYLINE_TEXTURE = 50; const PacketVersion VERSION_ENTITIES_HAVE_PARENTS = 51; -const PacketVersion VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52; +const PacketVersion VERSION_ENTITITES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52; +const PacketVersion VERSION_ENTITITES_HAVE_QUERY_BOX = 53; enum class AvatarMixerPacketVersion : PacketVersion { TranslationSupport = 17, diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index f063c60fd7..02ba0b937a 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -23,6 +23,11 @@ AtomicUIntStat OctreePacketData::_totalBytesOfValues { 0 }; AtomicUIntStat OctreePacketData::_totalBytesOfPositions { 0 }; AtomicUIntStat OctreePacketData::_totalBytesOfRawData { 0 }; +struct aaCubeData { + glm::vec3 corner; + float scale; +}; + OctreePacketData::OctreePacketData(bool enableCompression, int targetSize) { changeSettings(enableCompression, targetSize); // does reset... } @@ -461,6 +466,17 @@ bool OctreePacketData::appendValue(const QByteArray& bytes) { return success; } +bool OctreePacketData::appendValue(const AACube& aaCube) { + aaCubeData cube { aaCube.getCorner(), aaCube.getScale() }; + const unsigned char* data = (const unsigned char*)&cube; + int length = sizeof(aaCubeData); + bool success = append(data, length); + if (success) { + _bytesOfValues += length; + _totalBytesOfValues += length; + } + return success; +} bool OctreePacketData::appendPosition(const glm::vec3& value) { const unsigned char* data = (const unsigned char*)&value; @@ -638,3 +654,10 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QByteA result = value; return sizeof(length) + length; } + +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, AACube& result) { + aaCubeData cube; + memcpy(&cube, dataBytes, sizeof(aaCubeData)); + result = AACube(cube.corner, cube.scale); + return sizeof(aaCubeData); +} diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 2c86d518ad..8d7039e473 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -187,6 +187,9 @@ public: /// appends a QByteArray value to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const QByteArray& bytes); + /// appends an AACube value to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const AACube& aaCube); + /// appends a position to the end of the stream, may fail if new data stream is too long to fit in packet bool appendPosition(const glm::vec3& value); @@ -253,6 +256,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, AACube& result); private: /// appends raw bytes, might fail if byte would cause packet to be too large diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index f86369a514..cd07b4112f 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -374,6 +374,10 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s return true; } + if (_entity->queryAABoxNeedsUpdate()) { + return true; + } + if (_entity->getSimulatorID() != sessionID) { // we don't own the simulation, but maybe we should... if (_outgoingPriority != NO_PRORITY) { @@ -466,6 +470,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q properties.setActionData(_serverActionData); } + if (properties.parentRelatedPropertyChanged() && _entity->computePuffedQueryAACube()) { + // due to parenting, the server may not know where something is in world-space, so include the bounding cube. + properties.setQueryAACube(_entity->getQueryAACube()); + } + // set the LastEdited of the properties but NOT the entity itself quint64 now = usecTimestampNow(); properties.setLastEdited(now); @@ -502,6 +511,20 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, id, properties); _entity->setLastBroadcast(usecTimestampNow()); + // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server + // if they've changed. + _entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { + if (descendant->getNestableType() == NestableType::Entity) { + EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); + if (descendant->computePuffedQueryAACube()) { + EntityItemProperties newQueryCubeProperties; + newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); + entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties); + entityDescendant->setLastBroadcast(usecTimestampNow()); + } + } + }); + _lastStep = step; } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index efaa79ddbb..ef9a837b27 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -95,6 +95,9 @@ const float METERS_PER_MILLIMETER = 0.01f; void Model::setScaleInternal(const glm::vec3& scale) { if (glm::distance(_scale, scale) > METERS_PER_MILLIMETER) { _scale = scale; + if (_scale.x == 0.0f || _scale.y == 0.0f || _scale.z == 0.0f) { + assert(false); + } initJointTransforms(); } } diff --git a/libraries/shared/src/AACube.cpp b/libraries/shared/src/AACube.cpp index 32a304872c..4218826141 100644 --- a/libraries/shared/src/AACube.cpp +++ b/libraries/shared/src/AACube.cpp @@ -465,3 +465,16 @@ AABox AACube::clamp(float min, float max) const { return temp.clamp(min, max); } +AACube& AACube::operator += (const glm::vec3& point) { + glm::vec3 oldMaximumPoint = getMaximumPoint(); + _corner = glm::vec3(glm::min(_corner.x, point.x), + glm::min(_corner.y, point.y), + glm::min(_corner.z, point.z)); + + glm::vec3 scaleOld = oldMaximumPoint - _corner; + glm::vec3 scalePoint = point - _corner; + _scale = glm::max(_scale, scalePoint.x, scalePoint.y, scalePoint.z); + _scale = glm::max(_scale, scaleOld.x, scaleOld.y, scaleOld.z); + + return (*this); +} diff --git a/libraries/shared/src/AACube.h b/libraries/shared/src/AACube.h index d301207429..fbbbe9992a 100644 --- a/libraries/shared/src/AACube.h +++ b/libraries/shared/src/AACube.h @@ -64,6 +64,8 @@ public: AABox clamp(const glm::vec3& min, const glm::vec3& max) const; AABox clamp(float min, float max) const; + AACube& operator += (const glm::vec3& point); + private: glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const; glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 008ac238d5..e55e74c691 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -42,7 +42,7 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue); qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue); - + qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue); } QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) { @@ -238,6 +238,26 @@ QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color) { return object; } +QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube) { + QScriptValue obj = engine->newObject(); + const glm::vec3& corner = aaCube.getCorner(); + obj.setProperty("x", corner.x); + obj.setProperty("y", corner.y); + obj.setProperty("z", corner.z); + obj.setProperty("scale", aaCube.getScale()); + return obj; +} + +void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube) { + glm::vec3 corner; + corner.x = object.property("x").toVariant().toFloat(); + corner.y = object.property("y").toVariant().toFloat(); + corner.z = object.property("z").toVariant().toFloat(); + float scale = object.property("scale").toVariant().toFloat(); + + aaCube.setBox(corner, scale); +} + void qColorFromScriptValue(const QScriptValue& object, QColor& color) { if (object.isNumber()) { color.setRgb(object.toUInt32()); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index cd1e3b0d3e..61775a2762 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -18,6 +18,7 @@ #include #include +#include "AACube.h" #include "SharedUtil.h" class QColor; @@ -30,6 +31,7 @@ Q_DECLARE_METATYPE(glm::quat) Q_DECLARE_METATYPE(xColor) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(AACube) void registerMetaTypes(QScriptEngine* engine); @@ -67,6 +69,9 @@ QVector qVectorFloatFromScriptValue(const QScriptValue& array); QVector qVectorQUuidFromScriptValue(const QScriptValue& array); +QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube); +void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube); + class PickRay { public: PickRay() : origin(0.0f), direction(0.0f) { } diff --git a/libraries/shared/src/SpatialParentFinder.h b/libraries/shared/src/SpatialParentFinder.h index 936d497eae..9b49490fa5 100644 --- a/libraries/shared/src/SpatialParentFinder.h +++ b/libraries/shared/src/SpatialParentFinder.h @@ -31,7 +31,7 @@ public: SpatialParentFinder() { } virtual ~SpatialParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID) const = 0; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const = 0; }; #endif // hifi_SpatialParentFinder_h diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 98d127cc1e..07d5ddeeb0 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -14,6 +14,7 @@ #include "DependencyManager.h" #include "SpatiallyNestable.h" +const float defaultAACubeSize = 1.0f; SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) : _nestableType(nestableType), @@ -24,21 +25,25 @@ SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) : _transform.setRotation(glm::quat()); } -Transform SpatiallyNestable::getParentTransform() const { +Transform SpatiallyNestable::getParentTransform(bool& success) const { Transform result; - SpatiallyNestablePointer parent = getParentPointer(); + SpatiallyNestablePointer parent = getParentPointer(success); + if (!success) { + return result; + } if (parent) { - Transform parentTransform = parent->getTransform(_parentJointIndex); - result = parentTransform.setScale(1.0f); + Transform parentTransform = parent->getTransform(_parentJointIndex, success); + result = parentTransform.setScale(1.0f); // TODO: scaling } return result; } -SpatiallyNestablePointer SpatiallyNestable::getParentPointer() const { +SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) const { SpatiallyNestablePointer parent = _parent.lock(); if (!parent && _parentID.isNull()) { // no parent + success = true; return nullptr; } @@ -48,6 +53,7 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer() const { parent->beParentOfChild(getThisPointer()); _parentKnowsMe = true; } + success = true; return parent; } @@ -63,9 +69,14 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer() const { // we have a _parentID but no parent pointer, or our parent pointer was to the wrong thing QSharedPointer parentFinder = DependencyManager::get(); if (!parentFinder) { + success = false; return nullptr; } - _parent = parentFinder->find(_parentID); + _parent = parentFinder->find(_parentID, success); + if (!success) { + return nullptr; + } + parent = _parent.lock(); if (parent) { parent->beParentOfChild(thisPointer); @@ -73,7 +84,9 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer() const { } if (parent || _parentID.isNull()) { - thisPointer->parentChanged(); + success = true; + } else { + success = false; } return parent; @@ -96,131 +109,255 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) { _parentID = parentID; _parentKnowsMe = false; } - parentChanged(); } -glm::vec3 SpatiallyNestable::worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex) { +void SpatiallyNestable::setParentJointIndex(quint16 parentJointIndex) { + _parentJointIndex = parentJointIndex; +} + +glm::vec3 SpatiallyNestable::worldToLocal(const glm::vec3& position, + const QUuid& parentID, int parentJointIndex, + bool& success) { + Transform result; QSharedPointer parentFinder = DependencyManager::get(); - Transform parentTransform; - if (parentFinder) { - auto parentWP = parentFinder->find(parentID); - auto parent = parentWP.lock(); - if (parent) { - parentTransform = parent->getTransform(parentJointIndex); - parentTransform.setScale(1.0f); - } + if (!parentFinder) { + success = false; + return glm::vec3(0.0f); } + Transform parentTransform; + auto parentWP = parentFinder->find(parentID, success); + if (!success) { + return glm::vec3(0.0f); + } + + auto parent = parentWP.lock(); + if (!parentID.isNull() && !parent) { + success = false; + return glm::vec3(0.0f); + } + + if (parent) { + parentTransform = parent->getTransform(parentJointIndex, success); + if (!success) { + return glm::vec3(0.0f); + } + parentTransform.setScale(1.0f); // TODO: scale + } + success = true; + Transform positionTransform; positionTransform.setTranslation(position); Transform myWorldTransform; Transform::mult(myWorldTransform, parentTransform, positionTransform); - myWorldTransform.setTranslation(position); - Transform result; Transform::inverseMult(result, parentTransform, myWorldTransform); return result.getTranslation(); } -glm::quat SpatiallyNestable::worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex) { +glm::quat SpatiallyNestable::worldToLocal(const glm::quat& orientation, + const QUuid& parentID, int parentJointIndex, + bool& success) { + Transform result; QSharedPointer parentFinder = DependencyManager::get(); - Transform parentTransform; - if (parentFinder) { - auto parentWP = parentFinder->find(parentID); - auto parent = parentWP.lock(); - if (parent) { - parentTransform = parent->getTransform(parentJointIndex); - parentTransform.setScale(1.0f); - } + if (!parentFinder) { + success = false; + return glm::quat(); } + Transform parentTransform; + auto parentWP = parentFinder->find(parentID, success); + if (!success) { + return glm::quat(); + } + + auto parent = parentWP.lock(); + if (!parentID.isNull() && !parent) { + success = false; + return glm::quat(); + } + + if (parent) { + parentTransform = parent->getTransform(parentJointIndex, success); + if (!success) { + return glm::quat(); + } + parentTransform.setScale(1.0f); // TODO: scale + } + success = true; + Transform orientationTransform; orientationTransform.setRotation(orientation); Transform myWorldTransform; Transform::mult(myWorldTransform, parentTransform, orientationTransform); myWorldTransform.setRotation(orientation); - Transform result; Transform::inverseMult(result, parentTransform, myWorldTransform); return result.getRotation(); } -glm::vec3 SpatiallyNestable::localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex) { +glm::vec3 SpatiallyNestable::localToWorld(const glm::vec3& position, + const QUuid& parentID, int parentJointIndex, + bool& success) { + Transform result; QSharedPointer parentFinder = DependencyManager::get(); - Transform parentTransform; - if (parentFinder) { - auto parentWP = parentFinder->find(parentID); - auto parent = parentWP.lock(); - if (parent) { - parentTransform = parent->getTransform(parentJointIndex); - parentTransform.setScale(1.0f); - } + if (!parentFinder) { + success = false; + return glm::vec3(0.0f); } + + Transform parentTransform; + auto parentWP = parentFinder->find(parentID, success); + if (!success) { + return glm::vec3(0.0f); + } + + auto parent = parentWP.lock(); + if (!parentID.isNull() && !parent) { + success = false; + return glm::vec3(0.0f); + } + + if (parent) { + parentTransform = parent->getTransform(parentJointIndex, success); + if (!success) { + return glm::vec3(0.0f); + } + parentTransform.setScale(1.0f); // TODO: scale + } + success = true; + Transform positionTransform; positionTransform.setTranslation(position); - Transform result; Transform::mult(result, parentTransform, positionTransform); return result.getTranslation(); } -glm::quat SpatiallyNestable::localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex) { +glm::quat SpatiallyNestable::localToWorld(const glm::quat& orientation, + const QUuid& parentID, int parentJointIndex, + bool& success) { + Transform result; QSharedPointer parentFinder = DependencyManager::get(); - Transform parentTransform; - if (parentFinder) { - auto parentWP = parentFinder->find(parentID); - auto parent = parentWP.lock(); - if (parent) { - parentTransform = parent->getTransform(parentJointIndex); - parentTransform.setScale(1.0f); - } + if (!parentFinder) { + success = false; + return glm::quat(); } + + Transform parentTransform; + auto parentWP = parentFinder->find(parentID, success); + if (!success) { + return glm::quat(); + } + + auto parent = parentWP.lock(); + if (!parentID.isNull() && !parent) { + success = false; + return glm::quat(); + } + + if (parent) { + parentTransform = parent->getTransform(parentJointIndex, success); + if (!success) { + return glm::quat(); + } + parentTransform.setScale(1.0f); + } + success = true; + Transform orientationTransform; orientationTransform.setRotation(orientation); - Transform result; Transform::mult(result, parentTransform, orientationTransform); return result.getRotation(); } +glm::vec3 SpatiallyNestable::getPosition(bool& success) const { + return getTransform(success).getTranslation(); +} + glm::vec3 SpatiallyNestable::getPosition() const { - return getTransform().getTranslation(); + bool success; + auto result = getPosition(success); + #ifdef WANT_DEBUG + if (!success) { + qDebug() << "Warning -- getPosition failed" << getID(); + } + #endif + return result; } -glm::vec3 SpatiallyNestable::getPosition(int jointIndex) const { - return getTransform(jointIndex).getTranslation(); +glm::vec3 SpatiallyNestable::getPosition(int jointIndex, bool& success) const { + return getTransform(jointIndex, success).getTranslation(); } -void SpatiallyNestable::setPosition(const glm::vec3& position) { - Transform parentTransform = getParentTransform(); +void SpatiallyNestable::setPosition(const glm::vec3& position, bool& success) { + Transform parentTransform = getParentTransform(success); Transform myWorldTransform; _transformLock.withWriteLock([&] { Transform::mult(myWorldTransform, parentTransform, _transform); myWorldTransform.setTranslation(position); Transform::inverseMult(_transform, parentTransform, myWorldTransform); }); - locationChanged(); + if (success) { + locationChanged(); + } else { + qDebug() << "setPosition failed for" << getID(); + } +} + +void SpatiallyNestable::setPosition(const glm::vec3& position) { + bool success; + setPosition(position, success); + #ifdef WANT_DEBUG + if (!success) { + qDebug() << "Warning -- setPosition failed" << getID(); + } + #endif +} + +glm::quat SpatiallyNestable::getOrientation(bool& success) const { + return getTransform(success).getRotation(); } glm::quat SpatiallyNestable::getOrientation() const { - return getTransform().getRotation(); + bool success; + auto result = getOrientation(success); + #ifdef WANT_DEBUG + if (!success) { + qDebug() << "Warning -- getOrientation failed" << getID(); + } + #endif + return result; } -glm::quat SpatiallyNestable::getOrientation(int jointIndex) const { - return getTransform(jointIndex).getRotation(); +glm::quat SpatiallyNestable::getOrientation(int jointIndex, bool& success) const { + return getTransform(jointIndex, success).getRotation(); } -void SpatiallyNestable::setOrientation(const glm::quat& orientation) { - Transform parentTransform = getParentTransform(); +void SpatiallyNestable::setOrientation(const glm::quat& orientation, bool& success) { + Transform parentTransform = getParentTransform(success); Transform myWorldTransform; _transformLock.withWriteLock([&] { Transform::mult(myWorldTransform, parentTransform, _transform); myWorldTransform.setRotation(orientation); Transform::inverseMult(_transform, parentTransform, myWorldTransform); }); - locationChanged(); + if (success) { + locationChanged(); + } } -const Transform SpatiallyNestable::getTransform() const { +void SpatiallyNestable::setOrientation(const glm::quat& orientation) { + bool success; + setOrientation(orientation, success); + #ifdef WANT_DEBUG + if (!success) { + qDebug() << "Warning -- setOrientation failed" << getID(); + } + #endif +} + +const Transform SpatiallyNestable::getTransform(bool& success) const { // return a world-space transform for this object's location - Transform parentTransform = getParentTransform(); + Transform parentTransform = getParentTransform(success); Transform result; _transformLock.withReadLock([&] { Transform::mult(result, parentTransform, _transform); @@ -228,25 +365,34 @@ const Transform SpatiallyNestable::getTransform() const { return result; } -const Transform SpatiallyNestable::getTransform(int jointIndex) const { +const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success) const { // this returns the world-space transform for this object. It finds its parent's transform (which may // cause this object's parent to query its parent, etc) and multiplies this object's local transform onto it. - Transform worldTransform = getTransform(); - Transform jointInObjectFrame = getAbsoluteJointTransformInObjectFrame(jointIndex); Transform jointInWorldFrame; + + Transform worldTransform = getTransform(success); + if (!success) { + return jointInWorldFrame; + } + + Transform jointInObjectFrame = getAbsoluteJointTransformInObjectFrame(jointIndex); Transform::mult(jointInWorldFrame, worldTransform, jointInObjectFrame); + success = true; return jointInWorldFrame; } -void SpatiallyNestable::setTransform(const Transform& transform) { - Transform parentTransform = getParentTransform(); +void SpatiallyNestable::setTransform(const Transform& transform, bool& success) { + Transform parentTransform = getParentTransform(success); _transformLock.withWriteLock([&] { Transform::inverseMult(_transform, parentTransform, transform); }); - locationChanged(); + if (success) { + locationChanged(); + } } glm::vec3 SpatiallyNestable::getScale() const { + // TODO: scale glm::vec3 result; _transformLock.withReadLock([&] { result = _transform.getScale(); @@ -255,11 +401,12 @@ glm::vec3 SpatiallyNestable::getScale() const { } glm::vec3 SpatiallyNestable::getScale(int jointIndex) const { - // XXX ... something with joints + // TODO: scale return getScale(); } void SpatiallyNestable::setScale(const glm::vec3& scale) { + // TODO: scale _transformLock.withWriteLock([&] { _transform.setScale(scale); }); @@ -312,6 +459,7 @@ void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) { } glm::vec3 SpatiallyNestable::getLocalScale() const { + // TODO: scale glm::vec3 result; _transformLock.withReadLock([&] { result = _transform.getScale(); @@ -320,6 +468,7 @@ glm::vec3 SpatiallyNestable::getLocalScale() const { } void SpatiallyNestable::setLocalScale(const glm::vec3& scale) { + // TODO: scale _transformLock.withWriteLock([&] { _transform.setScale(scale); }); @@ -380,3 +529,83 @@ void SpatiallyNestable::locationChanged() { object->locationChanged(); }); } + +AACube SpatiallyNestable::getMaximumAACube(bool& success) const { + return AACube(getPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize); +} + +void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) { + _queryAACube = queryAACube; + if (queryAACube.getScale() > 0.0f) { + _queryAACubeSet = true; + } +} + +bool SpatiallyNestable::queryAABoxNeedsUpdate() const { + bool success; + AACube currentAACube = getMaximumAACube(success); + if (!success) { + qDebug() << "can't getMaximumAACube for" << getID(); + return false; + } + + // make sure children are still in their boxes, also. + bool childNeedsUpdate = false; + getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { + if (!childNeedsUpdate && descendant->queryAABoxNeedsUpdate()) { + childNeedsUpdate = true; + } + }); + if (childNeedsUpdate) { + return true; + } + + if (_queryAACubeSet && _queryAACube.contains(currentAACube)) { + return false; + } + + return true; +} + +bool SpatiallyNestable::computePuffedQueryAACube() { + if (!queryAABoxNeedsUpdate()) { + return false; + } + bool success; + AACube currentAACube = getMaximumAACube(success); + // make an AACube with edges thrice as long and centered on the object + _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(currentAACube.getScale()), currentAACube.getScale() * 3.0f); + _queryAACubeSet = true; + + getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { + bool success; + AACube descendantAACube = descendant->getQueryAACube(success); + if (success) { + if (_queryAACube.contains(descendantAACube)) { + return; + } + _queryAACube += descendantAACube.getMinimumPoint(); + _queryAACube += descendantAACube.getMaximumPoint(); + } + }); + + return true; +} + +AACube SpatiallyNestable::getQueryAACube(bool& success) const { + if (_queryAACubeSet) { + success = true; + return _queryAACube; + } + success = false; + return AACube(getPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize); +} + +AACube SpatiallyNestable::getQueryAACube() const { + bool success; + auto result = getQueryAACube(success); + if (!success) { + qDebug() << "getQueryAACube failed for" << getID(); + } + return result; +} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 7a43e2a563..6573c0c2ce 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -15,6 +15,7 @@ #include #include "Transform.h" +#include "AACube.h" #include "SpatialParentFinder.h" #include "shared/ReadWriteLockable.h" @@ -38,37 +39,49 @@ public: virtual const QUuid& getID() const { return _id; } virtual void setID(const QUuid& id) { _id = id; } - virtual const QUuid getParentID() const { return _parentID; } + virtual QUuid getParentID() const { return _parentID; } virtual void setParentID(const QUuid& parentID); virtual quint16 getParentJointIndex() const { return _parentJointIndex; } - virtual void setParentJointIndex(quint16 parentJointIndex) { _parentJointIndex = parentJointIndex; } + virtual void setParentJointIndex(quint16 parentJointIndex); - static glm::vec3 worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex); - static glm::quat worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex); + static glm::vec3 worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex, bool& success); + static glm::quat worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex, bool& success); - static glm::vec3 localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex); - static glm::quat localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex); + static glm::vec3 localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex, bool& success); + static glm::quat localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex, bool& success); // world frame - virtual const Transform getTransform() const; - virtual void setTransform(const Transform& transform); + virtual const Transform getTransform(bool& success) const; + virtual void setTransform(const Transform& transform, bool& success); - virtual Transform getParentTransform() const; + virtual Transform getParentTransform(bool& success) const; + virtual glm::vec3 getPosition(bool& success) const; virtual glm::vec3 getPosition() const; + virtual void setPosition(const glm::vec3& position, bool& success); virtual void setPosition(const glm::vec3& position); + virtual glm::quat getOrientation(bool& success) const; virtual glm::quat getOrientation() const; - virtual glm::quat getOrientation(int jointIndex) const; + virtual glm::quat getOrientation(int jointIndex, bool& success) const; + virtual void setOrientation(const glm::quat& orientation, bool& success); virtual void setOrientation(const glm::quat& orientation); + virtual AACube getMaximumAACube(bool& success) const; + virtual bool computePuffedQueryAACube(); + + virtual void setQueryAACube(const AACube& queryAACube); + virtual bool queryAABoxNeedsUpdate() const; + virtual AACube getQueryAACube(bool& success) const; + virtual AACube getQueryAACube() const; + virtual glm::vec3 getScale() const; virtual void setScale(const glm::vec3& scale); // get world-frame values for a specific joint - virtual const Transform getTransform(int jointIndex) const; - virtual glm::vec3 getPosition(int jointIndex) const; + virtual const Transform getTransform(int jointIndex, bool& success) const; + virtual glm::vec3 getPosition(int jointIndex, bool& success) const; virtual glm::vec3 getScale(int jointIndex) const; // object's parent's frame @@ -89,17 +102,23 @@ public: // this object's frame virtual const Transform getAbsoluteJointTransformInObjectFrame(int jointIndex) const; - virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const { assert(false); return glm::quat(); } - virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const { assert(false); return glm::vec3(); } - + virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const = 0; + virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const = 0; + SpatiallyNestablePointer getThisPointer() const; + void markAncestorMissing(bool value) { _missingAncestor = value; } + bool getAncestorMissing() { return _missingAncestor; } + + void forEachChild(std::function actor); + void forEachDescendant(std::function actor); + protected: const NestableType _nestableType; // EntityItem or an AvatarData QUuid _id; QUuid _parentID; // what is this thing's transform relative to? quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to? - SpatiallyNestablePointer getParentPointer() const; + SpatiallyNestablePointer getParentPointer(bool& success) const; mutable SpatiallyNestableWeakPointer _parent; virtual void beParentOfChild(SpatiallyNestablePointer newChild) const; @@ -108,12 +127,14 @@ protected: mutable ReadWriteLockable _childrenLock; mutable QHash _children; - virtual void parentChanged() {} // called when parent pointer is updated virtual void locationChanged(); // called when a this object's location has changed virtual void dimensionsChanged() {} // called when a this object's dimensions have changed - void forEachChild(std::function actor); - void forEachDescendant(std::function actor); + // _queryAACube is used to decide where something lives in the octree + mutable AACube _queryAACube; + mutable bool _queryAACubeSet { false }; + + bool _missingAncestor { false }; private: mutable ReadWriteLockable _transformLock;