Merge pull request #6796 from AndrewMeadows/collision-groups

entity collision masks can be set via script
This commit is contained in:
Seth Alves 2016-01-12 16:18:56 -08:00
commit 6f85ee135f
20 changed files with 261 additions and 106 deletions

View file

@ -106,7 +106,7 @@ var GRABBABLE_PROPERTIES = [
"position",
"rotation",
"gravity",
"ignoreForCollisions",
"collisionMask",
"collisionsWillMove",
"locked",
"name"
@ -117,7 +117,6 @@ var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js
var DEFAULT_GRABBABLE_DATA = {
grabbable: true,
invertSolidWhileHeld: false
};
@ -164,6 +163,11 @@ var POINT_HAND_STATES = [STATE_NEAR_TRIGGER, STATE_CONTINUE_NEAR_TRIGGER, STATE_
var FAR_GRASP_HAND_STATES = [STATE_DISTANCE_HOLDING, STATE_CONTINUE_DISTANCE_HOLDING];
// otherwise grasp
// collision masks are specified by comma-separated list of group names
// the possible list of names is: static, dynamic, kinematic, myAvatar, otherAvatar
var COLLISION_MASK_WHILE_GRABBED = "dynamic,otherAvatar";
function stateToName(state) {
switch (state) {
case STATE_OFF:
@ -1823,7 +1827,6 @@ function MyController(hand) {
this.activateEntity = function(entityID, grabbedProperties) {
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA);
var invertSolidWhileHeld = grabbableData["invertSolidWhileHeld"];
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
data["activated"] = true;
data["avatarId"] = MyAvatar.sessionUUID;
@ -1831,18 +1834,16 @@ function MyController(hand) {
// zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done
if (data["refCount"] == 1) {
data["gravity"] = grabbedProperties.gravity;
data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions;
data["collisionMask"] = grabbedProperties.collisionMask;
data["collisionsWillMove"] = grabbedProperties.collisionsWillMove;
var whileHeldProperties = {
gravity: {
x: 0,
y: 0,
z: 0
}
},
"collisionMask": COLLISION_MASK_WHILE_GRABBED
};
if (invertSolidWhileHeld) {
whileHeldProperties["ignoreForCollisions"] = !grabbedProperties.ignoreForCollisions;
}
Entities.editEntity(entityID, whileHeldProperties);
}
@ -1857,7 +1858,7 @@ function MyController(hand) {
if (data["refCount"] < 1) {
Entities.editEntity(entityID, {
gravity: data["gravity"],
ignoreForCollisions: data["ignoreForCollisions"],
collisionMask: data["collisionMask"],
collisionsWillMove: data["collisionsWillMove"]
});
data = null;

View file

@ -9,8 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <PhysicsHelpers.h>
#include <PhysicsCollisionGroups.h>
#include <PhysicsEngine.h>
#include <PhysicsHelpers.h>
#include "Avatar.h"
#include "AvatarMotionState.h"
@ -143,7 +144,8 @@ QUuid AvatarMotionState::getSimulatorID() const {
}
// virtual
int16_t AvatarMotionState::computeCollisionGroup() const {
return COLLISION_GROUP_OTHER_AVATAR;
void AvatarMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const {
group = BULLET_COLLISION_GROUP_OTHER_AVATAR;
mask = PhysicsEngine::getCollisionMask(group);
}

View file

@ -61,7 +61,7 @@ public:
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
virtual int16_t computeCollisionGroup() const override;
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const;
friend class AvatarManager;
friend class Avatar;

View file

@ -64,6 +64,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
_angularDamping(ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING),
_visible(ENTITY_ITEM_DEFAULT_VISIBLE),
_ignoreForCollisions(ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS),
_collisionMask(ENTITY_COLLISION_MASK_DEFAULT),
_collisionsWillMove(ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE),
_locked(ENTITY_ITEM_DEFAULT_LOCKED),
_userData(ENTITY_ITEM_DEFAULT_USER_DATA),
@ -123,6 +124,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_ANGULAR_DAMPING;
requestedProperties += PROP_VISIBLE;
requestedProperties += PROP_IGNORE_FOR_COLLISIONS;
requestedProperties += PROP_COLLISION_MASK;
requestedProperties += PROP_COLLISIONS_WILL_MOVE;
requestedProperties += PROP_LOCKED;
requestedProperties += PROP_USER_DATA;
@ -259,6 +261,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping());
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, getVisible());
APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, getIgnoreForCollisions());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_MASK, getCollisionMask());
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, getCollisionsWillMove());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData());
@ -678,6 +681,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping);
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions);
READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint8_t, updateCollisionMask);
READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, updateCollisionsWillMove);
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
@ -1041,6 +1045,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
COPY_ENTITY_PROPERTY_TO_PROPERTIES(localRenderAlpha, getLocalRenderAlpha);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(visible, getVisible);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignoreForCollisions, getIgnoreForCollisions);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionMask, getCollisionMask);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData);
@ -1096,6 +1101,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(restitution, updateRestitution);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(friction, updateFriction);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, updateIgnoreForCollisions);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionMask, updateCollisionMask);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
@ -1445,6 +1451,13 @@ void EntityItem::updateIgnoreForCollisions(bool value) {
}
}
void EntityItem::updateCollisionMask(uint8_t value) {
if ((_collisionMask & ENTITY_COLLISION_MASK_DEFAULT) != (value & ENTITY_COLLISION_MASK_DEFAULT)) {
_collisionMask = (value & ENTITY_COLLISION_MASK_DEFAULT);
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP;
}
}
void EntityItem::updateCollisionsWillMove(bool value) {
if (_collisionsWillMove != value) {
_collisionsWillMove = value;

View file

@ -21,6 +21,7 @@
#include <Octree.h> // for EncodeBitstreamParams class
#include <OctreeElement.h> // for OctreeElement::AppendState
#include <OctreePacketData.h>
#include <PhysicsCollisionGroups.h>
#include <ShapeInfo.h>
#include <Transform.h>
#include <SpatiallyNestable.h>
@ -273,6 +274,9 @@ public:
bool getIgnoreForCollisions() const { return _ignoreForCollisions; }
void setIgnoreForCollisions(bool value) { _ignoreForCollisions = value; }
uint8_t getCollisionMask() const { return _collisionMask; }
void setCollisionMask(uint8_t value) { _collisionMask = value; }
bool getCollisionsWillMove() const { return _collisionsWillMove; }
void setCollisionsWillMove(bool value) { _collisionsWillMove = value; }
@ -327,6 +331,7 @@ public:
void updateAngularVelocity(const glm::vec3& value);
void updateAngularDamping(float value);
void updateIgnoreForCollisions(bool value);
void updateCollisionMask(uint8_t value);
void updateCollisionsWillMove(bool value);
void updateLifetime(float value);
void updateCreated(uint64_t value);
@ -440,6 +445,7 @@ protected:
float _angularDamping;
bool _visible;
bool _ignoreForCollisions;
uint8_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT };
bool _collisionsWillMove;
bool _locked;
QString _userData;

View file

@ -116,6 +116,59 @@ void buildStringToShapeTypeLookup() {
addShapeType(SHAPE_TYPE_CYLINDER_Z);
}
QString getCollisionGroupAsString(uint8_t group) {
switch (group) {
case USER_COLLISION_GROUP_DYNAMIC:
return "dynamic";
case USER_COLLISION_GROUP_STATIC:
return "static";
case USER_COLLISION_GROUP_KINEMATIC:
return "kinematic";
case USER_COLLISION_GROUP_MY_AVATAR:
return "myAvatar";
case USER_COLLISION_GROUP_OTHER_AVATAR:
return "otherAvatar";
};
return "";
}
uint8_t getCollisionGroupAsBitMask(const QStringRef& name) {
if (0 == name.compare("dynamic")) {
return USER_COLLISION_GROUP_DYNAMIC;
} else if (0 == name.compare("static")) {
return USER_COLLISION_GROUP_STATIC;
} else if (0 == name.compare("kinematic")) {
return USER_COLLISION_GROUP_KINEMATIC;
} else if (0 == name.compare("myAvatar")) {
return USER_COLLISION_GROUP_MY_AVATAR;
} else if (0 == name.compare("otherAvatar")) {
return USER_COLLISION_GROUP_OTHER_AVATAR;
}
return 0;
}
QString EntityItemProperties::getCollisionMaskAsString() const {
QString maskString("");
for (int i = 0; i < NUM_USER_COLLISION_GROUPS; ++i) {
uint8_t group = 0x01 << i;
if (group & _collisionMask) {
maskString.append(getCollisionGroupAsString(group));
maskString.append(',');
}
}
return maskString;
}
void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) {
QVector<QStringRef> groups = maskString.splitRef(',');
uint8_t mask = 0x00;
for (auto group : groups) {
mask |= getCollisionGroupAsBitMask(group);
}
_collisionMask = mask;
_collisionMaskChanged = true;
}
QString EntityItemProperties::getShapeTypeAsString() const {
if (_shapeType < sizeof(shapeTypeNames) / sizeof(char *))
return QString(shapeTypeNames[_shapeType]);
@ -203,6 +256,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity);
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping);
CHECK_PROPERTY_CHANGE(PROP_IGNORE_FOR_COLLISIONS, ignoreForCollisions);
CHECK_PROPERTY_CHANGE(PROP_COLLISION_MASK, collisionMask);
CHECK_PROPERTY_CHANGE(PROP_COLLISIONS_WILL_MOVE, collisionsWillMove);
CHECK_PROPERTY_CHANGE(PROP_IS_SPOTLIGHT, isSpotlight);
CHECK_PROPERTY_CHANGE(PROP_INTENSITY, intensity);
@ -317,6 +371,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_DAMPING, angularDamping);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE, visible);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IGNORE_FOR_COLLISIONS, ignoreForCollisions);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_COLLISION_MASK, collisionMask, getCollisionMaskAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISIONS_WILL_MOVE, collisionsWillMove);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_HREF, href);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DESCRIPTION, description);
@ -538,6 +593,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(glowLevel, float, setGlowLevel);
COPY_PROPERTY_FROM_QSCRIPTVALUE(localRenderAlpha, float, setLocalRenderAlpha);
COPY_PROPERTY_FROM_QSCRIPTVALUE(ignoreForCollisions, bool, setIgnoreForCollisions);
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(collisionMask, CollisionMask);
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionsWillMove, bool, setCollisionsWillMove);
COPY_PROPERTY_FROM_QSCRIPTVALUE(isSpotlight, bool, setIsSpotlight);
COPY_PROPERTY_FROM_QSCRIPTVALUE(intensity, float, setIntensity);
@ -700,6 +756,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float);
ADD_PROPERTY_TO_MAP(PROP_IGNORE_FOR_COLLISIONS, IgnoreForCollisions, ignoreForCollisions, bool);
ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint8_t);
ADD_PROPERTY_TO_MAP(PROP_COLLISIONS_WILL_MOVE, CollisionsWillMove, collisionsWillMove, bool);
ADD_PROPERTY_TO_MAP(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool);
ADD_PROPERTY_TO_MAP(PROP_INTENSITY, Intensity, intensity, float);
@ -946,6 +1003,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, properties.getAngularDamping());
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, properties.getVisible());
APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, properties.getIgnoreForCollisions());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_MASK, properties.getCollisionMask());
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, properties.getCollisionsWillMove());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData());
@ -1238,6 +1296,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_FOR_COLLISIONS, bool, setIgnoreForCollisions);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint8_t, setCollisionMask);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONS_WILL_MOVE, bool, setCollisionsWillMove);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData);
@ -1419,6 +1478,7 @@ void EntityItemProperties::markAllChanged() {
_localRenderAlphaChanged = true;
_isSpotlightChanged = true;
_ignoreForCollisionsChanged = true;
_collisionMaskChanged = true;
_collisionsWillMoveChanged = true;
_intensityChanged = true;
@ -1537,7 +1597,7 @@ bool EntityItemProperties::hasTerseUpdateChanges() const {
bool EntityItemProperties::hasMiscPhysicsChanges() const {
return _gravityChanged || _dimensionsChanged || _densityChanged || _frictionChanged
|| _restitutionChanged || _dampingChanged || _angularDampingChanged || _registrationPointChanged ||
_compoundShapeURLChanged || _collisionsWillMoveChanged || _ignoreForCollisionsChanged;
_compoundShapeURLChanged || _collisionsWillMoveChanged || _ignoreForCollisionsChanged || _collisionMaskChanged;
}
void EntityItemProperties::clearSimulationOwner() {
@ -1653,6 +1713,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (ignoreForCollisionsChanged()) {
out += "ignoreForCollisions";
}
if (collisionMaskChanged()) {
out += "collisionMask";
}
if (collisionsWillMoveChanged()) {
out += "collisionsWillMove";
}

View file

@ -128,6 +128,7 @@ public:
DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY);
DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING);
DEFINE_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, IgnoreForCollisions, ignoreForCollisions, bool, ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS);
DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint8_t, ENTITY_COLLISION_MASK_DEFAULT);
DEFINE_PROPERTY(PROP_COLLISIONS_WILL_MOVE, CollisionsWillMove, collisionsWillMove, bool, ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE);
DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, false);
DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, 1.0f);
@ -273,6 +274,10 @@ public:
void setJointRotationsDirty() { _jointRotationsSetChanged = true; _jointRotationsChanged = true; }
void setJointTranslationsDirty() { _jointTranslationsSetChanged = true; _jointTranslationsChanged = true; }
protected:
QString getCollisionMaskAsString() const;
void setCollisionMaskFromString(const QString& maskString);
private:
QUuid _id;
bool _idSet;

