Merge branch 'core' of https://github.com/highfidelity/hifi into punk

This commit is contained in:
Sam Gateau 2015-08-04 16:31:33 -07:00
commit d2360d1406
40 changed files with 318 additions and 2814 deletions

View file

@ -169,9 +169,6 @@ public:
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
AvatarMotionState* getMotionState() { return _motionState; }
signals:
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
protected:
SkeletonModel _skeletonModel;
glm::vec3 _skeletonOffset;

View file

@ -29,7 +29,6 @@
#include <udt/PacketHeaders.h>
#include <PathUtils.h>
#include <PerfStat.h>
#include <ShapeCollider.h>
#include <SharedUtil.h>
#include <TextRenderer3D.h>
#include <UserActivityLogger.h>
@ -104,7 +103,6 @@ MyAvatar::MyAvatar(RigPointer rig) :
_rig(rig),
_prevShouldDrawHead(true)
{
ShapeCollider::initDispatchTable();
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
_driveKeys[i] = 0.0f;
}

View file

@ -24,7 +24,8 @@
ControllerScriptingInterface::ControllerScriptingInterface() :
_mouseCaptured(false),
_touchCaptured(false),
_wheelCaptured(false)
_wheelCaptured(false),
_actionsCaptured(false)
{
}

View file

@ -58,15 +58,18 @@ void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) {
void Rig::startAnimation(const QString& url, float fps, float priority,
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
//qCDebug(animation) << "startAnimation" << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints;
// This is different than startAnimationByRole, in which we use the existing values if the animation already exists.
// Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted).
AnimationHandlePointer handle = nullptr;
foreach (const AnimationHandlePointer& candidate, _animationHandles) {
if (candidate->getURL() == url) {
candidate->start();
return;
handle = candidate;
}
}
AnimationHandlePointer handle = createAnimationHandle();
handle->setURL(url);
if (!handle) {
handle = createAnimationHandle();
handle->setURL(url);
}
handle->setFPS(fps);
handle->setPriority(priority);
handle->setLoop(loop);
@ -90,8 +93,34 @@ void Rig::addAnimationByRole(const QString& role, const QString& url, float fps,
}
}
AnimationHandlePointer handle = createAnimationHandle();
QString standard = "";
if (url.isEmpty()) { // Default animations for fight club
const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/";
if (role == "walk") {
standard = base + "support/FightClubBotTest1/Animations/standard_walk.fbx";
lastFrame = 60;
} else if (role == "leftTurn") {
standard = base + "support/FightClubBotTest1/Animations/left_turn_noHipRotation.fbx";
lastFrame = 29;
} else if (role == "rightTurn") {
standard = base + "support/FightClubBotTest1/Animations/right_turn_noHipRotation.fbx";
lastFrame = 31;
} else if (role == "leftStrafe") {
standard = base + "animations/fightclub_bot_anims/side_step_left_inPlace.fbx";
lastFrame = 31;
} else if (role == "rightStrafe") {
standard = base + "animations/fightclub_bot_anims/side_step_right_inPlace.fbx";
lastFrame = 31;
} else if (role == "idle") {
standard = base + "support/FightClubBotTest1/Animations/standard_idle.fbx";
fps = 25.0f;
}
if (!standard.isEmpty()) {
loop = true;
}
}
handle->setRole(role);
handle->setURL(url);
handle->setURL(url.isEmpty() ? standard : url);
handle->setFPS(fps);
handle->setPriority(priority);
handle->setLoop(loop);
@ -135,6 +164,14 @@ void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) {
bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.contains(animationHandle);
}
bool Rig::isRunningRole(const QString& role) { //obviously, there are more efficient ways to do this
for (auto animation : _runningAnimations) {
if (animation->getRole() == role) {
return true;
}
}
return false;
}
void Rig::deleteAnimations() {
for (auto animation : _animationHandles) {
@ -356,37 +393,38 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const {
}
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
if (_enableRig) {
glm::vec3 front = worldRotation * IDENTITY_FRONT;
float forwardSpeed = glm::dot(worldVelocity, front);
float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime;
bool isWalking = std::abs(forwardSpeed) > 0.01f;
bool isTurning = std::abs(rotationalSpeed) > 0.5f;
// Crude, until we have blending:
isTurning = isTurning && !isWalking; // Only one of walk/turn, walk wins.
isTurning = false; // FIXME
bool isIdle = !isWalking && !isTurning;
auto singleRole = [](bool walking, bool turning, bool idling) {
return walking ? "walk" : (turning ? "turn" : (idling ? "idle" : ""));
};
QString toStop = singleRole(_isWalking && !isWalking, _isTurning && !isTurning, _isIdle && !isIdle);
if (!toStop.isEmpty()) {
//qCDebug(animation) << "isTurning" << isTurning << "fronts" << front << _lastFront << glm::angle(front, _lastFront) << rotationalSpeed;
stopAnimationByRole(toStop);
}
QString newRole = singleRole(isWalking && !_isWalking, isTurning && !_isTurning, isIdle && !_isIdle);
if (!newRole.isEmpty()) {
startAnimationByRole(newRole);
qCDebug(animation) << deltaTime << ":" << worldVelocity << "." << front << "=> " << forwardSpeed << newRole;
}
_lastPosition = worldPosition;
_lastFront = front;
_isWalking = isWalking;
_isTurning = isTurning;
_isIdle = isIdle;
if (!_enableRig) {
return;
}
bool isMoving = false;
glm::vec3 front = worldRotation * IDENTITY_FRONT;
float forwardSpeed = glm::dot(worldVelocity, front);
float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT);
float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime;
auto updateRole = [&](const QString& role, bool isOn) {
isMoving = isMoving || isOn;
if (isOn) {
if (!isRunningRole(role)) {
qCDebug(animation) << "Rig STARTING" << role;
startAnimationByRole(role);
}
} else {
if (isRunningRole(role)) {
qCDebug(animation) << "Rig stopping" << role;
stopAnimationByRole(role);
}
}
};
updateRole("walk", std::abs(forwardSpeed) > 0.01f);
bool isTurning = std::abs(rightTurningSpeed) > 0.5f;
updateRole("rightTurn", isTurning && (rightTurningSpeed > 0));
updateRole("leftTurn", isTurning && (rightTurningSpeed < 0));
bool isStrafing = std::abs(rightLateralSpeed) > 0.01f;
updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f));
updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f));
updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus.
_lastFront = front;
_lastPosition = worldPosition;
}
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {

View file

@ -76,6 +76,7 @@ public:
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
void addRunningAnimation(AnimationHandlePointer animationHandle);
bool isRunningAnimation(AnimationHandlePointer animationHandle);
bool isRunningRole(const QString& role); // There can be multiple animations per role, so this is more general than isRunningAnimation.
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
void deleteAnimations();
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
@ -161,9 +162,6 @@ public:
QList<AnimationHandlePointer> _runningAnimations;
bool _enableRig;
bool _isWalking;
bool _isTurning;
bool _isIdle;
glm::vec3 _lastFront;
glm::vec3 _lastPosition;
};

View file

@ -46,7 +46,6 @@ typedef unsigned long long quint64;
#include <QtScript/QScriptable>
#include <QReadWriteLock>
#include <CollisionInfo.h>
#include <NLPacket.h>
#include <Node.h>
#include <RegisteredMetaTypes.h>
@ -257,10 +256,6 @@ public:
const HeadData* getHeadData() const { return _headData; }
const HandData* getHandData() const { return _handData; }
virtual bool findSphereCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
return false;
}
bool hasIdentityChangedAfterParsing(NLPacket& packet);
QByteArray identityByteArray();

View file

