physics collisions emit script collision events

This commit is contained in:
Andrew Meadows 2015-01-21 11:27:32 -08:00
parent 790d07d346
commit 86583f3f3f
13 changed files with 119 additions and 53 deletions

View file

@ -192,7 +192,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_justStarted(true),
_physicsEngine(glm::vec3(0.0f)),
_entities(true, this, this),
_entityCollisionSystem(),
_entityClipboardRenderer(false, this, this),
_entityClipboard(),
_viewFrustum(),
@ -1689,17 +1688,16 @@ void Application::init() {
_entities.init();
_entities.setViewFrustum(getViewFrustum());
EntityTree* entityTree = _entities.getTree();
_entityCollisionSystem.init(&_entityEditSender, entityTree, &_avatarManager);
entityTree->setSimulation(&_entityCollisionSystem);
EntityTree* tree = _entities.getTree();
_physicsEngine.setEntityTree(tree);
tree->setSimulation(&_physicsEngine);
_physicsEngine.init(&_entityEditSender);
connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithEntity,
connect(&_physicsEngine, &EntitySimulation::emitEntityCollisionWithEntity,
ScriptEngine::getEntityScriptingInterface(), &EntityScriptingInterface::entityCollisionWithEntity);
// connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts
connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithEntity,
connect(&_physicsEngine, &EntitySimulation::emitEntityCollisionWithEntity,
&_entities, &EntityTreeRenderer::entityCollisionWithEntity);
// connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing
@ -1723,10 +1721,6 @@ void Application::init() {
// save settings when avatar changes
connect(_myAvatar, &MyAvatar::transformChanged, this, &Application::bumpSettings);
EntityTree* tree = _entities.getTree();
_physicsEngine.setEntityTree(tree);
tree->setSimulation(&_physicsEngine);
_physicsEngine.init(&_entityEditSender);
// make sure our texture cache knows about window size changes
DependencyManager::get<TextureCache>()->associateWithWidget(glCanvas.data());
@ -2047,9 +2041,6 @@ void Application::update(float deltaTime) {
// NOTE: the _entities.update() call below will wait for lock
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
_entities.update(); // update the models...
// The _entityCollisionSystem.updateCollisions() call below merely tries for lock,
// and on failure it skips collision detection.
_entityCollisionSystem.updateCollisions(); // collide the entities...
}
{

View file

@ -466,7 +466,6 @@ private:
PhysicsEngine _physicsEngine;
EntityTreeRenderer _entities;
EntityCollisionSystem _entityCollisionSystem;
EntityTreeRenderer _entityClipboardRenderer;
EntityTree _entityClipboard;

View file

@ -62,14 +62,6 @@ void EntityCollisionSystem::checkEntity(EntityItem* entity) {
updateCollisionWithAvatars(entity);
}
void EntityCollisionSystem::emitGlobalEntityCollisionWithEntity(EntityItem* entityA,
EntityItem* entityB, const Collision& collision) {
EntityItemID idA = entityA->getEntityItemID();
EntityItemID idB = entityB->getEntityItemID();
emit entityCollisionWithEntity(idA, idB, collision);
}
void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
if (entityA->getIgnoreForCollisions()) {
@ -193,7 +185,10 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
Collision collision;
collision.penetration = penetration;
collision.contactPoint = (0.5f * (float)TREE_SCALE) * (entityA->getPosition() + entityB->getPosition());
emitGlobalEntityCollisionWithEntity(entityA, entityB, collision);
EntityItemID idA = entityA->getEntityItemID();
EntityItemID idB = entityB->getEntityItemID();
emitEntityCollisionWithEntity(idA, idB, collision);
}
}
}

View file

@ -31,8 +31,7 @@ class AvatarData;
class EntityEditPacketSender;
class EntityTree;
class EntityCollisionSystem : public QObject, public SimpleEntitySimulation {
Q_OBJECT
class EntityCollisionSystem : public SimpleEntitySimulation {
public:
EntityCollisionSystem();
@ -45,19 +44,13 @@ public:
void checkEntity(EntityItem* Entity);
void updateCollisionWithEntities(EntityItem* Entity);
void updateCollisionWithAvatars(EntityItem* Entity);
void queueEntityPropertiesUpdate(EntityItem* Entity);
signals:
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
private:
void applyHardCollision(EntityItem* entity, const CollisionInfo& collisionInfo);
static bool updateOperation(OctreeElement* element, void* extraData);
void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const Collision& penetration);
EntityEditPacketSender* _packetSender;
AbstractAudioInterface* _audio;
AvatarHashMap* _avatars;
CollisionList _collisions;
};

View file

@ -12,6 +12,7 @@
#ifndef hifi_EntitySimulation_h
#define hifi_EntitySimulation_h
#include <QtCore/QObject>
#include <QSet>
#include <PerfStat.h>
@ -31,7 +32,8 @@ const int DIRTY_SIMULATION_FLAGS =
EntityItem::DIRTY_LIFETIME |
EntityItem::DIRTY_UPDATEABLE;
class EntitySimulation {
class EntitySimulation : public QObject {
Q_OBJECT
public:
EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL) { }
virtual ~EntitySimulation() { setEntityTree(NULL); }
@ -61,6 +63,9 @@ public:
EntityTree* getEntityTree() { return _entityTree; }
signals:
void emitEntityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
protected:
// These pure virtual methods are protected because they are not to be called will-nilly. The base class

View file

@ -0,0 +1,29 @@
//
// ContactEvent.cpp
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.20
// 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 "BulletUtil.h"
#include "ContactInfo.h"
void ContactInfo::update(uint32_t currentStep, btManifoldPoint& p, const glm::vec3& worldOffset) {
_lastStep = currentStep;
++_numSteps;
contactPoint = bulletToGLM(p.m_positionWorldOnB) + worldOffset;
penetration = bulletToGLM(p.m_distance1 * p.m_normalWorldOnB);
// TODO: also report normal
//_normal = bulletToGLM(p.m_normalWorldOnB);
}
ContactEventType ContactInfo::computeType(uint32_t thisStep) {
if (_lastStep != thisStep) {
return CONTACT_EVENT_TYPE_END;
}
return (_numSteps == 1) ? CONTACT_EVENT_TYPE_START : CONTACT_EVENT_TYPE_CONTINUE;
}

View file

@ -0,0 +1,37 @@
//
// ContactEvent.h
// libraries/physcis/src
//
// Created by Andrew Meadows 2015.01.20
// 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_ContactEvent_h
#define hifi_ContactEvent_h
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
#include "RegisteredMetaTypes.h"
enum ContactEventType {
CONTACT_EVENT_TYPE_START,
CONTACT_EVENT_TYPE_CONTINUE,
CONTACT_EVENT_TYPE_END
};
class ContactInfo : public Collision
{
public:
void update(uint32_t currentStep, btManifoldPoint& p, const glm::vec3& worldOffset);
ContactEventType computeType(uint32_t thisStep);
private:
uint32_t _lastStep = 0;
uint32_t _numSteps = 0;
};
#endif // hifi_ContactEvent_h

View file

@ -33,6 +33,7 @@ void EntityMotionState::enqueueOutgoingEntity(EntityItem* entity) {
EntityMotionState::EntityMotionState(EntityItem* entity)
: _entity(entity) {
_type = MOTION_STATE_TYPE_ENTITY;
assert(entity != NULL);
}

View file

@ -60,6 +60,8 @@ public:
uint32_t getIncomingDirtyFlags() const;
void clearIncomingDirtyFlags(uint32_t flags) { _entity->clearDirtyFlags(flags); }
EntityItem* getEntity() const { return _entity; }
protected:
EntityItem* _entity;
};

View file

@ -171,14 +171,6 @@ void ObjectMotionState::removeKinematicController() {
}
}
void ObjectMotionState::handleContactEvent(ContactEventType type, ObjectMotionState* otherState) {
if (type == CONTACT_TYPE_START) {
std::cout << "adebug start " << (void*)(this) << " vs " << (void*)(otherState) << std::endl; // adebug
} else if (type == CONTACT_TYPE_END) {
std::cout << "adebug end " << (void*)(this) << " vs " << (void*)(otherState) << std::endl; // adebug
}
}
void ObjectMotionState::setRigidBody(btRigidBody* body) {
// give the body a (void*) back-pointer to this ObjectMotionState
if (_body != body) {

View file

@ -26,6 +26,12 @@ enum MotionType {
MOTION_TYPE_KINEMATIC // keyframed motion
};
enum MotionStateType {
MOTION_STATE_TYPE_UNKNOWN,
MOTION_STATE_TYPE_ENTITY,
MOTION_STATE_TYPE_AVATAR
};
// The update flags trigger two varieties of updates: "hard" which require the body to be pulled
// and re-added to the physics engine and "easy" which just updates the body properties.
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE);
@ -59,6 +65,7 @@ public:
virtual void updateObjectEasy(uint32_t flags, uint32_t frame) = 0;
virtual void updateObjectVelocities() = 0;
MotionStateType getType() const { return _type; }
virtual MotionType getMotionType() const { return _motionType; }
virtual void computeShapeInfo(ShapeInfo& info) = 0;
@ -89,14 +96,14 @@ public:
virtual void addKinematicController() = 0;
virtual void removeKinematicController();
virtual void handleContactEvent(ContactEventType type, ObjectMotionState* otherState);
btRigidBody* getRigidBody() const { return _body; }
friend class PhysicsEngine;
protected:
void setRigidBody(btRigidBody* body);
MotionStateType _type = MOTION_STATE_TYPE_UNKNOWN;
// TODO: move these materials properties outside of ObjectMotionState
float _friction;
float _restitution;

View file

@ -256,8 +256,9 @@ void PhysicsEngine::computeCollisionEvents() {
void* a = objectA->getUserPointer();
void* b = objectB->getUserPointer();
if (a || b ) {
_contactMap[ContactKey(a, b)].update(_numSubsteps);
if (a || b) {
// the manifold has up to 4 distinct points, but only extract info from the first
_contactMap[ContactKey(a, b)].update(_numSubsteps, contactManifold->getContactPoint(0), _originOffset);
}
}
}
@ -265,16 +266,28 @@ void PhysicsEngine::computeCollisionEvents() {
// scan known contacts and trigger events
ContactMap::iterator contactItr = _contactMap.begin();
while (contactItr != _contactMap.end()) {
ContactEventType type = contactItr->second.computeType(_numSubsteps);
ObjectMotionState* A = static_cast<ObjectMotionState*>(contactItr->first._a);
ObjectMotionState* B = static_cast<ObjectMotionState*>(contactItr->first._b);
if (A) {
A->handleContactEvent(type, B);
// TODO: make triggering these events clean and efficient. The code at this context shouldn't
// have to figure out what kind of object (entity, avatar, etc) these are in order to properly
// emit a collision event.
if (A && A->getType() == MOTION_STATE_TYPE_ENTITY) {
EntityItemID idA = static_cast<EntityMotionState*>(A)->getEntity()->getEntityItemID();
EntityItemID idB;
if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) {
idB = static_cast<EntityMotionState*>(B)->getEntity()->getEntityItemID();
}
emitEntityCollisionWithEntity(idA, idB, contactItr->second);
} else if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) {
EntityItemID idA;
EntityItemID idB = static_cast<EntityMotionState*>(B)->getEntity()->getEntityItemID();
emitEntityCollisionWithEntity(idA, idB, contactItr->second);
}
if (B) {
B->handleContactEvent(type, A);
}
if (type == CONTACT_TYPE_END) {
// TODO: enable scripts to filter based on contact event type
ContactEventType type = contactItr->second.computeType(_numSubsteps);
if (type == CONTACT_EVENT_TYPE_END) {
ContactMap::iterator iterToDelete = contactItr;
++contactItr;
_contactMap.erase(iterToDelete);

View file

@ -38,11 +38,13 @@ public:
ContactKey() = delete;
ContactKey(void* a, void* b) : _a(a), _b(b) {}
bool operator<(const ContactKey& other) const { return _a < other._a || (_a == other._a && _b < other._b); }
bool operator==(const ContactKey& other) const { return _a == other._a && _b == other._b; }
void* _a;
void* _b;
};
typedef std::map<ContactKey, ContactInfo> ContactMap;
typedef std::pair<ContactKey, ContactInfo> ContactMapElement;
class PhysicsEngine : public EntitySimulation {
public: