Merge pull request #2649 from ey6es/master

Experimental "pushback" to avoid other avatars' intersecting the near clip plane.
This commit is contained in:
AndrewMeadows 2014-04-11 07:50:52 -07:00
commit e850faee79
11 changed files with 295 additions and 1 deletions

View file

@ -531,6 +531,42 @@ void Application::paintGL() {
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
glm::vec3 planeNormal = _myCamera.getTargetRotation() * IDENTITY_FRONT;
const float BASE_PUSHBACK_RADIUS = 0.25f;
float pushbackRadius = _myCamera.getNearClip() + _myAvatar->getScale() * BASE_PUSHBACK_RADIUS;
glm::vec4 plane(planeNormal, -glm::dot(planeNormal, _myCamera.getTargetPosition()) - pushbackRadius);
// push camera out of any intersecting avatars
float pushback = 0.0f;
foreach (const AvatarSharedPointer& avatarData, _avatarManager.getAvatarHash()) {
Avatar* avatar = static_cast<Avatar*>(avatarData.data());
if (avatar->isMyAvatar()) {
continue;
}
if (glm::distance(avatar->getPosition(), _myCamera.getTargetPosition()) >
avatar->getBoundingRadius() + pushbackRadius) {
continue;
}
float angle = angleBetween(avatar->getPosition() - _myCamera.getTargetPosition(), planeNormal);
if (angle > PI_OVER_TWO) {
continue;
}
float scale = 1.0f - angle / PI_OVER_TWO;
scale = qMin(1.0f, scale * 2.5f);
static CollisionList collisions(64);
collisions.clear();
if (!avatar->findPlaneCollisions(plane, collisions)) {
continue;
}
for (int i = 0; i < collisions.size(); i++) {
pushback = qMax(pushback, glm::length(collisions.getCollision(i)->_penetration) * scale);
}
}
const float MAX_PUSHBACK = 0.35f;
const float PUSHBACK_DECAY = 0.5f;
_myCamera.setDistance(qMax(qMin(pushback, MAX_PUSHBACK * _myAvatar->getScale()),
_myCamera.getDistance() * PUSHBACK_DECAY));
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());

View file

@ -529,6 +529,11 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet
//return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
}
bool Avatar::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) {
return _skeletonModel.findPlaneCollisions(plane, collisions) ||
getHead()->getFaceModel().findPlaneCollisions(plane, collisions);
}
void Avatar::updateShapePositions() {
_skeletonModel.updateShapePositions();
Model& headModel = getHead()->getFaceModel();

View file

@ -119,6 +119,12 @@ public:
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
CollisionList& collisions, int skeletonSkipIndex = -1);
/// Checks for penetration between the described plane and the avatar.
/// \param plane the penetration plane
/// \param collisions[out] a list to which collisions get appended
/// \return whether or not the plane penetrated
bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
/// 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

View file

@ -647,7 +647,7 @@ void MyAvatar::renderBody(RenderMode renderMode) {
_skeletonModel.render(1.0f, modelRenderMode);
// Render head so long as the camera isn't inside it
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.40f;
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f;
Camera* myCamera = Application::getInstance()->getCamera();
if (renderMode != NORMAL_RENDER_MODE || (glm::length(myCamera->getPosition() - getHead()->calculateAverageEyePosition()) >
RENDER_HEAD_CUTOFF_DISTANCE * _scale)) {

View file

@ -634,6 +634,21 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi
return collided;
}
bool Model::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) {
bool collided = false;
PlaneShape planeShape(plane);
for (int i = 0; i < _jointShapes.size(); i++) {
if (ShapeCollider::collideShapes(&planeShape, _jointShapes[i], collisions)) {
CollisionInfo* collision = collisions.getLastCollision();
collision->_type = MODEL_COLLISION;
collision->_data = (void*)(this);
collision->_flags = i;
collided = true;
}
}
return collided;
}
class Blender : public QRunnable {
public:

View file

@ -176,6 +176,8 @@ public:
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
CollisionList& collisions, int skipIndex = -1);
bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
/// \param collision details about the collisions
/// \return true if the collision is against a moveable joint