@ -18,7 +18,6 @@
#include <glm/glm.hpp>
#include <AnimationCache.h> // for Animation, AnimationCache, and AnimationPointer classes
#include <CollisionInfo.h>
#include <Octree.h> // for EncodeBitstreamParams class
#include <OctreeElement.h> // for OctreeElement::AppendState
#include <OctreePacketData.h>

View file

@ -16,7 +16,6 @@
#include <QtCore/QObject>
#include <CollisionInfo.h>
#include <DependencyManager.h>
#include <Octree.h>
#include <OctreeScriptingInterface.h>

View file

@ -15,7 +15,7 @@
#include <QDebug>
#include <ByteCountCoding.h>
#include <PlaneShape.h>
#include <GeometryUtil.h>
#include "EntityTree.h"
#include "EntityTreeElement.h"
@ -128,46 +128,13 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
}
bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const {
RayIntersectionInfo rayInfo;
rayInfo._rayStart = origin;
rayInfo._rayDirection = direction;
rayInfo._rayLength = std::numeric_limits<float>::max();
PlaneShape plane;
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
glm::vec3 normal = getRotation() * UNROTATED_NORMAL;
plane.setNormal(normal);
plane.setPoint(getPosition()); // the position is definitely a point on our plane
bool intersects = plane.findRayIntersection(rayInfo);
if (intersects) {
glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance);
// now we know the point the ray hit our plane
glm::mat4 rotation = glm::mat4_cast(getRotation());
glm::mat4 translation = glm::translate(getPosition());
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 dimensions = getDimensions();
glm::vec3 registrationPoint = getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
AABox entityFrameBox(corner, dimensions);
glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
intersects = entityFrameBox.contains(entityFrameHitAt);
}
if (intersects) {
distance = rayInfo._hitDistance;
}
return intersects;
glm::vec3 dimensions = getDimensions();
glm::vec2 xyDimensions(dimensions.x, dimensions.y);
glm::quat rotation = getRotation();
glm::vec3 position = getPosition() + rotation *
(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT));
return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance);
}

View file

@ -13,7 +13,7 @@
#include <QDebug>
#include <ByteCountCoding.h>
#include <PlaneShape.h>
#include <GeometryUtil.h>
#include "EntityTree.h"
#include "EntityTreeElement.h"
@ -98,50 +98,17 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl);
}
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const {
RayIntersectionInfo rayInfo;
rayInfo._rayStart = origin;
rayInfo._rayDirection = direction;
rayInfo._rayLength = std::numeric_limits<float>::max();
PlaneShape plane;
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
glm::vec3 normal = getRotation() * UNROTATED_NORMAL;
plane.setNormal(normal);
plane.setPoint(getPosition()); // the position is definitely a point on our plane
bool intersects = plane.findRayIntersection(rayInfo);
if (intersects) {
glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance);
// now we know the point the ray hit our plane
glm::mat4 rotation = glm::mat4_cast(getRotation());
glm::mat4 translation = glm::translate(getPosition());
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 dimensions = getDimensions();
glm::vec3 registrationPoint = getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
AABox entityFrameBox(corner, dimensions);
glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
intersects = entityFrameBox.contains(entityFrameHitAt);
}
if (intersects) {
distance = rayInfo._hitDistance;
}
return intersects;
glm::vec3 dimensions = getDimensions();
glm::vec2 xyDimensions(dimensions.x, dimensions.y);
glm::quat rotation = getRotation();
glm::vec3 position = getPosition() + rotation *
(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT));
return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance);
}
void WebEntityItem::setSourceUrl(const QString& value) {
if (_sourceUrl != value) {
_sourceUrl = value;

View file

@ -28,7 +28,6 @@
#include <GLMHelpers.h>
#include <NumericalConstants.h>
#include <OctalCode.h>
#include <Shape.h>
#include <gpu/Format.h>
#include <LogHandler.h>

View file

@ -23,7 +23,6 @@
#include <NetworkAccessManager.h>
#include "FBXReader.h"
#include "OBJReader.h"
#include "Shape.h"
#include "ModelFormatLogging.h"

View file

@ -38,7 +38,6 @@
#include <OctalCode.h>
#include <udt/PacketHeaders.h>
#include <SharedUtil.h>
#include <Shape.h>
#include <PathUtils.h>
#include "CoverageMap.h"
@ -791,13 +790,6 @@ public:
bool found;
};
class ShapeArgs {
public:
const Shape* shape;
CollisionList& collisions;
bool found;
};
class ContentArgs {
public:
AACube cube;

View file

@ -19,7 +19,6 @@
#include <LogHandler.h>
#include <NodeList.h>
#include <PerfStat.h>
#include <AACubeShape.h>
#include "AACube.h"
#include "OctalCode.h"
@ -640,50 +639,7 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float
// otherwise, we need to find which of our children we should recurse
glm::vec3 ourCenter = _cube.calcCenter();
int childIndex = CHILD_UNKNOWN;
// left half
if (x > ourCenter.x) {
if (y > ourCenter.y) {
// top left
if (z > ourCenter.z) {
// top left far
childIndex = CHILD_TOP_LEFT_FAR;
} else {
// top left near
childIndex = CHILD_TOP_LEFT_NEAR;
}
} else {
// bottom left
if (z > ourCenter.z) {
// bottom left far
childIndex = CHILD_BOTTOM_LEFT_FAR;
} else {
// bottom left near
childIndex = CHILD_BOTTOM_LEFT_NEAR;
}
}
} else {
// right half
if (y > ourCenter.y) {
// top right
if (z > ourCenter.z) {
// top right far
childIndex = CHILD_TOP_RIGHT_FAR;
} else {
// top right near
childIndex = CHILD_TOP_RIGHT_NEAR;
}
} else {
// bottom right
if (z > ourCenter.z) {
// bottom right far
childIndex = CHILD_BOTTOM_RIGHT_FAR;
} else {
// bottom right near
childIndex = CHILD_BOTTOM_RIGHT_NEAR;
}
}
}
int childIndex = getMyChildContainingPoint(glm::vec3(x, y, z));
// Now, check if we have a child at that location
child = getChildAtIndex(childIndex);

View file

@ -24,7 +24,6 @@
#include "ViewFrustum.h"
#include "OctreeConstants.h"
class CollisionList;
class EncodeBitstreamParams;
class Octree;
class OctreeElement;

View file

@ -16,12 +16,9 @@
#include <glm/gtx/transform.hpp>
#include <glm/gtx/norm.hpp>
#include <CapsuleShape.h>
#include <GeometryUtil.h>
#include <PathUtils.h>
#include <PerfStat.h>
#include <ShapeCollider.h>
#include <SphereShape.h>
#include <ViewFrustum.h>
#include <render/Scene.h>
#include <gpu/Batch.h>

View file

@ -18,9 +18,6 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <CollisionInfo.h>
#include <RayIntersectionInfo.h>
class PhysicsEntity {
public:

View file

@ -20,7 +20,6 @@
#include <AudioConstants.h>
#include <AudioEffectOptions.h>
#include <AvatarData.h>
#include <CollisionInfo.h>
#include <EntityScriptingInterface.h>
#include <NetworkAccessManager.h>
#include <NodeList.h>
@ -366,8 +365,6 @@ void ScriptEngine::init() {
// constants
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
globalObject().setProperty("COLLISION_GROUP_ENVIRONMENT", newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT)));
globalObject().setProperty("COLLISION_GROUP_AVATARS", newVariant(QVariant(COLLISION_GROUP_AVATARS)));
}
QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {

View file

@ -1,76 +0,0 @@
//
// AACubeShape.cpp
// libraries/shared/src
//
// Created by Andrew Meadows on 2014.08.22
// 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 <glm/glm.hpp>
#include <glm/gtx/norm.hpp>
#include "AACubeShape.h"
#include "NumericalConstants.h" // for SQUARE_ROOT_OF_3
glm::vec3 faceNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) };
bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const {
// A = ray point
// B = cube center
glm::vec3 BA = _translation - intersection._rayStart;
// check for ray intersection with cube's bounding sphere
// a = distance along line to closest approach to B
float a = glm::dot(intersection._rayDirection, BA);
// b2 = squared distance from cube center to point of closest approach
float b2 = glm::length2(a * intersection._rayDirection - BA);
// r = bounding radius of cube
float halfSide = 0.5f * _scale;
const float r = SQUARE_ROOT_OF_3 * halfSide;
if (b2 > r * r) {
// line doesn't hit cube's bounding sphere
return false;
}
// check for tuncated/short ray
// maxLength = maximum possible distance between rayStart and center of cube
const float maxLength = glm::min(intersection._rayLength, intersection._hitDistance) + r;
if (a * a + b2 > maxLength * maxLength) {
// ray is not long enough to reach cube's bounding sphere
// NOTE: we don't fall in here when ray's length if FLT_MAX because maxLength^2 will be inf or nan
return false;
}
// the trivial checks have been exhausted, so must trace to each face
bool hit = false;
for (int i = 0; i < 3; ++i) {
for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) {
glm::vec3 faceNormal = sign * faceNormals[i];
float rayDotPlane = glm::dot(intersection._rayDirection, faceNormal);
if (glm::abs(rayDotPlane) > EPSILON) {
float distanceToFace = (halfSide + glm::dot(BA, faceNormal)) / rayDotPlane;
if (distanceToFace >= 0.0f) {
glm::vec3 point = distanceToFace * intersection._rayDirection - BA;
int j = (i + 1) % 3;
int k = (i + 2) % 3;
glm::vec3 secondNormal = faceNormals[j];
glm::vec3 thirdNormal = faceNormals[k];
if (glm::abs(glm::dot(point, secondNormal)) > halfSide ||
glm::abs(glm::dot(point, thirdNormal)) > halfSide) {
continue;
}
if (distanceToFace < intersection._hitDistance && distanceToFace < intersection._rayLength) {
intersection._hitDistance = distanceToFace;
intersection._hitNormal = faceNormal;
intersection._hitShape = const_cast<AACubeShape*>(this);
hit = true;
}
}
}
}
}
return hit;
}

