diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index a2ff71d010..7c214624c2 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -287,6 +287,18 @@ allSections.push(elWebSections); var elWebSourceURL = document.getElementById("property-web-source-url"); + var elParticleSections = document.querySelectorAll(".particle-section"); + allSections.push(elParticleSections); + var elParticleMaxParticles = document.getElementById("property-particle-maxparticles"); + var elParticleLifeSpan = document.getElementById("property-particle-lifespan"); + var elParticleEmitRate = document.getElementById("property-particle-emit-rate"); + var elParticleEmitDirectionX = document.getElementById("property-particle-emit-direction-x"); + var elParticleEmitDirectionY = document.getElementById("property-particle-emit-direction-y"); + var elParticleEmitDirectionZ = document.getElementById("property-particle-emit-direction-z"); + var elParticleEmitStrength = document.getElementById("property-particle-emit-strength"); + var elParticleLocalGravity = document.getElementById("property-particle-localgravity"); + var elParticleRadius = document.getElementById("property-particle-radius"); + var elTextSections = document.querySelectorAll(".text-section"); allSections.push(elTextSections); var elTextText = document.getElementById("property-text-text"); @@ -455,7 +467,7 @@ } } - if (properties.type == "Box" || properties.type == "Sphere") { + if (properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") { elColorSection.style.display = 'block'; elColorRed.value = properties.color.red; elColorGreen.value = properties.color.green; @@ -465,7 +477,7 @@ elColorSection.style.display = 'none'; } - if (properties.type == "Model") { + if (properties.type == "Model" || properties.type == "ParticleEffect") { for (var i = 0; i < elModelSections.length; i++) { elModelSections[i].style.display = 'block'; } @@ -479,7 +491,7 @@ elModelAnimationFrame.value = properties.animationFrameIndex; elModelAnimationSettings.value = properties.animationSettings; elModelTextures.value = properties.textures; - elModelOriginalTextures.value = properties.originalTextures; + elModelOriginalTextures.value = properties.originalTextures; } else if (properties.type == "Web") { for (var i = 0; i < elWebSections.length; i++) { elWebSections[i].style.display = 'block'; @@ -562,6 +574,20 @@ showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox'); showElements(document.getElementsByClassName('atmosphere-section'), elZoneBackgroundMode.value == 'atmosphere'); + } else if (properties.type == "ParticleEffect") { + for (var i = 0; i < elParticleSections.length; i++) { + elParticleSections[i].style.display = 'block'; + } + + elParticleMaxParticles.value = properties.maxParticles; + elParticleLifeSpan.value = properties.lifespan.toFixed(2); + elParticleEmitRate.value = properties.emitRate.toFixed(1); + elParticleEmitDirectionX.value = properties.emitDirection.x.toFixed(2); + elParticleEmitDirectionY.value = properties.emitDirection.y.toFixed(2); + elParticleEmitDirectionZ.value = properties.emitDirection.z.toFixed(2); + elParticleEmitStrength.value = properties.emitStrength.toFixed(2); + elParticleLocalGravity.value = properties.localGravity.toFixed(2); + elParticleRadius.value = properties.particleRadius.toFixed(3); } if (selected) { @@ -678,6 +704,18 @@ elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff')); elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); + + elParticleMaxParticles.addEventListener('change', createEmitNumberPropertyUpdateFunction('maxParticles')); + elParticleLifeSpan.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifespan')); + elParticleEmitRate.addEventListener('change', createEmitNumberPropertyUpdateFunction('emitRate')); + var particleEmitDirectionChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier( + 'emitDirection', elParticleEmitDirectionX, elParticleEmitDirectionY, elParticleEmitDirectionZ, DEGREES_TO_RADIANS); + elParticleEmitDirectionX.addEventListener('change', particleEmitDirectionChangeFunction); + elParticleEmitDirectionY.addEventListener('change', particleEmitDirectionChangeFunction); + elParticleEmitDirectionZ.addEventListener('change', particleEmitDirectionChangeFunction); + elParticleEmitStrength.addEventListener('change', createEmitNumberPropertyUpdateFunction('emitStrength')); + elParticleLocalGravity.addEventListener('change', createEmitNumberPropertyUpdateFunction('localGravity')); + elParticleRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('particleRadius')); elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType')); @@ -1069,7 +1107,52 @@ - + +
+
Max Particles
+
+ +
+
+
+
Particle Life Span
+
+ +
+
+
+
Particle Emission Rate
+
+ +
+
+
+
Particle Emission Direction
+
+
X
+
Y
+
Z
+
+
+
+
Particle Emission Strength
+
+ +
+
+
+
Particle Local Gravity
+
+ +
+
+
+
Particle Radius
+
+ +
+
+
Model URL
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ef1aa2cced..36cea02bf0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -693,6 +693,10 @@ Application::~Application() { // stop the glWidget frame timer so it doesn't call paintGL _glWidget->stopFrameTimer(); + // remove avatars from physics engine + DependencyManager::get()->clearOtherAvatars(); + _physicsEngine.deleteObjects(DependencyManager::get()->getObjectsToDelete()); + DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); @@ -2166,7 +2170,7 @@ void Application::init() { _physicsEngine.init(); EntityTree* tree = _entities.getTree(); - _entitySimulation.init(tree, &_physicsEngine, &_shapeManager, &_entityEditSender); + _entitySimulation.init(tree, &_physicsEngine, &_entityEditSender); tree->setSimulation(&_entitySimulation); auto entityScriptingInterface = DependencyManager::get(); @@ -2486,12 +2490,21 @@ void Application::update(float deltaTime) { _physicsEngine.changeObjects(_entitySimulation.getObjectsToChange()); _entitySimulation.unlock(); + AvatarManager* avatarManager = DependencyManager::get().data(); + _physicsEngine.deleteObjects(avatarManager->getObjectsToDelete()); + _physicsEngine.addObjects(avatarManager->getObjectsToAdd()); + _physicsEngine.changeObjects(avatarManager->getObjectsToChange()); + _physicsEngine.stepSimulation(); if (_physicsEngine.hasOutgoingChanges()) { _entitySimulation.lock(); _entitySimulation.handleOutgoingChanges(_physicsEngine.getOutgoingChanges(), _physicsEngine.getSessionID()); _entitySimulation.unlock(); + + avatarManager->handleOutgoingChanges(_physicsEngine.getOutgoingChanges()); + avatarManager->handleCollisionEvents(_physicsEngine.getCollisionEvents()); + _physicsEngine.dumpStatsIfNecessary(); } } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index bf554f25bf..4d950d4386 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -38,6 +38,7 @@ #include "Application.h" #include "Avatar.h" #include "AvatarManager.h" +#include "AvatarMotionState.h" #include "Hand.h" #include "Head.h" #include "Menu.h" @@ -969,6 +970,9 @@ int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) { const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD; + if (_moving && _motionState) { + _motionState->addDirtyFlags(EntityItem::DIRTY_POSITION); + } return bytesRead; } @@ -1084,20 +1088,15 @@ void Avatar::setShowDisplayName(bool showDisplayName) { } -// virtual -void Avatar::rebuildSkeletonBody() { - /* TODO: implement this and remove override from MyAvatar (when we have AvatarMotionStates working) - if (_motionState) { - // compute localAABox - const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); - float radius = capsule.getRadius(); - float height = 2.0f * (capsule.getHalfHeight() + radius); - glm::vec3 corner(-radius, -0.5f * height, -radius); - corner += _skeletonModel.getBoundingShapeOffset(); - glm::vec3 scale(2.0f * radius, height, 2.0f * radius); - //_characterController.setLocalBoundingBox(corner, scale); - _motionState->setBoundingBox(corner, scale); - } - */ +// virtual +void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { + const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); + shapeInfo.setCapsuleY(capsule.getRadius(), capsule.getHalfHeight()); + shapeInfo.setOffset(_skeletonModel.getBoundingShapeOffset()); +} + +// virtual +void Avatar::rebuildSkeletonBody() { + DependencyManager::get()->updateAvatarPhysicsShape(getSessionUUID()); } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 63c537f1ce..b912a159af 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -18,11 +18,11 @@ #include #include +#include #include "Hand.h" #include "Head.h" #include "InterfaceConfig.h" -#include "Recorder.h" #include "SkeletonModel.h" #include "world.h" @@ -55,6 +55,7 @@ enum ScreenTintLayer { NUM_SCREEN_TINT_LAYERS }; +class AvatarMotionState; class Texture; class Avatar : public AvatarData { @@ -146,9 +147,9 @@ 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 const glm::vec3& getAcceleration() const { return _acceleration; } + Q_INVOKABLE const glm::vec3& getAngularVelocity() const { return _angularVelocity; } + Q_INVOKABLE const glm::vec3& getAngularAcceleration() const { return _angularAcceleration; } /// Scales a world space position vector relative to the avatar position and scale @@ -164,6 +165,10 @@ public: virtual void rebuildSkeletonBody(); + virtual void computeShapeInfo(ShapeInfo& shapeInfo); + + friend class AvatarManager; + signals: void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); @@ -231,7 +236,7 @@ private: int _voiceSphereID; - //AvatarMotionState* _motionState = nullptr; + AvatarMotionState* _motionState = nullptr; }; #endif // hifi_Avatar_h diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 5d38e89281..2b3b2f2df0 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -92,22 +92,18 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatars AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { - AvatarSharedPointer sharedAvatar = avatarIterator.value(); - Avatar* avatar = reinterpret_cast(sharedAvatar.data()); + Avatar* avatar = reinterpret_cast(avatarIterator.value().data()); - if (sharedAvatar == _myAvatar || !avatar->isInitialized()) { + if (avatar == _myAvatar || !avatar->isInitialized()) { // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. - // DO NOT update uninitialized Avatars + // DO NOT update or fade out uninitialized Avatars ++avatarIterator; - continue; - } - if (!shouldKillAvatar(sharedAvatar)) { - // this avatar's mixer is still around, go ahead and simulate it + } else if (avatar->shouldDie()) { + _avatarFades.push_back(avatarIterator.value()); + avatarIterator = _avatarHash.erase(avatarIterator); + } else { avatar->simulate(deltaTime); ++avatarIterator; - } else { - // the mixer that owned this avatar is gone, give it to the vector of fades and kill it - avatarIterator = erase(avatarIterator); } } @@ -175,24 +171,52 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { return AvatarSharedPointer(new Avatar()); } -AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) { - if (iterator.key() != MY_AVATAR_KEY) { - if (reinterpret_cast(iterator.value().data())->isInitialized()) { - _avatarFades.push_back(iterator.value()); +// virtual +AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { + AvatarSharedPointer avatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); + return avatar; +} + +// protected +void AvatarManager::removeAvatarMotionState(Avatar* avatar) { + AvatarMotionState* motionState= avatar->_motionState; + if (motionState) { + // clean up physics stuff + motionState->clearObjectBackPointer(); + avatar->_motionState = nullptr; + _avatarMotionStates.remove(motionState); + _motionStatesToAdd.remove(motionState); + _motionStatesToDelete.push_back(motionState); + } +} + +// virtual +void AvatarManager::removeAvatar(const QUuid& sessionUUID) { + AvatarHash::iterator avatarIterator = _avatarHash.find(sessionUUID); + if (avatarIterator != _avatarHash.end()) { + Avatar* avatar = reinterpret_cast(avatarIterator.value().data()); + if (avatar != _myAvatar && avatar->isInitialized()) { + removeAvatarMotionState(avatar); + + _avatarFades.push_back(avatarIterator.value()); + _avatarHash.erase(avatarIterator); } - return AvatarHashMap::erase(iterator); - } else { - // never remove _myAvatar from the list - AvatarHash::iterator returnIterator = iterator; - return ++returnIterator; } } void AvatarManager::clearOtherAvatars() { // clear any avatars that came from an avatar-mixer - AvatarHash::iterator removeAvatar = _avatarHash.begin(); - while (removeAvatar != _avatarHash.end()) { - removeAvatar = erase(removeAvatar); + AvatarHash::iterator avatarIterator = _avatarHash.begin(); + while (avatarIterator != _avatarHash.end()) { + Avatar* avatar = reinterpret_cast(avatarIterator.value().data()); + if (avatar == _myAvatar || !avatar->isInitialized()) { + // don't remove myAvatar or uninitialized avatars from the list + ++avatarIterator; + } else { + removeAvatarMotionState(avatar); + _avatarFades.push_back(avatarIterator.value()); + avatarIterator = _avatarHash.erase(avatarIterator); + } } _myAvatar->clearLookAtTargetAvatar(); } @@ -215,3 +239,57 @@ QVector AvatarManager::getLocalLights() const { return _localLights; } +VectorOfMotionStates& AvatarManager::getObjectsToDelete() { + _tempMotionStates.clear(); + _tempMotionStates.swap(_motionStatesToDelete); + return _tempMotionStates; +} + +VectorOfMotionStates& AvatarManager::getObjectsToAdd() { + _tempMotionStates.clear(); + + for (auto motionState : _motionStatesToAdd) { + _tempMotionStates.push_back(motionState); + } + _motionStatesToAdd.clear(); + return _tempMotionStates; +} + +VectorOfMotionStates& AvatarManager::getObjectsToChange() { + _tempMotionStates.clear(); + for (auto state : _avatarMotionStates) { + if (state->_dirtyFlags > 0) { + _tempMotionStates.push_back(state); + } + } + return _tempMotionStates; +} + +void AvatarManager::handleOutgoingChanges(VectorOfMotionStates& motionStates) { + // TODO: extract the MyAvatar results once we use a MotionState for it. +} + +void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) { + // TODO: expose avatar collision events to JS +} + +void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { + AvatarHash::iterator avatarItr = _avatarHash.find(id); + if (avatarItr != _avatarHash.end()) { + Avatar* avatar = static_cast(avatarItr.value().data()); + AvatarMotionState* motionState = avatar->_motionState; + if (motionState) { + motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE); + } else { + ShapeInfo shapeInfo; + avatar->computeShapeInfo(shapeInfo); + btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); + if (shape) { + AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); + avatar->_motionState = motionState; + _motionStatesToAdd.insert(motionState); + _avatarMotionStates.insert(motionState); + } + } + } +} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index e6a358401c..bc4f7765d8 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -17,8 +17,10 @@ #include #include +#include #include "Avatar.h" +#include "AvatarMotionState.h" class MyAvatar; @@ -51,6 +53,14 @@ public: Q_INVOKABLE void setLocalLights(const QVector& localLights); Q_INVOKABLE QVector getLocalLights() const; + + VectorOfMotionStates& getObjectsToDelete(); + VectorOfMotionStates& getObjectsToAdd(); + VectorOfMotionStates& getObjectsToChange(); + void handleOutgoingChanges(VectorOfMotionStates& motionStates); + void handleCollisionEvents(CollisionEvents& collisionEvents); + + void updateAvatarPhysicsShape(const QUuid& id); public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } @@ -62,10 +72,11 @@ private: void simulateAvatarFades(float deltaTime); void renderAvatarFades(RenderArgs* renderArgs, const glm::vec3& cameraPosition); - AvatarSharedPointer newSharedAvatar(); - // virtual overrides - AvatarHash::iterator erase(const AvatarHash::iterator& iterator); + virtual AvatarSharedPointer newSharedAvatar(); + virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); + void removeAvatarMotionState(Avatar* avatar); + virtual void removeAvatar(const QUuid& sessionUUID); QVector _avatarFades; QSharedPointer _myAvatar; @@ -74,6 +85,11 @@ private: QVector _localLights; bool _shouldShowReceiveStats = false; + + SetOfAvatarMotionStates _avatarMotionStates; + SetOfMotionStates _motionStatesToAdd; + VectorOfMotionStates _motionStatesToDelete; + VectorOfMotionStates _tempMotionStates; }; Q_DECLARE_METATYPE(AvatarManager::LocalLight) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp new file mode 100644 index 0000000000..03a49c069e --- /dev/null +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -0,0 +1,160 @@ +// +// AvatarMotionState.cpp +// interface/src/avatar/ +// +// Created by Andrew Meadows 2015.05.14 +// 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 + +#include "Avatar.h" +#include "AvatarMotionState.h" +#include "BulletUtil.h" + +AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { + assert(_avatar); + if (_shape) { + _mass = 100.0f; // HACK + } +} + +AvatarMotionState::~AvatarMotionState() { + _avatar = nullptr; +} + +// virtual +uint32_t AvatarMotionState::getAndClearIncomingDirtyFlags() { + uint32_t dirtyFlags = 0; + if (_body && _avatar) { + dirtyFlags = _dirtyFlags; + _dirtyFlags = 0; + } + return dirtyFlags; +} + +MotionType AvatarMotionState::computeObjectMotionType() const { + // TODO?: support non-DYNAMIC motion for avatars? (e.g. when sitting) + return MOTION_TYPE_DYNAMIC; +} + +// virtual and protected +btCollisionShape* AvatarMotionState::computeNewShape() { + if (_avatar) { + ShapeInfo shapeInfo; + _avatar->computeShapeInfo(shapeInfo); + return getShapeManager()->getShape(shapeInfo); + } + return nullptr; +} + +// virtual +bool AvatarMotionState::isMoving() const { + return false; +} + +// virtual +void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const { + if (!_avatar) { + return; + } + worldTrans.setOrigin(glmToBullet(getObjectPosition())); + worldTrans.setRotation(glmToBullet(getObjectRotation())); + if (_body) { + _body->setLinearVelocity(glmToBullet(getObjectLinearVelocity())); + _body->setAngularVelocity(glmToBullet(getObjectLinearVelocity())); + } +} + +// virtual +void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) { + if (!_avatar) { + return; + } + // HACK: The PhysicsEngine does not actually move OTHER avatars -- instead it slaves their local RigidBody to the transform + // as specified by a remote simulation. However, to give the remote simulation time to respond to our own objects we tie + // the other avatar's body to its true position with a simple spring. This is a HACK that will have to be improved later. + const float SPRING_TIMESCALE = 0.5f; + float tau = PHYSICS_ENGINE_FIXED_SUBSTEP / SPRING_TIMESCALE; + btVector3 currentPosition = worldTrans.getOrigin(); + btVector3 targetPosition = glmToBullet(getObjectPosition()); + btTransform newTransform; + newTransform.setOrigin((1.0f - tau) * currentPosition + tau * targetPosition); + newTransform.setRotation(glmToBullet(getObjectRotation())); + _body->setWorldTransform(newTransform); + _body->setLinearVelocity(glmToBullet(getObjectLinearVelocity())); + _body->setAngularVelocity(glmToBullet(getObjectLinearVelocity())); +} + +// These pure virtual methods must be implemented for each MotionState type +// and make it possible to implement more complicated methods in this base class. + +// virtual +float AvatarMotionState::getObjectRestitution() const { + return 0.5f; +} + +// virtual +float AvatarMotionState::getObjectFriction() const { + return 0.5f; +} + +// virtual +float AvatarMotionState::getObjectLinearDamping() const { + return 0.5f; +} + +// virtual +float AvatarMotionState::getObjectAngularDamping() const { + return 0.5f; +} + +// virtual +glm::vec3 AvatarMotionState::getObjectPosition() const { + return _avatar->getPosition(); +} + +// virtual +glm::quat AvatarMotionState::getObjectRotation() const { + return _avatar->getOrientation(); +} + +// virtual +const glm::vec3& AvatarMotionState::getObjectLinearVelocity() const { + return _avatar->getVelocity(); +} + +// virtual +const glm::vec3& AvatarMotionState::getObjectAngularVelocity() const { + return _avatar->getAngularVelocity(); +} + +// virtual +const glm::vec3& AvatarMotionState::getObjectGravity() const { + return _avatar->getAcceleration(); +} + +// virtual +const QUuid& AvatarMotionState::getObjectID() const { + return _avatar->getSessionUUID(); +} + +// virtual +QUuid AvatarMotionState::getSimulatorID() const { + return _avatar->getSessionUUID(); +} + +// virtual +void AvatarMotionState::bump() { +} + +// virtual +void AvatarMotionState::clearObjectBackPointer() { + ObjectMotionState::clearObjectBackPointer(); + _avatar = nullptr; +} + + diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h new file mode 100644 index 0000000000..a4fc9db20a --- /dev/null +++ b/interface/src/avatar/AvatarMotionState.h @@ -0,0 +1,75 @@ +// +// AvatarMotionState.h +// interface/src/avatar/ +// +// Created by Andrew Meadows 2015.05.14 +// 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_AvatarMotionState_h +#define hifi_AvatarMotionState_h + +#include + +#include + +class Avatar; + +class AvatarMotionState : public ObjectMotionState { +public: + AvatarMotionState(Avatar* avatar, btCollisionShape* shape); + ~AvatarMotionState(); + + virtual MotionType getMotionType() const { return _motionType; } + + virtual uint32_t getAndClearIncomingDirtyFlags(); + + virtual MotionType computeObjectMotionType() const; + + virtual bool isMoving() const; + + // this relays incoming position/rotation to the RigidBody + virtual void getWorldTransform(btTransform& worldTrans) const; + + // this relays outgoing position/rotation to the EntityItem + virtual void setWorldTransform(const btTransform& worldTrans); + + + // These pure virtual methods must be implemented for each MotionState type + // and make it possible to implement more complicated methods in this base class. + + virtual float getObjectRestitution() const; + virtual float getObjectFriction() const; + virtual float getObjectLinearDamping() const; + virtual float getObjectAngularDamping() const; + + virtual glm::vec3 getObjectPosition() const; + virtual glm::quat getObjectRotation() const; + virtual const glm::vec3& getObjectLinearVelocity() const; + virtual const glm::vec3& getObjectAngularVelocity() const; + virtual const glm::vec3& getObjectGravity() const; + + virtual const QUuid& getObjectID() const; + + virtual QUuid getSimulatorID() const; + virtual void bump(); + + void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal); + + void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; } + + friend class AvatarManager; + +protected: + virtual btCollisionShape* computeNewShape(); + virtual void clearObjectBackPointer(); + Avatar* _avatar; + uint32_t _dirtyFlags; +}; + +typedef QSet SetOfAvatarMotionStates; + +#endif // hifi_AvatarMotionState_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8c52c79ca0..e2f4ca51a0 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -41,6 +41,7 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : _headClipDistance(DEFAULT_NEAR_CLIP) { assert(_owningAvatar); + _enableShapes = true; } SkeletonModel::~SkeletonModel() { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 6a11f94cb6..603b5d76ea 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -69,6 +69,7 @@ const quint32 AVATAR_MOTION_DEFAULTS = const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; +const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND; // Bitset of state flags - we store the key state, hand state, faceshift, chat circling, and existance of // referential data in this bit set. The hand state is an octal, but is split into two sections to maintain @@ -290,7 +291,6 @@ public: QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); } void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); } - Node* getOwningAvatarMixer() { return _owningAvatarMixer.data(); } void setOwningAvatarMixer(const QWeakPointer& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } const AABox& getLocalAABox() const { return _localAABox; } @@ -301,8 +301,10 @@ public: int getReceiveRate() const; void setVelocity(const glm::vec3 velocity) { _velocity = velocity; } - Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; } - glm::vec3 getTargetVelocity() const { return _targetVelocity; } + Q_INVOKABLE const glm::vec3& getVelocity() const { return _velocity; } + const glm::vec3& getTargetVelocity() const { return _targetVelocity; } + + bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; } public slots: void sendAvatarDataPacket(); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index b4291ef435..6d0d9d8d76 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -20,19 +20,6 @@ AvatarHashMap::AvatarHashMap() { connect(DependencyManager::get().data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged); } - -AvatarHash::iterator AvatarHashMap::erase(const AvatarHash::iterator& iterator) { - qCDebug(avatars) << "Removing Avatar with UUID" << iterator.key() << "from AvatarHashMap."; - return _avatarHash.erase(iterator); -} - -const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND; - -bool AvatarHashMap::shouldKillAvatar(const AvatarSharedPointer& sharedAvatar) { - return (sharedAvatar->getOwningAvatarMixer() == NULL - || sharedAvatar->getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS); -} - void AvatarHashMap::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer& mixerWeakPointer) { switch (packetTypeForPacket(datagram)) { case PacketTypeBulkAvatarData: @@ -52,10 +39,6 @@ void AvatarHashMap::processAvatarMixerDatagram(const QByteArray& datagram, const } } -bool AvatarHashMap::containsAvatarWithDisplayName(const QString& displayName) { - return !avatarWithDisplayName(displayName).isNull(); -} - bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) { foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) { glm::vec3 avatarPosition = sharedAvatar->getPosition(); @@ -67,45 +50,19 @@ bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range return false; } -AvatarWeakPointer AvatarHashMap::avatarWithDisplayName(const QString& displayName) { - foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) { - if (sharedAvatar->getDisplayName() == displayName) { - // this is a match - // check if this avatar should still be around - if (!shouldKillAvatar(sharedAvatar)) { - // we have a match, return the AvatarData - return sharedAvatar; - } else { - // we should remove this avatar, but we might not be on a thread that is allowed - // so we just return NULL to the caller - return AvatarWeakPointer(); - } - } - } - - return AvatarWeakPointer(); -} - AvatarSharedPointer AvatarHashMap::newSharedAvatar() { return AvatarSharedPointer(new AvatarData()); } -AvatarSharedPointer AvatarHashMap::matchingOrNewAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { - AvatarSharedPointer matchingAvatar = _avatarHash.value(sessionUUID); - - if (!matchingAvatar) { - // insert the new avatar into our hash - matchingAvatar = newSharedAvatar(); - - qCDebug(avatars) << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap."; - - matchingAvatar->setSessionUUID(sessionUUID); - matchingAvatar->setOwningAvatarMixer(mixerWeakPointer); - - _avatarHash.insert(sessionUUID, matchingAvatar); - } - - return matchingAvatar; +AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { + qCDebug(avatars) << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap."; + + AvatarSharedPointer avatar = newSharedAvatar(); + avatar->setSessionUUID(sessionUUID); + avatar->setOwningAvatarMixer(mixerWeakPointer); + _avatarHash.insert(sessionUUID, avatar); + + return avatar; } void AvatarHashMap::processAvatarDataPacket(const QByteArray &datagram, const QWeakPointer &mixerWeakPointer) { @@ -118,10 +75,13 @@ void AvatarHashMap::processAvatarDataPacket(const QByteArray &datagram, const QW bytesRead += NUM_BYTES_RFC4122_UUID; if (sessionUUID != _lastOwnerSessionUUID) { - AvatarSharedPointer matchingAvatarData = matchingOrNewAvatar(sessionUUID, mixerWeakPointer); + AvatarSharedPointer avatar = _avatarHash.value(sessionUUID); + if (!avatar) { + avatar = addAvatar(sessionUUID, mixerWeakPointer); + } // have the matching (or new) avatar parse the data from the packet - bytesRead += matchingAvatarData->parseDataAtOffset(datagram, bytesRead); + bytesRead += avatar->parseDataAtOffset(datagram, bytesRead); } else { // create a dummy AvatarData class to throw this data on the ground AvatarData dummyData; @@ -145,24 +105,24 @@ void AvatarHashMap::processAvatarIdentityPacket(const QByteArray &packet, const identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName; // mesh URL for a UUID, find avatar in our list - AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer); - if (matchingAvatar) { - - if (matchingAvatar->getFaceModelURL() != faceMeshURL) { - matchingAvatar->setFaceModelURL(faceMeshURL); - } - - if (matchingAvatar->getSkeletonModelURL() != skeletonURL) { - matchingAvatar->setSkeletonModelURL(skeletonURL); - } - - if (matchingAvatar->getAttachmentData() != attachmentData) { - matchingAvatar->setAttachmentData(attachmentData); - } - - if (matchingAvatar->getDisplayName() != displayName) { - matchingAvatar->setDisplayName(displayName); - } + AvatarSharedPointer avatar = _avatarHash.value(sessionUUID); + if (!avatar) { + avatar = addAvatar(sessionUUID, mixerWeakPointer); + } + if (avatar->getFaceModelURL() != faceMeshURL) { + avatar->setFaceModelURL(faceMeshURL); + } + + if (avatar->getSkeletonModelURL() != skeletonURL) { + avatar->setSkeletonModelURL(skeletonURL); + } + + if (avatar->getAttachmentData() != attachmentData) { + avatar->setAttachmentData(attachmentData); + } + + if (avatar->getDisplayName() != displayName) { + avatar->setDisplayName(displayName); } } } @@ -171,24 +131,25 @@ void AvatarHashMap::processAvatarBillboardPacket(const QByteArray& packet, const int headerSize = numBytesForPacketHeader(packet); QUuid sessionUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID)); - AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer); - if (matchingAvatar) { - QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID); - if (matchingAvatar->getBillboard() != billboard) { - matchingAvatar->setBillboard(billboard); - } + AvatarSharedPointer avatar = _avatarHash.value(sessionUUID); + if (!avatar) { + avatar = addAvatar(sessionUUID, mixerWeakPointer); + } + + QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID); + if (avatar->getBillboard() != billboard) { + avatar->setBillboard(billboard); } } void AvatarHashMap::processKillAvatar(const QByteArray& datagram) { // read the node id QUuid sessionUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(datagram), NUM_BYTES_RFC4122_UUID)); - - // remove the avatar with that UUID from our hash, if it exists - AvatarHash::iterator matchedAvatar = _avatarHash.find(sessionUUID); - if (matchedAvatar != _avatarHash.end()) { - erase(matchedAvatar); - } + removeAvatar(sessionUUID); +} + +void AvatarHashMap::removeAvatar(const QUuid& sessionUUID) { + _avatarHash.remove(sessionUUID); } void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index b7d40e2acc..9204826d03 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -36,28 +36,26 @@ public: public slots: void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer& mixerWeakPointer); - bool containsAvatarWithDisplayName(const QString& displayName); bool isAvatarInRange(const glm::vec3 & position, const float range); - AvatarWeakPointer avatarWithDisplayName(const QString& displayname); private slots: void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID); protected: AvatarHashMap(); - virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator); - - bool shouldKillAvatar(const AvatarSharedPointer& sharedAvatar); virtual AvatarSharedPointer newSharedAvatar(); - AvatarSharedPointer matchingOrNewAvatar(const QUuid& nodeUUID, const QWeakPointer& mixerWeakPointer); + virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); + virtual void removeAvatar(const QUuid& sessionUUID); + AvatarHash _avatarHash; + +private: void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer& mixerWeakPointer); void processAvatarIdentityPacket(const QByteArray& packet, const QWeakPointer& mixerWeakPointer); void processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer& mixerWeakPointer); void processKillAvatar(const QByteArray& datagram); - AvatarHash _avatarHash; QUuid _lastOwnerSessionUUID; }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 62b40e1698..500e76dc8d 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -537,7 +537,7 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer en if (entityItem->getType() == EntityTypes::Model) { std::shared_ptr modelEntityItem = - std::dynamic_pointer_cast(entityItem); + std::dynamic_pointer_cast(entityItem); assert(modelEntityItem); // we need this!!! Model* model = modelEntityItem->getModel(this); if (model) { @@ -551,7 +551,7 @@ const Model* EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityI const Model* result = NULL; if (entityItem->getType() == EntityTypes::Model) { std::shared_ptr modelEntityItem = - std::dynamic_pointer_cast(entityItem); + std::dynamic_pointer_cast(entityItem); result = modelEntityItem->getModel(this); } return result; @@ -562,7 +562,7 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemP if (entityItem->getType() == EntityTypes::Model) { std::shared_ptr modelEntityItem = - std::dynamic_pointer_cast(entityItem); + std::dynamic_pointer_cast(entityItem); if (modelEntityItem->hasCompoundShapeURL()) { Model* model = modelEntityItem->getModel(this); if (model) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 2693f1af02..711f021426 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -173,7 +173,6 @@ private: bool _hasPreviousZone = false; std::shared_ptr _bestZone; - float _bestZoneVolume; glm::vec3 _previousKeyLightColor; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index d8d0496422..8981d149ae 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -29,7 +29,6 @@ void RenderableLineEntityItem::render(RenderArgs* args) { glm::vec3 p1 = ENTITY_ITEM_ZERO_VEC3; glm::vec3 p2 = getDimensions(); glm::vec4 lineColor(toGlm(getXColor()), getLocalRenderAlpha()); - Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; batch.setModelTransform(getTransformToCenter()); diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 156f593421..ed0201763a 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -43,18 +43,18 @@ void Mesh::addAttribute(Slot slot, const BufferView& buffer) { } void Mesh::evalVertexFormat() { - VertexFormat vf; + auto vf = new VertexFormat(); int channelNum = 0; if (hasVertexData()) { - vf.setAttribute(gpu::Stream::POSITION, channelNum, _vertexBuffer._element, 0); + vf->setAttribute(gpu::Stream::POSITION, channelNum, _vertexBuffer._element, 0); channelNum++; } for (auto attrib : _attributeBuffers) { - vf.setAttribute(attrib.first, channelNum, attrib.second._element, 0); + vf->setAttribute(attrib.first, channelNum, attrib.second._element, 0); channelNum++; } - _vertexFormat = vf; + _vertexFormat.reset(vf); } void Mesh::setIndexBuffer(const BufferView& buffer) { @@ -112,12 +112,12 @@ const gpu::BufferStream Mesh::makeBufferStream() const { int channelNum = 0; if (hasVertexData()) { - stream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat.getChannelStride(channelNum)); + stream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat->getChannelStride(channelNum)); channelNum++; } for (auto attrib : _attributeBuffers) { BufferView& view = attrib.second; - stream.addBuffer(view._buffer, view._offset, _vertexFormat.getChannelStride(channelNum)); + stream.addBuffer(view._buffer, view._offset, _vertexFormat->getChannelStride(channelNum)); channelNum++; } diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 31c06e6eca..95f1c3bce7 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -47,14 +47,14 @@ public: void setVertexBuffer(const BufferView& buffer); const BufferView& getVertexBuffer() const { return _vertexBuffer; } uint getNumVertices() const { return _vertexBuffer.getNumElements(); } - bool hasVertexData() const { return !_vertexBuffer._buffer; } + bool hasVertexData() const { return _vertexBuffer._buffer.get() != nullptr; } // Attribute Buffers int getNumAttributes() const { return _attributeBuffers.size(); } void addAttribute(Slot slot, const BufferView& buffer); // Stream format - const VertexFormat& getVertexFormat() const { return _vertexFormat; } + const gpu::Stream::FormatPointer getVertexFormat() const { return _vertexFormat; } // Index Buffer void setIndexBuffer(const BufferView& buffer); @@ -114,7 +114,7 @@ public: protected: - VertexFormat _vertexFormat; + gpu::Stream::FormatPointer _vertexFormat; BufferView _vertexBuffer; BufferViewMap _attributeBuffers; diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index db4c97f8d6..894a7b8aa9 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -27,7 +27,7 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi _nodePacketCounts[sendingNode->getUUID()]++; unlock(); - // Make sure to wake our actual processing thread because we now have packets for it to process. + // Make sure to wake our actual processing thread because we now have packets for it to process. _hasPackets.wakeAll(); } diff --git a/libraries/octree/src/OctreeConstants.h b/libraries/octree/src/OctreeConstants.h index 1246aeffd4..4a1baea2d5 100644 --- a/libraries/octree/src/OctreeConstants.h +++ b/libraries/octree/src/OctreeConstants.h @@ -34,9 +34,9 @@ const float VIEW_FRUSTUM_FOV_OVERSEND = 60.0f; // These are guards to prevent our voxel tree recursive routines from spinning out of control const int UNREASONABLY_DEEP_RECURSION = 29; // use this for something that you want to be shallow, but not spin out const int DANGEROUSLY_DEEP_RECURSION = 200; // use this for something that needs to go deeper -const float SCALE_AT_UNREASONABLY_DEEP_RECURSION = (1.0f / powf(2.0f, UNREASONABLY_DEEP_RECURSION)); -const float SCALE_AT_DANGEROUSLY_DEEP_RECURSION = (1.0f / powf(2.0f, DANGEROUSLY_DEEP_RECURSION)); -const float SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE = SCALE_AT_UNREASONABLY_DEEP_RECURSION * 2.0f; // 0.00006103515 meter ~1/10,0000th +const float SCALE_AT_UNREASONABLY_DEEP_RECURSION = (TREE_SCALE / powf(2.0f, UNREASONABLY_DEEP_RECURSION)); +const float SCALE_AT_DANGEROUSLY_DEEP_RECURSION = (TREE_SCALE / powf(2.0f, DANGEROUSLY_DEEP_RECURSION)); +const float SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE = SCALE_AT_UNREASONABLY_DEEP_RECURSION * 2.0f; // 0.00001525878 meter ~1/10,0000th const int DEFAULT_MAX_OCTREE_PPS = 600; // the default maximum PPS we think any octree based server should send to a client diff --git a/libraries/physics/src/DynamicCharacterController.cpp b/libraries/physics/src/DynamicCharacterController.cpp index eebd8f0c88..5d2a1798f9 100644 --- a/libraries/physics/src/DynamicCharacterController.cpp +++ b/libraries/physics/src/DynamicCharacterController.cpp @@ -311,9 +311,11 @@ void DynamicCharacterController::updateShapeIfNecessary() { // create new shape _shape = new btCapsuleShape(_radius, 2.0f * _halfHeight); + // HACK: use some simple mass property defaults for now + float mass = 100.0f; + btVector3 inertia(30.0f, 8.0f, 30.0f); + // create new body - float mass = 1.0f; - btVector3 inertia(1.0f, 1.0f, 1.0f); _rigidBody = new btRigidBody(mass, nullptr, _shape, inertia); _rigidBody->setSleepingThresholds(0.0f, 0.0f); _rigidBody->setAngularFactor(0.0f); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 12a6ef03ff..6c4b3dad75 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -39,8 +39,9 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer _loopsSinceOwnershipBid(0), _loopsWithoutOwner(0) { - _type = MOTION_STATE_TYPE_ENTITY; - assert(entity != nullptr); + _type = MOTIONSTATE_TYPE_ENTITY; + assert(_entity != nullptr); + setMass(_entity->computeMass()); } EntityMotionState::~EntityMotionState() { @@ -88,7 +89,6 @@ void EntityMotionState::handleEasyChanges(uint32_t flags) { if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { _body->activate(); } - } @@ -98,10 +98,9 @@ void EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* ObjectMotionState::handleHardAndEasyChanges(flags, engine); } -void EntityMotionState::clearEntity() { +void EntityMotionState::clearObjectBackPointer() { + ObjectMotionState::clearObjectBackPointer(); _entity = nullptr; - // set the type to INVALID so that external logic that pivots on the type won't try to access _entity - _type = MOTION_STATE_TYPE_INVALID; } MotionType EntityMotionState::computeObjectMotionType() const { @@ -178,10 +177,14 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { #endif } -void EntityMotionState::computeObjectShapeInfo(ShapeInfo& shapeInfo) { +// virtual and protected +btCollisionShape* EntityMotionState::computeNewShape() { if (_entity) { + ShapeInfo shapeInfo; _entity->computeShapeInfo(shapeInfo); + return getShapeManager()->getShape(shapeInfo); } + return nullptr; } // RELIABLE_SEND_HACK: until we have truly reliable resends of non-moving updates @@ -444,7 +447,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _lastStep = step; } -uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() const { +uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() { uint32_t dirtyFlags = 0; if (_body && _entity) { dirtyFlags = _entity->getDirtyFlags(); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index f359ec7fee..80b56ccf2a 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -43,14 +43,12 @@ public: // this relays outgoing position/rotation to the EntityItem virtual void setWorldTransform(const btTransform& worldTrans); - virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo); - bool isCandidateForOwnership(const QUuid& sessionID) const; bool remoteSimulationOutOfSync(uint32_t simulationStep); bool shouldSendUpdate(uint32_t simulationStep, const QUuid& sessionID); void sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step); - virtual uint32_t getAndClearIncomingDirtyFlags() const; + virtual uint32_t getAndClearIncomingDirtyFlags(); void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; } void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; } @@ -62,7 +60,7 @@ public: virtual float getObjectAngularDamping() const { return _entity->getAngularDamping(); } virtual glm::vec3 getObjectPosition() const { return _entity->getPosition() - ObjectMotionState::getWorldOffset(); } - virtual const glm::quat& getObjectRotation() const { return _entity->getRotation(); } + virtual glm::quat getObjectRotation() const { return _entity->getRotation(); } virtual const glm::vec3& getObjectLinearVelocity() const { return _entity->getVelocity(); } virtual const glm::vec3& getObjectAngularVelocity() const { return _entity->getAngularVelocity(); } virtual const glm::vec3& getObjectGravity() const { return _entity->getGravity(); } @@ -82,8 +80,8 @@ public: friend class PhysicalEntitySimulation; protected: - void clearEntity(); - + virtual btCollisionShape* computeNewShape(); + virtual void clearObjectBackPointer(); virtual void setMotionType(MotionType motionType); EntityItemPointer _entity; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index c5288cfa76..57b75f0f3b 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -140,20 +140,14 @@ void ObjectMotionState::handleEasyChanges(uint32_t flags) { } if (flags & EntityItem::DIRTY_MASS) { - float mass = getMass(); - btVector3 inertia(0.0f, 0.0f, 0.0f); - _body->getCollisionShape()->calculateLocalInertia(mass, inertia); - _body->setMassProps(mass, inertia); - _body->updateInertiaTensor(); + updateBodyMassProperties(); } } void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { if (flags & EntityItem::DIRTY_SHAPE) { // make sure the new shape is valid - ShapeInfo shapeInfo; - computeObjectShapeInfo(shapeInfo); - btCollisionShape* newShape = getShapeManager()->getShape(shapeInfo); + btCollisionShape* newShape = computeNewShape(); if (!newShape) { qCDebug(physics) << "Warning: failed to generate new shape!"; // failed to generate new shape! --> keep old shape and remove shape-change flag @@ -168,17 +162,22 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* } } getShapeManager()->releaseShape(_shape); - _shape = newShape; - _body->setCollisionShape(_shape); - if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - handleEasyChanges(flags); - } - } else { - if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - handleEasyChanges(flags); + if (_shape != newShape) { + _shape = newShape; + _body->setCollisionShape(_shape); + } else { + // huh... the shape didn't actually change, so we clear the DIRTY_SHAPE flag + flags &= ~EntityItem::DIRTY_SHAPE; } } - engine->reinsertObject(this); + if (flags & EASY_DIRTY_PHYSICS_FLAGS) { + handleEasyChanges(flags); + } + // it is possible that there are no HARD flags at this point (if DIRTY_SHAPE was removed) + // so we check again befoe we reinsert: + if (flags & HARD_DIRTY_PHYSICS_FLAGS) { + engine->reinsertObject(this); + } } void ObjectMotionState::updateBodyMaterialProperties() { @@ -193,3 +192,11 @@ void ObjectMotionState::updateBodyVelocities() { setBodyGravity(getObjectGravity()); _body->setActivationState(ACTIVE_TAG); } + +void ObjectMotionState::updateBodyMassProperties() { + float mass = getMass(); + btVector3 inertia(0.0f, 0.0f, 0.0f); + _body->getCollisionShape()->calculateLocalInertia(mass, inertia); + _body->setMassProps(mass, inertia); + _body->updateInertiaTensor(); +} diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index bfc9310ec6..337f09e719 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -15,6 +15,9 @@ #include #include +#include +#include + #include #include "ContactInfo.h" @@ -27,9 +30,9 @@ enum MotionType { }; enum MotionStateType { - MOTION_STATE_TYPE_INVALID, - MOTION_STATE_TYPE_ENTITY, - MOTION_STATE_TYPE_AVATAR + MOTIONSTATE_TYPE_INVALID, + MOTIONSTATE_TYPE_ENTITY, + MOTIONSTATE_TYPE_AVATAR }; // The update flags trigger two varieties of updates: "hard" which require the body to be pulled @@ -71,8 +74,9 @@ public: virtual void handleEasyChanges(uint32_t flags); virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); - virtual void updateBodyMaterialProperties(); - virtual void updateBodyVelocities(); + void updateBodyMaterialProperties(); + void updateBodyVelocities(); + virtual void updateBodyMassProperties(); MotionStateType getType() const { return _type; } virtual MotionType getMotionType() const { return _motionType; } @@ -87,11 +91,9 @@ public: glm::vec3 getBodyLinearVelocity() const; glm::vec3 getBodyAngularVelocity() const; - virtual uint32_t getAndClearIncomingDirtyFlags() const = 0; + virtual uint32_t getAndClearIncomingDirtyFlags() = 0; virtual MotionType computeObjectMotionType() const = 0; - virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo) = 0; - btCollisionShape* getShape() const { return _shape; } btRigidBody* getRigidBody() const { return _body; } @@ -109,7 +111,7 @@ public: virtual float getObjectAngularDamping() const = 0; virtual glm::vec3 getObjectPosition() const = 0; - virtual const glm::quat& getObjectRotation() const = 0; + virtual glm::quat getObjectRotation() const = 0; virtual const glm::vec3& getObjectLinearVelocity() const = 0; virtual const glm::vec3& getObjectAngularVelocity() const = 0; virtual const glm::vec3& getObjectGravity() const = 0; @@ -124,10 +126,15 @@ public: friend class PhysicsEngine; protected: - virtual void setMotionType(MotionType motionType); + virtual btCollisionShape* computeNewShape() = 0; + void setMotionType(MotionType motionType); + + // clearObjectBackPointer() overrrides should call the base method, then actually clear the object back pointer. + virtual void clearObjectBackPointer() { _type = MOTIONSTATE_TYPE_INVALID; } + void setRigidBody(btRigidBody* body); - MotionStateType _type = MOTION_STATE_TYPE_INVALID; // type of MotionState + MotionStateType _type = MOTIONSTATE_TYPE_INVALID; // type of MotionState MotionType _motionType; // type of motion: KINEMATIC, DYNAMIC, or STATIC btCollisionShape* _shape; @@ -137,4 +144,7 @@ protected: uint32_t _lastKinematicStep; }; +typedef QSet SetOfMotionStates; +typedef QVector VectorOfMotionStates; + #endif // hifi_ObjectMotionState_h diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 54a3048bed..b615cf0c77 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -9,11 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "EntityMotionState.h" #include "PhysicalEntitySimulation.h" #include "PhysicsHelpers.h" #include "PhysicsLogging.h" -#include "ShapeInfoUtil.h" #include "ShapeManager.h" PhysicalEntitySimulation::PhysicalEntitySimulation() { @@ -25,7 +23,6 @@ PhysicalEntitySimulation::~PhysicalEntitySimulation() { void PhysicalEntitySimulation::init( EntityTree* tree, PhysicsEngine* physicsEngine, - ShapeManager* shapeManager, EntityEditPacketSender* packetSender) { assert(tree); setEntityTree(tree); @@ -33,9 +30,6 @@ void PhysicalEntitySimulation::init( assert(physicsEngine); _physicsEngine = physicsEngine; - assert(shapeManager); - _shapeManager = shapeManager; - assert(packetSender); _entityPacketSender = packetSender; } @@ -60,7 +54,7 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) { EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); if (motionState) { - motionState->clearEntity(); + motionState->clearObjectBackPointer(); entity->setPhysicsInfo(nullptr); _pendingRemoves.insert(motionState); _outgoingChanges.remove(motionState); @@ -109,7 +103,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() { if (entity) { entity->setPhysicsInfo(nullptr); } - motionState->clearEntity(); + motionState->clearObjectBackPointer(); } // then delete the objects (aka MotionStates) @@ -135,7 +129,7 @@ VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToDelete() { if (entity) { _pendingAdds.remove(entity); entity->setPhysicsInfo(nullptr); - motionState->clearEntity(); + motionState->clearObjectBackPointer(); } _tempVector.push_back(motionState); } @@ -158,16 +152,14 @@ VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToAdd() { } else if (entity->isReadyToComputeShape()) { ShapeInfo shapeInfo; entity->computeShapeInfo(shapeInfo); - btCollisionShape* shape = _shapeManager->getShape(shapeInfo); + btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); if (shape) { EntityMotionState* motionState = new EntityMotionState(shape, entity); entity->setPhysicsInfo(static_cast(motionState)); - motionState->setMass(entity->computeMass()); _physicalObjects.insert(motionState); _tempVector.push_back(motionState); entityItr = _pendingAdds.erase(entityItr); } else { - // TODO: Seth to look into why this case is hit. //qDebug() << "Warning! Failed to generate new shape for entity." << entity->getName(); ++entityItr; } @@ -192,7 +184,7 @@ void PhysicalEntitySimulation::handleOutgoingChanges(VectorOfMotionStates& motio // walk the motionStates looking for those that correspond to entities for (auto stateItr : motionStates) { ObjectMotionState* state = &(*stateItr); - if (state && state->getType() == MOTION_STATE_TYPE_ENTITY) { + if (state && state->getType() == MOTIONSTATE_TYPE_ENTITY) { EntityMotionState* entityState = static_cast(state); EntityItemPointer entity = entityState->getEntity(); if (entity) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 8b3259dbae..1aae27962a 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -21,10 +21,7 @@ #include #include "PhysicsEngine.h" -#include "PhysicsTypedefs.h" - -class EntityMotionState; -class ShapeManager; +#include "EntityMotionState.h" typedef QSet SetOfEntityMotionStates; @@ -33,7 +30,7 @@ public: PhysicalEntitySimulation(); ~PhysicalEntitySimulation(); - void init(EntityTree* tree, PhysicsEngine* engine, ShapeManager* shapeManager, EntityEditPacketSender* packetSender); + void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender); protected: // only called by EntitySimulation // overrides for EntitySimulation @@ -63,7 +60,6 @@ private: SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine VectorOfMotionStates _tempVector; // temporary array reference, valid immediately after getObjectsToRemove() (and friends) - ShapeManager* _shapeManager = nullptr; PhysicsEngine* _physicsEngine = nullptr; EntityEditPacketSender* _entityPacketSender = nullptr; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index bfd0b6cb28..b622a37136 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -318,16 +318,16 @@ CollisionEvents& PhysicsEngine::getCollisionEvents() { ObjectMotionState* A = static_cast(contactItr->first._a); ObjectMotionState* B = static_cast(contactItr->first._b); - if (A && A->getType() == MOTION_STATE_TYPE_ENTITY) { + if (A && A->getType() == MOTIONSTATE_TYPE_ENTITY) { QUuid idA = A->getObjectID(); QUuid idB; - if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) { + if (B && B->getType() == MOTIONSTATE_TYPE_ENTITY) { idB = B->getObjectID(); } glm::vec3 position = bulletToGLM(contact.getPositionWorldOnB()) + _originOffset; glm::vec3 penetration = bulletToGLM(contact.distance * contact.normalWorldOnB); _collisionEvents.push_back(Collision(type, idA, idB, position, penetration)); - } else if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) { + } else if (B && B->getType() == MOTIONSTATE_TYPE_ENTITY) { QUuid idB = B->getObjectID(); glm::vec3 position = bulletToGLM(contact.getPositionWorldOnA()) + _originOffset; // NOTE: we're flipping the order of A and B (so that the first objectID is never NULL) diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index d1dc5bcd79..9ff85c9f11 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -22,13 +22,11 @@ #include "BulletUtil.h" #include "ContactInfo.h" #include "DynamicCharacterController.h" -#include "PhysicsTypedefs.h" +#include "ObjectMotionState.h" #include "ThreadSafeDynamicsWorld.h" const float HALF_SIMULATION_EXTENT = 512.0f; // meters -class ObjectMotionState; - // simple class for keeping track of contacts class ContactKey { public: diff --git a/libraries/physics/src/PhysicsTypedefs.h b/libraries/physics/src/PhysicsTypedefs.h deleted file mode 100644 index 9d9685a758..0000000000 --- a/libraries/physics/src/PhysicsTypedefs.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// PhysicsTypedefs.h -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.04.29 -// 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_PhysicsTypedefs_h -#define hifi_PhysicsTypedefs_h - -#include -#include - -class ObjectMotionState; - -typedef QSet SetOfMotionStates; -typedef QVector VectorOfMotionStates; - -#endif //hifi_PhysicsTypedefs_h diff --git a/libraries/physics/src/ShapeInfoUtil.cpp b/libraries/physics/src/ShapeFactory.cpp similarity index 81% rename from libraries/physics/src/ShapeInfoUtil.cpp rename to libraries/physics/src/ShapeFactory.cpp index 1cccd691c2..74b0304ace 100644 --- a/libraries/physics/src/ShapeInfoUtil.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -1,5 +1,5 @@ // -// ShapeInfoUtil.cpp +// ShapeFactory.cpp // libraries/physcis/src // // Created by Andrew Meadows 2014.12.01 @@ -9,14 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include // for MILLIMETERS_PER_METER -#include "ShapeInfoUtil.h" +#include "ShapeFactory.h" #include "BulletUtil.h" -btConvexHullShape* ShapeInfoUtil::createConvexHull(const QVector& points) { +btConvexHullShape* ShapeFactory::createConvexHull(const QVector& points) { assert(points.size() > 0); btConvexHullShape* hull = new btConvexHullShape(); @@ -57,9 +59,10 @@ btConvexHullShape* ShapeInfoUtil::createConvexHull(const QVector& poi return hull; } -btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) { +btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; - switch(info.getType()) { + int type = info.getType(); + switch(type) { case SHAPE_TYPE_BOX: { shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } @@ -95,5 +98,17 @@ btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) { } break; } + if (shape && type != SHAPE_TYPE_COMPOUND) { + if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) { + // this shape has an offset, which we support by wrapping the true shape + // in a btCompoundShape with a local transform + auto compound = new btCompoundShape(); + btTransform trans; + trans.setIdentity(); + trans.setOrigin(glmToBullet(info.getOffset())); + compound->addChildShape(trans, shape); + shape = compound; + } + } return shape; } diff --git a/libraries/physics/src/ShapeInfoUtil.h b/libraries/physics/src/ShapeFactory.h similarity index 81% rename from libraries/physics/src/ShapeInfoUtil.h rename to libraries/physics/src/ShapeFactory.h index 7284cd3550..699b8a0475 100644 --- a/libraries/physics/src/ShapeInfoUtil.h +++ b/libraries/physics/src/ShapeFactory.h @@ -1,5 +1,5 @@ // -// ShapeInfoUtil.h +// ShapeFactory.h // libraries/physcis/src // // Created by Andrew Meadows 2014.12.01 @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_ShapeInfoUtil_h -#define hifi_ShapeInfoUtil_h +#ifndef hifi_ShapeFactory_h +#define hifi_ShapeFactory_h #include #include @@ -20,11 +20,11 @@ // translates between ShapeInfo and btShape // TODO: rename this to ShapeFactory -namespace ShapeInfoUtil { +namespace ShapeFactory { btConvexHullShape* createConvexHull(const QVector& points); btCollisionShape* createShapeFromInfo(const ShapeInfo& info); }; -#endif // hifi_ShapeInfoUtil_h +#endif // hifi_ShapeFactory_h diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index dd67c2c73a..7a3065dfff 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -13,7 +13,7 @@ #include -#include "ShapeInfoUtil.h" +#include "ShapeFactory.h" #include "ShapeManager.h" ShapeManager::ShapeManager() { @@ -46,7 +46,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { shapeRef->refCount++; return shapeRef->shape; } - btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); + btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); if (shape) { ShapeReference newRef; newRef.refCount = 1; diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index a2d3ee97d3..b59103339c 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -17,7 +17,6 @@ #include -#include "ObjectMotionState.h" #include "ThreadSafeDynamicsWorld.h" ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 4a96eae311..eacc8ad74a 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -21,7 +21,7 @@ #include #include -#include "PhysicsTypedefs.h" +#include "ObjectMotionState.h" ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld { public: diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index ca812532fa..1d0cd56b86 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -17,7 +17,7 @@ void ShapeInfo::clear() { _type = SHAPE_TYPE_NONE; - _halfExtents = glm::vec3(0.0f); + _halfExtents = _offset = glm::vec3(0.0f); _doubleHashKey.clear(); } @@ -45,6 +45,7 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString _halfExtents = halfExtents; break; } + _doubleHashKey.clear(); } void ShapeInfo::setBox(const glm::vec3& halfExtents) { @@ -85,6 +86,11 @@ void ShapeInfo::setCapsuleY(float radius, float halfHeight) { _doubleHashKey.clear(); } +void ShapeInfo::setOffset(const glm::vec3& offset) { + _offset = offset; + _doubleHashKey.clear(); +} + uint32_t ShapeInfo::getNumSubShapes() const { if (_type == SHAPE_TYPE_NONE) { return 0; @@ -175,6 +181,7 @@ bool ShapeInfo::contains(const glm::vec3& point) const { const DoubleHashKey& ShapeInfo::getHash() const { // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) { + bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET; // The key is not yet cached therefore we must compute it! To this end we bypass the const-ness // of this method by grabbing a non-const pointer to "this" and a non-const reference to _doubleHashKey. ShapeInfo* thisPtr = const_cast(this); @@ -190,9 +197,14 @@ const DoubleHashKey& ShapeInfo::getHash() const { for (int j = 0; j < 3; ++j) { // NOTE: 0.49f is used to bump the float up almost half a millimeter // so the cast to int produces a round() effect rather than a floor() - uint32_t floatHash = - DoubleHashKey::hashFunction((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), primeIndex++); - hash ^= floatHash; + hash ^= DoubleHashKey::hashFunction( + (uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), + primeIndex++); + if (useOffset) { + hash ^= DoubleHashKey::hashFunction( + (uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f), + primeIndex++); + } } key.setHash(hash); @@ -201,8 +213,12 @@ const DoubleHashKey& ShapeInfo::getHash() const { for (int j = 0; j < 3; ++j) { // NOTE: 0.49f is used to bump the float up almost half a millimeter // so the cast to int produces a round() effect rather than a floor() - uint32_t floatHash = - DoubleHashKey::hashFunction2((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f)); + uint32_t floatHash = DoubleHashKey::hashFunction2( + (uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f)); + if (useOffset) { + floatHash ^= DoubleHashKey::hashFunction2( + (uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f)); + } hash += ~(floatHash << 17); hash ^= (floatHash >> 11); hash += (floatHash << 4); diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index e10cf1a149..a3fbe55f36 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -16,9 +16,12 @@ #include #include #include +#include #include "DoubleHashKey.h" +const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored + enum ShapeType { SHAPE_TYPE_NONE, SHAPE_TYPE_BOX, @@ -46,10 +49,12 @@ public: void setEllipsoid(const glm::vec3& halfExtents); void setConvexHulls(const QVector>& points); void setCapsuleY(float radius, float halfHeight); + void setOffset(const glm::vec3& offset); int getType() const { return _type; } const glm::vec3& getHalfExtents() const { return _halfExtents; } + const glm::vec3& getOffset() const { return _offset; } const QVector>& getPoints() const { return _points; } uint32_t getNumSubShapes() const; @@ -68,6 +73,7 @@ public: protected: ShapeType _type = SHAPE_TYPE_NONE; glm::vec3 _halfExtents = glm::vec3(0.0f); + glm::vec3 _offset = glm::vec3(0.0f); DoubleHashKey _doubleHashKey; QVector> _points; // points for convex collision hulls QUrl _url; // url for model of convex collision hulls diff --git a/tests/physics/src/ShapeInfoTests.cpp b/tests/physics/src/ShapeInfoTests.cpp index ef5bd0be39..365d6d6008 100644 --- a/tests/physics/src/ShapeInfoTests.cpp +++ b/tests/physics/src/ShapeInfoTests.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include "ShapeInfoTests.h" @@ -142,7 +142,7 @@ void ShapeInfoTests::testBoxShape() { info.setBox(halfExtents); DoubleHashKey key = info.getHash(); - btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); + btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); if (!shape) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: NULL Box shape" << std::endl; } @@ -168,7 +168,7 @@ void ShapeInfoTests::testSphereShape() { info.setSphere(radius); DoubleHashKey key = info.getHash(); - btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); + btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); ShapeInfo otherInfo = info; DoubleHashKey otherKey = otherInfo.getHash(); @@ -192,7 +192,7 @@ void ShapeInfoTests::testCylinderShape() { info.setCylinder(radius, height); DoubleHashKey key = info.getHash(); - btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); + btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); ShapeInfo otherInfo = info; DoubleHashKey otherKey = otherInfo.getHash(); @@ -217,7 +217,7 @@ void ShapeInfoTests::testCapsuleShape() { info.setCapsule(radius, height); DoubleHashKey key = info.getHash(); - btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); + btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); ShapeInfo otherInfo = info; DoubleHashKey otherKey = otherInfo.getHash(); diff --git a/tests/physics/src/ShapeManagerTests.cpp b/tests/physics/src/ShapeManagerTests.cpp index d2b4ca70fa..9ac75981dd 100644 --- a/tests/physics/src/ShapeManagerTests.cpp +++ b/tests/physics/src/ShapeManagerTests.cpp @@ -10,7 +10,6 @@ // #include -#include #include #include