mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
Merge pull request #3343 from AndrewMeadows/ragdoll
Ragdoll Part 12: first pass avatar skeleton collides with voxels
This commit is contained in:
commit
9648c0d64e
11 changed files with 365 additions and 110 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
99
interface/src/avatar/VoxelShapeManager.cpp
Normal file
99
interface/src/avatar/VoxelShapeManager.cpp
Normal 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();
|
||||
}
|
||||
}
|
51
interface/src/avatar/VoxelShapeManager.h
Normal file
51
interface/src/avatar/VoxelShapeManager.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue