mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-10 03:29:16 +02:00
Adding avatar body-body collisions to prevent near-clipping.
This commit is contained in:
parent
887fa0c938
commit
d0f9b78710
5 changed files with 81 additions and 7 deletions
|
@ -225,8 +225,6 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
updateCollisionWithVoxels(deltaTime, radius);
|
updateCollisionWithVoxels(deltaTime, radius);
|
||||||
}
|
}
|
||||||
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
||||||
// Note, hand-vs-avatar collisions are done elsewhere
|
|
||||||
// This is where we avatar-vs-avatar bounding capsule
|
|
||||||
updateCollisionWithAvatars(deltaTime);
|
updateCollisionWithAvatars(deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -974,7 +972,43 @@ void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const float DEFAULT_HAND_RADIUS = 0.1f;
|
bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float heightA,
|
||||||
|
const glm::vec3 positionB, float radiusB, float heightB, glm::vec3& penetration) {
|
||||||
|
glm::vec3 positionBA = positionB - positionA;
|
||||||
|
float xzDistance = sqrt(positionBA.x * positionBA.x + positionBA.z * positionBA.z);
|
||||||
|
if (xzDistance < (radiusA + radiusB)) {
|
||||||
|
float yDistance = fabs(positionBA.y);
|
||||||
|
float halfHeights = 0.5 * (heightA + heightB);
|
||||||
|
if (yDistance < halfHeights) {
|
||||||
|
// cylinders collide
|
||||||
|
if (xzDistance > 0.f) {
|
||||||
|
positionBA.y = 0.f;
|
||||||
|
// note, penetration should point from A into B
|
||||||
|
penetration = positionBA * ((radiusA + radiusB - xzDistance) / xzDistance);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// exactly coaxial -- we'll return false for this case
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (yDistance < halfHeights + radiusA + radiusB) {
|
||||||
|
// caps collide
|
||||||
|
if (positionBA.y < 0.f) {
|
||||||
|
// A is above B
|
||||||
|
positionBA.y += halfHeights;
|
||||||
|
float BA = glm::length(positionBA);
|
||||||
|
penetration = positionBA * (radiusA + radiusB - BA) / BA;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// A is below B
|
||||||
|
positionBA.y -= halfHeights;
|
||||||
|
float BA = glm::length(positionBA);
|
||||||
|
penetration = positionBA * (radiusA + radiusB - BA) / BA;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
||||||
// Reset detector for nearest avatar
|
// Reset detector for nearest avatar
|
||||||
|
@ -984,7 +1018,14 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
||||||
// no need to compute a bunch of stuff if we have one or fewer avatars
|
// no need to compute a bunch of stuff if we have one or fewer avatars
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float myRadius = getHeight();
|
float myBoundingRadius = 0.5f * getHeight();
|
||||||
|
|
||||||
|
// HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis
|
||||||
|
// TODO: make the collision work without assuming avatar orientation
|
||||||
|
Extents myStaticExtents = _skeletonModel.getStaticExtents();
|
||||||
|
glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum;
|
||||||
|
float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
|
||||||
|
float myCapsuleHeight = staticScale.y;
|
||||||
|
|
||||||
CollisionInfo collisionInfo;
|
CollisionInfo collisionInfo;
|
||||||
foreach (const AvatarSharedPointer& avatarPointer, avatars) {
|
foreach (const AvatarSharedPointer& avatarPointer, avatars) {
|
||||||
|
@ -997,9 +1038,20 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
||||||
if (_distanceToNearestAvatar > distance) {
|
if (_distanceToNearestAvatar > distance) {
|
||||||
_distanceToNearestAvatar = distance;
|
_distanceToNearestAvatar = distance;
|
||||||
}
|
}
|
||||||
float theirRadius = avatar->getHeight();
|
float theirBoundingRadius = 0.5f * avatar->getHeight();
|
||||||
if (distance < myRadius + theirRadius) {
|
if (distance < myBoundingRadius + theirBoundingRadius) {
|
||||||
// TODO: Andrew to make avatar-avatar capsule collisions work here
|
Extents theirStaticExtents = _skeletonModel.getStaticExtents();
|
||||||
|
glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum;
|
||||||
|
float theirCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
|
||||||
|
float theirCapsuleHeight = staticScale.y;
|
||||||
|
|
||||||
|
glm::vec3 penetration(0.f);
|
||||||
|
if (findAvatarAvatarPenetration(_position, myCapsuleRadius, myCapsuleHeight,
|
||||||
|
avatar->getPosition(), theirCapsuleRadius, theirCapsuleHeight, penetration)) {
|
||||||
|
// move the avatar out by half the penetration
|
||||||
|
setPosition(_position - 0.5f * penetration);
|
||||||
|
glm::vec3 pushOut = 0.5f * penetration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1268,6 +1268,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
|
|
||||||
geometry.bindExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
|
geometry.bindExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||||
geometry.bindExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
geometry.bindExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||||
|
geometry.staticExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||||
|
geometry.staticExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||||
|
|
||||||
QVariantHash springs = mapping.value("spring").toHash();
|
QVariantHash springs = mapping.value("spring").toHash();
|
||||||
QVariant defaultSpring = springs.value("default");
|
QVariant defaultSpring = springs.value("default");
|
||||||
|
@ -1424,6 +1426,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
boneDirection /= boneLength;
|
boneDirection /= boneLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bool jointIsStatic = joint.freeLineage.isEmpty();
|
||||||
|
glm::vec3 jointTranslation = extractTranslation(geometry.offset * joint.bindTransform);
|
||||||
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
|
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
|
||||||
float totalWeight = 0.0f;
|
float totalWeight = 0.0f;
|
||||||
for (int j = 0; j < cluster.indices.size(); j++) {
|
for (int j = 0; j < cluster.indices.size(); j++) {
|
||||||
|
@ -1441,6 +1445,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(
|
joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(
|
||||||
vertex, boneEnd + boneDirection * proj));
|
vertex, boneEnd + boneDirection * proj));
|
||||||
}
|
}
|
||||||
|
if (jointIsStatic) {
|
||||||
|
// expand the extents of static (nonmovable) joints
|
||||||
|
geometry.staticExtents.minimum = glm::min(geometry.staticExtents.minimum, vertex + jointTranslation);
|
||||||
|
geometry.staticExtents.maximum = glm::max(geometry.staticExtents.maximum, vertex + jointTranslation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for an unused slot in the weights vector
|
// look for an unused slot in the weights vector
|
||||||
|
|
|
@ -159,6 +159,7 @@ public:
|
||||||
glm::vec3 neckPivot;
|
glm::vec3 neckPivot;
|
||||||
|
|
||||||
Extents bindExtents;
|
Extents bindExtents;
|
||||||
|
Extents staticExtents;
|
||||||
|
|
||||||
QVector<FBXAttachment> attachments;
|
QVector<FBXAttachment> attachments;
|
||||||
};
|
};
|
||||||
|
|
|
@ -305,6 +305,15 @@ Extents Model::getBindExtents() const {
|
||||||
return scaledExtents;
|
return scaledExtents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Extents Model::getStaticExtents() const {
|
||||||
|
if (!isActive()) {
|
||||||
|
return Extents();
|
||||||
|
}
|
||||||
|
const Extents& staticExtents = _geometry->getFBXGeometry().staticExtents;
|
||||||
|
Extents scaledExtents = { staticExtents.minimum * _scale, staticExtents.maximum * _scale };
|
||||||
|
return scaledExtents;
|
||||||
|
}
|
||||||
|
|
||||||
int Model::getParentJointIndex(int jointIndex) const {
|
int Model::getParentJointIndex(int jointIndex) const {
|
||||||
return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).parentIndex : -1;
|
return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).parentIndex : -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,9 @@ public:
|
||||||
|
|
||||||
/// Returns the extents of the model in its bind pose.
|
/// Returns the extents of the model in its bind pose.
|
||||||
Extents getBindExtents() const;
|
Extents getBindExtents() const;
|
||||||
|
|
||||||
|
/// Returns the extents of the unmovable joints of the model.
|
||||||
|
Extents getStaticExtents() const;
|
||||||
|
|
||||||
/// Returns a reference to the shared geometry.
|
/// Returns a reference to the shared geometry.
|
||||||
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
|
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
|
||||||
|
|
Loading…
Reference in a new issue