diff --git a/examples/grab.js b/examples/grab.js index 7ed69e9664..3d95592068 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -13,6 +13,7 @@ var isGrabbing = false; var grabbedEntity = null; +var actionID = null; var prevMouse = {}; var deltaMouse = { z: 0 @@ -88,6 +89,7 @@ function mousePressEvent(event) { gravity: {x: 0, y: 0, z: 0} }); + Controller.mouseMoveEvent.connect(mouseMoveEvent); } } @@ -110,7 +112,10 @@ function updateDropLine(position) { function mouseReleaseEvent() { if (isGrabbing) { + Controller.mouseMoveEvent.disconnect(mouseMoveEvent); isGrabbing = false; + Entities.deleteAction(grabbedEntity, actionID); + actionID = null; // only restore the original gravity if it's not zero. This is to avoid... // 1. interface A grabs an entity and locally saves off its gravity @@ -228,21 +233,25 @@ function update(deltaTime) { } if (shouldRotate) { angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE)); + Entities.editEntity(grabbedEntity, { + rotation: currentRotation, + angularVelocity: angularVelocity + }); } else { angularVelocity = entityProps.angularVelocity; } - Entities.editEntity(grabbedEntity, { - position: currentPosition, - rotation: currentRotation, - velocity: newVelocity, - angularVelocity: angularVelocity - }); + var newSpeed = Vec3.length(newVelocity); + if (!actionID) { + actionID = Entities.addAction("pull-to-point", grabbedEntity, {target: targetPosition, speed: newSpeed}); + } else { + Entities.updateAction(grabbedEntity, actionID, {target: targetPosition, speed: newSpeed}); + } + updateDropLine(targetPosition); } } -Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); diff --git a/examples/paint.js b/examples/paint.js index 837b217797..6e4438d2ea 100644 --- a/examples/paint.js +++ b/examples/paint.js @@ -13,7 +13,7 @@ // Script.include('lineRider.js') var MAX_POINTS_PER_LINE = 30; -var DRAWING_DISTANCE = 5; + var colorPalette = [{ red: 236, @@ -69,6 +69,7 @@ function hydraCheck() { //************ Mouse Paint ************************** function MousePaint() { + var DRAWING_DISTANCE = 2; var lines = []; var deletedLines = []; var isDrawing = false; @@ -91,7 +92,7 @@ function MousePaint() { var points = []; - var BRUSH_SIZE = 0.08; + var BRUSH_SIZE = 0.02; var brush = Entities.addEntity({ type: 'Sphere', @@ -244,7 +245,6 @@ function HydraPaint() { var currentTime = 0; - var DISTANCE_FROM_HAND = 2; var minBrushSize = .02; var maxBrushSize = .04 @@ -268,8 +268,8 @@ function HydraPaint() { var STROKE_SMOOTH_FACTOR = 1; - var MIN_DRAW_DISTANCE = 1; - var MAX_DRAW_DISTANCE = 2; + var MIN_DRAW_DISTANCE = 0.2; + var MAX_DRAW_DISTANCE = 0.4; function controller(side, undoButton, redoButton, cycleColorButton, startRideButton) { this.triggerHeld = false; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bdc4b83aa6..dbdcaa122a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2458,8 +2458,9 @@ void Application::update(float deltaTime) { updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... - - DependencyManager::get()->updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them... + + //loop through all the other avatars and simulate them... + DependencyManager::get()->updateOtherAvatars(deltaTime); updateCamera(deltaTime); // handle various camera tweaks like off axis projection updateDialogs(deltaTime); // update various stats dialogs if present @@ -2473,6 +2474,7 @@ void Application::update(float deltaTime) { _physicsEngine.deleteObjects(_entitySimulation.getObjectsToDelete()); _physicsEngine.addObjects(_entitySimulation.getObjectsToAdd()); _physicsEngine.changeObjects(_entitySimulation.getObjectsToChange()); + _entitySimulation.applyActionChanges(); _entitySimulation.unlock(); AvatarManager* avatarManager = DependencyManager::get().data(); @@ -2495,7 +2497,8 @@ void Application::update(float deltaTime) { if (!_aboutToQuit) { PerformanceTimer perfTimer("entities"); - // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk deadlock.) + // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk + // deadlock.) _entitySimulation.handleCollisionEvents(collisionEvents); // NOTE: the _entities.update() call below will wait for lock // and will simulate entity motion (the EntityTree has been given an EntitySimulation). @@ -2508,11 +2511,12 @@ void Application::update(float deltaTime) { PerformanceTimer perfTimer("overlays"); _overlays.update(deltaTime); } - + { PerformanceTimer perfTimer("myAvatar"); updateMyAvatarLookAtPosition(); - DependencyManager::get()->updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes + // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes + DependencyManager::get()->updateMyAvatar(deltaTime); } { diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 22fef37ce3..5a37afca45 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -26,8 +26,8 @@ EntityItemPointer RenderableLineEntityItem::factory(const EntityItemID& entityID void RenderableLineEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableLineEntityItem::render"); assert(getType() == EntityTypes::Line); - glm::vec3 position = getPosition(); - glm::vec3 dimensions = getDimensions(); + // glm::vec3 position = getPosition(); + // glm::vec3 dimensions = getDimensions(); glm::quat rotation = getRotation(); glm::vec4 lineColor(toGlm(getXColor()), getLocalRenderAlpha()); glPushMatrix(); @@ -49,6 +49,6 @@ void RenderableLineEntityItem::render(RenderArgs* args) { } glPopMatrix(); - + RenderableDebugableEntityItem::render(this, args); }; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 2e623de6c5..74405ba586 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -8,10 +8,13 @@ #include "RenderableWebEntityItem.h" +#include + #include #include +#include #include #include #include @@ -64,7 +67,6 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { void RenderableWebEntityItem::render(RenderArgs* args) { QOpenGLContext * currentContext = QOpenGLContext::currentContext(); QSurface * currentSurface = currentContext->surface(); - if (!_webSurface) { _webSurface = new OffscreenQmlSurface(); _webSurface->create(currentContext); @@ -98,16 +100,35 @@ void RenderableWebEntityItem::render(RenderArgs* args) { return; } + if (event->button() == Qt::MouseButton::RightButton) { + if (event->type() == QEvent::MouseButtonPress) { + const QMouseEvent* mouseEvent = static_cast(event); + _lastPress = toGlm(mouseEvent->pos()); + } + } + if (intersection.entityID == getID()) { if (event->button() == Qt::MouseButton::RightButton) { if (event->type() == QEvent::MouseButtonRelease) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack"); - }); + const QMouseEvent* mouseEvent = static_cast(event); + ivec2 dist = glm::abs(toGlm(mouseEvent->pos()) - _lastPress); + if (!glm::any(glm::greaterThan(dist, ivec2(1)))) { + AbstractViewStateInterface::instance()->postLambdaEvent([this] { + QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack"); + }); + } + _lastPress = ivec2(INT_MIN); } return; } + // FIXME doesn't work... double click events not received + if (event->type() == QEvent::MouseButtonDblClick) { + AbstractViewStateInterface::instance()->postLambdaEvent([this] { + _webSurface->getRootItem()->setProperty("url", _sourceUrl); + }); + } + if (event->button() == Qt::MouseButton::MiddleButton) { if (event->type() == QEvent::MouseButtonRelease) { AbstractViewStateInterface::instance()->postLambdaEvent([this] { @@ -133,6 +154,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); } }; + EntityTreeRenderer* renderer = static_cast(args->_renderer); QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent); QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardMouseEvent); @@ -147,6 +169,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { _webSurface->resize(QSize(dims.x, dims.y)); currentContext->makeCurrent(currentSurface); + Glower glow(0); PerformanceTimer perfTimer("RenderableWebEntityItem::render"); assert(getType() == EntityTypes::Web); glm::vec3 position = getPosition(); @@ -181,6 +204,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { } void RenderableWebEntityItem::setSourceUrl(const QString& value) { + qDebug() << "Setting web entity source URL to " << value; if (_sourceUrl != value) { _sourceUrl = value; if (_webSurface) { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 8dad2a0855..559392367c 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -29,6 +29,7 @@ private: OffscreenQmlSurface* _webSurface{ nullptr }; QMetaObject::Connection _connection; uint32_t _texture{ 0 }; + ivec2 _lastPress{ INT_MIN }; QMutex _textureLock; }; diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp new file mode 100644 index 0000000000..f26dd006ff --- /dev/null +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -0,0 +1,101 @@ +// +// EntityActionInterface.cpp +// libraries/entities/src +// +// Created by Seth Alves on 2015-6-4 +// 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 +// + +#include "EntityItem.h" + +#include "EntityActionInterface.h" + + +EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeString) { + QString normalizedActionTypeString = actionTypeString.toLower().remove('-').remove('_'); + if (normalizedActionTypeString == "none") { + return ACTION_TYPE_NONE; + } + if (normalizedActionTypeString == "pulltopoint") { + return ACTION_TYPE_PULL_TO_POINT; + } + + qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; + return ACTION_TYPE_NONE; +} + +QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { + switch(actionType) { + case ACTION_TYPE_NONE: + return "none"; + case ACTION_TYPE_PULL_TO_POINT: + return "pullToPoint"; + } + assert(false); + return "none"; +} + +glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok) { + if (!arguments.contains(argumentName)) { + qDebug() << objectName << "requires argument:" << argumentName; + ok = false; + return vec3(); + } + + QVariant resultV = arguments[argumentName]; + if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { + qDebug() << objectName << "argument" << argumentName << "must be a map"; + ok = false; + return vec3(); + } + + QVariantMap resultVM = resultV.toMap(); + if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) { + qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z"; + ok = false; + return vec3(); + } + + QVariant xV = resultVM["x"]; + QVariant yV = resultVM["y"]; + QVariant zV = resultVM["z"]; + + bool xOk = true; + bool yOk = true; + bool zOk = true; + float x = xV.toFloat(&xOk); + float y = yV.toFloat(&yOk); + float z = zV.toFloat(&zOk); + if (!xOk || !yOk || !zOk) { + qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float."; + ok = false; + return vec3(); + } + + return vec3(x, y, z); +} + + +float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok) { + if (!arguments.contains(argumentName)) { + qDebug() << objectName << "requires argument:" << argumentName; + ok = false; + return 0.0f; + } + + QVariant vV = arguments[argumentName]; + bool vOk = true; + float v = vV.toFloat(&vOk); + + if (!vOk) { + ok = false; + return 0.0f; + } + + return v; +} diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h new file mode 100644 index 0000000000..74efae3239 --- /dev/null +++ b/libraries/entities/src/EntityActionInterface.h @@ -0,0 +1,49 @@ +// +// EntityActionInterface.h +// libraries/entities/src +// +// Created by Seth Alves on 2015-6-2 +// 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 +// + +#ifndef hifi_EntityActionInterface_h +#define hifi_EntityActionInterface_h + +#include + +class EntitySimulation; + +enum EntityActionType { + // keep these synchronized with actionTypeFromString and actionTypeToString + ACTION_TYPE_NONE, + ACTION_TYPE_PULL_TO_POINT +}; + + +class EntityActionInterface { +public: + EntityActionInterface() { } + virtual ~EntityActionInterface() { } + virtual const QUuid& getID() const = 0; + virtual void removeFromSimulation(EntitySimulation* simulation) const = 0; + virtual const EntityItemPointer& getOwnerEntity() const = 0; + virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0; + virtual bool updateArguments(QVariantMap arguments) = 0; + // virtual QByteArray serialize() = 0; + // static EntityActionPointer deserialize(EntityItemPointer ownerEntity, QByteArray data); + + static EntityActionType actionTypeFromString(QString actionTypeString); + static QString actionTypeToString(EntityActionType actionType); + +protected: + + static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); + static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); +}; + +typedef std::shared_ptr EntityActionPointer; + +#endif // hifi_EntityActionInterface_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5ec9a3e1a3..17d0412987 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -25,6 +25,7 @@ #include "EntityItem.h" #include "EntitiesLogging.h" #include "EntityTree.h" +#include "EntitySimulation.h" bool EntityItem::_sendPhysicsUpdates = true; @@ -82,7 +83,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert } EntityItem::~EntityItem() { - // these pointers MUST be NULL at delete, else we probably have a dangling backpointer + // these pointers MUST be correct at delete, else we probably have a dangling backpointer // to this EntityItem in the corresponding data structure. assert(!_simulated); assert(!_element); @@ -517,12 +518,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef EntityPropertyFlags propertyFlags = encodedPropertyFlags; dataAt += propertyFlags.getEncodedLength(); bytesRead += propertyFlags.getEncodedLength(); - bool useMeters = (args.bitstreamVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS); - if (useMeters) { - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); - } else { - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionInDomainUnits); - } + READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); // Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) { @@ -536,22 +532,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } } } else { - if (useMeters) { - READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions); - } else { - READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensionsInDomainUnits); - } + READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions); } READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation); READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity); - if (useMeters) { - READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity); - READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity); - } else { - READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocityInDomainUnits); - READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravityInDomainUnits); - } + READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity); + READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity); if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) { READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration); } @@ -562,11 +549,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime); READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript); READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); - if (useMeters) { - READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); - } else { - READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityInDegrees); - } + READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); + //READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityInDegrees); READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping); READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible); READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions); @@ -1134,11 +1118,6 @@ void EntityItem::computeShapeInfo(ShapeInfo& info) { info.setParams(getShapeType(), 0.5f * getDimensions()); } -void EntityItem::updatePositionInDomainUnits(const glm::vec3& value) { - glm::vec3 position = value * (float)TREE_SCALE; - updatePosition(position); -} - void EntityItem::updatePosition(const glm::vec3& value) { auto delta = glm::distance(_position, value); if (delta > IGNORE_POSITION_DELTA) { @@ -1150,11 +1129,6 @@ void EntityItem::updatePosition(const glm::vec3& value) { } } -void EntityItem::updateDimensionsInDomainUnits(const glm::vec3& value) { - glm::vec3 dimensions = value * (float)TREE_SCALE; - updateDimensions(dimensions); -} - void EntityItem::updateDimensions(const glm::vec3& value) { auto delta = glm::distance(_dimensions, value); if (delta > IGNORE_DIMENSIONS_DELTA) { @@ -1206,11 +1180,6 @@ void EntityItem::updateMass(float mass) { } } -void EntityItem::updateVelocityInDomainUnits(const glm::vec3& value) { - glm::vec3 velocity = value * (float)TREE_SCALE; - updateVelocity(velocity); -} - void EntityItem::updateVelocity(const glm::vec3& value) { auto delta = glm::distance(_velocity, value); if (delta > IGNORE_LINEAR_VELOCITY_DELTA) { @@ -1236,11 +1205,6 @@ void EntityItem::updateDamping(float value) { } } -void EntityItem::updateGravityInDomainUnits(const glm::vec3& value) { - glm::vec3 gravity = value * (float) TREE_SCALE; - updateGravity(gravity); -} - void EntityItem::updateGravity(const glm::vec3& value) { auto delta = glm::distance(_gravity, value); if (delta > IGNORE_GRAVITY_DELTA) { @@ -1343,3 +1307,46 @@ void EntityItem::updateSimulatorID(const QUuid& value) { _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; } } + +bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) { + assert(action); + const QUuid& actionID = action->getID(); + assert(!_objectActions.contains(actionID) || _objectActions[actionID] == action); + _objectActions[actionID] = action; + + assert(action->getOwnerEntity().get() == this); + + simulation->addAction(action); + + return false; +} + +bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments) { + if (!_objectActions.contains(actionID)) { + return false; + } + EntityActionPointer action = _objectActions[actionID]; + return action->updateArguments(arguments); +} + +bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionID) { + if (_objectActions.contains(actionID)) { + EntityActionPointer action = _objectActions[actionID]; + _objectActions.remove(actionID); + action->setOwnerEntity(nullptr); + action->removeFromSimulation(simulation); + return true; + } + return false; +} + +void EntityItem::clearActions(EntitySimulation* simulation) { + QHash::iterator i = _objectActions.begin(); + while (i != _objectActions.end()) { + const QUuid id = i.key(); + EntityActionPointer action = _objectActions[id]; + i = _objectActions.erase(i); + action->setOwnerEntity(nullptr); + action->removeFromSimulation(simulation); + } +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 5f37510b67..a32f3f17a7 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -23,9 +23,10 @@ #include #include -#include "EntityItemID.h" -#include "EntityItemProperties.h" -#include "EntityItemPropertiesDefaults.h" +#include "EntityItemID.h" +#include "EntityItemProperties.h" +#include "EntityItemPropertiesDefaults.h" +#include "EntityActionInterface.h" #include "EntityTypes.h" class EntitySimulation; @@ -88,7 +89,7 @@ public: }; DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly - + EntityItem(const EntityItemID& entityItemID); EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); virtual ~EntityItem(); @@ -100,7 +101,7 @@ public: // methods for getting/setting all properties of an entity virtual EntityItemProperties getProperties() const; - + /// returns true if something changed virtual bool setProperties(const EntityItemProperties& properties); @@ -114,7 +115,7 @@ public: /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } - void setLastEdited(quint64 lastEdited) + void setLastEdited(quint64 lastEdited) { _lastEdited = _lastUpdated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } float getEditedAgo() const /// Elapsed seconds since this entity was last edited { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } @@ -128,26 +129,26 @@ public: // TODO: eventually only include properties changed since the params.lastViewFrustumSent time virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - + virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData) const; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, + int& propertyCount, OctreeElement::AppendState& appendState) const { /* do nothing*/ }; - static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead, + static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); virtual int readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData) + EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { return 0; } virtual void render(RenderArgs* args) { } // by default entity items don't know how to render @@ -159,7 +160,7 @@ public: // perform update virtual void update(const quint64& now) { _lastUpdated = now; } quint64 getLastUpdated() const { return _lastUpdated; } - + // perform linear extrapolation for SimpleEntitySimulation void simulate(const quint64& now); void simulateKinematicMotion(float timeElapsed, bool setFlags=true); @@ -167,19 +168,17 @@ public: virtual bool needsToCallUpdate() const { return false; } virtual void debugDump() const; - + virtual bool supportsDetailedRayIntersection() const { return false; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { return true; } // attributes applicable to all entity types EntityTypes::EntityType getType() const { return _type; } const glm::vec3& getPosition() const { return _position; } /// get position in meters - - void setPosition(const glm::vec3& value) { - _position = value; - } + + void setPosition(const glm::vec3& value) { _position = value; } glm::vec3 getCenter() const; @@ -214,7 +213,7 @@ public: const glm::vec3& getAcceleration() const { return _acceleration; } /// get acceleration in meters/second/second void setAcceleration(const glm::vec3& value) { _acceleration = value; } /// acceleration in meters/second/second bool hasAcceleration() const { return _acceleration != ENTITY_ITEM_ZERO_VEC3; } - + float getDamping() const { return _damping; } void setDamping(float value) { _damping = value; } @@ -236,7 +235,7 @@ public: /// is this entity mortal, in that it has a lifetime set, and will automatically be deleted when that lifetime expires bool isMortal() const { return _lifetime != ENTITY_ITEM_IMMORTAL_LIFETIME; } - + /// age of this entity in seconds float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; } bool lifetimeHasExpired() const; @@ -255,7 +254,7 @@ public: const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } /// registration point as ratio of entity /// registration point as ratio of entity - void setRegistrationPoint(const glm::vec3& value) + void setRegistrationPoint(const glm::vec3& value) { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); } const glm::vec3& getAngularVelocity() const { return _angularVelocity; } @@ -283,7 +282,7 @@ public: bool getLocked() const { return _locked; } void setLocked(bool value) { _locked = value; } - + const QString& getUserData() const { return _userData; } void setUserData(const QString& value) { _userData = value; } @@ -291,13 +290,13 @@ public: void setSimulatorID(const QUuid& value); void updateSimulatorID(const QUuid& value); quint64 getSimulatorIDChangedTime() const { return _simulatorIDChangedTime; } - + const QString& getMarketplaceID() const { return _marketplaceID; } void setMarketplaceID(const QString& value) { _marketplaceID = value; } - - // TODO: get rid of users of getRadius()... + + // TODO: get rid of users of getRadius()... float getRadius() const; - + virtual bool contains(const glm::vec3& point) const; virtual bool isReadyToComputeShape() { return true; } @@ -308,22 +307,17 @@ public: virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags - void updatePositionInDomainUnits(const glm::vec3& value); void updatePosition(const glm::vec3& value); - void updateDimensionsInDomainUnits(const glm::vec3& value); void updateDimensions(const glm::vec3& value); void updateRotation(const glm::quat& rotation); void updateDensity(float value); void updateMass(float value); - void updateVelocityInDomainUnits(const glm::vec3& value); void updateVelocity(const glm::vec3& value); void updateDamping(float value); void updateRestitution(float value); void updateFriction(float value); - void updateGravityInDomainUnits(const glm::vec3& value); void updateGravity(const glm::vec3& value); void updateAngularVelocity(const glm::vec3& value); - void updateAngularVelocityInDegrees(const glm::vec3& value) { updateAngularVelocity(glm::radians(value)); } void updateAngularDamping(float value); void updateIgnoreForCollisions(bool value); void updateCollisionsWillMove(bool value); @@ -333,11 +327,11 @@ public: uint32_t getDirtyFlags() const { return _dirtyFlags; } void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; } - + bool isMoving() const; void* getPhysicsInfo() const { return _physicsInfo; } - + void setPhysicsInfo(void* data) { _physicsInfo = data; } EntityTreeElement* getElement() const { return _element; } @@ -353,12 +347,18 @@ public: void getAllTerseUpdateProperties(EntityItemProperties& properties) const; + bool addAction(EntitySimulation* simulation, EntityActionPointer action); + bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments); + bool removeAction(EntitySimulation* simulation, const QUuid& actionID); + void clearActions(EntitySimulation* simulation); + protected: static bool _sendPhysicsUpdates; EntityTypes::EntityType _type; QUuid _id; - quint64 _lastSimulated; // last time this entity called simulate(), this includes velocity, angular velocity, and physics changes + quint64 _lastSimulated; // last time this entity called simulate(), this includes velocity, angular velocity, + // and physics changes quint64 _lastUpdated; // last time this entity called update(), this includes animations and non-physics changes quint64 _lastEdited; // last official local or remote edit time quint64 _lastBroadcast; // the last time we sent an edit packet about this entity @@ -409,12 +409,12 @@ protected: // // damping = 1 - exp(-1 / timescale) // - - // NOTE: Radius support is obsolete, but these private helper functions are available for this class to + + // NOTE: Radius support is obsolete, but these private helper functions are available for this class to // parse old data streams - + /// set radius in domain scale units (0.0 - 1.0) this will also reset dimensions to be equal for each axis - void setRadius(float value); + void setRadius(float value); // DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about. uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation @@ -423,6 +423,8 @@ protected: EntityTreeElement* _element = nullptr; // set by EntityTreeElement void* _physicsInfo = nullptr; // set by EntitySimulation bool _simulated; // set by EntitySimulation + + QHash _objectActions; }; #endif // hifi_EntityItem_h diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 5a897f7fe7..836e7f5225 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -11,13 +11,14 @@ #include -#include "EntityScriptingInterface.h" #include "EntityTree.h" #include "LightEntityItem.h" #include "ModelEntityItem.h" #include "ZoneEntityItem.h" #include "EntitiesLogging.h" +#include "EntitySimulation.h" +#include "EntityScriptingInterface.h" EntityScriptingInterface::EntityScriptingInterface() : _entityTree(NULL) @@ -83,7 +84,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties entity->setLastBroadcast(usecTimestampNow()); // This Node is creating a new object. If it's in motion, set this Node as the simulator. bidForSimulationOwnership(propertiesWithSimID); - entity->setSimulatorID(propertiesWithSimID.getSimulatorID()); // and make note of it now, so we can act on it right away. + // and make note of it now, so we can act on it right away. + entity->setSimulatorID(propertiesWithSimID.getSimulatorID()); } else { qCDebug(entities) << "script failed to add new Entity to local Octree"; success = false; @@ -105,7 +107,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit _entityTree->lockForRead(); EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity)); - + if (entity) { results = entity->getProperties(); @@ -124,7 +126,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit } _entityTree->unlock(); } - + return results; } @@ -228,7 +230,7 @@ QVector EntityScriptingInterface::findEntities(const glm::vec3& center, f QVector entities; _entityTree->findEntities(center, radius, entities); _entityTree->unlock(); - + foreach (EntityItemPointer entity, entities) { result << entity->getEntityItemID(); } @@ -244,7 +246,7 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn QVector entities; _entityTree->findEntities(box, entities); _entityTree->unlock(); - + foreach (EntityItemPointer entity, entities) { result << entity->getEntityItemID(); } @@ -401,7 +403,6 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra } } - bool EntityScriptingInterface::setVoxels(QUuid entityID, std::function actor) { if (!_entityTree) { @@ -439,23 +440,83 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID, return true; } - bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) { return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) { polyVoxEntity.setSphere(center, radius, value); }); } - bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& position, int value) { return setVoxels(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) { polyVoxEntity.setVoxelInVolume(position, value); }); } - bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) { return setVoxels(entityID, [value](PolyVoxEntityItem& polyVoxEntity) { polyVoxEntity.setAll(value); }); } + + +bool EntityScriptingInterface::actionWorker(const QUuid& entityID, + std::function actor) { + if (!_entityTree) { + return false; + } + + _entityTree->lockForWrite(); + + EntitySimulation* simulation = _entityTree->getSimulation(); + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + if (!entity) { + qDebug() << "actionWorker -- unknown entity" << entityID; + _entityTree->unlock(); + return false; + } + + if (!simulation) { + qDebug() << "actionWorker -- no simulation" << entityID; + _entityTree->unlock(); + return false; + } + + bool success = actor(simulation, entity); + _entityTree->unlock(); + return success; +} + + +QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, + const QUuid& entityID, + const QVariantMap& arguments) { + QUuid actionID = QUuid::createUuid(); + bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { + EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString); + if (actionType == ACTION_TYPE_NONE) { + return false; + } + if (simulation->actionFactory(actionType, actionID, entity, arguments)) { + return true; + } + return false; + }); + if (success) { + return actionID; + } + return QUuid(); +} + + +bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments) { + return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { + return entity->updateAction(simulation, actionID, arguments); + }); +} + + +bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& actionID) { + return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { + return entity->removeAction(simulation, actionID); + }); +} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 738a011b15..c6bc43c8c6 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -54,14 +54,14 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende Q_OBJECT public: EntityScriptingInterface(); - + EntityEditPacketSender* getEntityPacketSender() const { return (EntityEditPacketSender*)getPacketSender(); } virtual NodeType_t getServerNodeType() const { return NodeType::EntityServer; } virtual OctreeEditPacketSender* createPacketSender() { return new EntityEditPacketSender(); } void setEntityTree(EntityTree* modelTree); EntityTree* getEntityTree(EntityTree*) { return _entityTree; } - + public slots: // returns true if the DomainServer will allow this Node/Avatar to make changes @@ -88,11 +88,11 @@ public slots: /// will return a EntityItemID.isKnownID = false if no models are in the radius /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const; - + /// finds models within the search sphere specified by the center point and radius /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const; - + /// finds models within the search sphere specified by the center point and radius /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const; @@ -118,13 +118,16 @@ public slots: Q_INVOKABLE void setSendPhysicsUpdates(bool value); Q_INVOKABLE bool getSendPhysicsUpdates() const; - bool setVoxels(QUuid entityID, std::function actor); Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value); Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); Q_INVOKABLE void dumpTree() const; + Q_INVOKABLE QUuid addAction(const QString& actionTypeString, const QUuid& entityID, const QVariantMap& arguments); + Q_INVOKABLE bool updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments); + Q_INVOKABLE bool deleteAction(const QUuid& entityID, const QUuid& actionID); + signals: void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); @@ -152,6 +155,8 @@ signals: void clearingEntities(); private: + bool actionWorker(const QUuid& entityID, std::function actor); + bool setVoxels(QUuid entityID, std::function actor); void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index bce16ba1c2..0c9b3efee6 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -24,9 +24,9 @@ typedef QSet SetOfEntities; typedef QVector VectorOfEntities; -// the EntitySimulation needs to know when these things change on an entity, +// the EntitySimulation needs to know when these things change on an entity, // so it can sort EntityItem or relay its state to the PhysicsEngine. -const int DIRTY_SIMULATION_FLAGS = +const int DIRTY_SIMULATION_FLAGS = EntityItem::DIRTY_POSITION | EntityItem::DIRTY_ROTATION | EntityItem::DIRTY_LINEAR_VELOCITY | @@ -56,6 +56,15 @@ public: friend class EntityTree; + virtual EntityActionPointer actionFactory(EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { return nullptr; } + virtual void addAction(EntityActionPointer action) { _actionsToAdd += action; } + virtual void removeAction(const QUuid actionID) { _actionsToRemove += actionID; } + virtual void removeActions(QList actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; } + virtual void applyActionChanges() { _actionsToAdd.clear(); _actionsToRemove.clear(); } + protected: // these only called by the EntityTree? /// \param entity pointer to EntityItem to be added /// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked @@ -112,8 +121,12 @@ protected: SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete) SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion -private: + private: void moveSimpleKinematics(); + + protected: + QList _actionsToAdd; + QList _actionsToRemove; }; #endif // hifi_EntitySimulation_h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 937472820e..363f3c56d7 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -279,7 +279,7 @@ void EntityTree::setSimulation(EntitySimulation* simulation) { if (simulation) { // assert that the simulation's backpointer has already been properly connected assert(simulation->getEntityTree() == this); - } + } if (_simulation && _simulation != simulation) { // It's important to clearEntities() on the simulation since taht will update each // EntityItem::_simulationState correctly so as to not confuse the next _simulation. @@ -381,6 +381,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) } if (_simulation) { + theEntity->clearActions(_simulation); _simulation->removeEntity(theEntity); } } @@ -681,7 +682,6 @@ void EntityTree::update() { QSet idsToDelete; for (auto entity : pendingDeletes) { - assert(!entity->getPhysicsInfo()); // TODO: Andrew to remove this after testing idsToDelete.insert(entity->getEntityItemID()); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 0d99c8e82d..1ad3ef6b1c 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -31,7 +31,7 @@ public: class EntityItemFBXService { public: virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) = 0; - virtual const Model* getModelForEntityItem(EntityItemPointer entityItem) = 0; + virtual const Model* getModelForEntityItem(EntityItemPointer entityItem) = 0; virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) = 0; }; @@ -63,23 +63,23 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool getWantSVOfileVersions() const { return true; } virtual PacketType expectedDataPacketType() const { return PacketTypeEntityData; } - virtual bool canProcessVersion(PacketVersion thisVersion) const - { return thisVersion >= VERSION_ENTITIES_SUPPORT_SPLIT_MTU; } // we support all versions with split mtu + virtual bool canProcessVersion(PacketVersion thisVersion) const + { return thisVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS; } virtual bool handlesEditPacketType(PacketType packetType) const; virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); virtual bool rootElementHasData() const { return true; } - + // the root at least needs to store the number of entities in the packet/buffer virtual int minimumRequiredRootDataBytes() const { return sizeof(uint16_t); } virtual bool suppressEmptySubtrees() const { return false; } virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const; virtual bool mustIncludeAllChildData() const { return false; } - virtual bool versionHasSVOfileBreaks(PacketVersion thisVersion) const + virtual bool versionHasSVOfileBreaks(PacketVersion thisVersion) const { return thisVersion >= VERSION_ENTITIES_HAS_FILE_BREAKS; } - + virtual void update(); // The newer API... @@ -111,13 +111,13 @@ public: /// \param foundEntities[out] vector of EntityItemPointer /// \remark Side effect: any initial contents in foundEntities will be lost void findEntities(const glm::vec3& center, float radius, QVector& foundEntities); - + /// finds all entities that touch a cube /// \param cube the query cube in world-frame (meters) /// \param foundEntities[out] vector of non-EntityItemPointer /// \remark Side effect: any initial contents in entities will be lost void findEntities(const AACube& cube, QVector& foundEntities); - + /// finds all entities that touch a box /// \param box the query box in world-frame (meters) /// \param foundEntities[out] vector of non-EntityItemPointer @@ -129,13 +129,13 @@ public: bool hasAnyDeletedEntities() const { return _recentlyDeletedEntityItemIDs.size() > 0; } bool hasEntitiesDeletedSince(quint64 sinceTime); - bool encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, + bool encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, unsigned char* packetData, size_t maxLength, size_t& outputLength); void forgetEntitiesDeletedBefore(quint64 sinceTime); int processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode); int processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode); - + EntityItemFBXService* getFBXService() const { return _fbxService; } void setFBXService(EntityItemFBXService* service) { _fbxService = service; } const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) { @@ -144,7 +144,7 @@ public: const Model* getModelForEntityItem(EntityItemPointer entityItem) { return _fbxService ? _fbxService->getModelForEntityItem(entityItem) : NULL; } - + EntityTreeElement* getContainingElement(const EntityItemID& entityItemID) /*const*/; void setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element); void debugDumpMap(); @@ -158,13 +158,14 @@ public: void emitEntityScriptChanging(const EntityItemID& entityItemID); void setSimulation(EntitySimulation* simulation); - + EntitySimulation* getSimulation() const { return _simulation; } + bool wantEditLogging() const { return _wantEditLogging; } void setWantEditLogging(bool value) { _wantEditLogging = value; } bool writeToMap(QVariantMap& entityDescription, OctreeElement* element, bool skipDefaultValues); bool readFromMap(QVariantMap& entityDescription); - + float getContentsLargestDimension(); signals: @@ -177,7 +178,7 @@ signals: private: void processRemovedEntities(const DeleteEntityOperator& theOperator); - bool updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& properties, + bool updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& properties, EntityTreeElement* containingElement, const SharedNodePointer& senderNode = SharedNodePointer(nullptr)); static bool findNearPointOperation(OctreeElement* element, void* extraData); @@ -198,7 +199,7 @@ private: QHash _entityToElementMap; EntitySimulation* _simulation; - + bool _wantEditLogging = false; void maybeNotifyNewCollisionSoundURL(const QString& oldCollisionSoundURL, const QString& newCollisionSoundURL); }; diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 90fb035d7b..05d1904384 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -33,7 +33,7 @@ public: _totalItems(0), _movingItems(0) { } - + QList _movingEntities; int _totalElements; int _totalItems; @@ -42,8 +42,8 @@ public: class EntityTreeElementExtraEncodeData { public: - EntityTreeElementExtraEncodeData() : - elementCompleted(false), + EntityTreeElementExtraEncodeData() : + elementCompleted(false), subtreeCompleted(false), entities() { memset(childCompleted, 0, sizeof(childCompleted)); @@ -140,7 +140,7 @@ public: virtual bool canRayIntersect() const { return hasEntities(); } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking, float distanceToElementCube); virtual bool findSpherePenetration(const glm::vec3& center, float radius, @@ -148,10 +148,11 @@ public: const EntityItems& getEntities() const { return *_entityItems; } EntityItems& getEntities() { return *_entityItems; } - + bool hasEntities() const { return _entityItems ? _entityItems->size() > 0 : false; } void setTree(EntityTree* tree) { _myTree = tree; } + EntityTree* getTree() const { return _myTree; } bool updateEntity(const EntityItem& entity); void addEntityItem(EntityItemPointer entity); diff --git a/libraries/physics/src/DynamicCharacterController.cpp b/libraries/physics/src/DynamicCharacterController.cpp index 1fca236f63..ebdba5f754 100644 --- a/libraries/physics/src/DynamicCharacterController.cpp +++ b/libraries/physics/src/DynamicCharacterController.cpp @@ -156,7 +156,7 @@ void DynamicCharacterController::playerStep(btCollisionWorld* dynaWorld, btScala velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp; } _rigidBody->setLinearVelocity(actualVelocity + tau * velocityCorrection); - } + } } } diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp new file mode 100644 index 0000000000..6ff4098ba8 --- /dev/null +++ b/libraries/physics/src/ObjectAction.cpp @@ -0,0 +1,35 @@ +// +// ObjectAction.cpp +// libraries/physcis/src +// +// Created by Seth Alves 2015-6-2 +// 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 +// + +#include "EntitySimulation.h" + +#include "ObjectAction.h" + +ObjectAction::ObjectAction(QUuid id, EntityItemPointer ownerEntity) : + btActionInterface(), + _id(id), + _active(false), + _ownerEntity(ownerEntity) { +} + +ObjectAction::~ObjectAction() { +} + +void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { + qDebug() << "ObjectAction::updateAction called"; +} + +void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { +} + +void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const { + simulation->removeAction(_id); +} diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h new file mode 100644 index 0000000000..10b086c07d --- /dev/null +++ b/libraries/physics/src/ObjectAction.h @@ -0,0 +1,50 @@ +// +// ObjectAction.h +// libraries/physcis/src +// +// Created by Seth Alves 2015-6-2 +// 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 +// +// http://bulletphysics.org/Bullet/BulletFull/classbtActionInterface.html + +#ifndef hifi_ObjectAction_h +#define hifi_ObjectAction_h + +#include + +#include + +#include + +class ObjectAction : public btActionInterface, public EntityActionInterface { +public: + ObjectAction(QUuid id, EntityItemPointer ownerEntity); + virtual ~ObjectAction(); + + const QUuid& getID() const { return _id; } + virtual void removeFromSimulation(EntitySimulation* simulation) const; + virtual const EntityItemPointer& getOwnerEntity() const { return _ownerEntity; } + virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; } + virtual bool updateArguments(QVariantMap arguments) { return false; } + + // these are from btActionInterface + virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); + virtual void debugDraw(btIDebugDraw* debugDrawer); + +private: + QUuid _id; + QReadWriteLock _lock; + +protected: + bool tryLockForRead() { return _lock.tryLockForRead(); } + void lockForWrite() { _lock.lockForWrite(); } + void unlock() { _lock.unlock(); } + + bool _active; + EntityItemPointer _ownerEntity; +}; + +#endif // hifi_ObjectAction_h diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp new file mode 100644 index 0000000000..78f202a24f --- /dev/null +++ b/libraries/physics/src/ObjectActionPullToPoint.cpp @@ -0,0 +1,69 @@ +// +// ObjectActionPullToPoint.cpp +// libraries/physics/src +// +// Created by Seth Alves 2015-6-2 +// 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 +// + +#include "ObjectMotionState.h" +#include "BulletUtil.h" + +#include "ObjectActionPullToPoint.h" + +ObjectActionPullToPoint::ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity) : + ObjectAction(id, ownerEntity) { + #if WANT_DEBUG + qDebug() << "ObjectActionPullToPoint::ObjectActionPullToPoint"; + #endif +} + +ObjectActionPullToPoint::~ObjectActionPullToPoint() { + #if WANT_DEBUG + qDebug() << "ObjectActionPullToPoint::~ObjectActionPullToPoint"; + #endif +} + +void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { + if (!tryLockForRead()) { + // don't risk hanging the thread running the physics simulation + return; + } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + + if (_active && physicsInfo) { + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState->getRigidBody(); + if (rigidBody) { + glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition()); + float offsetLength = glm::length(offset); + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * _speed; + rigidBody->setLinearVelocity(glmToBullet(newVelocity)); + rigidBody->activate(); + } else { + rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); + } + } + } + unlock(); +} + + +bool ObjectActionPullToPoint::updateArguments(QVariantMap arguments) { + bool ok = true; + glm::vec3 target = EntityActionInterface::extractVec3Argument("pull-to-point action", arguments, "target", ok); + float speed = EntityActionInterface::extractFloatArgument("pull-to-point action", arguments, "speed", ok); + if (ok) { + lockForWrite(); + _target = target; + _speed = speed; + _active = true; + unlock(); + return true; + } + return false; +} diff --git a/libraries/physics/src/ObjectActionPullToPoint.h b/libraries/physics/src/ObjectActionPullToPoint.h new file mode 100644 index 0000000000..3aca70d640 --- /dev/null +++ b/libraries/physics/src/ObjectActionPullToPoint.h @@ -0,0 +1,34 @@ +// +// ObjectActionPullToPoint.h +// libraries/physics/src +// +// Created by Seth Alves 2015-6-3 +// 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 +// + +#ifndef hifi_ObjectActionPullToPoint_h +#define hifi_ObjectActionPullToPoint_h + +#include + +#include +#include "ObjectAction.h" + +class ObjectActionPullToPoint : public ObjectAction { +public: + ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity); + virtual ~ObjectActionPullToPoint(); + + virtual bool updateArguments(QVariantMap arguments); + virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); + +private: + + glm::vec3 _target; + float _speed; +}; + +#endif // hifi_ObjectActionPullToPoint_h diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 314a0272d2..b17dc67cff 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -39,11 +39,12 @@ enum MotionStateType { // and re-added to the physics engine and "easy" which just updates the body properties. const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES | - EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP); + EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP | + EntityItem::DIRTY_MATERIAL); // These are the set of incoming flags that the PhysicsEngine needs to hear about: -const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS | - EntityItem::DIRTY_MATERIAL | (uint32_t)EntityItem::DIRTY_PHYSICS_ACTIVATION; +const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS | + EntityItem::DIRTY_PHYSICS_ACTIVATION); // These are the outgoing flags that the PhysicsEngine can affect: const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index b615cf0c77..711c5e49da 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -9,10 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "PhysicalEntitySimulation.h" #include "PhysicsHelpers.h" #include "PhysicsLogging.h" #include "ShapeManager.h" +#include "ObjectActionPullToPoint.h" + +#include "PhysicalEntitySimulation.h" PhysicalEntitySimulation::PhysicalEntitySimulation() { } @@ -36,7 +38,7 @@ void PhysicalEntitySimulation::init( // begin EntitySimulation overrides void PhysicalEntitySimulation::updateEntitiesInternal(const quint64& now) { - // TODO: add back non-physical kinematic objects and step them forward here + // Do nothing here because the "internal" update the PhysicsEngine::stepSimualtion() which is done elsewhere. } void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { @@ -232,3 +234,37 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE } } +EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { + EntityActionPointer action = nullptr; + switch (type) { + case ACTION_TYPE_NONE: + return nullptr; + case ACTION_TYPE_PULL_TO_POINT: + action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity); + break; + } + + bool ok = action->updateArguments(arguments); + if (ok) { + ownerEntity->addAction(this, action); + return action; + } + + action = nullptr; + return action; +} + +void PhysicalEntitySimulation::applyActionChanges() { + if (_physicsEngine) { + foreach (EntityActionPointer actionToAdd, _actionsToAdd) { + _physicsEngine->addAction(actionToAdd); + } + foreach (QUuid actionToRemove, _actionsToRemove) { + _physicsEngine->removeAction(actionToRemove); + } + } + EntitySimulation::applyActionChanges(); +} diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 1aae27962a..82b0f8ad51 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -32,6 +32,12 @@ public: void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender); + virtual EntityActionPointer actionFactory(EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments); + virtual void applyActionChanges(); + protected: // only called by EntitySimulation // overrides for EntitySimulation virtual void updateEntitiesInternal(const quint64& now); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 30dcaf9b30..e5c974dfbc 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -44,7 +44,6 @@ PhysicsEngine::~PhysicsEngine() { if (_characterController) { _characterController->setDynamicsWorld(nullptr); } - // TODO: delete engine components... if we ever plan to create more than one instance delete _collisionConfig; delete _collisionDispatcher; delete _broadphaseFilter; @@ -168,11 +167,11 @@ void PhysicsEngine::deleteObjects(VectorOfMotionStates& objects) { } } -// Same as above, but takes a Set instead of a Vector and ommits some cleanup operations. Only called during teardown. +// Same as above, but takes a Set instead of a Vector. Should only be called during teardown. void PhysicsEngine::deleteObjects(SetOfMotionStates& objects) { for (auto object : objects) { btRigidBody* body = object->getRigidBody(); - _dynamicsWorld->removeRigidBody(body); + removeObject(object); // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. object->setRigidBody(nullptr); @@ -437,3 +436,28 @@ int16_t PhysicsEngine::getCollisionMask(int16_t group) const { const int16_t* mask = _collisionMasks.find(btHashInt((int)group)); return mask ? *mask : COLLISION_MASK_DEFAULT; } + +void PhysicsEngine::addAction(EntityActionPointer action) { + assert(action); + const QUuid& actionID = action->getID(); + if (_objectActions.contains(actionID)) { + assert(_objectActions[actionID] == action); + return; + } + + _objectActions[actionID] = action; + + // bullet needs a pointer to the action, but it doesn't use shared pointers. + // is there a way to bump the reference count? + ObjectAction* objectAction = static_cast(action.get()); + _dynamicsWorld->addAction(objectAction); +} + +void PhysicsEngine::removeAction(const QUuid actionID) { + if (_objectActions.contains(actionID)) { + EntityActionPointer action = _objectActions[actionID]; + ObjectAction* objectAction = static_cast(action.get()); + _dynamicsWorld->removeAction(objectAction); + _objectActions.remove(actionID); + } +} diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index db00a2ba01..6bb6ef8305 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -24,6 +24,7 @@ #include "DynamicCharacterController.h" #include "ObjectMotionState.h" #include "ThreadSafeDynamicsWorld.h" +#include "ObjectAction.h" const float HALF_SIMULATION_EXTENT = 512.0f; // meters @@ -93,6 +94,9 @@ public: int16_t getCollisionMask(int16_t group) const; + void addAction(EntityActionPointer action); + void removeAction(const QUuid actionID); + private: void removeContacts(ObjectMotionState* motionState); @@ -120,6 +124,9 @@ private: QUuid _sessionID; CollisionEvents _collisionEvents; + + QHash _objectActions; + btHashMap _collisionMasks; }; diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 699b8a0475..f6a6dfb3e6 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -19,11 +19,8 @@ // translates between ShapeInfo and btShape -// TODO: rename this to ShapeFactory namespace ShapeFactory { - btConvexHullShape* createConvexHull(const QVector& points); - btCollisionShape* createShapeFromInfo(const ShapeInfo& info); }; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 175eff059f..58ecf4adb2 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -54,7 +54,6 @@ public: bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString("")); const QString& getScriptName() const { return _scriptName; } - void cleanupMenuItems(); QScriptValue registerGlobalObject(const QString& name, QObject* object); /// registers a global object by name void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,