From 31f72107daf7000ab2f54bb214e7e0288bdc7633 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 09:26:48 -0800 Subject: [PATCH 01/10] remove support for old pre-entity svo files --- libraries/entities/src/EntityTree.h | 3 +- libraries/entities/src/ModelEntityItem.cpp | 127 --------------------- libraries/entities/src/ModelEntityItem.h | 3 - 3 files changed, 2 insertions(+), 131 deletions(-) diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index e8c8a93165..405cbecd99 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -60,7 +60,8 @@ 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 true; } // we support all versions + virtual bool canProcessVersion(PacketVersion thisVersion) const + { return thisVersion >= VERSION_ENTITIES_SUPPORT_SPLIT_MTU; } // we support all versions with split mtu 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); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 4cdec0f29a..2c3dcd0600 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -81,17 +81,6 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } - - -int ModelEntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { - if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) { - return oldVersionReadEntityDataFromBuffer(data, bytesLeftToRead, args); - } - - // let our base class do most of the work... it will call us back for our porition... - return EntityItem::readEntityDataFromBuffer(data, bytesLeftToRead, args); -} - int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { @@ -131,122 +120,6 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, return bytesRead; } -int ModelEntityItem::oldVersionReadEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { - - int bytesRead = 0; - if (bytesLeftToRead >= expectedBytes()) { - int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; - - const unsigned char* dataAt = data; - - // id - // this old bitstream format had 32bit IDs. They are obsolete and need to be replaced with our new UUID - // format. We can simply read and ignore the old ID since they should not be repeated. This code should only - // run on loading from an old file. - quint32 oldID; - memcpy(&oldID, dataAt, sizeof(oldID)); - dataAt += sizeof(oldID); - bytesRead += sizeof(oldID); - _id = QUuid::createUuid(); - - // _lastUpdated - memcpy(&_lastUpdated, dataAt, sizeof(_lastUpdated)); - dataAt += sizeof(_lastUpdated); - bytesRead += sizeof(_lastUpdated); - _lastUpdated -= clockSkew; - - // _lastEdited - memcpy(&_lastEdited, dataAt, sizeof(_lastEdited)); - dataAt += sizeof(_lastEdited); - bytesRead += sizeof(_lastEdited); - _lastEdited -= clockSkew; - _created = _lastEdited; // NOTE: old models didn't have age or created time, assume their last edit was a create - - QString ageAsString = formatSecondsElapsed(getAge()); - qDebug() << "Loading old model file, _created = _lastEdited =" << _created - << " age=" << getAge() << "seconds - " << ageAsString - << "old ID=" << oldID << "new ID=" << _id; - - // radius - float radius; - memcpy(&radius, dataAt, sizeof(radius)); - dataAt += sizeof(radius); - bytesRead += sizeof(radius); - setRadius(radius); - - // position - memcpy(&_position, dataAt, sizeof(_position)); - dataAt += sizeof(_position); - bytesRead += sizeof(_position); - - // color - memcpy(&_color, dataAt, sizeof(_color)); - dataAt += sizeof(_color); - bytesRead += sizeof(_color); - - // TODO: how to handle this? Presumable, this would only ever be true if the model file was saved with - // a model being in a shouldBeDeleted state. Which seems unlikely. But if it happens, maybe we should delete the entity after loading? - // shouldBeDeleted - bool shouldBeDeleted = false; - memcpy(&shouldBeDeleted, dataAt, sizeof(shouldBeDeleted)); - dataAt += sizeof(shouldBeDeleted); - bytesRead += sizeof(shouldBeDeleted); - if (shouldBeDeleted) { - qDebug() << "UNEXPECTED - read shouldBeDeleted=TRUE from an old format file"; - } - - // modelURL - uint16_t modelURLLength; - memcpy(&modelURLLength, dataAt, sizeof(modelURLLength)); - dataAt += sizeof(modelURLLength); - bytesRead += sizeof(modelURLLength); - QString modelURLString((const char*)dataAt); - setModelURL(modelURLString); - dataAt += modelURLLength; - bytesRead += modelURLLength; - - // rotation - int bytes = unpackOrientationQuatFromBytes(dataAt, _rotation); - dataAt += bytes; - bytesRead += bytes; - - if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ANIMATION) { - // animationURL - uint16_t animationURLLength; - memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); - dataAt += sizeof(animationURLLength); - bytesRead += sizeof(animationURLLength); - QString animationURLString((const char*)dataAt); - setAnimationURL(animationURLString); - dataAt += animationURLLength; - bytesRead += animationURLLength; - - // animationIsPlaying - bool animationIsPlaying; - memcpy(&animationIsPlaying, dataAt, sizeof(animationIsPlaying)); - dataAt += sizeof(animationIsPlaying); - bytesRead += sizeof(animationIsPlaying); - setAnimationIsPlaying(animationIsPlaying); - - // animationFrameIndex - float animationFrameIndex; - memcpy(&animationFrameIndex, dataAt, sizeof(animationFrameIndex)); - dataAt += sizeof(animationFrameIndex); - bytesRead += sizeof(animationFrameIndex); - setAnimationFrameIndex(animationFrameIndex); - - // animationFPS - float animationFPS; - memcpy(&animationFPS, dataAt, sizeof(animationFPS)); - dataAt += sizeof(animationFPS); - bytesRead += sizeof(animationFPS); - setAnimationFPS(animationFPS); - } - } - return bytesRead; -} - - // TODO: eventually only include properties changed since the params.lastViewFrustumSent time EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index dc236644b7..a607745475 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -40,7 +40,6 @@ public: OctreeElement::AppendState& appendState) const; - virtual int readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData); @@ -116,8 +115,6 @@ public: protected: - /// For reading models from pre V3 bitstreams - int oldVersionReadEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); bool isAnimatingSomething() const; rgbColor _color; From d173afaa70dc2bc68cdad9d6b7cfefb4c21afa81 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 11:01:46 -0800 Subject: [PATCH 02/10] add support for non-physical kinematic movement --- libraries/entities/src/EntityItem.cpp | 23 +++- libraries/physics/src/EntityMotionState.cpp | 41 ++++--- libraries/physics/src/EntityMotionState.h | 5 +- libraries/physics/src/ObjectMotionState.cpp | 26 +++-- libraries/physics/src/ObjectMotionState.h | 14 ++- libraries/physics/src/PhysicsEngine.cpp | 119 +++++++++++++++++--- libraries/physics/src/PhysicsEngine.h | 9 +- 7 files changed, 176 insertions(+), 61 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 7e3e982fb8..c830236287 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -520,7 +520,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); recalculateCollisionShape(); - if (overwriteLocalData && (getDirtyFlags() & EntityItem::DIRTY_POSITION)) { + if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) { _lastSimulated = now; } } @@ -770,6 +770,9 @@ void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { float angularSpeed = glm::length(_angularVelocity); const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; // if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { + if (angularSpeed > 0.0f) { + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); } else { // NOTE: angularSpeed is currently in degrees/sec!!! @@ -799,10 +802,18 @@ void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { // "ground" plane of the domain, but for now it's what we've got velocity += getGravity() * timeElapsed; } - - // NOTE: the simulation should NOT set any DirtyFlags on this entity - setPosition(position); // this will automatically recalculate our collision shape - setVelocity(velocity); + + float speed = glm::length(velocity); + const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec + if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { + setVelocity(ENTITY_ITEM_ZERO_VEC3); + if (speed > 0.0f) { + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } + } else { + setPosition(position); // this will automatically recalculate our collision shape + setVelocity(velocity); + } } } @@ -886,7 +897,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { if (_created != UNKNOWN_CREATED_TIME) { setLastEdited(now); } - if (getDirtyFlags() & EntityItem::DIRTY_POSITION) { + if (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) { _lastSimulated = now; } } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8b6fb1ea9f..b4aa9d0e7e 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -14,7 +14,7 @@ #include "BulletUtil.h" #include "EntityMotionState.h" -#include "SimpleEntityKinematicController.h" +#include "PhysicsEngine.h" QSet* _outgoingEntityList; @@ -41,8 +41,6 @@ EntityMotionState::~EntityMotionState() { assert(_entity); _entity->setPhysicsInfo(NULL); _entity = NULL; - delete _kinematicController; - _kinematicController = NULL; } MotionType EntityMotionState::computeMotionType() const { @@ -52,13 +50,15 @@ MotionType EntityMotionState::computeMotionType() const { return _entity->isMoving() ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC; } -void EntityMotionState::addKinematicController() { - if (!_kinematicController) { - _kinematicController = new SimpleEntityKinematicController(_entity); - _kinematicController->start(); - } else { - _kinematicController->start(); - } +void EntityMotionState::updateKinematicState(uint32_t substep) { + setKinematic(_entity->isMoving(), substep); +} + +void EntityMotionState::stepKinematicSimulation(uint32_t substep) { + assert(_isKinematic); + float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; + _entity->simulateSimpleKinematicMotion(dt); + _lastKinematicSubstep = substep; } // This callback is invoked by the physics simulation in two cases: @@ -67,8 +67,11 @@ void EntityMotionState::addKinematicController() { // (2) at the beginning of each simulation frame for KINEMATIC RigidBody's -- // it is an opportunity for outside code to update the object's simulation position void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { - if (_kinematicController && _kinematicController->isRunning()) { - _kinematicController->stepForward(); + if (_isKinematic) { + uint32_t substep = PhysicsEngine::getNumSubsteps(); + // remove const-ness so we can actually update this instance + EntityMotionState* thisMotion = const_cast(this); + thisMotion->stepKinematicSimulation(substep); } worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset())); worldTrans.setRotation(glmToBullet(_entity->getRotation())); @@ -229,12 +232,14 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ uint32_t EntityMotionState::getIncomingDirtyFlags() const { uint32_t dirtyFlags = _entity->getDirtyFlags(); - // 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; + if (_body) { + // 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; + } } return dirtyFlags; } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 8eb639688a..192ac166b4 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -14,7 +14,6 @@ #include -#include "KinematicController.h" #include "ObjectMotionState.h" class EntityItem; @@ -39,8 +38,8 @@ public: /// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem MotionType computeMotionType() const; - // virtual override for ObjectMotionState - void addKinematicController(); + void updateKinematicState(uint32_t substep); + void stepKinematicSimulation(uint32_t substep); // this relays incoming position/rotation to the RigidBody void getWorldTransform(btTransform& worldTrans) const; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index cab36b8370..dfa059d47f 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -12,7 +12,6 @@ #include #include "BulletUtil.h" -#include "KinematicController.h" #include "ObjectMotionState.h" #include "PhysicsEngine.h" @@ -56,10 +55,6 @@ ObjectMotionState::ObjectMotionState() : ObjectMotionState::~ObjectMotionState() { // NOTE: you MUST remove this MotionState from the world before you call the dtor. assert(_body == NULL); - if (_kinematicController) { - delete _kinematicController; - _kinematicController = NULL; - } } void ObjectMotionState::setFriction(float friction) { @@ -108,6 +103,15 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const { bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { assert(_body); + // if we've never checked before, our _sentFrame will be 0, and we need to initialize our state + if (_sentFrame == 0) { + _sentPosition = bulletToGLM(_body->getWorldTransform().getOrigin()); + _sentVelocity = bulletToGLM(_body->getLinearVelocity()); + _sentAngularVelocity = bulletToGLM(_body->getAngularVelocity()); + _sentFrame = simulationFrame; + return false; + } + float dt = (float)(simulationFrame - _sentFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP; _sentFrame = simulationFrame; bool isActive = _body->isActive(); @@ -164,13 +168,6 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { return (fabsf(glm::dot(actualRotation, _sentRotation)) < MIN_ROTATION_DOT); } -void ObjectMotionState::removeKinematicController() { - if (_kinematicController) { - delete _kinematicController; - _kinematicController = NULL; - } -} - void ObjectMotionState::setRigidBody(btRigidBody* body) { // give the body a (void*) back-pointer to this ObjectMotionState if (_body != body) { @@ -183,3 +180,8 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { } } } + +void ObjectMotionState::setKinematic(bool kinematic, uint32_t substep) { + _isKinematic = kinematic; + _lastKinematicSubstep = substep; +} diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 223664ceec..424a7fb680 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -46,7 +46,6 @@ const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_POSITION | Entit class OctreeEditPacketSender; -class KinematicController; class ObjectMotionState : public btMotionState { public: @@ -93,11 +92,15 @@ public: virtual MotionType computeMotionType() const = 0; - virtual void addKinematicController() = 0; - virtual void removeKinematicController(); + virtual void updateKinematicState(uint32_t substep) = 0; btRigidBody* getRigidBody() const { return _body; } + bool isKinematic() const { return _isKinematic; } + + void setKinematic(bool kinematic, uint32_t substep); + virtual void stepKinematicSimulation(uint32_t substep) = 0; + friend class PhysicsEngine; protected: void setRigidBody(btRigidBody* body); @@ -114,6 +117,9 @@ protected: btRigidBody* _body; + bool _isKinematic = false; + uint32_t _lastKinematicSubstep = 0; + bool _sentMoving; // true if last update was moving int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects @@ -124,8 +130,6 @@ protected: glm::vec3 _sentVelocity; glm::vec3 _sentAngularVelocity; // radians per second glm::vec3 _sentAcceleration; - - KinematicController* _kinematicController = NULL; }; #endif // hifi_ObjectMotionState_h diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 02304b3b8d..dbeea165c4 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -62,7 +62,13 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) { entity->setPhysicsInfo(static_cast(motionState)); _entityMotionStates.insert(motionState); addObject(shapeInfo, shape, motionState); - } else { + } else if (entity->isMoving()) { + EntityMotionState* motionState = new EntityMotionState(entity); + entity->setPhysicsInfo(static_cast(motionState)); + _entityMotionStates.insert(motionState); + + motionState->setKinematic(true, _numSubsteps); + _nonPhysicalKinematicObjects.insert(motionState); // We failed to add the entity to the simulation. Probably because we couldn't create a shape for it. //qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine"; } @@ -74,10 +80,16 @@ void PhysicsEngine::removeEntityInternal(EntityItem* entity) { void* physicsInfo = entity->getPhysicsInfo(); if (physicsInfo) { EntityMotionState* motionState = static_cast(physicsInfo); - removeObject(motionState); + if (motionState->getRigidBody()) { + removeObject(motionState); + } else { + // only need to hunt in this list when there is no RigidBody + _nonPhysicalKinematicObjects.remove(motionState); + } _entityMotionStates.remove(motionState); _incomingChanges.remove(motionState); _outgoingPackets.remove(motionState); + // NOTE: EntityMotionState dtor will remove its backpointer from EntityItem delete motionState; } } @@ -117,6 +129,7 @@ void PhysicsEngine::clearEntitiesInternal() { delete (*stateItr); } _entityMotionStates.clear(); + _nonPhysicalKinematicObjects.clear(); _incomingChanges.clear(); _outgoingPackets.clear(); } @@ -127,19 +140,75 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { QSet::iterator stateItr = _incomingChanges.begin(); while (stateItr != _incomingChanges.end()) { ObjectMotionState* motionState = *stateItr; + ++stateItr; uint32_t flags = motionState->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; + bool removeMotionState = false; btRigidBody* body = motionState->getRigidBody(); if (body) { if (flags & HARD_DIRTY_PHYSICS_FLAGS) { // a HARD update requires the body be pulled out of physics engine, changed, then reinserted // but it also handles all EASY changes - updateObjectHard(body, motionState, flags); + bool success = updateObjectHard(body, motionState, flags); + if (!success) { + // NOTE: since updateObjectHard() failed we know that motionState has been removed + // from simulation and body has been deleted. Depending on what else has changed + // we might need to remove motionState altogether... + if (flags & EntityItem::DIRTY_VELOCITY) { + motionState->updateKinematicState(_numSubsteps); + if (motionState->isKinematic()) { + // all is NOT lost, we still need to move this object around kinematically + _nonPhysicalKinematicObjects.insert(motionState); + } else { + // no need to keep motionState around + removeMotionState = true; + } + } else { + // no need to keep motionState around + removeMotionState = true; + } + } } else if (flags) { // an EASY update does NOT require that the body be pulled out of physics engine // hence the MotionState has all the knowledge and authority to perform the update. motionState->updateObjectEasy(flags, _numSubsteps); } + } else { + // the only way we should ever get here (motionState exists but no body) is when the object + // is undergoing non-physical kinematic motion. + assert(_nonPhysicalKinematicObjects.contains(motionState)); + + // it is possible that the changes are such that the object can now be added to the physical simulation + if (flags & EntityItem::DIRTY_SHAPE) { + ShapeInfo shapeInfo; + motionState->computeShapeInfo(shapeInfo); + btCollisionShape* shape = _shapeManager.getShape(shapeInfo); + if (shape) { + addObject(shapeInfo, shape, motionState); + _nonPhysicalKinematicObjects.remove(motionState); + } else if (flags & EntityItem::DIRTY_VELOCITY) { + // although we couldn't add the object to the simulation, might need to update kinematic motion... + motionState->updateKinematicState(_numSubsteps); + if (!motionState->isKinematic()) { + _nonPhysicalKinematicObjects.remove(motionState); + removeMotionState = true; + } + } + } else if (flags & EntityItem::DIRTY_VELOCITY) { + // although we still can't add to physics simulation, might need to update kinematic motion... + motionState->updateKinematicState(_numSubsteps); + if (!motionState->isKinematic()) { + _nonPhysicalKinematicObjects.remove(motionState); + removeMotionState = true; + } + } + } + if (removeMotionState) { + // if we get here then there is no need to keep this motionState around (no physics or kinematics) + _outgoingPackets.remove(motionState); + // NOTE: motionState will clean up its own backpointers in the Object + delete motionState; + continue; } // NOTE: the grand order of operations is: @@ -152,7 +221,6 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { // outgoing changes at this point. motionState->clearOutgoingPacketFlags(flags); // clear outgoing flags that were trumped motionState->clearIncomingDirtyFlags(flags); // clear incoming flags that were processed - ++stateItr; } _incomingChanges.clear(); } @@ -229,6 +297,7 @@ void PhysicsEngine::stepSimulation() { // This is step (2). int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); _numSubsteps += (uint32_t)numSubsteps; + stepNonPhysicalKinematics(); unlock(); // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. @@ -248,6 +317,18 @@ void PhysicsEngine::stepSimulation() { computeCollisionEvents(); } +// TODO: need to update non-physical kinematic objects +void PhysicsEngine::stepNonPhysicalKinematics() { + QSet::iterator stateItr = _nonPhysicalKinematicObjects.begin(); + while (stateItr != _nonPhysicalKinematicObjects.end()) { + ObjectMotionState* motionState = *stateItr; + motionState->stepKinematicSimulation(_numSubsteps); + ++stateItr; + } +} + +// TODO?: need to occasionally scan for stopped non-physical kinematics objects + void PhysicsEngine::computeCollisionEvents() { // update all contacts int numManifolds = _collisionDispatcher->getNumManifolds(); @@ -322,7 +403,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->updateInertiaTensor(); motionState->setRigidBody(body); - motionState->addKinematicController(); + motionState->setKinematic(true, _numSubsteps); const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec body->setSleepingThresholds(KINEMATIC_LINEAR_VELOCITY_THRESHOLD, KINEMATIC_ANGULAR_VELOCITY_THRESHOLD); @@ -334,6 +415,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap body = new btRigidBody(mass, motionState, shape, inertia); body->updateInertiaTensor(); motionState->setRigidBody(body); + motionState->setKinematic(false, _numSubsteps); motionState->updateObjectVelocities(); // NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds. // (the 2 seconds is determined by: static btRigidBody::gDeactivationTime @@ -348,6 +430,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); body->updateInertiaTensor(); motionState->setRigidBody(body); + motionState->setKinematic(false, _numSubsteps); break; } } @@ -358,7 +441,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap _dynamicsWorld->addRigidBody(body); } -bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { +void PhysicsEngine::removeObject(ObjectMotionState* motionState) { assert(motionState); btRigidBody* body = motionState->getRigidBody(); if (body) { @@ -369,16 +452,14 @@ bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { _shapeManager.releaseShape(shapeInfo); delete body; motionState->setRigidBody(NULL); - motionState->removeKinematicController(); + motionState->setKinematic(false, _numSubsteps); removeContacts(motionState); - return true; } - return false; } // private -void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) { +bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) { MotionType newType = motionState->computeMotionType(); // pull body out of physics engine @@ -393,7 +474,16 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio ShapeInfo shapeInfo; motionState->computeShapeInfo(shapeInfo); btCollisionShape* newShape = _shapeManager.getShape(shapeInfo); - if (newShape != oldShape) { + if (!newShape) { + // FAIL! we are unable to support these changes! + _shapeManager.releaseShape(oldShape); + + delete body; + motionState->setRigidBody(NULL); + motionState->setKinematic(false, _numSubsteps); + removeContacts(motionState); + return false; + } else if (newShape != oldShape) { // BUG: if shape doesn't change but density does then we won't compute new mass properties // TODO: fix this BUG by replacing DIRTY_MASS with DIRTY_DENSITY and then fix logic accordingly. body->setCollisionShape(newShape); @@ -426,7 +516,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio body->setMassProps(0.0f, btVector3(0.0f, 0.0f, 0.0f)); body->updateInertiaTensor(); - motionState->addKinematicController(); + motionState->setKinematic(true, _numSubsteps); break; } case MOTION_TYPE_DYNAMIC: { @@ -443,7 +533,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio body->updateInertiaTensor(); } body->forceActivationState(ACTIVE_TAG); - motionState->removeKinematicController(); + motionState->setKinematic(false, _numSubsteps); break; } default: { @@ -458,7 +548,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio body->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); body->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f)); - motionState->removeKinematicController(); + motionState->setKinematic(false, _numSubsteps); break; } } @@ -467,4 +557,5 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio _dynamicsWorld->addRigidBody(body); body->activate(); + return true; } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 73a02607e8..9ae9f88e7e 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -67,6 +67,7 @@ public: virtual void init(EntityEditPacketSender* packetSender); void stepSimulation(); + void stepNonPhysicalKinematics(); void computeCollisionEvents(); @@ -81,15 +82,16 @@ public: void addObject(const ShapeInfo& shapeInfo, btCollisionShape* shape, ObjectMotionState* motionState); /// \param motionState pointer to Object's MotionState - /// \return true if Object removed - bool removeObject(ObjectMotionState* motionState); + void removeObject(ObjectMotionState* motionState); /// process queue of changed from external sources void relayIncomingChangesToSimulation(); private: void removeContacts(ObjectMotionState* motionState); - void updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); + + // return 'true' of update was successful + bool updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); btClock _clock; @@ -104,6 +106,7 @@ private: // EntitySimulation stuff QSet _entityMotionStates; // all entities that we track + QSet _nonPhysicalKinematicObjects; // not in physics simulation, but still need kinematic simulation QSet _incomingChanges; // entities with pending physics changes by script or packet QSet _outgoingPackets; // MotionStates with pending changes that need to be sent over wire From eefd32b42b173392150c560b01ece0d57ca6ac6f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 11:04:02 -0800 Subject: [PATCH 03/10] remove KinematicController classes --- libraries/physics/src/KinematicController.cpp | 22 ----------- libraries/physics/src/KinematicController.h | 36 ------------------ .../src/SimpleEntityKinematicController.cpp | 21 ---------- .../src/SimpleEntityKinematicController.h | 38 ------------------- 4 files changed, 117 deletions(-) delete mode 100644 libraries/physics/src/KinematicController.cpp delete mode 100644 libraries/physics/src/KinematicController.h delete mode 100644 libraries/physics/src/SimpleEntityKinematicController.cpp delete mode 100644 libraries/physics/src/SimpleEntityKinematicController.h diff --git a/libraries/physics/src/KinematicController.cpp b/libraries/physics/src/KinematicController.cpp deleted file mode 100644 index 354b285bc1..0000000000 --- a/libraries/physics/src/KinematicController.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// KinematicController.cpp -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// 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 "KinematicController.h" -#include "PhysicsEngine.h" - -KinematicController::KinematicController() { - _lastSubstep = PhysicsEngine::getNumSubsteps(); -} - -void KinematicController::start() { - _enabled = true; - _lastSubstep = PhysicsEngine::getNumSubsteps(); -} diff --git a/libraries/physics/src/KinematicController.h b/libraries/physics/src/KinematicController.h deleted file mode 100644 index 60b8548607..0000000000 --- a/libraries/physics/src/KinematicController.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// KinematicController.h -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// 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_KinematicController_h -#define hifi_KinematicController_h - -#include - -/// KinematicController defines an API for derived classes. - -class KinematicController { -public: - KinematicController(); - - virtual ~KinematicController() {} - - virtual void stepForward() = 0; - - void start(); - void stop() { _enabled = false; } - bool isRunning() const { return _enabled; } - -protected: - bool _enabled = false; - uint32_t _lastSubstep; -}; - -#endif // hifi_KinematicController_h diff --git a/libraries/physics/src/SimpleEntityKinematicController.cpp b/libraries/physics/src/SimpleEntityKinematicController.cpp deleted file mode 100644 index e834d4e91b..0000000000 --- a/libraries/physics/src/SimpleEntityKinematicController.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// SimpleEntityKinematicController.cpp -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// 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 "PhysicsEngine.h" -#include "SimpleEntityKinematicController.h" - -void SimpleEntityKinematicController:: stepForward() { - uint32_t substep = PhysicsEngine::getNumSubsteps(); - float dt = (substep - _lastSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; - _entity->simulateSimpleKinematicMotion(dt); - _lastSubstep = substep; -} - diff --git a/libraries/physics/src/SimpleEntityKinematicController.h b/libraries/physics/src/SimpleEntityKinematicController.h deleted file mode 100644 index 1edfaf8d2c..0000000000 --- a/libraries/physics/src/SimpleEntityKinematicController.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// SimpleEntityKinematicController.h -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// 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_SimpleEntityKinematicController_h -#define hifi_SimpleEntityKinematicController_h - -/// SimpleKinematicConstroller performs simple exrapolation of velocities. - -#include -#include - -#include - -#include "KinematicController.h" - -class SimpleEntityKinematicController : public KinematicController { -public: - SimpleEntityKinematicController() = delete; // prevent compiler from making a default ctor - - SimpleEntityKinematicController(EntityItem* entity) : KinematicController(), _entity(entity) { assert(entity); } - - ~SimpleEntityKinematicController() { _entity = NULL; } - - void stepForward(); - -private: - EntityItem* _entity; -}; - -#endif // hifi_SimpleEntityKinematicController_h From f2bcdfa2b479cbe917f0afd1d6a127f34694f421 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 13:10:41 -0800 Subject: [PATCH 04/10] update _lastSimulated for kinematic motion --- libraries/entities/src/EntityItem.cpp | 101 ++++---------------- libraries/entities/src/EntityItem.h | 4 +- libraries/physics/src/EntityMotionState.cpp | 20 ++-- libraries/physics/src/EntityMotionState.h | 2 +- libraries/physics/src/ObjectMotionState.h | 2 +- libraries/physics/src/PhysicsEngine.cpp | 7 +- libraries/physics/src/PhysicsEngine.h | 2 +- 7 files changed, 39 insertions(+), 99 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c830236287..c1fc24fdc7 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -607,11 +607,6 @@ bool EntityItem::isRestingOnSurface() const { } void EntityItem::simulate(const quint64& now) { - if (_physicsInfo) { - // we rely on bullet for simulation, so bail - return; - } - bool wantDebug = false; if (_lastSimulated == 0) { @@ -661,9 +656,13 @@ void EntityItem::simulate(const quint64& now) { qDebug() << " ********** EntityItem::simulate() .... SETTING _lastSimulated=" << _lastSimulated; } - if (hasAngularVelocity()) { - glm::quat rotation = getRotation(); + simulateKinematicMotion(timeElapsed); + _lastSimulated = now; +} +void EntityItem::simulateKinematicMotion(float timeElapsed) { + bool wantDebug = false; + if (hasAngularVelocity()) { // angular damping glm::vec3 angularVelocity = getAngularVelocity(); if (_angularDamping > 0.0f) { @@ -679,6 +678,9 @@ void EntityItem::simulate(const quint64& now) { const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; // if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { + if (angularSpeed > 0.0f) { + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); } else { // NOTE: angularSpeed is currently in degrees/sec!!! @@ -686,7 +688,7 @@ void EntityItem::simulate(const quint64& now) { float angle = timeElapsed * glm::radians(angularSpeed); glm::vec3 axis = _angularVelocity / angularSpeed; glm::quat dQ = glm::angleAxis(angle, axis); - rotation = glm::normalize(dQ * rotation); + glm::quat rotation = glm::normalize(dQ * getRotation()); setRotation(rotation); } } @@ -722,80 +724,6 @@ void EntityItem::simulate(const quint64& now) { position = newPosition; - // handle bounces off the ground... We bounce at the distance to the bottom of our entity - if (position.y <= getDistanceToBottomOfEntity()) { - velocity = velocity * glm::vec3(1,-1,1); - position.y = getDistanceToBottomOfEntity(); - } - - // apply gravity - if (hasGravity()) { - // handle resting on surface case, this is definitely a bit of a hack, and it only works on the - // "ground" plane of the domain, but for now it's what we've got - if (isRestingOnSurface()) { - velocity.y = 0.0f; - position.y = getDistanceToBottomOfEntity(); - } else { - velocity += getGravity() * timeElapsed; - } - } - - // NOTE: we don't zero out very small velocities --> we rely on a remote Bullet simulation - // to tell us when the entity has stopped. - - // NOTE: the simulation should NOT set any DirtyFlags on this entity - setPosition(position); // this will automatically recalculate our collision shape - setVelocity(velocity); - - if (wantDebug) { - qDebug() << " new position:" << position; - qDebug() << " new velocity:" << velocity; - qDebug() << " new AACube:" << getMaximumAACube(); - qDebug() << " old getAABox:" << getAABox(); - } - } - - _lastSimulated = now; -} - -void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { - if (hasAngularVelocity()) { - // angular damping - glm::vec3 angularVelocity = getAngularVelocity(); - if (_angularDamping > 0.0f) { - angularVelocity *= powf(1.0f - _angularDamping, timeElapsed); - setAngularVelocity(angularVelocity); - } - - float angularSpeed = glm::length(_angularVelocity); - const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; // - if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { - if (angularSpeed > 0.0f) { - _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; - } - setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); - } else { - // NOTE: angularSpeed is currently in degrees/sec!!! - // TODO: Andrew to convert to radians/sec - float angle = timeElapsed * glm::radians(angularSpeed); - glm::vec3 axis = _angularVelocity / angularSpeed; - glm::quat dQ = glm::angleAxis(angle, axis); - glm::quat rotation = getRotation(); - rotation = glm::normalize(dQ * rotation); - setRotation(rotation); - } - } - - if (hasVelocity()) { - // linear damping - glm::vec3 velocity = getVelocity(); - if (_damping > 0.0f) { - velocity *= powf(1.0f - _damping, timeElapsed); - } - - // integrate position forward - glm::vec3 position = getPosition() + (velocity * timeElapsed); - // apply gravity if (hasGravity()) { // handle resting on surface case, this is definitely a bit of a hack, and it only works on the @@ -811,9 +739,16 @@ void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; } } else { - setPosition(position); // this will automatically recalculate our collision shape + setPosition(position); setVelocity(velocity); } + + if (wantDebug) { + qDebug() << " new position:" << position; + qDebug() << " new velocity:" << velocity; + qDebug() << " new AACube:" << getMaximumAACube(); + qDebug() << " old getAABox:" << getAABox(); + } } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a9a82c5209..d266a30f62 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -82,6 +82,7 @@ public: void recordCreationTime(); // set _created to 'now' quint64 getLastSimulated() const { return _lastSimulated; } /// Last simulated time of this entity universal usecs + void setLastSimulated(quint64 now) { _lastSimulated = now; } /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } @@ -128,9 +129,8 @@ public: // perform linear extrapolation for SimpleEntitySimulation void simulate(const quint64& now); + void simulateKinematicMotion(float timeElapsed); - void simulateSimpleKinematicMotion(float timeElapsed); - virtual bool needsToCallUpdate() const { return false; } virtual void debugDump() const; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index b4aa9d0e7e..f22487b8ea 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -54,11 +54,12 @@ void EntityMotionState::updateKinematicState(uint32_t substep) { setKinematic(_entity->isMoving(), substep); } -void EntityMotionState::stepKinematicSimulation(uint32_t substep) { +void EntityMotionState::stepKinematicSimulation(quint64 now) { assert(_isKinematic); - float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; - _entity->simulateSimpleKinematicMotion(dt); - _lastKinematicSubstep = substep; + // NOTE: this is non-physical kinematic motion which steps to real run-time (now) + // which is different from physical kinematic motion (inside getWorldTransform()) + // which steps in physics simulation time. + _entity->simulate(now); } // This callback is invoked by the physics simulation in two cases: @@ -68,10 +69,15 @@ void EntityMotionState::stepKinematicSimulation(uint32_t substep) { // it is an opportunity for outside code to update the object's simulation position void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { if (_isKinematic) { + // This is physical kinematic motion which steps strictly by the subframe count + // of the physics simulation. uint32_t substep = PhysicsEngine::getNumSubsteps(); - // remove const-ness so we can actually update this instance - EntityMotionState* thisMotion = const_cast(this); - thisMotion->stepKinematicSimulation(substep); + float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; + _entity->simulateKinematicMotion(dt); + _entity->setLastSimulated(usecTimestampNow()); + + // bypass const-ness so we can remember the substep + const_cast(this)->_lastKinematicSubstep = substep; } worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset())); worldTrans.setRotation(glmToBullet(_entity->getRotation())); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 192ac166b4..5d98e545d9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -39,7 +39,7 @@ public: MotionType computeMotionType() const; void updateKinematicState(uint32_t substep); - void stepKinematicSimulation(uint32_t substep); + void stepKinematicSimulation(quint64 now); // this relays incoming position/rotation to the RigidBody void getWorldTransform(btTransform& worldTrans) const; diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 424a7fb680..ceeea219cf 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -99,7 +99,7 @@ public: bool isKinematic() const { return _isKinematic; } void setKinematic(bool kinematic, uint32_t substep); - virtual void stepKinematicSimulation(uint32_t substep) = 0; + virtual void stepKinematicSimulation(quint64 now) = 0; friend class PhysicsEngine; protected: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index dbeea165c4..62693e3c9a 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -297,7 +297,7 @@ void PhysicsEngine::stepSimulation() { // This is step (2). int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); _numSubsteps += (uint32_t)numSubsteps; - stepNonPhysicalKinematics(); + stepNonPhysicalKinematics(usecTimestampNow()); unlock(); // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. @@ -317,12 +317,11 @@ void PhysicsEngine::stepSimulation() { computeCollisionEvents(); } -// TODO: need to update non-physical kinematic objects -void PhysicsEngine::stepNonPhysicalKinematics() { +void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { QSet::iterator stateItr = _nonPhysicalKinematicObjects.begin(); while (stateItr != _nonPhysicalKinematicObjects.end()) { ObjectMotionState* motionState = *stateItr; - motionState->stepKinematicSimulation(_numSubsteps); + motionState->stepKinematicSimulation(now); ++stateItr; } } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 9ae9f88e7e..1dbfe2646e 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -67,7 +67,7 @@ public: virtual void init(EntityEditPacketSender* packetSender); void stepSimulation(); - void stepNonPhysicalKinematics(); + void stepNonPhysicalKinematics(const quint64& now); void computeCollisionEvents(); From 36c20c24fe7f3d90c3bb7d5fd25bfa77edcd6b5d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 13:20:12 -0800 Subject: [PATCH 05/10] fix for velocity in units of domain size --- libraries/entities/src/EntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c1fc24fdc7..1ebde85d65 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -732,7 +732,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed) { } float speed = glm::length(velocity); - const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec + const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f / (float)TREE_SCALE; // 1mm/sec if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { setVelocity(ENTITY_ITEM_ZERO_VEC3); if (speed > 0.0f) { From 3c38a9eb53316e867eadea0ddbec5d766cdbd005 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 13:32:38 -0800 Subject: [PATCH 06/10] add last simulated to the protocal --- libraries/entities/src/EntityItem.cpp | 41 ++++++++++++++++++++-- libraries/entities/src/EntityItem.h | 8 +++-- libraries/networking/src/PacketHeaders.cpp | 2 +- libraries/networking/src/PacketHeaders.h | 1 + 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 7e3e982fb8..cb90875c43 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -140,9 +140,17 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet ByteCountCoded typeCoder = getType(); QByteArray encodedType = typeCoder; - quint64 updateDelta = getLastSimulated() <= getLastEdited() ? 0 : getLastSimulated() - getLastEdited(); + // last updated (animations, non-physics changes) + quint64 updateDelta = getLastUpdated() <= getLastEdited() ? 0 : getLastUpdated() - getLastEdited(); ByteCountCoded updateDeltaCoder = updateDelta; QByteArray encodedUpdateDelta = updateDeltaCoder; + + // last simulated (velocity, angular velocity, physics changes) + quint64 simulatedDelta = getLastSimulated() <= getLastEdited() ? 0 : getLastSimulated() - getLastEdited(); + ByteCountCoded simulatedDeltaCoder = simulatedDelta; + QByteArray encodedSimulatedDelta = simulatedDeltaCoder; + + EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); EntityPropertyFlags propertiesDidntFit = requestedProperties; @@ -170,6 +178,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet bool successCreatedFits = false; bool successLastEditedFits = false; bool successLastUpdatedFits = false; + bool successLastSimulatedFits = false; bool successPropertyFlagsFits = false; int propertyFlagsOffset = 0; int oldPropertyFlagsLength = 0; @@ -189,8 +198,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet if (successLastEditedFits) { successLastUpdatedFits = packetData->appendValue(encodedUpdateDelta); } - if (successLastUpdatedFits) { + successLastSimulatedFits = packetData->appendValue(encodedSimulatedDelta); + } + + if (successLastSimulatedFits) { propertyFlagsOffset = packetData->getUncompressedByteOffset(); encodedPropertyFlags = propertyFlags; oldPropertyFlagsLength = encodedPropertyFlags.length(); @@ -458,6 +470,25 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef encodedUpdateDelta = updateDeltaCoder; // determine true length dataAt += encodedUpdateDelta.size(); bytesRead += encodedUpdateDelta.size(); + + // Newer bitstreams will have a last simulated and a last updated value + if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME) { + // last simulated is stored as ByteCountCoded delta from lastEdited + QByteArray encodedSimulatedDelta = originalDataBuffer.mid(bytesRead); // maximum possible size + ByteCountCoded simulatedDeltaCoder = encodedSimulatedDelta; + quint64 simulatedDelta = simulatedDeltaCoder; + if (overwriteLocalData) { + _lastSimulated = lastEditedFromBufferAdjusted + simulatedDelta; // don't adjust for clock skew since we already did that + if (wantDebug) { + qDebug() << "_lastSimulated =" << _lastSimulated; + qDebug() << "_lastEdited=" << _lastEdited; + qDebug() << "lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted; + } + } + encodedSimulatedDelta = simulatedDeltaCoder; // determine true length + dataAt += encodedSimulatedDelta.size(); + bytesRead += encodedSimulatedDelta.size(); + } // Property Flags QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size @@ -521,6 +552,12 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef recalculateCollisionShape(); if (overwriteLocalData && (getDirtyFlags() & EntityItem::DIRTY_POSITION)) { + // TODO: Andrew & Brad to discuss -- this probably should not be "now" but instead should be the last + // simulated time from server. The logic should maybe be: the position changed from the server, so the + // position we just set can be thought of as the position at the time it was last simulated by the + // server (clock skew adjusted). By setting it to "now" we are saying that the last position is to be + // considered to be the correct position for "now" which is likely in the future from when it actually + // was at that last known positition. _lastSimulated = now; } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a9a82c5209..c68b638553 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -86,7 +86,7 @@ public: /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } void setLastEdited(quint64 lastEdited) - { _lastEdited = _lastUpdated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } + { _lastEdited = _lastUpdated = _lastSimulated = 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; } @@ -125,6 +125,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); @@ -296,9 +297,10 @@ protected: QUuid _id; uint32_t _creatorTokenID; bool _newlyCreated; - quint64 _lastSimulated; // last time this entity called simulate() - quint64 _lastUpdated; // last time this entity called update() + 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 _lastEditedFromRemote; // last time we received and edit from the server quint64 _lastEditedFromRemoteInRemoteTime; // last time we received and edit from the server (in server-time-frame) quint64 _created; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 110892a106..07228c8351 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_HAVE_USER_DATA; + return VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 87d93b931f..f0d21ca9f8 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -126,6 +126,7 @@ const PacketVersion VERSION_ENTITIES_HAS_FILE_BREAKS = VERSION_ENTITIES_SUPPORT_ const PacketVersion VERSION_ENTITIES_SUPPORT_DIMENSIONS = 4; const PacketVersion VERSION_ENTITIES_MODELS_HAVE_ANIMATION_SETTINGS = 5; const PacketVersion VERSION_ENTITIES_HAVE_USER_DATA = 6; +const PacketVersion VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME = 7; const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1; #endif // hifi_PacketHeaders_h From fa485d21b7edc7fe08ce4d4e4c0f1fd81d168a4a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 13:52:28 -0800 Subject: [PATCH 07/10] added comment --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cb90875c43..fd6502d045 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -924,6 +924,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { setLastEdited(now); } if (getDirtyFlags() & EntityItem::DIRTY_POSITION) { + // TODO: Andrew & Brad to discuss. Is this correct? Maybe it is. Need to think through all cases. _lastSimulated = now; } } From c01d2d657c242e2e8cc1c7d6c8a493717fbc620f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 13:55:12 -0800 Subject: [PATCH 08/10] added comment --- libraries/physics/src/EntityMotionState.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8b6fb1ea9f..a799a3d7d2 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -208,6 +208,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ if (_numNonMovingUpdates <= 1) { // we only update lastEdited when we're sending new physics data // (i.e. NOT when we just simulate the positions forward, nore when we resend non-moving data) + // NOTE: Andrew & Brad to discuss. Let's make sure we're using lastEdited, lastSimulated, and lastUpdated correctly quint64 lastSimulated = _entity->getLastSimulated(); _entity->setLastEdited(lastSimulated); properties.setLastEdited(lastSimulated); From 9a0576adfb3381e9c7bb5e680018ade4e8e34969 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 15:54:26 -0800 Subject: [PATCH 09/10] remove setting last simulated in setLastEdited() because it may cause problems --- libraries/entities/src/EntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 020f7ee0d4..df619e2f69 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -87,7 +87,7 @@ public: /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } void setLastEdited(quint64 lastEdited) - { _lastEdited = _lastUpdated = _lastSimulated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } + { _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; } From 7a1300d66cf0ba54e056ba0fa720bb8e1bf61d27 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 23 Jan 2015 16:04:06 -0800 Subject: [PATCH 10/10] Request blocking calls --- examples/controllers/hydra/hydraGrab.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 4d0b873fd2..0c6e135739 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -274,7 +274,7 @@ function controller(wichSide) { this.glowedIntersectingModel.isKnownID = false; } if (!this.grabbing) { - var intersection = Entities.findRayIntersection({ + var intersection = Entities.findRayIntersectionBlocking({ origin: this.palmPosition, direction: this.front }); @@ -304,7 +304,7 @@ function controller(wichSide) { if (this.grabbing) { if (!this.entityID.isKnownID) { print("Unknown grabbed ID " + this.entityID.id + ", isKnown: " + this.entityID.isKnownID); - this.entityID = Entities.findRayIntersection({ + this.entityID = Entities.findRayIntersectionBlocking({ origin: this.palmPosition, direction: this.front }).entityID; @@ -475,7 +475,7 @@ function controller(wichSide) { Vec3.print("Looking at: ", this.palmPosition); var pickRay = { origin: this.palmPosition, direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) }; - var foundIntersection = Entities.findRayIntersection(pickRay); + var foundIntersection = Entities.findRayIntersectionBlocking(pickRay); if(!foundIntersection.accurate) { print("No accurate intersection");