View file

@ -0,0 +1,36 @@
//
// PlaneShape.cpp
// libraries/shared/src
//
// Created by Andrzej Kapolka on 4/10/2014.
// Copyright 2014 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 "PlaneShape.h"
#include "SharedUtil.h"
const glm::vec3 UNROTATED_NORMAL(0.0f, 1.0f, 0.0f);
PlaneShape::PlaneShape(const glm::vec4& coefficients) :
Shape(Shape::PLANE_SHAPE) {
glm::vec3 normal = glm::vec3(coefficients);
_position = -normal * coefficients.w;
float angle = acosf(glm::dot(normal, UNROTATED_NORMAL));
if (angle > EPSILON) {
if (angle > PI - EPSILON) {
_rotation = glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f));
} else {
_rotation = glm::angleAxis(angle, glm::normalize(glm::cross(UNROTATED_NORMAL, normal)));
}
}
}
glm::vec4 PlaneShape::getCoefficients() const {
glm::vec3 normal = _rotation * UNROTATED_NORMAL;
return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _position));
}

View file

@ -0,0 +1,24 @@
//
// PlaneShape.h
// libraries/shared/src
//
// Created by Andrzej Kapolka on 4/9/2014.
// Copyright 2014 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_PlaneShape_h
#define hifi_PlaneShape_h
#include "Shape.h"
class PlaneShape : public Shape {
public:
PlaneShape(const glm::vec4& coefficients = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
glm::vec4 getCoefficients() const;
};
#endif // hifi_PlaneShape_h

View file

@ -22,6 +22,7 @@ public:
UNKNOWN_SHAPE = 0,
SPHERE_SHAPE,
CAPSULE_SHAPE,
PLANE_SHAPE,
BOX_SHAPE,
LIST_SHAPE
};

View file