View file

@ -1,50 +0,0 @@
//
// AACubeShape.h
// libraries/shared/src
//
// Created by Andrew Meadows on 2014.08.22
// 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_AACubeShape_h
#define hifi_AACubeShape_h
#include <QDebug>
#include "Shape.h"
class AACubeShape : public Shape {
public:
AACubeShape() : Shape(AACUBE_SHAPE), _scale(1.0f) { }
AACubeShape(float scale) : Shape(AACUBE_SHAPE), _scale(scale) { }
AACubeShape(float scale, const glm::vec3& position) : Shape(AACUBE_SHAPE, position), _scale(scale) { }
virtual ~AACubeShape() { }
float getScale() const { return _scale; }
void setScale(float scale) { _scale = scale; }
bool findRayIntersection(RayIntersectionInfo& intersection) const;
float getVolume() const { return _scale * _scale * _scale; }
virtual QDebug& dumpToDebug(QDebug& debugConext) const;
protected:
float _scale;
};
inline QDebug& AACubeShape::dumpToDebug(QDebug& debugConext) const {
debugConext << "AACubeShape[ ("
<< "type: " << getType()
<< "position: "
<< getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z
<< "scale: "
<< getScale()
<< "]";
return debugConext;
}
#endif // hifi_AACubeShape_h

View file

@ -1,218 +0,0 @@
//
// CapsuleShape.cpp
// libraries/shared/src
//
// Created by Andrew Meadows on 02/20/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 <iostream>
#include <glm/gtx/vector_angle.hpp>
#include "CapsuleShape.h"
#include "GeometryUtil.h"
#include "NumericalConstants.h"
CapsuleShape::CapsuleShape() : Shape(CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {}
CapsuleShape::CapsuleShape(float radius, float halfHeight) : Shape(CAPSULE_SHAPE),
_radius(radius), _halfHeight(halfHeight) {
updateBoundingRadius();
}
CapsuleShape::CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation) :
Shape(CAPSULE_SHAPE, position, rotation), _radius(radius), _halfHeight(halfHeight) {
updateBoundingRadius();
}
CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint) :
Shape(CAPSULE_SHAPE), _radius(radius), _halfHeight(0.0f) {
setEndPoints(startPoint, endPoint);
}
/// \param[out] startPoint is the center of start cap
void CapsuleShape::getStartPoint(glm::vec3& startPoint) const {
startPoint = _translation - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f);
}
/// \param[out] endPoint is the center of the end cap
void CapsuleShape::getEndPoint(glm::vec3& endPoint) const {
endPoint = _translation + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f);
}
void CapsuleShape::computeNormalizedAxis(glm::vec3& axis) const {
// default axis of a capsule is along the yAxis
axis = _rotation * DEFAULT_CAPSULE_AXIS;
}
void CapsuleShape::setRadius(float radius) {
_radius = radius;
updateBoundingRadius();
}
void CapsuleShape::setHalfHeight(float halfHeight) {
_halfHeight = halfHeight;
updateBoundingRadius();
}
void CapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) {
_radius = radius;
_halfHeight = halfHeight;
updateBoundingRadius();
}
void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) {
glm::vec3 axis = endPoint - startPoint;
_translation = 0.5f * (endPoint + startPoint);
float height = glm::length(axis);
if (height > EPSILON) {
_halfHeight = 0.5f * height;
axis /= height;
_rotation = computeNewRotation(axis);
}
updateBoundingRadius();
}
// helper
bool findRayIntersectionWithCap(const glm::vec3& sphereCenter, float sphereRadius,
const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) {
float r2 = sphereRadius * sphereRadius;
// compute closest approach (CA)
float a = glm::dot(sphereCenter - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA
float b2 = glm::distance2(sphereCenter, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA
if (b2 > r2) {
// ray does not hit sphere
return false;
}
float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection
float d2 = glm::distance2(intersection._rayStart, sphereCenter); // d2 = squared distance from sphere-center to ray-start
float distance = FLT_MAX;
if (a < 0.0f) {
// ray points away from sphere-center
if (d2 > r2) {
// ray starts outside sphere
return false;
}
// ray starts inside sphere
distance = c + a;
} else if (d2 > r2) {
// ray starts outside sphere
distance = a - c;
} else {
// ray starts inside sphere
distance = a + c;
}
if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) {
glm::vec3 sphereCenterToHitPoint = intersection._rayStart + distance * intersection._rayDirection - sphereCenter;
if (glm::dot(sphereCenterToHitPoint, sphereCenter - capsuleCenter) >= 0.0f) {
intersection._hitDistance = distance;
intersection._hitNormal = glm::normalize(sphereCenterToHitPoint);
return true;
}
}
return false;
}
bool CapsuleShape::findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const {
glm::vec3 capCenter;
getStartPoint(capCenter);
bool hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection);
getEndPoint(capCenter);
hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection) || hit;
if (hit) {
intersection._hitShape = const_cast<CapsuleShape*>(this);
}
return hit;
}
bool CapsuleShape::findRayIntersection(RayIntersectionInfo& intersection) const {
// ray is U, capsule is V
glm::vec3 axisV;
computeNormalizedAxis(axisV);
glm::vec3 centerV = getTranslation();
// first handle parallel case
float uDotV = glm::dot(axisV, intersection._rayDirection);
glm::vec3 UV = intersection._rayStart - centerV;
if (glm::abs(1.0f - glm::abs(uDotV)) < EPSILON) {
// line and cylinder are parallel
float distanceV = glm::dot(UV, intersection._rayDirection);
if (glm::length2(UV - distanceV * intersection._rayDirection) <= _radius * _radius) {
// ray is inside cylinder's radius and might intersect caps
return findRayIntersectionWithCaps(centerV, intersection);
}
return false;
}
// Given a line with point 'U' and normalized direction 'u' and
// a cylinder with axial point 'V', radius 'r', and normalized direction 'v'
// the intersection of the two is on the line at distance 't' from 'U'.
//
// Determining the values of t reduces to solving a quadratic equation: At^2 + Bt + C = 0
//
// where:
//
// UV = U-V
// w = u-(u.v)v
// Q = UV-(UV.v)v
//
// A = w^2
// B = 2(w.Q)
// C = Q^2 - r^2
glm::vec3 w = intersection._rayDirection - uDotV * axisV;
glm::vec3 Q = UV - glm::dot(UV, axisV) * axisV;
// we save a few multiplies by storing 2*A rather than just A
float A2 = 2.0f * glm::dot(w, w);
float B = 2.0f * glm::dot(w, Q);
// since C is only ever used once (in the determinant) we compute it inline
float determinant = B * B - 2.0f * A2 * (glm::dot(Q, Q) - _radius * _radius);
if (determinant < 0.0f) {
return false;
}
float hitLow = (-B - sqrtf(determinant)) / A2;
float hitHigh = -(hitLow + 2.0f * B / A2);
if (hitLow > hitHigh) {
// re-arrange so hitLow is always the smaller value
float temp = hitHigh;
hitHigh = hitLow;
hitLow = temp;
}
if (hitLow < 0.0f) {
if (hitHigh < 0.0f) {
// capsule is completely behind rayStart
return false;
}
hitLow = hitHigh;
}
glm::vec3 p = intersection._rayStart + hitLow * intersection._rayDirection;
float d = glm::dot(p - centerV, axisV);
if (glm::abs(d) <= getHalfHeight()) {
// we definitely hit the cylinder wall
intersection._hitDistance = hitLow;
intersection._hitNormal = glm::normalize(p - centerV - d * axisV);
intersection._hitShape = const_cast<CapsuleShape*>(this);
return true;
}
// ray still might hit the caps
return findRayIntersectionWithCaps(centerV, intersection);
}
// static
glm::quat CapsuleShape::computeNewRotation(const glm::vec3& newAxis) {
float angle = glm::angle(newAxis, DEFAULT_CAPSULE_AXIS);
if (angle > EPSILON) {
glm::vec3 rotationAxis = glm::normalize(glm::cross(DEFAULT_CAPSULE_AXIS, newAxis));
return glm::angleAxis(angle, rotationAxis);
}
return glm::quat();
}