View file

@ -191,6 +191,7 @@ inline quint16 quint16_convertFromScriptValue(const QScriptValue& v, bool& isVal
inline uint16_t uint16_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); }
inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); }
inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toBool(); }
inline uint8_t uint8_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return (uint8_t)(0xff & v.toVariant().toInt(&isValid)); }
inline QString QString_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toString().trimmed(); }
inline QUuid QUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); }
inline EntityItemID EntityItemID_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); }

View file

@ -165,6 +165,8 @@ enum EntityPropertyList {
PROP_JOINT_TRANSLATIONS_SET,
PROP_JOINT_TRANSLATIONS,
PROP_COLLISION_MASK, // one byte of collision group flags
////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties to end of list just ABOVE this line
PROP_AFTER_LAST_ITEM,

View file

@ -41,7 +41,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityAdd:
case PacketType::EntityEdit:
case PacketType::EntityData:
return VERSION_ENTITITES_HAVE_QUERY_BOX;
return VERSION_ENTITITES_HAVE_COLLISION_MASK;
case PacketType::AvatarData:
case PacketType::BulkAvatarData:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SoftAttachmentSupport);

View file

@ -164,6 +164,7 @@ const PacketVersion VERSION_ENTITIES_HAVE_PARENTS = 51;
const PacketVersion VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52;
const PacketVersion VERSION_MODEL_ENTITIES_JOINTS_ON_WIRE = 53;
const PacketVersion VERSION_ENTITITES_HAVE_QUERY_BOX = 54;
const PacketVersion VERSION_ENTITITES_HAVE_COLLISION_MASK = 55;
enum class AvatarMixerPacketVersion : PacketVersion {
TranslationSupport = 17,

View file

@ -27,6 +27,9 @@ class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback {
public:
ClosestNotMe(btRigidBody* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) {
_me = me;
// the RayResultCallback's group and mask must match MY_AVATAR
m_collisionFilterGroup = BULLET_COLLISION_GROUP_MY_AVATAR;
m_collisionFilterMask = BULLET_COLLISION_MASK_MY_AVATAR;
}
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) {
if (rayResult.m_collisionObject == _me) {
@ -84,7 +87,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
// Before adding the RigidBody to the world we must save its oldGravity to the side
// because adding an object to the world will overwrite it with the default gravity.
btVector3 oldGravity = _rigidBody->getGravity();
_dynamicsWorld->addRigidBody(_rigidBody, COLLISION_GROUP_MY_AVATAR, COLLISION_MASK_MY_AVATAR);
_dynamicsWorld->addRigidBody(_rigidBody, BULLET_COLLISION_GROUP_MY_AVATAR, BULLET_COLLISION_MASK_MY_AVATAR);
_dynamicsWorld->addAction(this);
// restore gravity settings
_rigidBody->setGravity(oldGravity);

View file

@ -80,9 +80,9 @@ EntityMotionState::~EntityMotionState() {
_entity = nullptr;
}
void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) {
void EntityMotionState::updateServerPhysicsVariables() {
assert(entityTreeIsLocked());
if (_entity->getSimulatorID() == sessionID) {
if (_entity->getSimulatorID() == PhysicsEngine::getSessionID()) {
// don't slam these values if we are the simulation owner
return;
}
@ -96,10 +96,10 @@ void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) {
}
// virtual
bool EntityMotionState::handleEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
bool EntityMotionState::handleEasyChanges(uint32_t& flags) {
assert(entityTreeIsLocked());
updateServerPhysicsVariables(engine->getSessionID());
ObjectMotionState::handleEasyChanges(flags, engine);
updateServerPhysicsVariables();
ObjectMotionState::handleEasyChanges(flags);
if (flags & Simulation::DIRTY_SIMULATOR_ID) {
_loopsWithoutOwner = 0;
@ -113,7 +113,7 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags, PhysicsEngine* engine
_outgoingPriority = NO_PRORITY;
} else {
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
if (engine->getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) {
if (PhysicsEngine::getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) {
// we own the simulation or our priority looses to (or ties with) remote
_outgoingPriority = NO_PRORITY;
}
@ -135,7 +135,7 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags, PhysicsEngine* engine
// virtual
bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
updateServerPhysicsVariables(engine->getSessionID());
updateServerPhysicsVariables();
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
}
@ -523,6 +523,17 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() {
uint32_t dirtyFlags = 0;
if (_body && _entity) {
dirtyFlags = _entity->getDirtyFlags();
if (dirtyFlags | Simulation::DIRTY_SIMULATOR_ID) {
// when SIMULATOR_ID changes we must check for reinterpretation of asymmetric collision mask
// bits for the avatar groups (e.g. MY_AVATAR vs OTHER_AVATAR)
uint8_t entityCollisionMask = _entity->getCollisionMask();
if ((bool)(entityCollisionMask & USER_COLLISION_GROUP_MY_AVATAR) !=
(bool)(entityCollisionMask & USER_COLLISION_GROUP_OTHER_AVATAR)) {
// bits are asymmetric --> flag for reinsertion in physics simulation
dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP;
}
}
// we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings
int bodyFlags = _body->getCollisionFlags();
bool isMoving = _entity->isMoving();
@ -610,19 +621,40 @@ QString EntityMotionState::getName() const {
}
// virtual
int16_t EntityMotionState::computeCollisionGroup() const {
if (_entity->getIgnoreForCollisions()) {
return COLLISION_GROUP_COLLISIONLESS;
void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const {
group = BULLET_COLLISION_GROUP_STATIC;
if (_entity) {
if (_entity->getIgnoreForCollisions()) {
group = BULLET_COLLISION_GROUP_COLLISIONLESS;
}
switch (computeObjectMotionType()){
case MOTION_TYPE_STATIC:
group = BULLET_COLLISION_GROUP_STATIC;
break;
case MOTION_TYPE_DYNAMIC:
group = BULLET_COLLISION_GROUP_DYNAMIC;
break;
case MOTION_TYPE_KINEMATIC:
group = BULLET_COLLISION_GROUP_KINEMATIC;
break;
default:
break;
}
}
switch (computeObjectMotionType()){
case MOTION_TYPE_STATIC:
return COLLISION_GROUP_STATIC;
case MOTION_TYPE_KINEMATIC:
return COLLISION_GROUP_KINEMATIC;
default:
break;
mask = PhysicsEngine::getCollisionMask(group);
if (_entity) {
uint8_t entityCollisionMask = _entity->getCollisionMask();
if ((bool)(entityCollisionMask & USER_COLLISION_GROUP_MY_AVATAR) !=
(bool)(entityCollisionMask & USER_COLLISION_GROUP_OTHER_AVATAR)) {
// asymmetric avatar collision mask bits
if (!_entity->getSimulatorID().isNull() && _entity->getSimulatorID() != PhysicsEngine::getSessionID()) {
// someone else owns the simulation, so we swap the interpretation of the bits
entityCollisionMask ^= USER_COLLISION_MASK_AVATARS | ~entityCollisionMask;
}
}
mask &= (int16_t)(entityCollisionMask);
}
return COLLISION_GROUP_DEFAULT;
}
void EntityMotionState::setOutgoingPriority(quint8 priority) {

View file

@ -28,8 +28,8 @@ public:
EntityMotionState(btCollisionShape* shape, EntityItemPointer item);
virtual ~EntityMotionState();
void updateServerPhysicsVariables(const QUuid& sessionID);
virtual bool handleEasyChanges(uint32_t& flags, PhysicsEngine* engine);
void updateServerPhysicsVariables();
virtual bool handleEasyChanges(uint32_t& flags);
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
@ -80,7 +80,7 @@ public:
virtual QString getName() const override;
virtual int16_t computeCollisionGroup() const override;
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const;
// eternal logic can suggest a simuator priority bid for the next outgoing update
void setOutgoingPriority(quint8 priority);

View file

@ -62,9 +62,10 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
float offsetLength = offset.length();
btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
if (offsetLength > 0) {
float speed = (offsetLength > FLT_EPSILON) ? glm::min(offsetLength / _linearTimeScale, SPRING_MAX_SPEED) : 0.0f;
float speed = (offsetLength > FLT_EPSILON) ? glm::min(offsetLength / _linearTimeScale, SPRING_MAX_SPEED) : 0.0f;
if (speed > rigidBody->getLinearSleepingThreshold()) {
targetVelocity = (-speed / offsetLength) * offset;
rigidBody->activate();
}
// this action is aggresively critically damped and defeats the current velocity
@ -90,10 +91,10 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
//
// dQ = Q1 * Q0^
btQuaternion deltaQ = target * bodyRotation.inverse();
float angle = deltaQ.getAngle();
const float MIN_ANGLE = 1.0e-4f;
if (angle > MIN_ANGLE) {
targetVelocity = (angle / _angularTimeScale) * deltaQ.getAxis();
float speed = deltaQ.getAngle() / _angularTimeScale;
if (speed > rigidBody->getAngularSleepingThreshold()) {
targetVelocity = speed * deltaQ.getAxis();
rigidBody->activate();
}
}
// this action is aggresively critically damped and defeats the current velocity

View file

@ -164,7 +164,7 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) {
}
}
bool ObjectMotionState::handleEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
bool ObjectMotionState::handleEasyChanges(uint32_t& flags) {
if (flags & Simulation::DIRTY_POSITION) {
btTransform worldTrans = _body->getWorldTransform();
btVector3 newPosition = glmToBullet(getObjectPosition());
@ -251,7 +251,7 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine*
if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) {
// no HARD flags remain, so do any EASY changes
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
handleEasyChanges(flags, engine);
handleEasyChanges(flags);
}
return true;
}
@ -268,7 +268,7 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine*
}
}
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
handleEasyChanges(flags, engine);
handleEasyChanges(flags);
}
// it is possible there are no HARD flags at this point (if DIRTY_SHAPE was removed)
// so we check again before we reinsert:

View file

@ -80,7 +80,7 @@ public:
ObjectMotionState(btCollisionShape* shape);
~ObjectMotionState();
virtual bool handleEasyChanges(uint32_t& flags, PhysicsEngine* engine);
virtual bool handleEasyChanges(uint32_t& flags);
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);
void updateBodyMaterialProperties();
@ -136,7 +136,7 @@ public:
virtual QString getName() const { return ""; }
virtual int16_t computeCollisionGroup() const = 0;
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const = 0;
bool isActive() const { return _body ? _body->isActive() : false; }

View file

@ -22,20 +22,43 @@ uint32_t PhysicsEngine::getNumSubsteps() {
return _numSubsteps;
}
btHashMap<btHashInt, int16_t> _collisionMasks;
void initCollisionMaskTable() {
if (_collisionMasks.size() == 0) {
// build table of masks with their group as the key
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_DYNAMIC), BULLET_COLLISION_MASK_DYNAMIC);
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_STATIC), BULLET_COLLISION_MASK_STATIC);
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_KINEMATIC), BULLET_COLLISION_MASK_KINEMATIC);
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_MY_AVATAR), BULLET_COLLISION_MASK_MY_AVATAR);
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_OTHER_AVATAR), BULLET_COLLISION_MASK_OTHER_AVATAR);
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_COLLISIONLESS), BULLET_COLLISION_MASK_COLLISIONLESS);
}
}
// static
int16_t PhysicsEngine::getCollisionMask(int16_t group) {
const int16_t* mask = _collisionMasks.find(btHashInt((int)group));
return mask ? *mask : BULLET_COLLISION_MASK_DEFAULT;
}
QUuid _sessionID;
// static
void PhysicsEngine::setSessionUUID(const QUuid& sessionID) {
_sessionID = sessionID;
}
// static
const QUuid& PhysicsEngine::getSessionID() {
return _sessionID;
}
PhysicsEngine::PhysicsEngine(const glm::vec3& offset) :
_originOffset(offset),
_myAvatarController(nullptr) {
// build table of masks with their group as the key
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_DEFAULT), COLLISION_MASK_DEFAULT);
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_STATIC), COLLISION_MASK_STATIC);
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_KINEMATIC), COLLISION_MASK_KINEMATIC);
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_DEBRIS), COLLISION_MASK_DEBRIS);
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_TRIGGER), COLLISION_MASK_TRIGGER);
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_MY_AVATAR), COLLISION_MASK_MY_AVATAR);
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_MY_ATTACHMENT), COLLISION_MASK_MY_ATTACHMENT);
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_OTHER_AVATAR), COLLISION_MASK_OTHER_AVATAR);
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_OTHER_ATTACHMENT), COLLISION_MASK_OTHER_ATTACHMENT);
_collisionMasks.insert(btHashInt((int)COLLISION_GROUP_COLLISIONLESS), COLLISION_MASK_COLLISIONLESS);
initCollisionMaskTable();
}
PhysicsEngine::~PhysicsEngine() {
@ -139,8 +162,9 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
body->setFlags(BT_DISABLE_WORLD_GRAVITY);
motionState->updateBodyMaterialProperties();
int16_t group = motionState->computeCollisionGroup();
_dynamicsWorld->addRigidBody(body, group, getCollisionMask(group));
int16_t group, mask;
motionState->computeCollisionGroupAndMask(group, mask);
_dynamicsWorld->addRigidBody(body, group, mask);
motionState->clearIncomingDirtyFlags();
}
@ -198,7 +222,7 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob
stillNeedChange.push_back(object);
}
} else if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
if (object->handleEasyChanges(flags, this)) {
if (object->handleEasyChanges(flags)) {
object->clearIncomingDirtyFlags();
} else {
stillNeedChange.push_back(object);
@ -457,11 +481,6 @@ void PhysicsEngine::setCharacterController(CharacterController* character) {
}
}
int16_t PhysicsEngine::getCollisionMask(int16_t group) const {
const int16_t* mask = _collisionMasks.find(btHashInt((int)group));
return mask ? *mask : COLLISION_MASK_DEFAULT;
}
EntityActionPointer PhysicsEngine::getActionByID(const QUuid& actionID) const {
if (_objectActions.contains(actionID)) {
return _objectActions[actionID];

View file

@ -45,14 +45,16 @@ typedef QVector<Collision> CollisionEvents;
class PhysicsEngine {
public:
static int16_t getCollisionMask(int16_t group);
uint32_t getNumSubsteps();
PhysicsEngine(const glm::vec3& offset);
~PhysicsEngine();
void init();
void setSessionUUID(const QUuid& sessionID) { _sessionID = sessionID; }
const QUuid& getSessionID() const { return _sessionID; }
static void setSessionUUID(const QUuid& sessionID);
static const QUuid& getSessionID();
void removeObjects(const VectorOfMotionStates& objects);
void removeObjects(const SetOfMotionStates& objects); // only called during teardown
@ -88,8 +90,6 @@ public:
void dumpNextStats() { _dumpNextStats = true; }
int16_t getCollisionMask(int16_t group) const;
EntityActionPointer getActionByID(const QUuid& actionID) const;
void addAction(EntityActionPointer action);
void removeAction(const QUuid actionID);
@ -122,12 +122,10 @@ private:
bool _dumpNextStats = false;
bool _hasOutgoingChanges = false;
QUuid _sessionID;
CollisionEvents _collisionEvents;
QHash<QUuid, EntityActionPointer> _objectActions;
btHashMap<btHashInt, int16_t> _collisionMasks;
uint32_t _numSubsteps;
};

View file

@ -14,8 +14,8 @@
#include <stdint.h>
/* Note: These are the Bullet collision groups defined in btBroadphaseProxy. Only
* DefaultFilter and StaticFilter are explicitly used by Bullet (when the collision
/* Note: These are the Bullet collision groups defined in btBroadphaseProxy. Only
* DefaultFilter and StaticFilter are explicitly used by Bullet (when the collision
* filter of an object is not manually specified), the rest are merely suggestions.
*
enum CollisionFilterGroups {
@ -28,52 +28,60 @@ enum CollisionFilterGroups {
AllFilter = -1
}
*
* When using custom collision filters we pretty much need to do all or nothing.
* We'll be doing it all which means we define our own groups and build custom masks
* When using custom collision filters we pretty much need to do all or nothing.
* We'll be doing it all which means we define our own groups and build custom masks
* for everything.
*
*/
const int16_t COLLISION_GROUP_DEFAULT = 1 << 0;
const int16_t COLLISION_GROUP_STATIC = 1 << 1;
const int16_t COLLISION_GROUP_KINEMATIC = 1 << 2;
const int16_t COLLISION_GROUP_DEBRIS = 1 << 3;
const int16_t COLLISION_GROUP_TRIGGER = 1 << 4;
const int16_t COLLISION_GROUP_MY_AVATAR = 1 << 5;
const int16_t COLLISION_GROUP_OTHER_AVATAR = 1 << 6;
const int16_t COLLISION_GROUP_MY_ATTACHMENT = 1 << 7;
const int16_t COLLISION_GROUP_OTHER_ATTACHMENT = 1 << 8;
const int16_t BULLET_COLLISION_GROUP_STATIC = 1 << 0;
const int16_t BULLET_COLLISION_GROUP_DYNAMIC = 1 << 1;
const int16_t BULLET_COLLISION_GROUP_KINEMATIC = 1 << 2;
const int16_t BULLET_COLLISION_GROUP_MY_AVATAR = 1 << 3;
const int16_t BULLET_COLLISION_GROUP_OTHER_AVATAR = 1 << 4;
// ...
const int16_t COLLISION_GROUP_COLLISIONLESS = 1 << 14;
const int16_t BULLET_COLLISION_GROUP_COLLISIONLESS = 1 << 14;
/* Note: In order for objectA to collide with objectB at the filter stage
/* Note: In order for objectA to collide with objectB at the filter stage
* both (groupA & maskB) and (groupB & maskA) must be non-zero.
*/
// DEFAULT collides with everything except COLLISIONLESS
const int16_t COLLISION_MASK_DEFAULT = ~ COLLISION_GROUP_COLLISIONLESS;
// the default collision mask is: collides with everything except collisionless
const int16_t BULLET_COLLISION_MASK_DEFAULT = ~ BULLET_COLLISION_GROUP_COLLISIONLESS;
// STATIC also doesn't collide with other STATIC
const int16_t COLLISION_MASK_STATIC = ~ (COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_STATIC);
// STATIC does not collide with itself (as optimization of physics simulation)
const int16_t BULLET_COLLISION_MASK_STATIC = ~ (BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_STATIC);
const int16_t COLLISION_MASK_KINEMATIC = COLLISION_MASK_DEFAULT;
const int16_t BULLET_COLLISION_MASK_DYNAMIC = BULLET_COLLISION_MASK_DEFAULT;
const int16_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_DEFAULT;
// DEBRIS also doesn't collide with other DEBRIS, or TRIGGER
const int16_t COLLISION_MASK_DEBRIS = ~ (COLLISION_GROUP_COLLISIONLESS
| COLLISION_GROUP_DEBRIS
| COLLISION_GROUP_TRIGGER);
// MY_AVATAR does not collide with itself
const int16_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR);
// TRIGGER also doesn't collide with DEBRIS, TRIGGER, or STATIC (TRIGGER only detects moveable things that matter)
const int16_t COLLISION_MASK_TRIGGER = COLLISION_MASK_DEBRIS & ~(COLLISION_GROUP_STATIC);
// AVATAR also doesn't collide with corresponding ATTACHMENTs
const int16_t COLLISION_MASK_MY_AVATAR = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_MY_ATTACHMENT);
const int16_t COLLISION_MASK_MY_ATTACHMENT = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_MY_AVATAR);
const int16_t COLLISION_MASK_OTHER_AVATAR = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_OTHER_ATTACHMENT);
const int16_t COLLISION_MASK_OTHER_ATTACHMENT = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_OTHER_AVATAR);
const int16_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_MASK_DEFAULT;
// COLLISIONLESS gets an empty mask.
const int16_t COLLISION_MASK_COLLISIONLESS = 0;
const int16_t BULLET_COLLISION_MASK_COLLISIONLESS = 0;
// The USER collision groups are exposed to script and can be used to generate per-object collision masks.
// They are not necessarily the same as the BULLET_COLLISION_GROUPS, but we start them off with matching numbers.
const uint8_t USER_COLLISION_GROUP_STATIC = 1 << 0;
const uint8_t USER_COLLISION_GROUP_DYNAMIC = 1 << 1;
const uint8_t USER_COLLISION_GROUP_KINEMATIC = 1 << 2;
const uint8_t USER_COLLISION_GROUP_MY_AVATAR = 1 << 3;
const uint8_t USER_COLLISION_GROUP_OTHER_AVATAR = 1 << 4;
const uint8_t ENTITY_COLLISION_MASK_DEFAULT =
USER_COLLISION_GROUP_STATIC |
USER_COLLISION_GROUP_DYNAMIC |
USER_COLLISION_GROUP_KINEMATIC |
USER_COLLISION_GROUP_MY_AVATAR |
USER_COLLISION_GROUP_OTHER_AVATAR;
const uint8_t USER_COLLISION_MASK_AVATARS = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR;
const int NUM_USER_COLLISION_GROUPS = 5;
#endif // hifi_PhysicsCollisionGroups_h