mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 09:33:36 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into voxelnotshownbug
This commit is contained in:
commit
44b60c92a1
16 changed files with 324 additions and 196 deletions
|
@ -169,14 +169,7 @@ Menu::Menu() :
|
|||
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ClickToFly);
|
||||
|
||||
QMenu* collisionsOptionsMenu = editMenu->addMenu("Collision Options");
|
||||
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithEnvironment, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithAvatars, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithVoxels, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
// TODO: make this option work
|
||||
//addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithParticles, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addAvatarCollisionSubMenu(editMenu);
|
||||
|
||||
QMenu* toolsMenu = addMenu("Tools");
|
||||
|
||||
|
@ -345,6 +338,8 @@ Menu::Menu() :
|
|||
SLOT(setTCPEnabled(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false);
|
||||
|
||||
addAvatarCollisionSubMenu(avatarOptionsMenu);
|
||||
|
||||
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu,
|
||||
|
@ -519,6 +514,11 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
Application::getInstance()->getProfile()->loadData(settings);
|
||||
Application::getInstance()->updateWindowTitle();
|
||||
NodeList::getInstance()->loadData(settings);
|
||||
|
||||
// MyAvatar caches some menu options, so we have to update them whenever we load settings.
|
||||
// TODO: cache more settings in MyAvatar that are checked with very high frequency.
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
myAvatar->updateCollisionFlags();
|
||||
}
|
||||
|
||||
void Menu::saveSettings(QSettings* settings) {
|
||||
|
@ -1232,6 +1232,22 @@ void Menu::updateFrustumRenderModeAction() {
|
|||
}
|
||||
}
|
||||
|
||||
void Menu::addAvatarCollisionSubMenu(QMenu* overMenu) {
|
||||
// add avatar collisions subMenu to overMenu
|
||||
QMenu* subMenu = overMenu->addMenu("Collision Options");
|
||||
|
||||
Application* appInstance = Application::getInstance();
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithEnvironment,
|
||||
0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithAvatars,
|
||||
0, true, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithVoxels,
|
||||
0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithParticles,
|
||||
0, true, avatar, SLOT(updateCollisionFlags()));
|
||||
}
|
||||
|
||||
QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string) {
|
||||
int lastIndex;
|
||||
lastIndex = string.lastIndexOf(search);
|
||||
|
@ -1242,4 +1258,3 @@ QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string)
|
|||
|
||||
return string;
|
||||
}
|
||||
|
||||
|
|
|
@ -152,6 +152,8 @@ private:
|
|||
|
||||
void updateFrustumRenderModeAction();
|
||||
|
||||
void addAvatarCollisionSubMenu(QMenu* overMenu);
|
||||
|
||||
QHash<QString, QAction*> _actionHash;
|
||||
int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback
|
||||
BandwidthDialog* _bandwidthDialog;
|
||||
|
|
|
@ -273,27 +273,19 @@ bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
}
|
||||
|
||||
bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionList& collisions, int skeletonSkipIndex) {
|
||||
bool didPenetrate = false;
|
||||
glm::vec3 skeletonPenetration;
|
||||
ModelCollisionInfo collisionInfo;
|
||||
/* Temporarily disabling collisions against the skeleton because the collision proxies up
|
||||
* near the neck are bad and prevent the hand from hitting the face.
|
||||
if (_skeletonModel.findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo, 1.0f, skeletonSkipIndex)) {
|
||||
collisionInfo._model = &_skeletonModel;
|
||||
collisions.push_back(collisionInfo);
|
||||
didPenetrate = true;
|
||||
}
|
||||
*/
|
||||
if (_head.getFaceModel().findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo)) {
|
||||
collisionInfo._model = &(_head.getFaceModel());
|
||||
collisions.push_back(collisionInfo);
|
||||
didPenetrate = true;
|
||||
}
|
||||
return didPenetrate;
|
||||
CollisionList& collisions, int skeletonSkipIndex) {
|
||||
// Temporarily disabling collisions against the skeleton because the collision proxies up
|
||||
// near the neck are bad and prevent the hand from hitting the face.
|
||||
//return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions, 1.0f, skeletonSkipIndex);
|
||||
return _head.getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
|
||||
}
|
||||
|
||||
bool Avatar::findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
||||
if (_collisionFlags & COLLISION_GROUP_PARTICLES) {
|
||||
return false;
|
||||
}
|
||||
bool collided = false;
|
||||
// first do the hand collisions
|
||||
const HandData* handData = getHandData();
|
||||
if (handData) {
|
||||
for (int i = 0; i < NUM_HANDS; i++) {
|
||||
|
@ -311,41 +303,55 @@ bool Avatar::findSphereCollisionWithHands(const glm::vec3& sphereCenter, float s
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int jointIndex = -1;
|
||||
glm::vec3 handPosition;
|
||||
if (i == 0) {
|
||||
_skeletonModel.getLeftHandPosition(handPosition);
|
||||
jointIndex = _skeletonModel.getLeftHandJointIndex();
|
||||
}
|
||||
else {
|
||||
_skeletonModel.getRightHandPosition(handPosition);
|
||||
jointIndex = _skeletonModel.getRightHandJointIndex();
|
||||
}
|
||||
glm::vec3 diskCenter = handPosition + HAND_PADDLE_OFFSET * fingerAxis;
|
||||
glm::vec3 diskNormal = palm->getNormal();
|
||||
float diskThickness = 0.08f;
|
||||
const float DISK_THICKNESS = 0.08f;
|
||||
|
||||
// collide against the disk
|
||||
if (findSphereDiskPenetration(sphereCenter, sphereRadius,
|
||||
diskCenter, HAND_PADDLE_RADIUS, diskThickness, diskNormal,
|
||||
collision._penetration)) {
|
||||
collision._addedVelocity = palm->getVelocity();
|
||||
return true;
|
||||
glm::vec3 penetration;
|
||||
if (findSphereDiskPenetration(particleCenter, particleRadius,
|
||||
diskCenter, HAND_PADDLE_RADIUS, DISK_THICKNESS, diskNormal,
|
||||
penetration)) {
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (collision) {
|
||||
collision->_type = PADDLE_HAND_COLLISION;
|
||||
collision->_flags = jointIndex;
|
||||
collision->_penetration = penetration;
|
||||
collision->_addedVelocity = palm->getVelocity();
|
||||
collided = true;
|
||||
} else {
|
||||
// collisions are full, so we might as well bail now
|
||||
return collided;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* adebug TODO: make this work again
|
||||
bool Avatar::findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
int jointIndex = _skeletonModel.findSphereCollision(sphereCenter, sphereRadius, collision._penetration);
|
||||
if (jointIndex != -1) {
|
||||
collision._penetration /= (float)(TREE_SCALE);
|
||||
collision._addedVelocity = getVelocity();
|
||||
return true;
|
||||
// then collide against the models
|
||||
int preNumCollisions = collisions.size();
|
||||
if (_skeletonModel.findSphereCollisions(particleCenter, particleRadius, collisions)) {
|
||||
// the Model doesn't have velocity info, so we have to set it for each new collision
|
||||
int postNumCollisions = collisions.size();
|
||||
for (int i = preNumCollisions; i < postNumCollisions; ++i) {
|
||||
CollisionInfo* collision = collisions.getCollision(i);
|
||||
collision->_penetration /= (float)(TREE_SCALE);
|
||||
collision->_addedVelocity = getVelocity();
|
||||
}
|
||||
collided = true;
|
||||
}
|
||||
return false;
|
||||
return collided;
|
||||
}
|
||||
*/
|
||||
|
||||
void Avatar::setFaceModelURL(const QUrl &faceModelURL) {
|
||||
AvatarData::setFaceModelURL(faceModelURL);
|
||||
|
@ -430,9 +436,9 @@ void Avatar::updateCollisionFlags() {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithVoxels)) {
|
||||
_collisionFlags |= COLLISION_GROUP_VOXELS;
|
||||
}
|
||||
//if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) {
|
||||
// _collisionFlags |= COLLISION_GROUP_PARTICLES;
|
||||
//}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) {
|
||||
_collisionFlags |= COLLISION_GROUP_PARTICLES;
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::setScale(float scale) {
|
||||
|
@ -449,34 +455,34 @@ float Avatar::getHeight() const {
|
|||
return extents.maximum.y - extents.minimum.y;
|
||||
}
|
||||
|
||||
bool Avatar::collisionWouldMoveAvatar(ModelCollisionInfo& collision) const {
|
||||
// ATM only the Skeleton is pokeable
|
||||
// TODO: make poke affect head
|
||||
if (!collision._model) {
|
||||
bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
||||
if (!collision._data || collision._type != MODEL_COLLISION) {
|
||||
return false;
|
||||
}
|
||||
if (collision._model == &_skeletonModel && collision._jointIndex != -1) {
|
||||
Model* model = static_cast<Model*>(collision._data);
|
||||
int jointIndex = collision._flags;
|
||||
|
||||
if (model == &(_skeletonModel) && jointIndex != -1) {
|
||||
// collision response of skeleton is temporarily disabled
|
||||
return false;
|
||||
//return _skeletonModel.collisionHitsMoveableJoint(collision);
|
||||
}
|
||||
if (collision._model == &(_head.getFaceModel())) {
|
||||
if (model == &(_head.getFaceModel())) {
|
||||
// ATM we always handle MODEL_COLLISIONS against the face.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Avatar::applyCollision(ModelCollisionInfo& collision) {
|
||||
if (!collision._model) {
|
||||
void Avatar::applyCollision(CollisionInfo& collision) {
|
||||
if (!collision._data || collision._type != MODEL_COLLISION) {
|
||||
return;
|
||||
}
|
||||
if (collision._model == &(_head.getFaceModel())) {
|
||||
// TODO: make skeleton also respond to collisions
|
||||
Model* model = static_cast<Model*>(collision._data);
|
||||
if (model == &(_head.getFaceModel())) {
|
||||
_head.applyCollision(collision);
|
||||
}
|
||||
// TODO: make skeleton respond to collisions
|
||||
//if (collision._model == &_skeletonModel && collision._jointIndex != -1) {
|
||||
// _skeletonModel.applyCollision(collision);
|
||||
//}
|
||||
}
|
||||
|
||||
float Avatar::getPelvisFloatingHeight() const {
|
||||
|
|
|
@ -57,8 +57,6 @@ enum ScreenTintLayer {
|
|||
NUM_SCREEN_TINT_LAYERS
|
||||
};
|
||||
|
||||
typedef QVector<ModelCollisionInfo> ModelCollisionList;
|
||||
|
||||
// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found)
|
||||
// this is basically in the center of the ground plane. Slightly adjusted. This was asked for by
|
||||
// Grayson as he's building a street around here for demo dinner 2
|
||||
|
@ -97,26 +95,19 @@ public:
|
|||
/// Checks for penetration between the described sphere and the avatar.
|
||||
/// \param penetratorCenter the center of the penetration test sphere
|
||||
/// \param penetratorRadius the radius of the penetration test sphere
|
||||
/// \param collisions[out] a list of collisions
|
||||
/// \param collisions[out] a list to which collisions get appended
|
||||
/// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model
|
||||
/// \return whether or not the sphere penetrated
|
||||
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionList& collisions, int skeletonSkipIndex = -1);
|
||||
CollisionList& collisions, int skeletonSkipIndex = -1);
|
||||
|
||||
/// Checks for collision between the a sphere and the avatar's (paddle) hands.
|
||||
/// \param collisionCenter the center of the penetration test sphere
|
||||
/// \param collisionRadius the radius of the penetration test sphere
|
||||
/// \param collision[out] the details of the collision point
|
||||
/// \return whether or not the sphere collided
|
||||
bool findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision);
|
||||
/// Checks for collision between the a spherical particle and the avatar (including paddle hands)
|
||||
/// \param collisionCenter the center of particle's bounding sphere
|
||||
/// \param collisionRadius the radius of particle's bounding sphere
|
||||
/// \param collisions[out] a list to which collisions get appended
|
||||
/// \return whether or not the particle collided
|
||||
bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions);
|
||||
|
||||
/// Checks for collision between the a sphere and the avatar's skeleton (including hand capsules).
|
||||
/// \param collisionCenter the center of the penetration test sphere
|
||||
/// \param collisionRadius the radius of the penetration test sphere
|
||||
/// \param collision[out] the details of the collision point
|
||||
/// \return whether or not the sphere collided
|
||||
//bool findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision);
|
||||
|
||||
virtual bool isMyAvatar() { return false; }
|
||||
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
|
@ -126,13 +117,13 @@ public:
|
|||
|
||||
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
|
||||
|
||||
float getHeight() const;
|
||||
|
||||
/// \return true if we expect the avatar would move as a result of the collision
|
||||
bool collisionWouldMoveAvatar(ModelCollisionInfo& collision) const;
|
||||
bool collisionWouldMoveAvatar(CollisionInfo& collision) const;
|
||||
|
||||
/// \param collision a data structure for storing info about collisions against Models
|
||||
void applyCollision(ModelCollisionInfo& collision);
|
||||
void applyCollision(CollisionInfo& collision);
|
||||
|
||||
float getBoundingRadius() const { return 0.5f * getHeight(); }
|
||||
|
||||
public slots:
|
||||
void updateCollisionFlags();
|
||||
|
@ -164,6 +155,7 @@ protected:
|
|||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void setScale(float scale);
|
||||
|
||||
float getHeight() const;
|
||||
float getPelvisFloatingHeight() const;
|
||||
float getPelvisToHeadLength() const;
|
||||
|
||||
|
|
|
@ -125,6 +125,10 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
}
|
||||
}
|
||||
|
||||
// We create a static CollisionList that is recycled for each collision test.
|
||||
const float MAX_COLLISIONS_PER_AVATAR = 32;
|
||||
static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR);
|
||||
|
||||
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
||||
if (!avatar || avatar == _owningAvatar) {
|
||||
// don't collide with our own hands (that is done elsewhere)
|
||||
|
@ -137,7 +141,6 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
|||
continue;
|
||||
}
|
||||
glm::vec3 totalPenetration;
|
||||
ModelCollisionList collisions;
|
||||
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
||||
// Check for palm collisions
|
||||
glm::vec3 myPalmPosition = palm.getPosition();
|
||||
|
@ -171,20 +174,22 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) {
|
||||
for (int j = 0; j < collisions.size(); ++j) {
|
||||
handCollisions.clear();
|
||||
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions)) {
|
||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
||||
if (isMyHand) {
|
||||
if (!avatar->collisionWouldMoveAvatar(collisions[j])) {
|
||||
if (!avatar->collisionWouldMoveAvatar(*collision)) {
|
||||
// we resolve the hand from collision when it belongs to MyAvatar AND the other Avatar is
|
||||
// not expected to respond to the collision (hand hit unmovable part of their Avatar)
|
||||
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
|
||||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
||||
}
|
||||
} else {
|
||||
// when !isMyHand then avatar is MyAvatar and we apply the collision
|
||||
// which might not do anything (hand hit unmovable part of MyAvatar) however
|
||||
// we don't resolve the hand's penetration because we expect the remote
|
||||
// simulation to do the right thing.
|
||||
avatar->applyCollision(collisions[j]);
|
||||
avatar->applyCollision(*collision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +205,6 @@ void Hand::collideAgainstOurself() {
|
|||
return;
|
||||
}
|
||||
|
||||
ModelCollisionList collisions;
|
||||
int leftPalmIndex, rightPalmIndex;
|
||||
getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
|
||||
|
@ -210,16 +214,18 @@ void Hand::collideAgainstOurself() {
|
|||
if (!palm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 totalPenetration;
|
||||
// and the current avatar (ignoring everything below the parent of the parent of the last free joint)
|
||||
collisions.clear();
|
||||
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
|
||||
// ignoring everything below the parent of the parent of the last free joint
|
||||
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
|
||||
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
|
||||
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
|
||||
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions, skipIndex)) {
|
||||
for (int j = 0; j < collisions.size(); ++j) {
|
||||
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
|
||||
|
||||
handCollisions.clear();
|
||||
glm::vec3 totalPenetration;
|
||||
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions, skipIndex)) {
|
||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
||||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
||||
}
|
||||
}
|
||||
// resolve penetration
|
||||
|
|
|
@ -219,7 +219,7 @@ float Head::getTweakedRoll() const {
|
|||
return glm::clamp(_roll + _tweakedRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
|
||||
}
|
||||
|
||||
void Head::applyCollision(ModelCollisionInfo& collisionInfo) {
|
||||
void Head::applyCollision(CollisionInfo& collision) {
|
||||
// HACK: the collision proxies for the FaceModel are bad. As a temporary workaround
|
||||
// we collide against a hard coded collision proxy.
|
||||
// TODO: get a better collision proxy here.
|
||||
|
@ -229,7 +229,7 @@ void Head::applyCollision(ModelCollisionInfo& collisionInfo) {
|
|||
// collide the contactPoint against the collision proxy to obtain a new penetration
|
||||
// NOTE: that penetration is in opposite direction (points the way out for the point, not the sphere)
|
||||
glm::vec3 penetration;
|
||||
if (findPointSpherePenetration(collisionInfo._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) {
|
||||
if (findPointSpherePenetration(collision._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) {
|
||||
// compute lean angles
|
||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
|
||||
glm::quat bodyRotation = owningAvatar->getOrientation();
|
||||
|
@ -239,8 +239,8 @@ void Head::applyCollision(ModelCollisionInfo& collisionInfo) {
|
|||
glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f);
|
||||
float neckLength = glm::length(_position - neckPosition);
|
||||
if (neckLength > 0.f) {
|
||||
float forward = glm::dot(collisionInfo._penetration, zAxis) / neckLength;
|
||||
float sideways = - glm::dot(collisionInfo._penetration, xAxis) / neckLength;
|
||||
float forward = glm::dot(collision._penetration, zAxis) / neckLength;
|
||||
float sideways = - glm::dot(collision._penetration, xAxis) / neckLength;
|
||||
addLean(sideways, forward);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
float getTweakedYaw() const;
|
||||
float getTweakedRoll() const;
|
||||
|
||||
void applyCollision(ModelCollisionInfo& collisionInfo);
|
||||
void applyCollision(CollisionInfo& collisionInfo);
|
||||
|
||||
private:
|
||||
// disallow copies of the Head, copy of owning Avatar is disallowed too
|
||||
|
|
|
@ -955,7 +955,7 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
// no need to compute a bunch of stuff if we have one or fewer avatars
|
||||
return;
|
||||
}
|
||||
float myBoundingRadius = 0.5f * getHeight();
|
||||
float myBoundingRadius = getBoundingRadius();
|
||||
|
||||
// HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis
|
||||
// TODO: make the collision work without assuming avatar orientation
|
||||
|
@ -975,7 +975,7 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
if (_distanceToNearestAvatar > distance) {
|
||||
_distanceToNearestAvatar = distance;
|
||||
}
|
||||
float theirBoundingRadius = 0.5f * avatar->getHeight();
|
||||
float theirBoundingRadius = avatar->getBoundingRadius();
|
||||
if (distance < myBoundingRadius + theirBoundingRadius) {
|
||||
Extents theirStaticExtents = _skeletonModel.getStaticExtents();
|
||||
glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum;
|
||||
|
|
|
@ -457,9 +457,9 @@ bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Model::findSphereCollision(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionInfo& collisionInfo, float boneScale, int skipIndex) const {
|
||||
int jointIndex = -1;
|
||||
bool Model::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
CollisionList& collisions, float boneScale, int skipIndex) const {
|
||||
bool collided = false;
|
||||
const glm::vec3 relativeCenter = penetratorCenter - _translation;
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::vec3 totalPenetration;
|
||||
|
@ -488,22 +488,22 @@ bool Model::findSphereCollision(const glm::vec3& penetratorCenter, float penetra
|
|||
if (findSphereCapsuleConePenetration(relativeCenter, penetratorRadius, start, end,
|
||||
startRadius, endRadius, bonePenetration)) {
|
||||
totalPenetration = addPenetrations(totalPenetration, bonePenetration);
|
||||
// BUG: we currently overwrite the jointIndex with the last one found
|
||||
// which can cause incorrect collisions when colliding against more than
|
||||
// one joint.
|
||||
// TODO: fix this.
|
||||
jointIndex = i;
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (collision) {
|
||||
collision->_type = MODEL_COLLISION;
|
||||
collision->_data = (void*)(this);
|
||||
collision->_flags = i;
|
||||
collision->_contactPoint = penetratorCenter + penetratorRadius * glm::normalize(totalPenetration);
|
||||
collision->_penetration = totalPenetration;
|
||||
collided = true;
|
||||
} else {
|
||||
// collisions are full, so we might as well break
|
||||
break;
|
||||
}
|
||||
}
|
||||
outerContinue: ;
|
||||
}
|
||||
if (jointIndex != -1) {
|
||||
// don't store collisionInfo._model at this stage, let the outer context do that
|
||||
collisionInfo._penetration = totalPenetration;
|
||||
collisionInfo._jointIndex = jointIndex;
|
||||
collisionInfo._contactPoint = penetratorCenter + penetratorRadius * glm::normalize(totalPenetration);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return collided;
|
||||
}
|
||||
|
||||
void Model::updateJointState(int index) {
|
||||
|
@ -736,24 +736,30 @@ void Model::renderCollisionProxies(float alpha) {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
bool Model::collisionHitsMoveableJoint(ModelCollisionInfo& collision) const {
|
||||
// the joint is pokable by a collision if it exists and is free to move
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._jointIndex];
|
||||
if (joint.parentIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
bool Model::collisionHitsMoveableJoint(CollisionInfo& collision) const {
|
||||
if (collision._type == MODEL_COLLISION) {
|
||||
// the joint is pokable by a collision if it exists and is free to move
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._flags];
|
||||
if (joint.parentIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// an empty freeLineage means the joint can't move
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
int jointIndex = collision._flags;
|
||||
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
||||
return !freeLineage.isEmpty();
|
||||
}
|
||||
// an empty freeLineage means the joint can't move
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<int>& freeLineage = geometry.joints.at(collision._jointIndex).freeLineage;
|
||||
return !freeLineage.isEmpty();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Model::applyCollision(ModelCollisionInfo& collision) {
|
||||
// This needs work. At the moment it can wiggle joints that are free to move (such as arms)
|
||||
// but unmovable joints (such as torso) cannot be influenced at all.
|
||||
void Model::applyCollision(CollisionInfo& collision) {
|
||||
if (collision._type != MODEL_COLLISION) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 jointPosition(0.f);
|
||||
if (getJointPosition(collision._jointIndex, jointPosition)) {
|
||||
int jointIndex = collision._jointIndex;
|
||||
int jointIndex = collision._flags;
|
||||
if (getJointPosition(jointIndex, jointPosition)) {
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
|
||||
if (joint.parentIndex != -1) {
|
||||
// compute the approximate distance (travel) that the joint needs to move
|
||||
|
|
|
@ -17,16 +17,6 @@
|
|||
#include "ProgramObject.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
class Model;
|
||||
|
||||
// TODO: Andrew to move this into its own file
|
||||
class ModelCollisionInfo : public CollisionInfo {
|
||||
public:
|
||||
ModelCollisionInfo() : CollisionInfo(), _model(NULL), _jointIndex(-1) {}
|
||||
Model* _model;
|
||||
int _jointIndex;
|
||||
};
|
||||
|
||||
/// A generic 3D model displaying geometry loaded from a URL.
|
||||
class Model : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -162,17 +152,18 @@ public:
|
|||
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
bool findSphereCollision(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionInfo& collision, float boneScale = 1.0f, int skipIndex = -1) const;
|
||||
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
CollisionList& collisions, float boneScale = 1.0f, int skipIndex = -1) const;
|
||||
|
||||
void renderCollisionProxies(float alpha);
|
||||
|
||||
/// \param collision details about the collisions
|
||||
/// \return true if the collision is against a moveable joint
|
||||
bool collisionHitsMoveableJoint(ModelCollisionInfo& collision) const;
|
||||
bool collisionHitsMoveableJoint(CollisionInfo& collision) const;
|
||||
|
||||
/// \param collisionInfo info about the collision
|
||||
/// Use the collisionInfo to affect the model
|
||||
void applyCollision(ModelCollisionInfo& collisionInfo);
|
||||
/// \param collision details about the collision
|
||||
/// Use the collision to affect the model
|
||||
void applyCollision(CollisionInfo& collision);
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -135,14 +135,10 @@ public:
|
|||
|
||||
virtual const glm::vec3& getVelocity() const { return vec3Zero; }
|
||||
|
||||
virtual bool findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
virtual bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasIdentityChangedAfterParsing(const QByteArray& packet);
|
||||
QByteArray identityByteArray();
|
||||
|
||||
|
@ -150,6 +146,8 @@ public:
|
|||
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
virtual float getBoundingRadius() const { return 1.f; }
|
||||
|
||||
protected:
|
||||
glm::vec3 _position;
|
||||
|
|
|
@ -791,7 +791,6 @@ const char* OctreeSceneStats::getItemValue(Item item) {
|
|||
break;
|
||||
}
|
||||
default:
|
||||
sprintf(_itemValueBuffer, "");
|
||||
break;
|
||||
}
|
||||
return _itemValueBuffer;
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
#include "ParticleEditPacketSender.h"
|
||||
#include "ParticleTree.h"
|
||||
|
||||
const int MAX_COLLISIONS_PER_PARTICLE = 16;
|
||||
|
||||
ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender,
|
||||
ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio,
|
||||
AvatarHashMap* avatars) {
|
||||
AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_PARTICLE) {
|
||||
init(packetSender, particles, voxels, audio, avatars);
|
||||
}
|
||||
|
||||
|
@ -181,39 +183,53 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
const float COLLISION_FREQUENCY = 0.5f;
|
||||
glm::vec3 penetration;
|
||||
|
||||
_collisions.clear();
|
||||
foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) {
|
||||
AvatarData* avatar = avatarPointer.data();
|
||||
CollisionInfo collisionInfo;
|
||||
collisionInfo._damping = DAMPING;
|
||||
collisionInfo._elasticity = ELASTICITY;
|
||||
if (avatar->findSphereCollisionWithHands(center, radius, collisionInfo)) {
|
||||
// TODO: Andrew to resurrect particles-vs-avatar body collisions
|
||||
//avatar->findSphereCollisionWithSkeleton(center, radius, collisionInfo)) {
|
||||
collisionInfo._addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
|
||||
if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
|
||||
// only collide when particle and collision point are moving toward each other
|
||||
// (doing this prevents some "collision snagging" when particle penetrates the object)
|
||||
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
|
||||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
|
||||
float damping = DAMPING;
|
||||
if (attenuationFactor < 1.f) {
|
||||
collisionInfo._addedVelocity *= attenuationFactor;
|
||||
elasticity *= attenuationFactor;
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// while ramping it up to 1.0 when attenuationFactor = 0
|
||||
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
// use a very generous bounding radius since the arms can stretch
|
||||
float totalRadius = 2.f * avatar->getBoundingRadius() + radius;
|
||||
glm::vec3 relativePosition = center - avatar->getPosition();
|
||||
if (glm::dot(relativePosition, relativePosition) > (totalRadius * totalRadius)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (avatar->findParticleCollisions(center, radius, _collisions)) {
|
||||
int numCollisions = _collisions.size();
|
||||
for (int i = 0; i < numCollisions; ++i) {
|
||||
CollisionInfo* collision = _collisions.getCollision(i);
|
||||
collision->_damping = DAMPING;
|
||||
collision->_elasticity = ELASTICITY;
|
||||
|
||||
collision->_addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collision->_addedVelocity - particle->getVelocity();
|
||||
|
||||
if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) {
|
||||
// only collide when particle and collision point are moving toward each other
|
||||
// (doing this prevents some "collision snagging" when particle penetrates the object)
|
||||
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against them.
|
||||
if (collision->_type == PADDLE_HAND_COLLISION) {
|
||||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
float attenuationFactor = glm::length(collision->_addedVelocity) / HALTING_SPEED;
|
||||
float damping = DAMPING;
|
||||
if (attenuationFactor < 1.f) {
|
||||
collision->_addedVelocity *= attenuationFactor;
|
||||
elasticity *= attenuationFactor;
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// while ramping it up to 1 when attenuationFactor = 0
|
||||
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
}
|
||||
}
|
||||
// HACK END
|
||||
|
||||
updateCollisionSound(particle, collision->_penetration, COLLISION_FREQUENCY);
|
||||
collision->_penetration /= (float)(TREE_SCALE);
|
||||
particle->applyHardCollision(*collision);
|
||||
queueParticlePropertiesUpdate(particle);
|
||||
}
|
||||
// HACK END
|
||||
|
||||
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
||||
collisionInfo._penetration /= (float)(TREE_SCALE);
|
||||
particle->applyHardCollision(collisionInfo);
|
||||
queueParticlePropertiesUpdate(particle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ private:
|
|||
VoxelTree* _voxels;
|
||||
AbstractAudioInterface* _audio;
|
||||
AvatarHashMap* _avatars;
|
||||
CollisionList _collisions;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticleCollisionSystem__) */
|
||||
|
|
42
libraries/shared/src/CollisionInfo.cpp
Normal file
42
libraries/shared/src/CollisionInfo.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// CollisionInfo.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.02.14
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "CollisionInfo.h"
|
||||
|
||||
CollisionList::CollisionList(int maxSize) :
|
||||
_maxSize(maxSize),
|
||||
_size(0) {
|
||||
_collisions.resize(_maxSize);
|
||||
}
|
||||
|
||||
CollisionInfo* CollisionList::getNewCollision() {
|
||||
// return pointer to existing CollisionInfo, or NULL of list is full
|
||||
return (_size < _maxSize) ? &(_collisions[++_size]) : NULL;
|
||||
}
|
||||
|
||||
CollisionInfo* CollisionList::getCollision(int index) {
|
||||
return (index > -1 && index < _size) ? &(_collisions[index]) : NULL;
|
||||
}
|
||||
|
||||
void CollisionList::clear() {
|
||||
for (int i = 0; i < _size; ++i) {
|
||||
// we only clear the important stuff
|
||||
CollisionInfo& collision = _collisions[i];
|
||||
collision._type = BASE_COLLISION;
|
||||
collision._data = NULL; // CollisionInfo does not own whatever this points to.
|
||||
collision._flags = 0;
|
||||
// we rely on the consumer to properly overwrite these fields when the collision is "created"
|
||||
//collision._damping;
|
||||
//collision._elasticity;
|
||||
//collision._contactPoint;
|
||||
//collision._penetration;
|
||||
//collision._addedVelocity;
|
||||
}
|
||||
_size = 0;
|
||||
}
|
||||
|
|
@ -11,15 +11,42 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
const uint32_t COLLISION_GROUP_ENVIRONMENT = 1U << 0;
|
||||
const uint32_t COLLISION_GROUP_AVATARS = 1U << 1;
|
||||
const uint32_t COLLISION_GROUP_VOXELS = 1U << 2;
|
||||
const uint32_t COLLISION_GROUP_PARTICLES = 1U << 3;
|
||||
#include <QVector>
|
||||
|
||||
enum CollisionType {
|
||||
BASE_COLLISION = 0,
|
||||
PADDLE_HAND_COLLISION,
|
||||
MODEL_COLLISION,
|
||||
};
|
||||
|
||||
const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0;
|
||||
const quint32 COLLISION_GROUP_AVATARS = 1U << 1;
|
||||
const quint32 COLLISION_GROUP_VOXELS = 1U << 2;
|
||||
const quint32 COLLISION_GROUP_PARTICLES = 1U << 3;
|
||||
|
||||
// CollisionInfo contains details about the collision between two things: BodyA and BodyB.
|
||||
// The assumption is that the context that analyzes the collision knows about BodyA but
|
||||
// does not necessarily know about BodyB. Hence the data storred in the CollisionInfo
|
||||
// is expected to be relative to BodyA (for example the penetration points from A into B).
|
||||
|
||||
class CollisionInfo {
|
||||
public:
|
||||
CollisionInfo()
|
||||
: _damping(0.f),
|
||||
: _type(0),
|
||||
_data(NULL),
|
||||
_flags(0),
|
||||
_damping(0.f),
|
||||
_elasticity(1.f),
|
||||
_contactPoint(0.f),
|
||||
_penetration(0.f),
|
||||
_addedVelocity(0.f) {
|
||||
}
|
||||
|
||||
CollisionInfo(qint32 type)
|
||||
: _type(type),
|
||||
_data(NULL),
|
||||
_flags(0),
|
||||
_damping(0.f),
|
||||
_elasticity(1.f),
|
||||
_contactPoint(0.f),
|
||||
_penetration(0.f),
|
||||
|
@ -28,13 +55,40 @@ public:
|
|||
|
||||
~CollisionInfo() {}
|
||||
|
||||
//glm::vec3 _normal;
|
||||
float _damping;
|
||||
float _elasticity;
|
||||
glm::vec3 _contactPoint; // world-frame point on bodyA that is deepest into bodyB
|
||||
glm::vec3 _penetration; // depth that bodyA penetrates into bodyB
|
||||
glm::vec3 _addedVelocity;
|
||||
qint32 _type; // type of Collision (will determine what is supposed to be in _data and _flags)
|
||||
void* _data; // pointer to user supplied data
|
||||
quint32 _flags; // 32 bits for whatever
|
||||
|
||||
float _damping; // range [0,1] of friction coeficient
|
||||
float _elasticity; // range [0,1] of energy conservation
|
||||
glm::vec3 _contactPoint; // world-frame point on BodyA that is deepest into BodyB
|
||||
glm::vec3 _penetration; // depth that BodyA penetrates into BodyB
|
||||
glm::vec3 _addedVelocity; // velocity of BodyB
|
||||
};
|
||||
|
||||
// CollisionList is intended to be a recycled container. Fill the CollisionInfo's,
|
||||
// use them, and then clear them for the next frame or context.
|
||||
|
||||
class CollisionList {
|
||||
public:
|
||||
CollisionList(int maxSize);
|
||||
|
||||
/// \return pointer to next collision. NULL if list is full.
|
||||
CollisionInfo* getNewCollision();
|
||||
|
||||
/// \return pointer to collision by index. NULL if index out of bounds.
|
||||
CollisionInfo* getCollision(int index);
|
||||
|
||||
/// \return number of valid collisions
|
||||
int size() const { return _size; }
|
||||
|
||||
/// Clear valid collisions.
|
||||
void clear();
|
||||
|
||||
private:
|
||||
int _maxSize;
|
||||
int _size;
|
||||
QVector<CollisionInfo> _collisions;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__CollisionInfo__) */
|
||||
|
|
Loading…
Reference in a new issue