View file

@ -1,62 +0,0 @@
//
// CapsuleShape.h
// libraries/shared/src
//
// Created by Andrew Meadows on 02/20/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_CapsuleShape_h
#define hifi_CapsuleShape_h
#include "NumericalConstants.h"
#include "Shape.h"
// default axis of CapsuleShape is Y-axis
const glm::vec3 DEFAULT_CAPSULE_AXIS(0.0f, 1.0f, 0.0f);
class CapsuleShape : public Shape {
public:
CapsuleShape();
CapsuleShape(float radius, float halfHeight);
CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation);
CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint);
virtual ~CapsuleShape() {}
float getRadius() const { return _radius; }
virtual float getHalfHeight() const { return _halfHeight; }
/// \param[out] startPoint is the center of start cap
virtual void getStartPoint(glm::vec3& startPoint) const;
/// \param[out] endPoint is the center of the end cap
virtual void getEndPoint(glm::vec3& endPoint) const;
virtual void computeNormalizedAxis(glm::vec3& axis) const;
void setRadius(float radius);
virtual void setHalfHeight(float height);
virtual void setRadiusAndHalfHeight(float radius, float height);
/// Sets the endpoints and updates center, rotation, and halfHeight to agree.
virtual void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint);
bool findRayIntersection(RayIntersectionInfo& intersection) const;
virtual float getVolume() const { return (PI * _radius * _radius) * (1.3333333333f * _radius + getHalfHeight()); }
protected:
bool findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const;
virtual void updateBoundingRadius() { _boundingRadius = _radius + getHalfHeight(); }
static glm::quat computeNewRotation(const glm::vec3& newAxis);
float _radius;
float _halfHeight;
};
#endif // hifi_CapsuleShape_h

View file

@ -1,110 +0,0 @@
//
// CollisionInfo.cpp
// libraries/shared/src
//
// Created by Andrew Meadows on 02/14/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 "CollisionInfo.h"
#include "NumericalConstants.h"
#include "Shape.h"
CollisionInfo::CollisionInfo() :
_data(NULL),
_intData(0),
_shapeA(NULL),
_shapeB(NULL),
_damping(0.0f),
_elasticity(1.0f),
_contactPoint(0.0f),
_penetration(0.0f),
_addedVelocity(0.0f) {
}
quint64 CollisionInfo::getShapePairKey() const {
if (_shapeB == NULL || _shapeA == NULL) {
// zero is an invalid key
return 0;
}
quint32 idA = _shapeA->getID();
quint32 idB = _shapeB->getID();
return idA < idB ? ((quint64)idA << 32) + (quint64)idB : ((quint64)idB << 32) + (quint64)idA;
}
CollisionList::CollisionList(int maxSize) :
_maxSize(maxSize),
_size(0) {
_collisions.resize(_maxSize);
}
void CollisionInfo::apply() {
assert(_shapeA);
// NOTE: Shape::computeEffectiveMass() has side effects: computes and caches partial Lagrangian coefficients
Shape* shapeA = const_cast<Shape*>(_shapeA);
float massA = shapeA->computeEffectiveMass(_penetration, _contactPoint);
float massB = MAX_SHAPE_MASS;
float totalMass = massA + massB;
if (_shapeB) {
Shape* shapeB = const_cast<Shape*>(_shapeB);
massB = shapeB->computeEffectiveMass(-_penetration, _contactPoint - _penetration);
totalMass = massA + massB;
if (totalMass < EPSILON) {
massA = massB = 1.0f;
totalMass = 2.0f;
}
// remember that _penetration points from A into B
shapeB->accumulateDelta(massA / totalMass, _penetration);
}
// NOTE: Shape::accumulateDelta() uses the coefficients from previous call to Shape::computeEffectiveMass()
// remember that _penetration points from A into B
shapeA->accumulateDelta(massB / totalMass, -_penetration);
}
CollisionInfo* CollisionList::getNewCollision() {
// return pointer to existing CollisionInfo, or NULL of list is full
return (_size < _maxSize) ? &(_collisions[_size++]) : NULL;
}
void CollisionList::deleteLastCollision() {
if (_size > 0) {
--_size;
}
}
CollisionInfo* CollisionList::getCollision(int index) {
return (index > -1 && index < _size) ? &(_collisions[index]) : NULL;
}
CollisionInfo* CollisionList::getLastCollision() {
return (_size > 0) ? &(_collisions[_size - 1]) : NULL;
}
void CollisionList::clear() {
// we rely on the external context to properly set or clear the data members of CollisionInfos
/*
for (int i = 0; i < _size; ++i) {
// we only clear the important stuff
CollisionInfo& collision = _collisions[i];
//collision._data = NULL;
//collision._intData = 0;
//collision._floatDAta = 0.0f;
//collision._vecData = glm::vec3(0.0f);
//collision._shapeA = NULL;
//collision._shapeB = NULL;
//collision._damping;
//collision._elasticity;
//collision._contactPoint;
//collision._penetration;
//collision._addedVelocity;
}
*/
_size = 0;
}
CollisionInfo* CollisionList::operator[](int index) {
return (index > -1 && index < _size) ? &(_collisions[index]) : NULL;
}

View file

