mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 19:55:07 +02:00
Sphere/box collisions for voxels, various utility functions.
This commit is contained in:
parent
aae6d0cf3c
commit
70b25fd6c4
7 changed files with 234 additions and 33 deletions
|
@ -615,15 +615,19 @@ void Avatar::updateCollisionWithEnvironment() {
|
|||
void Avatar::updateCollisionWithVoxels() {
|
||||
float radius = _height * 0.125f;
|
||||
glm::vec3 penetration;
|
||||
if (Application::getInstance()->getVoxels()->findCapsulePenetration(
|
||||
_position - glm::vec3(0.0f, _pelvisFloatingHeight - radius, 0.0f),
|
||||
_position + glm::vec3(0.0f, _height - _pelvisFloatingHeight - radius, 0.0f), radius, penetration)) {
|
||||
//if (Application::getInstance()->getVoxels()->findCapsulePenetration(
|
||||
// _position - glm::vec3(0.0f, _pelvisFloatingHeight - radius, 0.0f),
|
||||
// _position + glm::vec3(0.0f, _height - _pelvisFloatingHeight - radius, 0.0f), radius, penetration)) {
|
||||
// applyCollisionWithScene(penetration);
|
||||
//}
|
||||
|
||||
if (Application::getInstance()->getVoxels()->findSpherePenetration(_position, _height/2, penetration)) {
|
||||
applyCollisionWithScene(penetration);
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::applyCollisionWithScene(const glm::vec3& penetration) {
|
||||
_position += penetration;
|
||||
_position -= penetration;
|
||||
|
||||
// reflect the velocity component in the direction of penetration
|
||||
glm::vec3 direction = glm::normalize(penetration);
|
||||
|
|
|
@ -101,13 +101,7 @@ const EnvironmentData Environment::getClosestData(const glm::vec3& position) {
|
|||
bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end,
|
||||
float radius, glm::vec3& penetration) {
|
||||
// collide with the "floor"
|
||||
bool found = false;
|
||||
penetration = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
float floorDist = qMin(start.y, end.y) - radius;
|
||||
if (floorDist < 0.0f) {
|
||||
penetration.y = -floorDist;
|
||||
found = true;
|
||||
}
|
||||
bool found = findCapsulePlanePenetration(start, end, radius, glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), penetration);
|
||||
|
||||
// get the lock for the duration of the call
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
@ -117,11 +111,10 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
|
|||
if (environmentData.getGravity() == 0.0f) {
|
||||
continue; // don't bother colliding with gravity-less environments
|
||||
}
|
||||
glm::vec3 vector = computeVectorFromPointToSegment(environmentData.getAtmosphereCenter(), start, end);
|
||||
float vectorLength = glm::length(vector);
|
||||
float distance = vectorLength - environmentData.getAtmosphereInnerRadius() - radius;
|
||||
if (distance < 0.0f) {
|
||||
penetration += vector * (-distance / vectorLength);
|
||||
glm::vec3 environmentPenetration;
|
||||
if (findCapsuleSpherePenetration(start, end, radius, environmentData.getAtmosphereCenter(),
|
||||
environmentData.getAtmosphereInnerRadius(), environmentPenetration)) {
|
||||
penetration = addPenetrations(penetration, environmentPenetration);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "SharedUtil.h"
|
||||
|
||||
#include "AABox.h"
|
||||
#include "GeometryUtil.h"
|
||||
|
||||
|
||||
void AABox::scale(float scale) {
|
||||
|
@ -82,6 +83,17 @@ bool AABox::contains(const glm::vec3& point) const {
|
|||
isWithin(point.z, _corner.z, _size.z);
|
||||
}
|
||||
|
||||
// determines whether a value is within the expanded extents
|
||||
static bool isWithinExpanded(float value, float corner, float size, float expansion) {
|
||||
return value >= corner - expansion && value <= corner + size + expansion;
|
||||
}
|
||||
|
||||
bool AABox::expandedContains(const glm::vec3& point, float expansion) const {
|
||||
return isWithinExpanded(point.x, _corner.x, _size.x, expansion) &&
|
||||
isWithinExpanded(point.y, _corner.y, _size.y, expansion) &&
|
||||
isWithinExpanded(point.z, _corner.z, _size.z, expansion);
|
||||
}
|
||||
|
||||
// finds the intersection between a ray and the facing plane on one axis
|
||||
static bool findIntersection(float origin, float direction, float corner, float size, float& distance) {
|
||||
if (direction > EPSILON) {
|
||||
|
@ -126,3 +138,61 @@ bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AABox::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const {
|
||||
glm::vec4 center4 = glm::vec4(center, 1.0f);
|
||||
|
||||
float minPenetrationLength = FLT_MAX;
|
||||
for (int i = 0; i < FACE_COUNT; i++) {
|
||||
glm::vec3 vector = getClosestPointOnFace(center, (BoxFace)i) - center;
|
||||
if (glm::dot(center4, getPlane((BoxFace)i)) >= 0.0f) {
|
||||
return ::findSpherePenetration(vector, radius, penetration);
|
||||
}
|
||||
float vectorLength = glm::length(vector);
|
||||
if (vectorLength < minPenetrationLength) {
|
||||
penetration = vector * ((vectorLength + radius) / -vectorLength);
|
||||
minPenetrationLength = vectorLength;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) const {
|
||||
switch (face) {
|
||||
case MIN_X_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
|
||||
case MAX_X_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x + _size.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
|
||||
case MIN_Y_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y, _corner.z + _size.z));
|
||||
|
||||
case MAX_Y_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y + _size.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
|
||||
case MIN_Z_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z));
|
||||
|
||||
case MAX_Z_FACE:
|
||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _size.z),
|
||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec4 AABox::getPlane(BoxFace face) const {
|
||||
switch (face) {
|
||||
case MIN_X_FACE: return glm::vec4(-1.0f, 0.0f, 0.0f, _corner.x);
|
||||
case MAX_X_FACE: return glm::vec4(1.0f, 0.0f, 0.0f, -_corner.x - _size.x);
|
||||
case MIN_Y_FACE: return glm::vec4(0.0f, -1.0f, 0.0f, _corner.y);
|
||||
case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _size.y);
|
||||
case MIN_Z_FACE: return glm::vec4(0.0f, 0.0f, -1.0f, _corner.z);
|
||||
case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _size.z);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ enum BoxFace {
|
|||
MIN_Y_FACE,
|
||||
MAX_Y_FACE,
|
||||
MIN_Z_FACE,
|
||||
MAX_Z_FACE
|
||||
MAX_Z_FACE,
|
||||
FACE_COUNT
|
||||
};
|
||||
|
||||
class AABox
|
||||
|
@ -46,9 +47,15 @@ public:
|
|||
const glm::vec3& getCenter() const { return _center; };
|
||||
|
||||
bool contains(const glm::vec3& point) const;
|
||||
bool expandedContains(const glm::vec3& point, float expansion) const;
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
|
||||
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||
glm::vec4 getPlane(BoxFace face) const;
|
||||
|
||||
glm::vec3 _corner;
|
||||
glm::vec3 _center;
|
||||
glm::vec3 _size;
|
||||
|
|
|
@ -7,7 +7,36 @@
|
|||
|
||||
#include "GeometryUtil.h"
|
||||
|
||||
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
|
||||
bool findSpherePenetration(const glm::vec3& penetratorToPenetratee, float combinedRadius, glm::vec3& penetration) {
|
||||
float vectorLength = glm::length(penetratorToPenetratee);
|
||||
float distance = vectorLength - combinedRadius;
|
||||
if (distance < 0.0f) {
|
||||
penetration = penetratorToPenetratee * (-distance / vectorLength);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool findSpherePointPenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec3& penetrateeLocation, glm::vec3& penetration) {
|
||||
return findSpherePenetration(penetrateeLocation - penetratorCenter, penetratorRadius, penetration);
|
||||
}
|
||||
|
||||
bool findSphereSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec3& penetrateeCenter, float penetrateeRadius, glm::vec3& penetration) {
|
||||
return findSpherePointPenetration(penetratorCenter, penetratorRadius + penetrateeRadius, penetrateeCenter, penetration);
|
||||
}
|
||||
|
||||
bool findSphereLinePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec3& penetrateeOrigin, const glm::vec3& penetrateeDirection,
|
||||
glm::vec3& penetration) {
|
||||
// compute the projection of the penetrator vector onto the line
|
||||
float proj = glm::dot(penetratorCenter - penetrateeOrigin, penetrateeDirection);
|
||||
return findSpherePenetration((penetrateeOrigin + penetrateeDirection*proj) - penetratorCenter,
|
||||
penetratorRadius, penetration);
|
||||
}
|
||||
|
||||
static glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
|
||||
// compute the projection of the point vector onto the segment vector
|
||||
glm::vec3 segmentVector = end - start;
|
||||
float proj = glm::dot(point - start, segmentVector) / glm::dot(segmentVector, segmentVector);
|
||||
|
@ -21,3 +50,74 @@ glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec
|
|||
return start + segmentVector*proj - point;
|
||||
}
|
||||
}
|
||||
|
||||
bool findSphereSegmentPenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec3& penetrateeStart, const glm::vec3& penetrateeEnd, glm::vec3& penetration) {
|
||||
return findSpherePenetration(computeVectorFromPointToSegment(penetratorCenter, penetrateeStart, penetrateeEnd),
|
||||
penetratorRadius, penetration);
|
||||
}
|
||||
|
||||
bool findSphereCapsulePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, const glm::vec3& penetrateeStart,
|
||||
const glm::vec3& penetrateeEnd, float penetrateeRadius, glm::vec3& penetration) {
|
||||
return findSphereSegmentPenetration(penetratorCenter, penetratorRadius + penetrateeRadius,
|
||||
penetrateeStart, penetrateeEnd, penetration);
|
||||
}
|
||||
|
||||
bool findSpherePlanePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec4& penetrateePlane, glm::vec3& penetration) {
|
||||
float distance = glm::dot(penetrateePlane, glm::vec4(penetratorCenter, 1.0f)) - penetratorRadius;
|
||||
if (distance < 0.0f) {
|
||||
penetration = glm::vec3(penetrateePlane) * distance;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool findCapsulePointPenetration(const glm::vec3& penetratorStart, const glm::vec3& penetratorEnd, float penetratorRadius,
|
||||
const glm::vec3& penetrateeLocation, glm::vec3& penetration) {
|
||||
if (findSphereSegmentPenetration(penetrateeLocation, penetratorRadius, penetratorStart, penetratorEnd, penetration)) {
|
||||
penetration = -penetration;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool findCapsuleSpherePenetration(const glm::vec3& penetratorStart, const glm::vec3& penetratorEnd, float penetratorRadius,
|
||||
const glm::vec3& penetrateeCenter, float penetrateeRadius, glm::vec3& penetration) {
|
||||
if (findSphereCapsulePenetration(penetrateeCenter, penetrateeRadius,
|
||||
penetratorStart, penetratorEnd, penetratorRadius, penetration)) {
|
||||
penetration = -penetration;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool findCapsulePlanePenetration(const glm::vec3& penetratorStart, const glm::vec3& penetratorEnd, float penetratorRadius,
|
||||
const glm::vec4& penetrateePlane, glm::vec3& penetration) {
|
||||
float distance = glm::min(glm::dot(penetrateePlane, glm::vec4(penetratorStart, 1.0f)),
|
||||
glm::dot(penetrateePlane, glm::vec4(penetratorEnd, 1.0f))) - penetratorRadius;
|
||||
if (distance < 0.0f) {
|
||||
penetration = glm::vec3(penetrateePlane) * distance;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration) {
|
||||
// find the component of the new penetration in the direction of the current
|
||||
float currentLength = glm::length(currentPenetration);
|
||||
if (currentLength == 0.0f) {
|
||||
return newPenetration;
|
||||
}
|
||||
glm::vec3 currentDirection = currentPenetration / currentLength;
|
||||
float directionalComponent = glm::dot(newPenetration, currentDirection);
|
||||
|
||||
// if orthogonal or in the opposite direction, we can simply add
|
||||
if (directionalComponent <= 0.0f) {
|
||||
return currentPenetration + newPenetration;
|
||||
}
|
||||
|
||||
// otherwise, we need to take the maximum component of current and new
|
||||
return currentDirection * glm::max(directionalComponent, currentLength) +
|
||||
newPenetration - (currentDirection * directionalComponent);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,35 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end);
|
||||
bool findSpherePenetration(const glm::vec3& penetratorToPenetratee, float combinedRadius, glm::vec3& penetration);
|
||||
|
||||
bool findSpherePointPenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec3& penetrateeLocation, glm::vec3& penetration);
|
||||
|
||||
bool findSphereSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec3& penetrateeCenter, float penetrateeRadius, glm::vec3& penetration);
|
||||
|
||||
bool findSphereLinePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec3& penetrateeOrigin, const glm::vec3& penetrateeDirection, glm::vec3& penetration);
|
||||
|
||||
bool findSphereSegmentPenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec3& penetrateeStart, const glm::vec3& penetrateeEnd, glm::vec3& penetration);
|
||||
|
||||
bool findSphereCapsulePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, const glm::vec3& penetrateeStart,
|
||||
const glm::vec3& penetrateeEnd, float penetrateeRadius, glm::vec3& penetration);
|
||||
|
||||
bool findSpherePlanePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
const glm::vec4& penetrateePlane, glm::vec3& penetration);
|
||||
|
||||
bool findCapsulePointPenetration(const glm::vec3& penetratorStart, const glm::vec3& penetratorEnd, float penetratorRadius,
|
||||
const glm::vec3& penetrateeLocation, glm::vec3& penetration);
|
||||
|
||||
bool findCapsuleSpherePenetration(const glm::vec3& penetratorStart, const glm::vec3& penetratorEnd, float penetratorRadius,
|
||||
const glm::vec3& penetrateeCenter, float penetrateeRadius, glm::vec3& penetration);
|
||||
|
||||
bool findCapsulePlanePenetration(const glm::vec3& penetratorStart, const glm::vec3& penetratorEnd, float penetratorRadius,
|
||||
const glm::vec4& penetrateePlane, glm::vec3& penetration);
|
||||
|
||||
glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration);
|
||||
|
||||
#endif /* defined(__interface__GeometryUtil__) */
|
||||
|
|
|
@ -716,20 +716,20 @@ public:
|
|||
bool findSpherePenetrationOp(VoxelNode* node, void* extraData) {
|
||||
SphereArgs* args = static_cast<SphereArgs*>(extraData);
|
||||
|
||||
// currently, we treat each node as a sphere enveloping the box
|
||||
const glm::vec3& nodeCenter = node->getCenter();
|
||||
glm::vec3 vector = args->center - nodeCenter;
|
||||
float vectorLength = glm::length(vector);
|
||||
float distance = vectorLength - node->getEnclosingRadius() - args->radius;
|
||||
if (distance >= 0.0f) {
|
||||
// coarse check against bounds
|
||||
const AABox& box = node->getAABox();
|
||||
if (!box.expandedContains(args->center, args->radius)) {
|
||||
return false;
|
||||
}
|
||||
if (!node->isLeaf()) {
|
||||
return true; // recurse on children
|
||||
}
|
||||
if (node->isColored()) {
|
||||
args->penetration += vector * (-distance * TREE_SCALE / vectorLength);
|
||||
args->found = true;
|
||||
glm::vec3 nodePenetration;
|
||||
if (box.findSpherePenetration(args->center, args->radius, nodePenetration)) {
|
||||
args->penetration = addPenetrations(args->penetration, nodePenetration * (float)TREE_SCALE);
|
||||
args->found = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -754,18 +754,16 @@ bool findCapsulePenetrationOp(VoxelNode* node, void* extraData) {
|
|||
CapsuleArgs* args = static_cast<CapsuleArgs*>(extraData);
|
||||
|
||||
// currently, we treat each node as a sphere enveloping the box
|
||||
const glm::vec3& nodeCenter = node->getCenter();
|
||||
glm::vec3 vector = computeVectorFromPointToSegment(nodeCenter, args->start, args->end);
|
||||
float vectorLength = glm::length(vector);
|
||||
float distance = vectorLength - node->getEnclosingRadius() - args->radius;
|
||||
if (distance >= 0.0f) {
|
||||
glm::vec3 nodePenetration;
|
||||
if (!findCapsuleSpherePenetration(args->start, args->end, args->radius, node->getCenter(),
|
||||
node->getEnclosingRadius(), nodePenetration)) {
|
||||
return false;
|
||||
}
|
||||
if (!node->isLeaf()) {
|
||||
return true; // recurse on children
|
||||
}
|
||||
if (node->isColored()) {
|
||||
args->penetration += vector * (-distance * TREE_SCALE / vectorLength);
|
||||
args->penetration = addPenetrations(args->penetration, nodePenetration * (float)TREE_SCALE);
|
||||
args->found = true;
|
||||
}
|
||||
return false;
|
||||
|
|
Loading…
Reference in a new issue