diff --git a/examples/moving-platform.js b/examples/moving-platform.js index 94e45bb571..a3022a3904 100644 --- a/examples/moving-platform.js +++ b/examples/moving-platform.js @@ -16,8 +16,8 @@ function init() { isDynamic: false }); if (platform) { - MyAvatar.position = { x: 0, y: 3, z: 0 }; if (MyAvatar.getParentID() != platform) { + MyAvatar.position = { x: 0, y: 3, z: 0 }; MyAvatar.setParentID(platform); } } @@ -34,8 +34,7 @@ function shutdown() { } } - -init(); +Script.setTimeout(init, 3000); Script.update.connect(update); Script.scriptEnding.connect(shutdown); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b67d176852..2eab67f192 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -86,9 +86,7 @@ Avatar::Avatar(RigPointer rig) : _positionDeltaAccumulator(0.0f), _lastVelocity(0.0f), _acceleration(0.0f), - _angularVelocity(0.0f), _lastAngularVelocity(0.0f), - _angularAcceleration(0.0f), _lastOrientation(), _leanScale(0.5f), _worldUpDirection(DEFAULT_UP_DIRECTION), @@ -235,9 +233,6 @@ void Avatar::simulate(float deltaTime) { _displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha; } - // NOTE: we shouldn't extrapolate an Avatar instance forward in time... - // until velocity is included in AvatarData update message. - //_position += _velocity * deltaTime; measureMotionDerivatives(deltaTime); simulateAttachments(deltaTime); @@ -255,7 +250,7 @@ bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const { void Avatar::slamPosition(const glm::vec3& newPosition) { setPosition(newPosition); _positionDeltaAccumulator = glm::vec3(0.0f); - _velocity = glm::vec3(0.0f); + setVelocity(glm::vec3(0.0f)); _lastVelocity = glm::vec3(0.0f); } @@ -269,15 +264,17 @@ void Avatar::measureMotionDerivatives(float deltaTime) { float invDeltaTime = 1.0f / deltaTime; // Floating point error prevents us from computing velocity in a naive way // (e.g. vel = (pos - oldPos) / dt) so instead we use _positionOffsetAccumulator. - _velocity = _positionDeltaAccumulator * invDeltaTime; + glm::vec3 velocity = _positionDeltaAccumulator * invDeltaTime; _positionDeltaAccumulator = glm::vec3(0.0f); - _acceleration = (_velocity - _lastVelocity) * invDeltaTime; - _lastVelocity = _velocity; + _acceleration = (velocity - _lastVelocity) * invDeltaTime; + _lastVelocity = velocity; + setVelocity(velocity); + // angular glm::quat orientation = getOrientation(); glm::quat delta = glm::inverse(_lastOrientation) * orientation; - _angularVelocity = safeEulerAngles(delta) * invDeltaTime; - _angularAcceleration = (_angularVelocity - _lastAngularVelocity) * invDeltaTime; + glm::vec3 angularVelocity = glm::axis(delta) * glm::angle(delta) * invDeltaTime; + setAngularVelocity(angularVelocity); _lastOrientation = getOrientation(); } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index b23b64b384..0f84f35a25 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -135,8 +135,6 @@ public: Q_INVOKABLE glm::vec3 getNeckPosition() const; Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; } - Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; } - Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; } Q_INVOKABLE bool getShouldRender() const { return !_shouldSkipRender; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1436c192b5..bce08d6e19 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1079,17 +1079,8 @@ void MyAvatar::rebuildCollisionShape() { void MyAvatar::prepareForPhysicsSimulation() { relayDriveKeysToCharacterController(); - glm::vec3 parentVelocity; bool success; - auto parentPtr = getParentPointer(success); - if (success && parentPtr && parentPtr->getNestableType() == NestableType::Entity) { - auto entityPtr = std::dynamic_pointer_cast(parentPtr); - if (entityPtr) { - parentVelocity = entityPtr->getVelocity(); - } - } - - _characterController.setParentVelocity(parentVelocity); + _characterController.setParentVelocity(getParentVelocity(success)); _characterController.setTargetVelocity(getTargetVelocity()); _characterController.setPositionAndOrientation(getPosition(), getOrientation()); if (qApp->isHMDMode()) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index a942830f68..c77c4fdd78 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -149,25 +149,9 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); - auto velocity = myAvatar->getVelocity(); - auto position = myAvatar->getPosition(); - auto orientation = myAvatar->getOrientation(); - - // check to see if we are attached to an Entity. - bool success; - auto parentPtr = myAvatar->getParentPointer(success); - if (success && parentPtr && parentPtr->getNestableType() == NestableType::Entity) { - auto entityPtr = std::dynamic_pointer_cast(parentPtr); - if (entityPtr) { - // TODO: Do we need account for angularVelocity of entity? - - auto invParentRot = glm::inverse(entityPtr->getOrientation()); - velocity = invParentRot * (myAvatar->getVelocity() - entityPtr->getVelocity()); - position = myAvatar->getLocalPosition(); - orientation = myAvatar->getLocalOrientation(); - } - } - + auto velocity = myAvatar->getLocalVelocity(); + auto position = myAvatar->getLocalPosition(); + auto orientation = myAvatar->getLocalOrientation(); _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); // evaluate AnimGraph animation and update jointStates. diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3933d705fc..d14df7b05a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -64,7 +64,6 @@ AvatarData::AvatarData() : _billboard(), _errorLogExpiry(0), _owningAvatarMixer(), - _velocity(0.0f), _targetVelocity(0.0f), _localAABox(DEFAULT_LOCAL_AABOX_CORNER, DEFAULT_LOCAL_AABOX_SCALE) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index eac1917533..cf6cf80162 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -151,6 +151,9 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(float headYaw READ getHeadYaw WRITE setHeadYaw) Q_PROPERTY(float headRoll READ getHeadRoll WRITE setHeadRoll) + Q_PROPERTY(glm::vec3 velocity READ getVelocity WRITE setVelocity) + Q_PROPERTY(glm::vec3 angularVelocity READ getAngularVelocity WRITE setAngularVelocity) + Q_PROPERTY(float audioLoudness READ getAudioLoudness WRITE setAudioLoudness) Q_PROPERTY(float audioAverageLoudness READ getAudioAverageLoudness WRITE setAudioAverageLoudness) @@ -334,8 +337,6 @@ public: int getAverageBytesReceivedPerSecond() const; int getReceiveRate() const; - void setVelocity(const glm::vec3 velocity) { _velocity = velocity; } - Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; } const glm::vec3& getTargetVelocity() const { return _targetVelocity; } bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; } @@ -406,7 +407,6 @@ protected: /// Loads the joint indices, names from the FST file (if any) virtual void updateJointMappings(); - glm::vec3 _velocity; glm::vec3 _targetVelocity; AABox _localAABox; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a4a646f532..c3859d32de 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -49,7 +49,6 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _localRenderAlpha(ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA), _density(ENTITY_ITEM_DEFAULT_DENSITY), _volumeMultiplier(1.0f), - _velocity(ENTITY_ITEM_DEFAULT_VELOCITY), _gravity(ENTITY_ITEM_DEFAULT_GRAVITY), _acceleration(ENTITY_ITEM_DEFAULT_ACCELERATION), _damping(ENTITY_ITEM_DEFAULT_DAMPING), @@ -60,7 +59,6 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _scriptTimestamp(ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP), _collisionSoundURL(ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL), _registrationPoint(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT), - _angularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY), _angularDamping(ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING), _visible(ENTITY_ITEM_DEFAULT_VISIBLE), _collisionless(ENTITY_ITEM_DEFAULT_COLLISIONLESS), @@ -78,6 +76,8 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _physicsInfo(nullptr), _simulated(false) { + setVelocity(ENTITY_ITEM_DEFAULT_VELOCITY); + setAngularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY); // explicitly set transform parts to set dirty flags used by batch rendering setScale(ENTITY_ITEM_DEFAULT_DIMENSIONS); quint64 now = usecTimestampNow(); @@ -886,41 +886,45 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { } if (hasAngularVelocity()) { + glm::vec3 angularVelocity = getAngularVelocity(); + // angular damping if (_angularDamping > 0.0f) { - _angularVelocity *= powf(1.0f - _angularDamping, timeElapsed); + angularVelocity *= powf(1.0f - _angularDamping, timeElapsed); #ifdef WANT_DEBUG qCDebug(entities) << " angularDamping :" << _angularDamping; - qCDebug(entities) << " newAngularVelocity:" << _angularVelocity; + qCDebug(entities) << " newAngularVelocity:" << angularVelocity; #endif } - float angularSpeed = glm::length(_angularVelocity); + float angularSpeed = glm::length(angularVelocity); const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { if (setFlags && angularSpeed > 0.0f) { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } - _angularVelocity = ENTITY_ITEM_ZERO_VEC3; + angularVelocity = ENTITY_ITEM_ZERO_VEC3; } else { // for improved agreement with the way Bullet integrates rotations we use an approximation // and break the integration into bullet-sized substeps glm::quat rotation = getRotation(); float dt = timeElapsed; while (dt > PHYSICS_ENGINE_FIXED_SUBSTEP) { - glm::quat dQ = computeBulletRotationStep(_angularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP); + glm::quat dQ = computeBulletRotationStep(angularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP); rotation = glm::normalize(dQ * rotation); dt -= PHYSICS_ENGINE_FIXED_SUBSTEP; } // NOTE: this final partial substep can drift away from a real Bullet simulation however // it only becomes significant for rapidly rotating objects // (e.g. around PI/4 radians per substep, or 7.5 rotations/sec at 60 substeps/sec). - glm::quat dQ = computeBulletRotationStep(_angularVelocity, dt); + glm::quat dQ = computeBulletRotationStep(angularVelocity, dt); rotation = glm::normalize(dQ * rotation); setRotation(rotation); } + + setAngularVelocity(angularVelocity); } if (hasVelocity()) { @@ -1077,9 +1081,9 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) const { // a TerseUpdate includes the transform and its derivatives properties._position = getLocalPosition(); - properties._velocity = _velocity; + properties._velocity = getLocalVelocity(); properties._rotation = getLocalOrientation(); - properties._angularVelocity = _angularVelocity; + properties._angularVelocity = getLocalAngularVelocity(); properties._acceleration = _acceleration; properties._positionChanged = true; @@ -1406,13 +1410,15 @@ void EntityItem::updateVelocity(const glm::vec3& value) { if (shouldSuppressLocationEdits()) { return; } - if (_velocity != value) { + glm::vec3 velocity = getLocalVelocity(); + if (velocity != value) { const float MIN_LINEAR_SPEED = 0.001f; if (glm::length(value) < MIN_LINEAR_SPEED) { - _velocity = ENTITY_ITEM_ZERO_VEC3; + velocity = ENTITY_ITEM_ZERO_VEC3; } else { - _velocity = value; + velocity = value; } + setLocalVelocity(velocity); _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY; } } @@ -1436,13 +1442,15 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) { if (shouldSuppressLocationEdits()) { return; } - if (_angularVelocity != value) { + glm::vec3 angularVelocity = getLocalAngularVelocity(); + if (angularVelocity != value) { const float MIN_ANGULAR_SPEED = 0.0002f; if (glm::length(value) < MIN_ANGULAR_SPEED) { - _angularVelocity = ENTITY_ITEM_ZERO_VEC3; + angularVelocity = ENTITY_ITEM_ZERO_VEC3; } else { - _angularVelocity = value; + angularVelocity = value; } + setLocalAngularVelocity(angularVelocity); _dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY; } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 8271aedb15..c8b54bca87 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -193,9 +193,7 @@ public: float getDensity() const { return _density; } - const glm::vec3& getVelocity() const { return _velocity; } /// get velocity in meters - void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters - bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; } + bool hasVelocity() const { return getVelocity() != ENTITY_ITEM_ZERO_VEC3; } const glm::vec3& getGravity() const { return _gravity; } /// get gravity in meters void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters @@ -255,9 +253,7 @@ public: void setRegistrationPoint(const glm::vec3& value) { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); requiresRecalcBoxes(); } - const glm::vec3& getAngularVelocity() const { return _angularVelocity; } - void setAngularVelocity(const glm::vec3& value) { _angularVelocity = value; } - bool hasAngularVelocity() const { return _angularVelocity != ENTITY_ITEM_ZERO_VEC3; } + bool hasAngularVelocity() const { return getAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; } float getAngularDamping() const { return _angularDamping; } void setAngularDamping(float value) { _angularDamping = value; } @@ -435,7 +431,6 @@ protected: // rather than in all of the derived classes. If we ever collapse these classes to one we could do it a // different way. float _volumeMultiplier = 1.0f; - glm::vec3 _velocity; glm::vec3 _gravity; glm::vec3 _acceleration; float _damping; @@ -446,7 +441,6 @@ protected: quint64 _scriptTimestamp; QString _collisionSoundURL; glm::vec3 _registrationPoint; - glm::vec3 _angularVelocity; float _angularDamping; bool _visible; bool _collisionless; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index e5719c17ff..3db714964f 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -391,6 +391,77 @@ void SpatiallyNestable::setOrientation(const glm::quat& orientation) { #endif } +glm::vec3 SpatiallyNestable::getVelocity(bool& success) const { + glm::vec3 parentVelocity = getParentVelocity(success); + Transform parentTransform = getParentTransform(success); + glm::vec3 result; + _transformLock.withReadLock([&] { + // TODO: take parent angularVelocity into account. + result = parentVelocity + parentTransform.getRotation() * _velocity; + }); + return result; +} + +glm::vec3 SpatiallyNestable::getVelocity() const { + bool success; + return getVelocity(success); +} + +void SpatiallyNestable::setVelocity(const glm::vec3& velocity, bool& success) { + glm::vec3 parentVelocity = getParentVelocity(success); + Transform parentTransform = getParentTransform(success); + _transformLock.withWriteLock([&] { + // TODO: take parent angularVelocity into account. + _velocity = glm::inverse(parentTransform.getRotation()) * velocity - parentVelocity; + }); +} + +void SpatiallyNestable::setVelocity(const glm::vec3& velocity) { + bool success; + setVelocity(velocity, success); +} + +glm::vec3 SpatiallyNestable::getParentVelocity(bool& success) const { + glm::vec3 result; + SpatiallyNestablePointer parent = getParentPointer(success); + if (success && parent) { + result = parent->getVelocity(success); + } + return result; +} + +glm::vec3 SpatiallyNestable::getAngularVelocity(bool& success) const { + glm::vec3 parentAngularVelocity = getParentAngularVelocity(success); + Transform parentTransform = getParentTransform(success); + glm::vec3 result; + _transformLock.withReadLock([&] { + result = parentAngularVelocity + parentTransform.getRotation() * _angularVelocity; + }); + return result; +} + +glm::vec3 SpatiallyNestable::getAngularVelocity() const { + bool success; + return getAngularVelocity(success); +} + +void SpatiallyNestable::setAngularVelocity(const glm::vec3& angularVelocity, bool& success) { + glm::vec3 parentAngularVelocity = getParentAngularVelocity(success); + Transform parentTransform = getParentTransform(success); + _transformLock.withWriteLock([&] { + _angularVelocity = glm::inverse(parentTransform.getRotation()) * angularVelocity - parentAngularVelocity; + }); +} + +void SpatiallyNestable::setAngularVelocity(const glm::vec3& angularVelocity) { + bool success; + setAngularVelocity(angularVelocity, success); +} + +glm::vec3 SpatiallyNestable::getParentAngularVelocity(bool& success) const { + return glm::vec3(); +} + const Transform SpatiallyNestable::getTransform(bool& success) const { // return a world-space transform for this object's location Transform parentTransform = getParentTransform(success); @@ -519,6 +590,34 @@ void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) { locationChanged(); } +glm::vec3 SpatiallyNestable::getLocalVelocity() const { + glm::vec3 result(glm::vec3::_null); + _transformLock.withReadLock([&] { + result = _velocity; + }); + return result; +} + +void SpatiallyNestable::setLocalVelocity(const glm::vec3& velocity) { + _transformLock.withWriteLock([&] { + _velocity = velocity; + }); +} + +glm::vec3 SpatiallyNestable::getLocalAngularVelocity() const { + glm::vec3 result(glm::vec3::_null); + _transformLock.withReadLock([&] { + result = _angularVelocity; + }); + return result; +} + +void SpatiallyNestable::setLocalAngularVelocity(const glm::vec3& angularVelocity) { + _transformLock.withWriteLock([&] { + _angularVelocity = angularVelocity; + }); +} + glm::vec3 SpatiallyNestable::getLocalScale() const { // TODO: scale glm::vec3 result; diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 8153316ed9..ad2a9e2dd2 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -68,6 +68,18 @@ public: virtual void setOrientation(const glm::quat& orientation, bool& success); virtual void setOrientation(const glm::quat& orientation); + virtual glm::vec3 getVelocity(bool& success) const; + virtual glm::vec3 getVelocity() const; + virtual void setVelocity(const glm::vec3& velocity, bool& success); + virtual void setVelocity(const glm::vec3& velocity); + virtual glm::vec3 getParentVelocity(bool& success) const; + + virtual glm::vec3 getAngularVelocity(bool& success) const; + virtual glm::vec3 getAngularVelocity() const; + virtual void setAngularVelocity(const glm::vec3& angularVelocity, bool& success); + virtual void setAngularVelocity(const glm::vec3& angularVelocity); + virtual glm::vec3 getParentAngularVelocity(bool& success) const; + virtual AACube getMaximumAACube(bool& success) const; virtual bool computePuffedQueryAACube(); @@ -94,6 +106,12 @@ public: virtual glm::quat getLocalOrientation() const; virtual void setLocalOrientation(const glm::quat& orientation); + virtual glm::vec3 getLocalVelocity() const; + virtual void setLocalVelocity(const glm::vec3& velocity); + + virtual glm::vec3 getLocalAngularVelocity() const; + virtual void setLocalAngularVelocity(const glm::vec3& angularVelocity); + virtual glm::vec3 getLocalScale() const; virtual void setLocalScale(const glm::vec3& scale); @@ -148,6 +166,8 @@ private: mutable ReadWriteLockable _transformLock; mutable ReadWriteLockable _idLock; Transform _transform; // this is to be combined with parent's world-transform to produce this' world-transform. + glm::vec3 _velocity; + glm::vec3 _angularVelocity; mutable bool _parentKnowsMe { false }; bool _isDead { false }; };