@ -1,101 +0,0 @@
//
// CollisionInfo.h
// libraries/shared/src
//
// Created by Andrew Meadows on 02/14/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_CollisionInfo_h
#define hifi_CollisionInfo_h
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QtGlobal>
#include <QVector>
class Shape;
const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0;
const quint32 COLLISION_GROUP_AVATARS = 1U << 1;
const quint32 COLLISION_GROUP_VOXELS = 1U << 2;
const quint32 VALID_COLLISION_GROUPS = 0x0f;
// 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();
~CollisionInfo() {}
// TODO: Andrew to get rid of these data members
void* _data;
int _intData;
float _floatData;
glm::vec3 _vecData;
/// accumulates position changes for the shapes in this collision to resolve penetration
void apply();
Shape* getShapeA() const { return const_cast<Shape*>(_shapeA); }
Shape* getShapeB() const { return const_cast<Shape*>(_shapeB); }
/// \return unique key for shape pair
quint64 getShapePairKey() const;
const Shape* _shapeA; // pointer to shapeA in this collision
const Shape* _shapeB; // pointer to shapeB in this collision
void* _extraData; // pointer to extraData for this collision, opaque to the collision info, useful for external data
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();
/// \forget about collision at the end
void deleteLastCollision();
/// \return pointer to collision by index. NULL if index out of bounds.
CollisionInfo* getCollision(int index);
/// \return pointer to last collision on the list. NULL if list is empty
CollisionInfo* getLastCollision();
/// \return true if list is full
bool isFull() const { return _size == _maxSize; }
/// \return number of valid collisions
int size() const { return _size; }
/// Clear valid collisions.
void clear();
CollisionInfo* operator[](int index);
private:
int _maxSize; // the container cannot get larger than this
int _size; // the current number of valid collisions in the list
QVector<CollisionInfo> _collisions;
};
#endif // hifi_CollisionInfo_h

View file

@ -10,7 +10,6 @@ QSurfaceFormat getDefaultOpenGlSurfaceFormat() {
#ifdef DEBUG
format.setOption(QSurfaceFormat::DebugContext);
#endif
// FIXME move to core as soon as possible
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
return format;
}

View file

@ -11,11 +11,10 @@
#include <cstring>
#include <cmath>
#include <glm/gtx/quaternion.hpp>
#include "GeometryUtil.h"
#include "NumericalConstants.h"
#include "PlaneShape.h"
#include "RayIntersectionInfo.h"
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
@ -496,31 +495,45 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len
bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::quat& rotation,
const glm::vec3& position, const glm::vec2& dimensions, float& distance) {
RayIntersectionInfo rayInfo;
rayInfo._rayStart = origin;
rayInfo._rayDirection = direction;
rayInfo._rayLength = std::numeric_limits<float>::max();
PlaneShape plane;
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
glm::vec3 normal = rotation * UNROTATED_NORMAL;
plane.setNormal(normal);
plane.setPoint(position); // the position is definitely a point on our plane
bool intersects = plane.findRayIntersection(rayInfo);
bool maybeIntersects = false;
float denominator = glm::dot(normal, direction);
glm::vec3 offset = origin - position;
float normDotOffset = glm::dot(offset, normal);
float d = 0.0f;
if (fabsf(denominator) < EPSILON) {
// line is perpendicular to plane
if (fabsf(normDotOffset) < EPSILON) {
// ray starts on the plane
maybeIntersects = true;
if (intersects) {
distance = rayInfo._hitDistance;
glm::vec3 hitPosition = origin + (distance * direction);
glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position);
glm::vec2 halfDimensions = 0.5f * dimensions;
intersects = -halfDimensions.x <= localHitPosition.x && localHitPosition.x <= halfDimensions.x
&& -halfDimensions.y <= localHitPosition.y && localHitPosition.y <= halfDimensions.y;
// compute distance to closest approach
d = - glm::dot(offset, direction); // distance to closest approach of center of rectangle
if (d < 0.0f) {
// ray points away from center of rectangle, so ray's start is the closest approach
d = 0.0f;
}
}
} else {
d = - normDotOffset / denominator;
if (d > 0.0f) {
// ray points toward plane
maybeIntersects = true;
}
}
return intersects;
if (maybeIntersects) {
glm::vec3 hitPosition = origin + (d * direction);
glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position);
glm::vec2 halfDimensions = 0.5f * dimensions;
if (fabsf(localHitPosition.x) < halfDimensions.x && fabsf(localHitPosition.y) < halfDimensions.y) {
// only update distance on intersection
distance = d;
return true;
}
}
return false;
}

View file

@ -1,90 +0,0 @@
//
// ListShape.cpp
// libraries/shared/src
//
// Created by Andrew Meadows on 02/20/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 "ListShape.h"
// ListShapeEntry
void ListShapeEntry::updateTransform(const glm::vec3& rootPosition, const glm::quat& rootRotation) {
_shape->setTranslation(rootPosition + rootRotation * _localPosition);
_shape->setRotation(_localRotation * rootRotation);
}
// ListShape
ListShape::~ListShape() {
clear();
}
void ListShape::setTranslation(const glm::vec3& position) {
_subShapeTransformsAreDirty = true;
Shape::setTranslation(position);
}
void ListShape::setRotation(const glm::quat& rotation) {
_subShapeTransformsAreDirty = true;
Shape::setRotation(rotation);
}
const Shape* ListShape::getSubShape(int index) const {
if (index < 0 || index > _subShapeEntries.size()) {
return NULL;
}
return _subShapeEntries[index]._shape;
}
void ListShape::updateSubTransforms() {
if (_subShapeTransformsAreDirty) {
for (int i = 0; i < _subShapeEntries.size(); ++i) {
_subShapeEntries[i].updateTransform(_translation, _rotation);
}
_subShapeTransformsAreDirty = false;
}
}
void ListShape::addShape(Shape* shape, const glm::vec3& localPosition, const glm::quat& localRotation) {
if (shape) {
ListShapeEntry entry;
entry._shape = shape;
entry._localPosition = localPosition;
entry._localRotation = localRotation;
_subShapeEntries.push_back(entry);
}
}
void ListShape::setShapes(QVector<ListShapeEntry>& shapes) {
clear();
_subShapeEntries.swap(shapes);
// TODO: audit our new list of entries and delete any that have null pointers
computeBoundingRadius();
}
void ListShape::clear() {
// the ListShape owns its subShapes, so they need to be deleted
for (int i = 0; i < _subShapeEntries.size(); ++i) {
delete _subShapeEntries[i]._shape;
}
_subShapeEntries.clear();
setBoundingRadius(0.0f);
}
void ListShape::computeBoundingRadius() {
float maxRadius = 0.0f;
for (int i = 0; i < _subShapeEntries.size(); ++i) {
ListShapeEntry& entry = _subShapeEntries[i];
float radius = glm::length(entry._localPosition) + entry._shape->getBoundingRadius();
if (radius > maxRadius) {
maxRadius = radius;
}
}
setBoundingRadius(maxRadius);
}

View file

