Merge pull request #3343 from AndrewMeadows/ragdoll

Ragdoll Part 12: first pass avatar skeleton collides with voxels
This commit is contained in:
Brad Hefta-Gaub 2014-09-04 23:09:08 -07:00
commit 9648c0d64e
11 changed files with 365 additions and 110 deletions

View file

@ -79,7 +79,8 @@ MyAvatar::MyAvatar() :
_lookAtTargetAvatar(),
_shouldRender(true),
_billboardValid(false),
_physicsSimulation()
_physicsSimulation(),
_voxelShapeManager()
{
ShapeCollider::initDispatchTable();
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
@ -90,11 +91,11 @@ MyAvatar::MyAvatar() :
_skeletonModel.setEnableShapes(true);
Ragdoll* ragdoll = _skeletonModel.buildRagdoll();
_physicsSimulation.setRagdoll(ragdoll);
_physicsSimulation.addEntity(&_voxelShapeManager);
}
MyAvatar::~MyAvatar() {
_physicsSimulation.setRagdoll(NULL);
_physicsSimulation.setEntity(NULL);
_physicsSimulation.clear();
_lookAtTargetAvatar.clear();
}
@ -1486,112 +1487,125 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
static CollisionList myCollisions(64);
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
const float MAX_VOXEL_COLLISION_SPEED = 100.0f;
float speed = glm::length(_velocity);
if (speed > MAX_VOXEL_COLLISION_SPEED) {
// don't even bother to try to collide against voxles when moving very fast
_trapDuration = 0.0f;
return;
}
bool isTrapped = false;
myCollisions.clear();
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) {
const float VOXEL_ELASTICITY = 0.0f;
const float VOXEL_DAMPING = 0.0f;
float capsuleRadius = boundingShape.getRadius();
float capsuleHalfHeight = boundingShape.getHalfHeight();
const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight;
const float MIN_STEP_HEIGHT = 0.0f;
glm::vec3 footBase = boundingShape.getTranslation() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection;
float highestStep = 0.0f;
float lowestStep = MAX_STEP_HEIGHT;
glm::vec3 floorPoint;
glm::vec3 stepPenetration(0.0f);
glm::vec3 totalPenetration(0.0f);
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) {
// We use a multiple of the avatar's boundingRadius as the size of the cube of interest.
float cubeScale = 4.0f * getBoundingRadius();
glm::vec3 corner = getPosition() - glm::vec3(0.5f * cubeScale);
AACube boundingCube(corner, cubeScale);
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
glm::vec3 cubeCenter = collision->_vecData;
float cubeSide = collision->_floatData;
float verticalDepth = glm::dot(collision->_penetration, _worldUpDirection);
float horizontalDepth = glm::length(collision->_penetration - verticalDepth * _worldUpDirection);
const float MAX_TRAP_PERIOD = 0.125f;
if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) {
isTrapped = true;
if (_trapDuration > MAX_TRAP_PERIOD) {
float distance = glm::dot(boundingShape.getTranslation() - cubeCenter, _worldUpDirection);
if (distance < 0.0f) {
distance = fabsf(distance) + 0.5f * cubeSide;
}
distance += capsuleRadius + capsuleHalfHeight;
totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection);
continue;
}
} else if (_trapDuration > MAX_TRAP_PERIOD) {
// we're trapped, ignore this collision
continue;
}
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
if (glm::dot(collision->_penetration, _velocity) >= 0.0f) {
glm::vec3 cubeTop = cubeCenter + (0.5f * cubeSide) * _worldUpDirection;
float stepHeight = glm::dot(_worldUpDirection, cubeTop - footBase);
if (stepHeight > highestStep) {
highestStep = stepHeight;
stepPenetration = collision->_penetration;
}
if (stepHeight < lowestStep) {
lowestStep = stepHeight;
floorPoint = collision->_contactPoint - collision->_penetration;
}
}
// query the VoxelTree for cubes that touch avatar's boundingCube
CubeList cubes;
if (Application::getInstance()->getVoxelTree()->findContentInCube(boundingCube, cubes)) {
_voxelShapeManager.updateVoxels(cubes);
}
if (lowestStep < MAX_STEP_HEIGHT) {
_lastFloorContactPoint = floorPoint;
}
float penetrationLength = glm::length(totalPenetration);
if (penetrationLength < EPSILON) {
} else {
const float MAX_VOXEL_COLLISION_SPEED = 100.0f;
float speed = glm::length(_velocity);
if (speed > MAX_VOXEL_COLLISION_SPEED) {
// don't even bother to try to collide against voxles when moving very fast
_trapDuration = 0.0f;
return;
}
float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection);
if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) {
// we're colliding against an edge
glm::vec3 targetVelocity = _motorVelocity;
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
// rotate _motorVelocity into world frame
glm::quat rotation = getHead()->getCameraOrientation();
targetVelocity = rotation * _motorVelocity;
}
if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) {
// we're puhing into the edge, so we want to lift
// remove unhelpful horizontal component of the step's penetration
totalPenetration -= stepPenetration - (glm::dot(stepPenetration, _worldUpDirection) * _worldUpDirection);
// further adjust penetration to help lift
float liftSpeed = glm::max(MAX_WALKING_SPEED, speed);
float thisStep = glm::min(liftSpeed * deltaTime, highestStep);
float extraStep = glm::dot(totalPenetration, _worldUpDirection) + thisStep;
if (extraStep > 0.0f) {
totalPenetration -= extraStep * _worldUpDirection;
bool isTrapped = false;
myCollisions.clear();
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) {
const float VOXEL_ELASTICITY = 0.0f;
const float VOXEL_DAMPING = 0.0f;
float capsuleRadius = boundingShape.getRadius();
float capsuleHalfHeight = boundingShape.getHalfHeight();
const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight;
const float MIN_STEP_HEIGHT = 0.0f;
glm::vec3 footBase = boundingShape.getTranslation() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection;
float highestStep = 0.0f;
float lowestStep = MAX_STEP_HEIGHT;
glm::vec3 floorPoint;
glm::vec3 stepPenetration(0.0f);
glm::vec3 totalPenetration(0.0f);
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
glm::vec3 cubeCenter = collision->_vecData;
float cubeSide = collision->_floatData;
float verticalDepth = glm::dot(collision->_penetration, _worldUpDirection);
float horizontalDepth = glm::length(collision->_penetration - verticalDepth * _worldUpDirection);
const float MAX_TRAP_PERIOD = 0.125f;
if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) {
isTrapped = true;
if (_trapDuration > MAX_TRAP_PERIOD) {
float distance = glm::dot(boundingShape.getTranslation() - cubeCenter, _worldUpDirection);
if (distance < 0.0f) {
distance = fabsf(distance) + 0.5f * cubeSide;
}
distance += capsuleRadius + capsuleHalfHeight;
totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection);
continue;
}
} else if (_trapDuration > MAX_TRAP_PERIOD) {
// we're trapped, ignore this collision
continue;
}
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
if (glm::dot(collision->_penetration, _velocity) >= 0.0f) {
glm::vec3 cubeTop = cubeCenter + (0.5f * cubeSide) * _worldUpDirection;
float stepHeight = glm::dot(_worldUpDirection, cubeTop - footBase);
if (stepHeight > highestStep) {
highestStep = stepHeight;
stepPenetration = collision->_penetration;
}
if (stepHeight < lowestStep) {
lowestStep = stepHeight;
floorPoint = collision->_contactPoint - collision->_penetration;
}
}
}
if (lowestStep < MAX_STEP_HEIGHT) {
_lastFloorContactPoint = floorPoint;
}
float penetrationLength = glm::length(totalPenetration);
if (penetrationLength < EPSILON) {
_trapDuration = 0.0f;
return;
}
float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection);
if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) {
// we're colliding against an edge
glm::vec3 targetVelocity = _motorVelocity;
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
// rotate _motorVelocity into world frame
glm::quat rotation = getHead()->getCameraOrientation();
targetVelocity = rotation * _motorVelocity;
}
if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) {
// we're puhing into the edge, so we want to lift
// remove unhelpful horizontal component of the step's penetration
totalPenetration -= stepPenetration - (glm::dot(stepPenetration, _worldUpDirection) * _worldUpDirection);
// further adjust penetration to help lift
float liftSpeed = glm::max(MAX_WALKING_SPEED, speed);
float thisStep = glm::min(liftSpeed * deltaTime, highestStep);
float extraStep = glm::dot(totalPenetration, _worldUpDirection) + thisStep;
if (extraStep > 0.0f) {
totalPenetration -= extraStep * _worldUpDirection;
}
_position -= totalPenetration;
} else {
// we're not pushing into the edge, so let the avatar fall
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
_position -= totalPenetration;
} else {
// we're not pushing into the edge, so let the avatar fall
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
} else {
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
// Don't make a collision sound against voxlels by default -- too annoying when walking
//const float VOXEL_COLLISION_FREQUENCY = 0.5f;
//updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
}
_trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f;
// Don't make a collision sound against voxlels by default -- too annoying when walking
//const float VOXEL_COLLISION_FREQUENCY = 0.5f;
//updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
}
_trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f;
}
}
void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) {
@ -1957,6 +1971,9 @@ void MyAvatar::updateMotionBehaviorsFromMenu() {
} else {
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
}
if (!(_collisionGroups | COLLISION_GROUP_VOXELS)) {
_voxelShapeManager.clearShapes();
}
}
void MyAvatar::renderAttachments(RenderMode renderMode) {

View file

@ -17,6 +17,7 @@
#include <PhysicsSimulation.h>
#include "Avatar.h"
#include "VoxelShapeManager.h"
class ModelItemID;
@ -214,6 +215,7 @@ private:
QList<AnimationHandlePointer> _animationHandles;
PhysicsSimulation _physicsSimulation;
VoxelShapeManager _voxelShapeManager;
RecorderPointer _recorder;

View file

@ -0,0 +1,99 @@
//
// VoxelShapeManager.cpp
// interface/src/avatar
//
// Created by Andrew Meadows on 2014.09.02
// Copyright 2012 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 <AACubeShape.h>
#include <PhysicsSimulation.h>
#include <SharedUtil.h>
#include "VoxelShapeManager.h"
VoxelShapeManager::VoxelShapeManager() : PhysicsEntity(), _lastSimulationTranslation(0.0f) {
}
VoxelShapeManager::~VoxelShapeManager() {
clearShapes();
}
void VoxelShapeManager::stepForward(float deltaTime) {
PhysicsSimulation* simulation = getSimulation();
if (simulation) {
glm::vec3 simulationOrigin = simulation->getTranslation();
if (glm::distance2(_lastSimulationTranslation, simulationOrigin) > EPSILON) {
VoxelPool::const_iterator voxelItr = _voxels.constBegin();
while (voxelItr != _voxels.constEnd()) {
// the shape's position is stored in the simulation-frame
const VoxelInfo& voxel = voxelItr.value();
voxel._shape->setTranslation(voxel._cube.calcCenter() - simulationOrigin);
++voxelItr;
}
_lastSimulationTranslation = simulationOrigin;
}
}
}
void VoxelShapeManager::buildShapes() {
// the shapes are owned by the elements of _voxels,
// so _shapes is constructed by harvesting them from _voxels
_shapes.clear();
VoxelPool::const_iterator voxelItr = _voxels.constBegin();
while (voxelItr != _voxels.constEnd()) {
_shapes.push_back(voxelItr.value()._shape);
++voxelItr;
}
}
void VoxelShapeManager::clearShapes() {
PhysicsEntity::clearShapes();
_voxels.clear();
}
void VoxelShapeManager::updateVoxels(CubeList& cubes) {
PhysicsSimulation* simulation = getSimulation();
if (!simulation) {
return;
}
int numChanges = 0;
VoxelPool::iterator voxelItr = _voxels.begin();
while (voxelItr != _voxels.end()) {
// look for this voxel in cubes
CubeList::iterator cubeItr = cubes.find(voxelItr.key());
if (cubeItr == cubes.end()) {
// did not find it --> remove the voxel
simulation->removeShape(voxelItr.value()._shape);
voxelItr = _voxels.erase(voxelItr);
++numChanges;
} else {
// found it --> remove the cube
cubes.erase(cubeItr);
voxelItr++;
}
}
// add remaining cubes to _voxels
glm::vec3 simulationOrigin = simulation->getTranslation();
CubeList::const_iterator cubeItr = cubes.constBegin();
while (cubeItr != cubes.constEnd()) {
AACube cube = cubeItr.value();
AACubeShape* shape = new AACubeShape(cube.getScale(), cube.calcCenter() - simulationOrigin);
shape->setEntity(this);
VoxelInfo voxel = {cube, shape };
_voxels.insert(cubeItr.key(), voxel);
++numChanges;
++cubeItr;
}
if (numChanges > 0) {
buildShapes();
}
}

View file

@ -0,0 +1,51 @@
//
// VoxelShapeManager.h
// interface/src/avatar
//
// Created by Andrew Meadows on 2014.09.02
// Copyright 2012 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_VoxelShapeManager_h
#define hifi_VoxelShapeManager_h
#include <QHash>
#include <AACube.h>
#include <PhysicsEntity.h>
#include <Octree.h>
#include "VoxelShapeManager.h"
class AACubeShape;
class VoxelInfo{
public:
AACube _cube;
AACubeShape* _shape;
};
typedef QHash<quint64, VoxelInfo> VoxelPool;
class VoxelShapeManager : public PhysicsEntity {
public:
VoxelShapeManager();
~VoxelShapeManager();
void stepForward(float deltaTime);
void buildShapes();
void clearShapes();
/// \param cubes list of AACubes representing all of the voxels that should be in this VoxelShapeManager
void updateVoxels(CubeList& cubes);
private:
glm::vec3 _lastSimulationTranslation;
VoxelPool _voxels;
};
#endif // hifi_VoxelShapeManager_h

View file

@ -19,6 +19,7 @@
#include <fstream> // to load voxels from file
#include <QDebug>
#include <QVector>
#include <GeometryUtil.h>
#include <OctalCode.h>
@ -744,6 +745,12 @@ public:
bool found;
};
class ContentArgs {
public:
AACube cube;
CubeList* cubes;
};
bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) {
CapsuleArgs* args = static_cast<CapsuleArgs*>(extraData);
@ -786,6 +793,39 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) {
return false;
}
quint64 cubeListHashKey(const glm::vec3& point) {
// NOTE: TREE_SCALE = 16384 (15 bits) and multiplier is 1024 (11 bits),
// so each component (26 bits) uses more than its alloted 21 bits.
// however we don't expect to span huge cubes so it is ok if we wrap
// (every 2^21 / 2^10 = 2048 meters).
const uint BITS_PER_COMPONENT = 21;
const quint64 MAX_SCALED_COMPONENT = 2097152; // 2^21
const float RESOLUTION_PER_METER = 1024.0f; // 2^10
return (quint64)(point.x * RESOLUTION_PER_METER) % MAX_SCALED_COMPONENT +
(((quint64)(point.y * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << BITS_PER_COMPONENT) +
(((quint64)(point.z * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << 2 * BITS_PER_COMPONENT);
}
bool findContentInCubeOp(OctreeElement* element, void* extraData) {
ContentArgs* args = static_cast<ContentArgs*>(extraData);
// coarse check against bounds
AACube cube = element->getAACube();
cube.scale(TREE_SCALE);
if (!cube.touches(args->cube)) {
return false;
}
if (!element->isLeaf()) {
return true; // recurse on children
}
if (element->hasContent()) {
// NOTE: the voxel's center is unique so we use it as the input for the key
args->cubes->insert(cubeListHashKey(cube.calcCenter()), cube);
return true;
}
return false;
}
bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
glm::vec3& penetration, Octree::lockType lockType, bool* accurateResult) {
@ -854,6 +894,16 @@ bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions,
return args.found;
}
bool Octree::findContentInCube(const AACube& cube, CubeList& cubes) {
if (!tryLockForRead()) {
return false;
}
ContentArgs args = { cube, &cubes };
recurseTreeWithOperation(findContentInCubeOp, &args);
unlock();
return true;
}
class GetElementEnclosingArgs {
public:
OctreeElement* element;

View file

@ -33,6 +33,7 @@ class Shape;
#include <CollisionInfo.h>
#include <QHash>
#include <QObject>
#include <QReadWriteLock>
@ -47,6 +48,7 @@ public:
// Callback function, for recuseTreeWithOperation
typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData);
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
typedef QHash<quint64, AACube> CubeList;
const bool NO_EXISTS_BITS = false;
const bool WANT_EXISTS_BITS = true;
@ -308,6 +310,8 @@ public:
bool findShapeCollisions(const Shape* shape, CollisionList& collisions,
Octree::lockType = Octree::TryLock, bool* accurateResult = NULL);
bool findContentInCube(const AACube& cube, CubeList& cubes);
OctreeElement* getElementEnclosingPoint(const glm::vec3& point,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);

View file

@ -33,6 +33,8 @@ public:
PhysicsEntity();
virtual ~PhysicsEntity();
virtual void stepForward(float deltaTime) { }
void setTranslation(const glm::vec3& translation);
void setRotation(const glm::quat& rotation);

View file

@ -30,6 +30,10 @@ PhysicsSimulation::PhysicsSimulation() : _translation(0.0f), _frameCount(0), _en
}
PhysicsSimulation::~PhysicsSimulation() {
clear();
}
void PhysicsSimulation::clear() {
// entities have a backpointer to this simulator that must be cleaned up
int numEntities = _otherEntities.size();
for (int i = 0; i < numEntities; ++i) {
@ -43,6 +47,9 @@ PhysicsSimulation::~PhysicsSimulation() {
// but Ragdolls do not
_ragdoll = NULL;
_otherRagdolls.clear();
// contacts have backpointers to shapes so we clear them
_contacts.clear();
}
void PhysicsSimulation::setRagdoll(Ragdoll* ragdoll) {
@ -134,6 +141,18 @@ void PhysicsSimulation::removeShapes(const PhysicsEntity* entity) {
}
}
void PhysicsSimulation::removeShape(const Shape* shape) {
// remove data structures with pointers to shape
QMap<quint64, ContactPoint>::iterator itr = _contacts.begin();
while (itr != _contacts.end()) {
if (shape == itr.value().getShapeA() || shape == itr.value().getShapeB()) {
itr = _contacts.erase(itr);
} else {
++itr;
}
}
}
const float OTHER_RAGDOLL_MASS_SCALE = 10.0f;
bool PhysicsSimulation::addRagdoll(Ragdoll* doll) {
@ -195,7 +214,7 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter
quint64 startTime = now;
quint64 expiry = startTime + maxUsec;
moveRagdolls(deltaTime);
integrate(deltaTime);
enforceContacts();
int numDolls = _otherRagdolls.size();
{
@ -238,8 +257,12 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter
pruneContacts();
}
void PhysicsSimulation::moveRagdolls(float deltaTime) {
void PhysicsSimulation::integrate(float deltaTime) {
PerformanceTimer perfTimer("integrate");
int numEntities = _otherEntities.size();
for (int i = 0; i < numEntities; ++i) {
_otherEntities[i]->stepForward(deltaTime);
}
_ragdoll->stepForward(deltaTime);
int numDolls = _otherRagdolls.size();
for (int i = 0; i < numDolls; ++i) {

View file

@ -27,6 +27,8 @@ public:
PhysicsSimulation();
~PhysicsSimulation();
void clear();
void setTranslation(const glm::vec3& translation) { _translation = translation; }
const glm::vec3& getTranslation() const { return _translation; }
@ -39,6 +41,7 @@ public:
void removeEntity(PhysicsEntity* entity);
void removeShapes(const PhysicsEntity* entity);
void removeShape(const Shape* shape);
/// \return true if doll was added to or is already in the list
bool addRagdoll(Ragdoll* doll);
@ -52,7 +55,7 @@ public:
void stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec);
protected:
void moveRagdolls(float deltaTime);
void integrate(float deltaTime);
/// \return true if main ragdoll collides with other avatar
bool computeCollisions();

View file

@ -81,17 +81,22 @@ public:
protected:
// these ctors are protected (used by derived classes only)
Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation() {
Shape(Type type) : _type(type), _owningEntity(NULL),
_boundingRadius(0.f), _translation(0.f),
_rotation(), _mass(MAX_SHAPE_MASS) {
_id = getNextID();
}
Shape(Type type, const glm::vec3& position)
: _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(position), _rotation() {
Shape(Type type, const glm::vec3& position) :
_type(type), _owningEntity(NULL),
_boundingRadius(0.f), _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.f), _translation(position), _rotation(rotation) {
Shape(Type type, const glm::vec3& position, const glm::quat& rotation) : _type(type), _owningEntity(NULL),
_boundingRadius(0.f), _translation(position),
_rotation(rotation), _mass(MAX_SHAPE_MASS) {
_id = getNextID();
}

View file

@ -676,8 +676,7 @@ CollisionInfo* sphereVsAACubeHelper(const glm::vec3& sphereCenter, float sphereR
// sphereCenter is touching cube surface, so we can't use the difference between those two
// points to compute the penetration direction. Instead we use the unitary components of
// cubeContact.
direction = cubeContact / halfCubeSide;
glm::modf(BA, direction);
glm::modf(cubeContact / halfCubeSide, direction);
lengthDirection = glm::length(direction);
} else if (lengthDirection > sphereRadius) {
collisions.deleteLastCollision();