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 @@
-
+
+
+
+
Particle Life Span
+
+
+
+
+
+
Particle Emission Rate
+
+
+
+
+
+
Particle Emission Direction
+
+
+
+
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