@ -1,72 +0,0 @@
//
// ListShape.h
// libraries/shared/src
//
// Created by Andrew Meadows on 02/20/2014.
// Copyright 2014 High Fidelity, Inc.
//
// ListShape: A collection of shapes, each with a local transform.
//
// 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_ListShape_h
#define hifi_ListShape_h
#include <QVector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/norm.hpp>
#include "Shape.h"
class ListShapeEntry {
public:
void updateTransform(const glm::vec3& position, const glm::quat& rotation);
Shape* _shape;
glm::vec3 _localPosition;
glm::quat _localRotation;
};
class ListShape : public Shape {
public:
ListShape() : Shape(LIST_SHAPE), _subShapeEntries(), _subShapeTransformsAreDirty(false) {}
ListShape(const glm::vec3& position, const glm::quat& rotation) :
Shape(LIST_SHAPE, position, rotation), _subShapeEntries(), _subShapeTransformsAreDirty(false) {}
~ListShape();
void setTranslation(const glm::vec3& position);
void setRotation(const glm::quat& rotation);
const Shape* getSubShape(int index) const;
void updateSubTransforms();
int size() const { return _subShapeEntries.size(); }
void addShape(Shape* shape, const glm::vec3& localPosition, const glm::quat& localRotation);
void setShapes(QVector<ListShapeEntry>& shapes);
// TODO: either implement this or remove ListShape altogether
bool findRayIntersection(RayIntersectionInfo& intersection) const { return false; }
protected:
void clear();
void computeBoundingRadius();
QVector<ListShapeEntry> _subShapeEntries;
bool _subShapeTransformsAreDirty;
private:
ListShape(const ListShape& otherList); // don't implement this
};
#endif // hifi_ListShape_h

View file

@ -1,78 +0,0 @@
//
// 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 "GLMHelpers.h"
#include "NumericalConstants.h"
#include "PlaneShape.h"
const glm::vec3 UNROTATED_NORMAL(0.0f, 1.0f, 0.0f);
PlaneShape::PlaneShape(const glm::vec4& coefficients) :
Shape(PLANE_SHAPE) {
glm::vec3 normal = glm::vec3(coefficients);
_translation = -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::vec3 PlaneShape::getNormal() const {
return _rotation * UNROTATED_NORMAL;
}
void PlaneShape::setNormal(const glm::vec3& direction) {
glm::vec3 oldTranslation = _translation;
_rotation = rotationBetween(UNROTATED_NORMAL, direction);
glm::vec3 normal = getNormal();
_translation = glm::dot(oldTranslation, normal) * normal;
}
void PlaneShape::setPoint(const glm::vec3& point) {
glm::vec3 normal = getNormal();
_translation = glm::dot(point, 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, _translation));
}
bool PlaneShape::findRayIntersection(RayIntersectionInfo& intersection) const {
glm::vec3 n = getNormal();
float denominator = glm::dot(n, intersection._rayDirection);
if (fabsf(denominator) < EPSILON) {
// line is parallel to plane
if (glm::dot(_translation - intersection._rayStart, n) < EPSILON) {
// ray starts on the plane
intersection._hitDistance = 0.0f;
intersection._hitNormal = n;
intersection._hitShape = const_cast<PlaneShape*>(this);
return true;
}
} else {
float d = glm::dot(_translation - intersection._rayStart, n) / denominator;
if (d > 0.0f && d < intersection._rayLength && d < intersection._hitDistance) {
// ray points toward plane
intersection._hitDistance = d;
intersection._hitNormal = n;
intersection._hitShape = const_cast<PlaneShape*>(this);
return true;
}
}
return false;
}

View file

@ -1,30 +0,0 @@
//
// 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::vec3 getNormal() const;
glm::vec4 getCoefficients() const;
void setNormal(const glm::vec3& normal);
void setPoint(const glm::vec3& point);
bool findRayIntersection(RayIntersectionInfo& intersection) const;
};
#endif // hifi_PlaneShape_h

View file

@ -1,37 +0,0 @@
//
// RayIntersectionInfo.h
// libraries/physcis/src
//
// Created by Andrew Meadows 2014.09.09
// 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_RayIntersectionInfo_h
#define hifi_RayIntersectionInfo_h
#include <glm/glm.hpp>
class Shape;
class RayIntersectionInfo {
public:
RayIntersectionInfo() : _rayStart(0.0f), _rayDirection(1.0f, 0.0f, 0.0f), _rayLength(FLT_MAX),
_hitDistance(FLT_MAX), _hitNormal(1.0f, 0.0f, 0.0f), _hitShape(NULL) { }
glm::vec3 getIntersectionPoint() const { return _rayStart + _hitDistance * _rayDirection; }
// input
glm::vec3 _rayStart;
glm::vec3 _rayDirection;
float _rayLength;
// output
float _hitDistance;
glm::vec3 _hitNormal;
Shape* _hitShape;
};
#endif // hifi_RayIntersectionInfo_h

View file

@ -1,144 +0,0 @@
//
// Shape.h
// libraries/shared/src
//
// Created by Andrew Meadows on 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_Shape_h
#define hifi_Shape_h
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QDebug>
#include <QtGlobal>
#include <QVector>
#include "RayIntersectionInfo.h"
class PhysicsEntity;
const float MAX_SHAPE_MASS = 1.0e18f; // something less than sqrt(FLT_MAX)
// DANGER: until ShapeCollider goes away the order of these values matter. Specifically,
// UNKNOWN_SHAPE must be equal to the number of shapes that ShapeCollider actually supports.
const quint8 SPHERE_SHAPE = 0;
const quint8 CAPSULE_SHAPE = 1;
const quint8 PLANE_SHAPE = 2;
const quint8 AACUBE_SHAPE = 3;
const quint8 LIST_SHAPE = 4;
const quint8 UNKNOWN_SHAPE = 5;
const quint8 INVALID_SHAPE = 5;
// new shapes to be supported by Bullet
const quint8 BOX_SHAPE = 7;
const quint8 CYLINDER_SHAPE = 8;
class Shape {
public:
typedef quint8 Type;
static quint32 getNextID() { static quint32 nextID = 0; return ++nextID; }
Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.0f),
_translation(0.0f), _rotation(), _mass(MAX_SHAPE_MASS) {
_id = getNextID();
}
virtual ~Shape() { }
Type getType() const { return _type; }
quint32 getID() const { return _id; }
void setEntity(PhysicsEntity* entity) { _owningEntity = entity; }
PhysicsEntity* getEntity() const { return _owningEntity; }
float getBoundingRadius() const { return _boundingRadius; }
virtual const glm::quat& getRotation() const { return _rotation; }
virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; }
virtual void setTranslation(const glm::vec3& translation) { _translation = translation; }
virtual const glm::vec3& getTranslation() const { return _translation; }
virtual void setMass(float mass) { _mass = mass; }
virtual float getMass() const { return _mass; }
virtual bool findRayIntersection(RayIntersectionInfo& intersection) const = 0;
/// \param penetration of collision
/// \param contactPoint of collision
/// \return the effective mass for the collision
/// For most shapes has side effects: computes and caches the partial Lagrangian coefficients which will be
/// used in the next accumulateDelta() call.
virtual float computeEffectiveMass(const glm::vec3& penetration, const glm::vec3& contactPoint) { return _mass; }
/// \param relativeMassFactor the final ingredient for partial Lagrangian coefficients from computeEffectiveMass()
/// \param penetration the delta movement
virtual void accumulateDelta(float relativeMassFactor, const glm::vec3& penetration) {}
virtual void applyAccumulatedDelta() {}
/// \return volume of shape in cubic meters
virtual float getVolume() const { return 1.0; }
virtual QDebug& dumpToDebug(QDebug& debugConext) const;
protected:
// these ctors are protected (used by derived classes only)
Shape(Type type) : _type(type), _owningEntity(NULL),
_boundingRadius(0.0f), _translation(0.0f),
_rotation(), _mass(MAX_SHAPE_MASS) {
_id = getNextID();
}
Shape(Type type, const glm::vec3& position) :
_type(type), _owningEntity(NULL),
_boundingRadius(0.0f), _translation(position),
_rotation(), _mass(MAX_SHAPE_MASS) {
_id = getNextID();
}
Shape(Type type, const glm::vec3& position, const glm::quat& rotation) : _type(type), _owningEntity(NULL),
_boundingRadius(0.0f), _translation(position),
_rotation(rotation), _mass(MAX_SHAPE_MASS) {
_id = getNextID();
}
void setBoundingRadius(float radius) { _boundingRadius = radius; }
Type _type;
quint32 _id;
PhysicsEntity* _owningEntity;
float _boundingRadius;
glm::vec3 _translation;
glm::quat _rotation;
float _mass;
};
inline QDebug& Shape::dumpToDebug(QDebug& debugConext) const {
debugConext << "Shape[ ("
<< "type: " << getType()
<< "position: "
<< getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z
<< "radius: "
<< getBoundingRadius()
<< "]";
return debugConext;
}
inline QDebug operator<<(QDebug debug, const Shape& shape) {
return shape.dumpToDebug(debug);
}
inline QDebug operator<<(QDebug debug, const Shape* shape) {
return shape->dumpToDebug(debug);
}
#endif // hifi_Shape_h

