diff --git a/interface/resources/images/NoPictureProvided.svg b/interface/resources/images/NoPictureProvided.svg new file mode 100644 index 0000000000..003b499bbc --- /dev/null +++ b/interface/resources/images/NoPictureProvided.svg @@ -0,0 +1,3 @@ + + +2015-06-22 17:35ZCanvas 1No infoNo Picture Provided diff --git a/interface/resources/images/header_sep.svg b/interface/resources/images/header_sep.svg new file mode 100644 index 0000000000..ae1434860c --- /dev/null +++ b/interface/resources/images/header_sep.svg @@ -0,0 +1,3 @@ + + +2015-06-22 17:35ZCanvas 1No info diff --git a/interface/resources/images/tooltip_container.svg b/interface/resources/images/tooltip_container.svg new file mode 100644 index 0000000000..2c89bbbd7d --- /dev/null +++ b/interface/resources/images/tooltip_container.svg @@ -0,0 +1,3 @@ + + +2015-06-22 17:35ZCanvas 1Info diff --git a/interface/resources/qml/Tooltip.qml b/interface/resources/qml/Tooltip.qml index c836030ba1..169e5fe211 100644 --- a/interface/resources/qml/Tooltip.qml +++ b/interface/resources/qml/Tooltip.qml @@ -1,31 +1,76 @@ import Hifi 1.0 as Hifi -import QtQuick 2.3 as Original +import QtQuick 2.4 +import QtQuick.Layouts 1.1 import "controls" import "styles" Hifi.Tooltip { id: root HifiConstants { id: hifi } - // FIXME adjust position based on the edges of the screen - x: (lastMousePosition.x > surfaceSize.width/2) ? lastMousePosition.x - 140 : lastMousePosition.x + 20 - //y: lastMousePosition.y + 5 - y: (lastMousePosition.y > surfaceSize.height/2) ? lastMousePosition.y - 70 : lastMousePosition.y + 5 - implicitWidth: border.implicitWidth - implicitHeight: border.implicitHeight + x: lastMousePosition.x + offsetX + y: lastMousePosition.y + offsetY + property int offsetX: 0 + property int offsetY: 0 + width: border.width + height: border.height - Border { + Component.onCompleted: { + offsetX = (lastMousePosition.x > surfaceSize.width/2) ? -root.width : 0 + offsetY = (lastMousePosition.y > surfaceSize.height/2) ? -root.height : 0 + } + + Rectangle { id: border - anchors.fill: parent - implicitWidth: text.implicitWidth - implicitHeight: Math.max(text.implicitHeight, 64) + color: "#7f000000" + width: 322 + height: col.height + hifi.layout.spacing * 2 - Text { - id: text - anchors.fill: parent - anchors.margins: 16 - font.pixelSize: hifi.fonts.pixelSize / 2 - text: root.text - wrapMode: Original.Text.WordWrap + Column { + id: col + x: hifi.layout.spacing + y: hifi.layout.spacing + anchors.left: parent.left + anchors.leftMargin: hifi.layout.spacing + anchors.right: parent.right + anchors.rightMargin: hifi.layout.spacing + spacing: 5 + + Text { + id: textPlace + color: "white" + font.underline: true + anchors.left: parent.left + anchors.right: parent.right + font.pixelSize: hifi.fonts.pixelSize / 2 + text: root.text + wrapMode: Text.WrapAnywhere + + /* Uncomment for debugging to see the extent of the + Rectangle { + anchors.fill: parent + color: "#7fff00ff" + } + */ + } + + Image { + id: tooltipPic + source: "../images/NoPictureProvided.svg" + anchors.left: parent.left + anchors.right: parent.right + verticalAlignment: Image.AlignVCenter + } + + Text { + id: textDescription + color: "white" + width: border.implicitWidth + anchors.left: parent.left + anchors.right: parent.right + font.pixelSize: hifi.fonts.pixelSize / 2 + text: root.text + wrapMode: Text.WrapAnywhere + } } } -} +} \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8a572d2d41..d9c5589631 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2496,24 +2496,45 @@ void Application::update(float deltaTime) { _entitySimulation.lock(); _physicsEngine.deleteObjects(_entitySimulation.getObjectsToDelete()); + _entitySimulation.unlock(); + + _entities.getTree()->lockForWrite(); + _entitySimulation.lock(); _physicsEngine.addObjects(_entitySimulation.getObjectsToAdd()); + _entitySimulation.unlock(); + _entities.getTree()->unlock(); + + _entities.getTree()->lockForWrite(); + _entitySimulation.lock(); _physicsEngine.changeObjects(_entitySimulation.getObjectsToChange()); + _entitySimulation.unlock(); + _entities.getTree()->unlock(); + + _entitySimulation.lock(); _entitySimulation.applyActionChanges(); _entitySimulation.unlock(); + AvatarManager* avatarManager = DependencyManager::get().data(); _physicsEngine.deleteObjects(avatarManager->getObjectsToDelete()); _physicsEngine.addObjects(avatarManager->getObjectsToAdd()); _physicsEngine.changeObjects(avatarManager->getObjectsToChange()); + _entities.getTree()->lockForWrite(); _physicsEngine.stepSimulation(); + _entities.getTree()->unlock(); if (_physicsEngine.hasOutgoingChanges()) { + _entities.getTree()->lockForWrite(); _entitySimulation.lock(); _entitySimulation.handleOutgoingChanges(_physicsEngine.getOutgoingChanges(), _physicsEngine.getSessionID()); _entitySimulation.unlock(); + _entities.getTree()->unlock(); + _entities.getTree()->lockForWrite(); avatarManager->handleOutgoingChanges(_physicsEngine.getOutgoingChanges()); + _entities.getTree()->unlock(); + auto collisionEvents = _physicsEngine.getCollisionEvents(); avatarManager->handleCollisionEvents(collisionEvents); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index fe580eb137..0ea6080165 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -43,6 +43,7 @@ #include "RenderableLineEntityItem.h" #include "RenderablePolyVoxEntityItem.h" #include "EntitiesRendererLogging.h" +#include "AddressManager.h" EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : @@ -836,6 +837,14 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking); if (rayPickResult.intersects) { //qCDebug(entitiesrenderer) << "mousePressEvent over entity:" << rayPickResult.entityID; + + QString urlString = rayPickResult.properties.getHref(); + QUrl url = QUrl(urlString, QUrl::StrictMode); + if (url.isValid() && !url.isEmpty()){ + DependencyManager::get()->handleLookupString(urlString); + + } + emit mousePressOnEntity(rayPickResult, event, deviceID); QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index eeb9d5ca10..5e9591a031 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -19,10 +19,42 @@ #include "PhysicsHelpers.h" #include "PhysicsLogging.h" +#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS +#include "EntityTree.h" +#endif + static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; +#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS +bool EntityMotionState::entityTreeIsLocked() const { + EntityTreeElement* element = _entity ? _entity->getElement() : nullptr; + EntityTree* tree = element ? element->getTree() : nullptr; + if (tree) { + bool readSuccess = tree->tryLockForRead(); + if (readSuccess) { + tree->unlock(); + } + bool writeSuccess = tree->tryLockForWrite(); + if (writeSuccess) { + tree->unlock(); + } + if (readSuccess && writeSuccess) { + return false; // if we can take either kind of lock, there was no tree lock. + } + return true; // either read or write failed, so there is some lock in place. + } else { + return true; + } +} +#else +bool entityTreeIsLocked() { + return true; +} +#endif + + EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer entity) : ObjectMotionState(shape), _entity(entity), @@ -42,6 +74,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer { _type = MOTIONSTATE_TYPE_ENTITY; assert(_entity != nullptr); + assert(entityTreeIsLocked()); setMass(_entity->computeMass()); } @@ -51,6 +84,7 @@ EntityMotionState::~EntityMotionState() { } void EntityMotionState::updateServerPhysicsVariables() { + assert(entityTreeIsLocked()); _serverPosition = _entity->getPosition(); _serverRotation = _entity->getRotation(); _serverVelocity = _entity->getVelocity(); @@ -60,6 +94,7 @@ void EntityMotionState::updateServerPhysicsVariables() { // virtual void EntityMotionState::handleEasyChanges(uint32_t flags) { + assert(entityTreeIsLocked()); updateServerPhysicsVariables(); ObjectMotionState::handleEasyChanges(flags); if (flags & EntityItem::DIRTY_SIMULATOR_ID) { @@ -101,6 +136,7 @@ MotionType EntityMotionState::computeObjectMotionType() const { if (!_entity) { return MOTION_TYPE_STATIC; } + assert(entityTreeIsLocked()); if (_entity->getCollisionsWillMove()) { return MOTION_TYPE_DYNAMIC; } @@ -108,6 +144,7 @@ MotionType EntityMotionState::computeObjectMotionType() const { } bool EntityMotionState::isMoving() const { + assert(entityTreeIsLocked()); return _entity && _entity->isMoving(); } @@ -120,6 +157,7 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { if (!_entity) { return; } + assert(entityTreeIsLocked()); if (_motionType == MOTION_TYPE_KINEMATIC) { // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation. @@ -140,6 +178,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { if (!_entity) { return; } + assert(entityTreeIsLocked()); measureBodyAcceleration(); _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset()); _entity->setRotation(bulletToGLM(worldTrans.getRotation())); @@ -164,9 +203,12 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { #ifdef WANT_DEBUG quint64 now = usecTimestampNow(); qCDebug(physics) << "EntityMotionState::setWorldTransform()... changed entity:" << _entity->getEntityItemID(); - qCDebug(physics) << " last edited:" << _entity->getLastEdited() << formatUsecTime(now - _entity->getLastEdited()) << "ago"; - qCDebug(physics) << " last simulated:" << _entity->getLastSimulated() << formatUsecTime(now - _entity->getLastSimulated()) << "ago"; - qCDebug(physics) << " last updated:" << _entity->getLastUpdated() << formatUsecTime(now - _entity->getLastUpdated()) << "ago"; + qCDebug(physics) << " last edited:" << _entity->getLastEdited() + << formatUsecTime(now - _entity->getLastEdited()) << "ago"; + qCDebug(physics) << " last simulated:" << _entity->getLastSimulated() + << formatUsecTime(now - _entity->getLastSimulated()) << "ago"; + qCDebug(physics) << " last updated:" << _entity->getLastUpdated() + << formatUsecTime(now - _entity->getLastUpdated()) << "ago"; #endif } @@ -174,16 +216,18 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { btCollisionShape* EntityMotionState::computeNewShape() { if (_entity) { ShapeInfo shapeInfo; + assert(entityTreeIsLocked()); _entity->computeShapeInfo(shapeInfo); return getShapeManager()->getShape(shapeInfo); } return nullptr; } -bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { +bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { if (!_body || !_entity) { return false; } + assert(entityTreeIsLocked()); return _candidateForOwnership || sessionID == _entity->getSimulatorID(); } @@ -200,7 +244,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _sentActive = false; return false; } - + #ifdef WANT_DEBUG glm::vec3 wasPosition = _serverPosition; glm::quat wasRotation = _serverRotation; @@ -213,7 +257,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { const float INACTIVE_UPDATE_PERIOD = 0.5f; if (!_sentActive) { // we resend the inactive update every INACTIVE_UPDATE_PERIOD - // until it is removed from the outgoing updates + // until it is removed from the outgoing updates // (which happens when we don't own the simulation and it isn't touching our simulation) return (dt > INACTIVE_UPDATE_PERIOD); } @@ -231,10 +275,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverPosition += dt * _serverVelocity; } - // Else we measure the error between current and extrapolated transform (according to expected behavior + // Else we measure the error between current and extrapolated transform (according to expected behavior // of remote EntitySimulation) and return true if the error is significant. - // NOTE: math is done in the simulation-frame, which is NOT necessarily the same as the world-frame + // NOTE: math is done in the simulation-frame, which is NOT necessarily the same as the world-frame // due to _worldOffset. // TODO: compensate for _worldOffset offset here @@ -242,7 +286,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { btTransform worldTrans = _body->getWorldTransform(); glm::vec3 position = bulletToGLM(worldTrans.getOrigin()); - + float dx2 = glm::distance2(position, _serverPosition); const float MAX_POSITION_ERROR_SQUARED = 0.000004f; // Sqrt() - corresponds to 2 millimeters @@ -258,13 +302,13 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { return true; } - + if (glm::length2(_serverAngularVelocity) > 0.0f) { // compute rotation error float attenuation = powf(1.0f - _body->getAngularDamping(), dt); _serverAngularVelocity *= attenuation; - - // Bullet caps the effective rotation velocity inside its rotation integration step, therefore + + // Bullet caps the effective rotation velocity inside its rotation integration step, therefore // we must integrate with the same algorithm and timestep in order achieve similar results. for (int i = 0; i < numSteps; ++i) { _serverRotation = glm::normalize(computeBulletRotationStep(_serverAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP) * _serverRotation); @@ -276,7 +320,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { #ifdef WANT_DEBUG if ((fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT)) { qCDebug(physics) << ".... ((fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT)) ...."; - + qCDebug(physics) << "wasAngularVelocity:" << wasAngularVelocity; qCDebug(physics) << "_serverAngularVelocity:" << _serverAngularVelocity; @@ -293,10 +337,11 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { } bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& sessionID) { - // NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called - // after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL. + // NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called + // after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL. assert(_entity); assert(_body); + assert(entityTreeIsLocked()); if (!remoteSimulationOutOfSync(simulationStep)) { _candidateForOwnership = false; @@ -326,6 +371,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step) { assert(_entity); + assert(entityTreeIsLocked()); bool active = _body->isActive(); if (!active) { @@ -435,17 +481,18 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _lastStep = step; } -uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() { +uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() { + assert(entityTreeIsLocked()); uint32_t dirtyFlags = 0; if (_body && _entity) { - dirtyFlags = _entity->getDirtyFlags(); + dirtyFlags = _entity->getDirtyFlags(); _entity->clearDirtyFlags(); // we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings int bodyFlags = _body->getCollisionFlags(); bool isMoving = _entity->isMoving(); if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) || (bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) { - dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; } } return dirtyFlags; @@ -455,6 +502,7 @@ uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() { // virtual QUuid EntityMotionState::getSimulatorID() const { if (_entity) { + assert(entityTreeIsLocked()); return _entity->getSimulatorID(); } return QUuid(); @@ -469,12 +517,12 @@ void EntityMotionState::bump() { void EntityMotionState::resetMeasuredBodyAcceleration() { _lastMeasureStep = ObjectMotionState::getWorldSimulationStep(); if (_body) { - _lastVelocity = bulletToGLM(_body->getLinearVelocity()); + _lastVelocity = bulletToGLM(_body->getLinearVelocity()); } else { _lastVelocity = glm::vec3(0.0f); } _measuredAcceleration = glm::vec3(0.0f); -} +} void EntityMotionState::measureBodyAcceleration() { // try to manually measure the true acceleration of the object @@ -504,7 +552,7 @@ glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const { return _measuredAcceleration * _measuredDeltaTime; } -// virtual +// virtual void EntityMotionState::setMotionType(MotionType motionType) { ObjectMotionState::setMotionType(motionType); resetMeasuredBodyAcceleration(); @@ -514,12 +562,13 @@ void EntityMotionState::setMotionType(MotionType motionType) { // virtual QString EntityMotionState::getName() { if (_entity) { + assert(entityTreeIsLocked()); return _entity->getName(); } return ""; } -// virtual +// virtual int16_t EntityMotionState::computeCollisionGroup() { switch (computeObjectMotionType()){ case MOTION_TYPE_STATIC: diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 2d732f8ee0..4f777a4575 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -83,6 +83,10 @@ public: friend class PhysicalEntitySimulation; protected: + #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS + bool entityTreeIsLocked() const; + #endif + virtual btCollisionShape* computeNewShape(); virtual void clearObjectBackPointer(); virtual void setMotionType(MotionType motionType);