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 @@
+
+
+
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 @@
+
+
+
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 @@
+
+
+
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);