File diff suppressed because it is too large Load diff

View file

@ -1,156 +0,0 @@
//
// ShapeCollider.h
// libraries/shared/src
//
// Created by Andrew Meadows on 02/20/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_ShapeCollider_h
#define hifi_ShapeCollider_h
#include <QVector>
#include "CollisionInfo.h"
#include "RayIntersectionInfo.h"
#include "SharedUtil.h"
class Shape;
class SphereShape;
class CapsuleShape;
namespace ShapeCollider {
/// MUST CALL this FIRST before using the ShapeCollider
void initDispatchTable();
/// \param shapeA pointer to first shape (cannot be NULL)
/// \param shapeB pointer to second shape (cannot be NULL)
/// \param collisions[out] collision details
/// \return true if shapes collide
bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
bool collideShapeWithShapes(const Shape* shapeA, const QVector<Shape*>& shapes, int startIndex, CollisionList& collisions);
bool collideShapesWithShapes(const QVector<Shape*>& shapesA, const QVector<Shape*>& shapesB, CollisionList& collisions);
/// \param shapeA a pointer to a shape (cannot be NULL)
/// \param cubeCenter center of cube
/// \param cubeSide lenght of side of cube
/// \param collisions[out] average collision details
/// \return true if shapeA collides with axis aligned cube
bool collideShapeWithAACubeLegacy(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
/// \param sphereA pointer to first shape (cannot be NULL)
/// \param sphereB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool sphereVsSphere(const Shape* sphereA, const Shape* sphereB, CollisionList& collisions);
/// \param sphereA pointer to first shape (cannot be NULL)
/// \param capsuleB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool sphereVsCapsule(const Shape* sphereA, const Shape* capsuleB, CollisionList& collisions);
/// \param sphereA pointer to first shape (cannot be NULL)
/// \param planeB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool sphereVsPlane(const Shape* sphereA, const Shape* planeB, CollisionList& collisions);
/// \param capsuleA pointer to first shape (cannot be NULL)
/// \param sphereB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool capsuleVsSphere(const Shape* capsuleA, const Shape* sphereB, CollisionList& collisions);
/// \param capsuleA pointer to first shape (cannot be NULL)
/// \param capsuleB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool capsuleVsCapsule(const Shape* capsuleA, const Shape* capsuleB, CollisionList& collisions);
/// \param capsuleA pointer to first shape (cannot be NULL)
/// \param planeB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool capsuleVsPlane(const Shape* capsuleA, const Shape* planeB, CollisionList& collisions);
/// \param planeA pointer to first shape (cannot be NULL)
/// \param sphereB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool planeVsSphere(const Shape* planeA, const Shape* sphereB, CollisionList& collisions);
/// \param planeA pointer to first shape (cannot be NULL)
/// \param capsuleB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool planeVsCapsule(const Shape* planeA, const Shape* capsuleB, CollisionList& collisions);
/// \param planeA pointer to first shape (cannot be NULL)
/// \param planeB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool planeVsPlane(const Shape* planeA, const Shape* planeB, CollisionList& collisions);
/// helper function for *VsAACube() methods
/// \param sphereCenter center of sphere
/// \param sphereRadius radius of sphere
/// \param cubeCenter center of AACube
/// \param cubeSide scale of cube
/// \param[out] collisions where to append collision details
/// \return valid pointer to CollisionInfo if sphere and cube overlap or NULL if not
CollisionInfo* sphereVsAACubeHelper(const glm::vec3& sphereCenter, float sphereRadius,
const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
bool sphereVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
bool capsuleVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
bool aaCubeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
bool aaCubeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
bool aaCubeVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
/// \param shapeA pointer to first shape (cannot be NULL)
/// \param listB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool shapeVsList(const Shape* shapeA, const Shape* listB, CollisionList& collisions);
/// \param listA pointer to first shape (cannot be NULL)
/// \param shapeB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool listVsShape(const Shape* listA, const Shape* shapeB, CollisionList& collisions);
/// \param listA pointer to first shape (cannot be NULL)
/// \param listB pointer to second shape (cannot be NULL)
/// \param[out] collisions where to append collision details
/// \return true if shapes collide
bool listVsList(const Shape* listA, const Shape* listB, CollisionList& collisions);
/// \param sphereA pointer to sphere (cannot be NULL)
/// \param cubeCenter center of cube
/// \param cubeSide lenght of side of cube
/// \param[out] collisions where to append collision details
/// \return true if sphereA collides with axis aligned cube
bool sphereVsAACubeLegacy(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
/// \param capsuleA pointer to capsule (cannot be NULL)
/// \param cubeCenter center of cube
/// \param cubeSide lenght of side of cube
/// \param[out] collisions where to append collision details
/// \return true if capsuleA collides with axis aligned cube
bool capsuleVsAACubeLegacy(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
/// \param shapes list of pointers to shapes (shape pointers may be NULL)
/// \param intersection[out] struct with info about Ray and hit
/// \return true if ray hits any shape in shapes
bool findRayIntersection(const QVector<Shape*>& shapes, RayIntersectionInfo& intersection);
} // namespace ShapeCollider
#endif // hifi_ShapeCollider_h

View file

@ -1,51 +0,0 @@
//
// SphereShape.cpp
// libraries/shared/src
//
// Created by Andrew Meadows on 2014.06.17
// 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 <glm/gtx/norm.hpp>
#include "SphereShape.h"
bool SphereShape::findRayIntersection(RayIntersectionInfo& intersection) const {
float r2 = _boundingRadius * _boundingRadius;
// compute closest approach (CA)
float a = glm::dot(_translation - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA
float b2 = glm::distance2(_translation, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA
if (b2 > r2) {
// ray does not hit sphere
return false;
}
float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection
float d2 = glm::distance2(intersection._rayStart, _translation); // d2 = squared distance from sphere-center to ray-start
float distance = FLT_MAX;
if (a < 0.0f) {
// ray points away from sphere-center
if (d2 > r2) {
// ray starts outside sphere
return false;
}
// ray starts inside sphere
distance = c + a;
} else if (d2 > r2) {
// ray starts outside sphere
distance = a - c;
} else {
// ray starts inside sphere
distance = a + c;
}
if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) {
intersection._hitDistance = distance;
intersection._hitNormal = glm::normalize(intersection._rayStart + distance * intersection._rayDirection - _translation);
intersection._hitShape = const_cast<SphereShape*>(this);
return true;
}
return false;
}

View file

@ -1,63 +0,0 @@
//
// SphereShape.h
// libraries/shared/src
//
// Created by Andrew Meadows on 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_SphereShape_h
#define hifi_SphereShape_h
#include "NumericalConstants.h"
#include "Shape.h"
class SphereShape : public Shape {
public:
SphereShape() : Shape(SPHERE_SHAPE) {}
SphereShape(float radius) : Shape(SPHERE_SHAPE) {
_boundingRadius = radius;
}
SphereShape(float radius, const glm::vec3& position) : Shape(SPHERE_SHAPE, position) {
_boundingRadius = radius;
}
virtual ~SphereShape() {}
float getRadius() const { return _boundingRadius; }
void setRadius(float radius) { _boundingRadius = radius; }
bool findRayIntersection(RayIntersectionInfo& intersection) const;
float getVolume() const { return 1.3333333333f * PI * _boundingRadius * _boundingRadius * _boundingRadius; }
};
inline QDebug operator<<(QDebug debug, const SphereShape& shape) {
debug << "SphereShape[ ("
<< "position: "
<< shape.getTranslation().x << ", " << shape.getTranslation().y << ", " << shape.getTranslation().z
<< "radius: "
<< shape.getRadius()
<< "]";
return debug;
}
inline QDebug operator<<(QDebug debug, const SphereShape* shape) {
debug << "SphereShape[ ("
<< "center: "
<< shape->getTranslation().x << ", " << shape->getTranslation().y << ", " << shape->getTranslation().z
<< "radius: "
<< shape->getRadius()
<< "]";
return debug;
}
#endif // hifi_SphereShape_h

View file

@ -65,38 +65,6 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion) {
return in >> quaternion.x >> quaternion.y >> quaternion.z >> quaternion.w;
}
// less common utils can be enabled with DEBUG
// FIXME, remove the second defined clause once these compile, or remove the
// functions.
#if defined(DEBUG) && defined(FIXED_STREAMS)
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) {
s << "{penetration=" << c._penetration
<< ", contactPoint=" << c._contactPoint
<< ", addedVelocity=" << c._addedVelocity
<< "}";
return s;
}
std::ostream& operator<<(std::ostream& s, const SphereShape& sphere) {
s << "{type='sphere', center=" << sphere.getPosition()
<< ", radius=" << sphere.getRadius()
<< "}";
return s;
}
std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) {
s << "{type='capsule', center=" << capsule.getPosition()
<< ", radius=" << capsule.getRadius()
<< ", length=" << (2.0f * capsule.getHalfHeight())
<< ", begin=" << capsule.getStartPoint()
<< ", end=" << capsule.getEndPoint()
<< "}";
return s;
}
#endif // DEBUG
#ifndef QT_NO_DEBUG_STREAM
#include <QDebug>

View file

@ -37,16 +37,6 @@ QDataStream& operator>>(QDataStream& in, glm::vec3& vector);
QDataStream& operator<<(QDataStream& out, const glm::quat& quaternion);
QDataStream& operator>>(QDataStream& in, glm::quat& quaternion);
// less common utils can be enabled with DEBUG
#ifdef DEBUG
class CollisionInfo;
class SphereShape;
class CapsuleShape;
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c);
std::ostream& operator<<(std::ostream& s, const SphereShape& shape);
std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule);
#endif // DEBUG
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
// Add support for writing these to qDebug().