@ -34,6 +34,8 @@ bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& coll
return sphereSphere(sphereA, static_cast<const SphereShape*>(shapeB), collisions);
} else if (typeB == Shape::CAPSULE_SHAPE) {
return sphereCapsule(sphereA, static_cast<const CapsuleShape*>(shapeB), collisions);
} else if (typeB == Shape::PLANE_SHAPE) {
return spherePlane(sphereA, static_cast<const PlaneShape*>(shapeB), collisions);
}
} else if (typeA == Shape::CAPSULE_SHAPE) {
const CapsuleShape* capsuleA = static_cast<const CapsuleShape*>(shapeA);
@ -41,6 +43,17 @@ bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& coll
return capsuleSphere(capsuleA, static_cast<const SphereShape*>(shapeB), collisions);
} else if (typeB == Shape::CAPSULE_SHAPE) {
return capsuleCapsule(capsuleA, static_cast<const CapsuleShape*>(shapeB), collisions);
} else if (typeB == Shape::PLANE_SHAPE) {
return capsulePlane(capsuleA, static_cast<const PlaneShape*>(shapeB), collisions);
}
} else if (typeA == Shape::PLANE_SHAPE) {
const PlaneShape* planeA = static_cast<const PlaneShape*>(shapeA);
if (typeB == Shape::SPHERE_SHAPE) {
return planeSphere(planeA, static_cast<const SphereShape*>(shapeB), collisions);
} else if (typeB == Shape::CAPSULE_SHAPE) {
return planeCapsule(planeA, static_cast<const CapsuleShape*>(shapeB), collisions);
} else if (typeB == Shape::PLANE_SHAPE) {
return planePlane(planeA, static_cast<const PlaneShape*>(shapeB), collisions);
}
} else if (typeA == Shape::LIST_SHAPE) {
const ListShape* listA = static_cast<const ListShape*>(shapeA);
@ -48,6 +61,8 @@ bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& coll
return listSphere(listA, static_cast<const SphereShape*>(shapeB), collisions);
} else if (typeB == Shape::CAPSULE_SHAPE) {
return listCapsule(listA, static_cast<const CapsuleShape*>(shapeB), collisions);
} else if (typeB == Shape::PLANE_SHAPE) {
return listPlane(listA, static_cast<const PlaneShape*>(shapeB), collisions);
}
}
return false;
@ -168,6 +183,20 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
return false;
}
bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions) {
glm::vec3 penetration;
if (findSpherePlanePenetration(sphereA->getPosition(), sphereA->getRadius(), planeB->getCoefficients(), penetration)) {
CollisionInfo* collision = collisions.getNewCollision();
if (!collision) {
return false; // collision list is full
}
collision->_penetration = penetration;
collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * glm::normalize(penetration);
return true;
}
return false;
}
bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions) {
// find sphereB's closest approach to axis of capsuleA
glm::vec3 AB = capsuleA->getPosition() - sphereB->getPosition();
@ -374,6 +403,63 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB,
return false;
}
bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions) {
glm::vec3 start, end, penetration;
capsuleA->getStartPoint(start);
capsuleA->getEndPoint(end);
glm::vec4 plane = planeB->getCoefficients();
if (findCapsulePlanePenetration(start, end, capsuleA->getRadius(), plane, penetration)) {
CollisionInfo* collision = collisions.getNewCollision();
if (!collision) {
return false; // collision list is full
}
collision->_penetration = penetration;
glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end;
collision->_contactPoint = deepestEnd + capsuleA->getRadius() * glm::normalize(penetration);
return true;
}
return false;
}
bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions) {
glm::vec3 penetration;
if (findSpherePlanePenetration(sphereB->getPosition(), sphereB->getRadius(), planeA->getCoefficients(), penetration)) {
CollisionInfo* collision = collisions.getNewCollision();
if (!collision) {
return false; // collision list is full
}
collision->_penetration = -penetration;
collision->_contactPoint = sphereB->getPosition() +
(sphereB->getRadius() / glm::length(penetration) - 1.0f) * penetration;
return true;
}
return false;
}
bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions) {
glm::vec3 start, end, penetration;
capsuleB->getStartPoint(start);
capsuleB->getEndPoint(end);
glm::vec4 plane = planeA->getCoefficients();
if (findCapsulePlanePenetration(start, end, capsuleB->getRadius(), plane, penetration)) {
CollisionInfo* collision = collisions.getNewCollision();
if (!collision) {
return false; // collision list is full
}
collision->_penetration = -penetration;
glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end;
collision->_contactPoint = deepestEnd + (capsuleB->getRadius() / glm::length(penetration) - 1.0f) * penetration;
return true;
}
return false;
}
bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions) {
// technically, planes always collide unless they're parallel and not coincident; however, that's
// not going to give us any useful information
return false;
}
bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionList& collisions) {
bool touching = false;
for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) {
@ -383,6 +469,8 @@ bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionLis
touching = sphereSphere(sphereA, static_cast<const SphereShape*>(subShape), collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = sphereCapsule(sphereA, static_cast<const CapsuleShape*>(subShape), collisions) || touching;
} else if (subType == Shape::PLANE_SHAPE) {
touching = spherePlane(sphereA, static_cast<const PlaneShape*>(subShape), collisions) || touching;
}
}
return touching;
@ -397,6 +485,24 @@ bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, Collision
touching = capsuleSphere(capsuleA, static_cast<const SphereShape*>(subShape), collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = capsuleCapsule(capsuleA, static_cast<const CapsuleShape*>(subShape), collisions) || touching;
} else if (subType == Shape::PLANE_SHAPE) {
touching = capsulePlane(capsuleA, static_cast<const PlaneShape*>(subShape), collisions) || touching;
}
}
return touching;
}
bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions) {
bool touching = false;
for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) {
const Shape* subShape = listB->getSubShape(i);
int subType = subShape->getType();
if (subType == Shape::SPHERE_SHAPE) {
touching = planeSphere(planeA, static_cast<const SphereShape*>(subShape), collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = planeCapsule(planeA, static_cast<const CapsuleShape*>(subShape), collisions) || touching;
} else if (subType == Shape::PLANE_SHAPE) {
touching = planePlane(planeA, static_cast<const PlaneShape*>(subShape), collisions) || touching;
}
}
return touching;
@ -411,6 +517,8 @@ bool listSphere(const ListShape* listA, const SphereShape* sphereB, CollisionLis
touching = sphereSphere(static_cast<const SphereShape*>(subShape), sphereB, collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = capsuleSphere(static_cast<const CapsuleShape*>(subShape), sphereB, collisions) || touching;
} else if (subType == Shape::PLANE_SHAPE) {
touching = planeSphere(static_cast<const PlaneShape*>(subShape), sphereB, collisions) || touching;
}
}
return touching;
@ -425,6 +533,24 @@ bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, Collision
touching = sphereCapsule(static_cast<const SphereShape*>(subShape), capsuleB, collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = capsuleCapsule(static_cast<const CapsuleShape*>(subShape), capsuleB, collisions) || touching;
} else if (subType == Shape::PLANE_SHAPE) {
touching = planeCapsule(static_cast<const PlaneShape*>(subShape), capsuleB, collisions) || touching;
}
}
return touching;
}
bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions) {
bool touching = false;
for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) {
const Shape* subShape = listA->getSubShape(i);
int subType = subShape->getType();
if (subType == Shape::SPHERE_SHAPE) {
touching = spherePlane(static_cast<const SphereShape*>(subShape), planeB, collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = capsulePlane(static_cast<const CapsuleShape*>(subShape), planeB, collisions) || touching;
} else if (subType == Shape::PLANE_SHAPE) {
touching = planePlane(static_cast<const PlaneShape*>(subShape), planeB, collisions) || touching;
}
}
return touching;

View file

@ -15,6 +15,7 @@
#include "CapsuleShape.h"
#include "CollisionInfo.h"
#include "ListShape.h"
#include "PlaneShape.h"
#include "SharedUtil.h"
#include "SphereShape.h"
@ -44,6 +45,12 @@ namespace ShapeCollider {
/// \return true if shapes collide
bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions);
/// \param sphereA pointer to first shape
/// \param planeB pointer to second shape
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions);
/// \param capsuleA pointer to first shape
/// \param sphereB pointer to second shape
/// \param[out] collisions where to append collision details
@ -56,6 +63,30 @@ namespace ShapeCollider {
/// \return true if shapes collide
bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionList& collisions);
/// \param capsuleA pointer to first shape
/// \param planeB pointer to second shape
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions);
/// \param planeA pointer to first shape
/// \param sphereB pointer to second shape
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions);
/// \param planeA pointer to first shape
/// \param capsuleB pointer to second shape
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions);
/// \param planeA pointer to first shape
/// \param planeB pointer to second shape
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions);
/// \param sphereA pointer to first shape
/// \param listB pointer to second shape
/// \param[out] collisions where to append collision details
@ -68,6 +99,12 @@ namespace ShapeCollider {
/// \return true if shapes collide
bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, CollisionList& collisions);
/// \param planeA pointer to first shape
/// \param listB pointer to second shape
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions);
/// \param listA pointer to first shape
/// \param sphereB pointer to second shape
/// \param[out] collisions where to append collision details
@ -80,6 +117,12 @@ namespace ShapeCollider {
/// \return true if shapes collide
bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, CollisionList& collisions);
/// \param listA pointer to first shape
/// \param planeB pointer to second shape
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions);
/// \param listA pointer to first shape
/// \param capsuleB pointer to second shape
/// \param[out] collisions where to append collision details