View file

@ -0,0 +1,160 @@
//
// GeometryUtilTests.cpp
// tests/physics/src
//
// Created by Andrew Meadows on 2015.07.27
// Copyright 2015 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 <iostream>
#include "GeometryUtilTests.h"
#include <GeometryUtil.h>
#include <NumericalConstants.h>
#include <StreamUtils.h>
#include <../QTestExtensions.h>
QTEST_MAIN(GeometryUtilTests)
float getErrorDifference(const float& a, const float& b) {
return fabsf(a - b);
}
float getErrorDifference(const glm::vec3& a, const glm::vec3& b) {
return glm::distance(a, b);
}
void GeometryUtilTests::testLocalRayRectangleIntersection() {
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
// create a rectangle in the local frame on the XY plane with normal along -zAxis
// (this is the assumed local orientation of the rectangle in findRayRectangleIntersection())
glm::vec2 rectDimensions(3.0f, 5.0f);
glm::vec3 rectCenter(0.0f, 0.0f, 0.0f);
glm::quat rectRotation = glm::quat(); // identity
// create points for generating rays that hit the plane and don't
glm::vec3 rayStart(1.0f, 2.0f, 3.0f);
float delta = 0.1f;
{ // verify hit
glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.x - delta) * xAxis);
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
float expectedDistance = glm::length(rayEnd - rayStart);
float distance = FLT_MAX;
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
QCOMPARE(hit, true);
QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON);
}
{ // verify miss
glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.y + delta) * yAxis);
glm::vec3 rayMissDirection = glm::normalize(rayEnd - rayStart);
float distance = FLT_MAX;
bool hit = findRayRectangleIntersection(rayStart, rayMissDirection, rectRotation, rectCenter, rectDimensions, distance);
QCOMPARE(hit, false);
QCOMPARE(distance, FLT_MAX); // distance should be unchanged
}
{ // hit with co-planer line
float yFraction = 0.25f;
rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + yFraction * rectDimensions.y * yAxis);
glm::vec3 rayEnd = rectCenter - rectRotation * (rectDimensions.x * xAxis + yFraction * rectDimensions.y * yAxis);
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
float expectedDistance = rectDimensions.x;
float distance = FLT_MAX;
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
QCOMPARE(hit, true);
QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON);
}
{ // miss with co-planer line
float yFraction = 0.75f;
rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
glm::vec3 rayEnd = rectCenter + rectRotation * (- rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
float distance = FLT_MAX;
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
QCOMPARE(hit, false);
QCOMPARE(distance, FLT_MAX); // distance should be unchanged
}
}
void GeometryUtilTests::testWorldRayRectangleIntersection() {
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
// create a rectangle in the local frame on the XY plane with normal along -zAxis
// (this is the assumed local orientation of the rectangle in findRayRectangleIntersection())
// and then rotate and shift everything into the world frame
glm::vec2 rectDimensions(3.0f, 5.0f);
glm::vec3 rectCenter(0.7f, 0.5f, -0.3f);
glm::quat rectRotation = glm::quat(); // identity
// create points for generating rays that hit the plane and don't
glm::vec3 rayStart(1.0f, 2.0f, 3.0f);
float delta = 0.1f;
{ // verify hit
glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.x - delta) * xAxis);
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
float expectedDistance = glm::length(rayEnd - rayStart);
float distance = FLT_MAX;
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
QCOMPARE(hit, true);
QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON);
}
{ // verify miss
glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.y + delta) * yAxis);
glm::vec3 rayMissDirection = glm::normalize(rayEnd - rayStart);
float distance = FLT_MAX;
bool hit = findRayRectangleIntersection(rayStart, rayMissDirection, rectRotation, rectCenter, rectDimensions, distance);
QCOMPARE(hit, false);
QCOMPARE(distance, FLT_MAX); // distance should be unchanged
}
{ // hit with co-planer line
float yFraction = 0.25f;
rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
glm::vec3 rayEnd = rectCenter - rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
float expectedDistance = rectDimensions.x;
float distance = FLT_MAX;
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
QCOMPARE(hit, true);
QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON);
}
{ // miss with co-planer line
float yFraction = 0.75f;
rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
glm::vec3 rayEnd = rectCenter + rectRotation * (-rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
float distance = FLT_MAX;
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
QCOMPARE(hit, false);
QCOMPARE(distance, FLT_MAX); // distance should be unchanged
}
}

View file

@ -0,0 +1,28 @@
//
// GeometryUtilTests.h
// tests/physics/src
//
// Created by Andrew Meadows on 2014.05.30
// 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_AngularConstraintTests_h
#define hifi_AngularConstraintTests_h
#include <QtTest/QtTest>
#include <glm/glm.hpp>
class GeometryUtilTests : public QObject {
Q_OBJECT
private slots:
void testLocalRayRectangleIntersection();
void testWorldRayRectangleIntersection();
};
float getErrorDifference(const float& a, const float& b);
float getErrorDifference(const glm::vec3& a, const glm::vec3& b);
#endif // hifi_AngularConstraintTests_h