mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-16 18:18:19 +02:00
Merge pull request #1965 from AndrewMeadows/avatar-interaction
Avatar interaction first pass
This commit is contained in:
commit
0527cf886e
18 changed files with 287 additions and 157 deletions
|
@ -162,14 +162,18 @@ Menu::Menu() :
|
|||
|
||||
addDisabledActionAndSeparator(editMenu, "Physics");
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, true);
|
||||
addCheckableActionToQMenuAndActionHash(editMenu,
|
||||
MenuOption::Collisions,
|
||||
0,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantCollisionsOn(bool)));
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ClickToFly);
|
||||
|
||||
QMenu* collisionsOptionsMenu = editMenu->addMenu("Collision Options");
|
||||
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithEnvironment, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithAvatars, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithVoxels, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
// TODO: make this option work
|
||||
//addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithParticles, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
|
||||
QMenu* toolsMenu = addMenu("Tools");
|
||||
|
||||
|
|
|
@ -167,6 +167,10 @@ namespace MenuOption {
|
|||
const QString ChatCircling = "Chat Circling";
|
||||
const QString CollisionProxies = "Collision Proxies";
|
||||
const QString Collisions = "Collisions";
|
||||
const QString CollideWithAvatars = "Collide With Avatars";
|
||||
const QString CollideWithParticles = "Collide With Particles";
|
||||
const QString CollideWithVoxels = "Collide With Voxels";
|
||||
const QString CollideWithEnvironment = "Collide With World Boundaries";
|
||||
const QString CopyVoxels = "Copy";
|
||||
const QString CoverageMap = "Render Coverage Map";
|
||||
const QString CoverageMapV2 = "Render Coverage Map V2";
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <glm/gtc/noise.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <AvatarData.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
|
|
@ -74,6 +74,7 @@ Avatar::Avatar() :
|
|||
_mouseRayDirection(0.0f, 0.0f, 0.0f),
|
||||
_moving(false),
|
||||
_owningAvatarMixer(),
|
||||
_collisionFlags(0),
|
||||
_initialized(false)
|
||||
{
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
|
@ -159,7 +160,13 @@ void Avatar::render(bool forceRenderHead) {
|
|||
Glower glower(_moving && glm::length(toTarget) > GLOW_DISTANCE ? 1.0f : 0.0f);
|
||||
|
||||
// render body
|
||||
renderBody(forceRenderHead);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
|
||||
_skeletonModel.renderCollisionProxies(1.f);
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
renderBody(forceRenderHead);
|
||||
}
|
||||
|
||||
// render sphere when far away
|
||||
const float MAX_ANGLE = 10.f;
|
||||
|
@ -264,34 +271,28 @@ bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Avatar::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
glm::vec3& penetration, int skeletonSkipIndex) const {
|
||||
bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionList& collisions, int skeletonSkipIndex) {
|
||||
bool didPenetrate = false;
|
||||
glm::vec3 totalPenetration;
|
||||
glm::vec3 skeletonPenetration;
|
||||
if (_skeletonModel.findSpherePenetration(penetratorCenter, penetratorRadius,
|
||||
skeletonPenetration, 1.0f, skeletonSkipIndex)) {
|
||||
totalPenetration = addPenetrations(totalPenetration, skeletonPenetration);
|
||||
ModelCollisionInfo collisionInfo;
|
||||
if (_skeletonModel.findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo, 1.0f, skeletonSkipIndex)) {
|
||||
collisionInfo._model = &_skeletonModel;
|
||||
collisions.push_back(collisionInfo);
|
||||
didPenetrate = true;
|
||||
}
|
||||
glm::vec3 facePenetration;
|
||||
if (_head.getFaceModel().findSpherePenetration(penetratorCenter, penetratorRadius, facePenetration)) {
|
||||
totalPenetration = addPenetrations(totalPenetration, facePenetration);
|
||||
if (_head.getFaceModel().findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo)) {
|
||||
collisionInfo._model = &(_head.getFaceModel());
|
||||
collisions.push_back(collisionInfo);
|
||||
didPenetrate = true;
|
||||
}
|
||||
if (didPenetrate) {
|
||||
penetration = totalPenetration;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return didPenetrate;
|
||||
}
|
||||
|
||||
bool Avatar::findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
// TODO: provide an early exit using bounding sphere of entire avatar
|
||||
|
||||
bool Avatar::findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
const HandData* handData = getHandData();
|
||||
if (handData) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int i = 0; i < NUM_HANDS; i++) {
|
||||
const PalmData* palm = handData->getPalm(i);
|
||||
if (palm && palm->hasPaddle()) {
|
||||
// create a disk collision proxy where the hand is
|
||||
|
@ -327,14 +328,20 @@ bool Avatar::findSphereCollision(const glm::vec3& sphereCenter, float sphereRadi
|
|||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_skeletonModel.findSpherePenetration(sphereCenter, sphereRadius, collision._penetration)) {
|
||||
/* adebug TODO: make this work again
|
||||
bool Avatar::findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
int jointIndex = _skeletonModel.findSphereCollision(sphereCenter, sphereRadius, collision._penetration);
|
||||
if (jointIndex != -1) {
|
||||
collision._penetration /= (float)(TREE_SCALE);
|
||||
collision._addedVelocity = getVelocity();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
void Avatar::setFaceModelURL(const QUrl &faceModelURL) {
|
||||
AvatarData::setFaceModelURL(faceModelURL);
|
||||
|
@ -406,6 +413,22 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2,
|
|||
glEnd();
|
||||
}
|
||||
|
||||
void Avatar::updateCollisionFlags() {
|
||||
_collisionFlags = 0;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithEnvironment)) {
|
||||
_collisionFlags |= COLLISION_GROUP_ENVIRONMENT;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) {
|
||||
_collisionFlags |= COLLISION_GROUP_AVATARS;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithVoxels)) {
|
||||
_collisionFlags |= COLLISION_GROUP_VOXELS;
|
||||
}
|
||||
//if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) {
|
||||
// _collisionFlags |= COLLISION_GROUP_PARTICLES;
|
||||
//}
|
||||
}
|
||||
|
||||
void Avatar::setScale(float scale) {
|
||||
_scale = scale;
|
||||
|
||||
|
@ -420,6 +443,15 @@ float Avatar::getHeight() const {
|
|||
return extents.maximum.y - extents.minimum.y;
|
||||
}
|
||||
|
||||
bool Avatar::poke(ModelCollisionInfo& collision) {
|
||||
// ATM poke() can only affect the Skeleton (not the head)
|
||||
// TODO: make poke affect head
|
||||
if (collision._model == &_skeletonModel && collision._jointIndex != -1) {
|
||||
return _skeletonModel.poke(collision);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float Avatar::getPelvisFloatingHeight() const {
|
||||
return -_skeletonModel.getBindExtents().minimum.y;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ enum ScreenTintLayer {
|
|||
NUM_SCREEN_TINT_LAYERS
|
||||
};
|
||||
|
||||
class MyAvatar;
|
||||
typedef QVector<ModelCollisionInfo> ModelCollisionList;
|
||||
|
||||
// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found)
|
||||
// this is basically in the center of the ground plane. Slightly adjusted. This was asked for by
|
||||
|
@ -97,18 +97,25 @@ public:
|
|||
/// Checks for penetration between the described sphere and the avatar.
|
||||
/// \param penetratorCenter the center of the penetration test sphere
|
||||
/// \param penetratorRadius the radius of the penetration test sphere
|
||||
/// \param penetration[out] the vector in which to store the penetration
|
||||
/// \param collisions[out] a list of collisions
|
||||
/// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model
|
||||
/// \return whether or not the sphere penetrated
|
||||
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
glm::vec3& penetration, int skeletonSkipIndex = -1) const;
|
||||
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionList& collisions, int skeletonSkipIndex = -1);
|
||||
|
||||
/// Checks for collision between the a sphere and the avatar.
|
||||
/// Checks for collision between the a sphere and the avatar's (paddle) hands.
|
||||
/// \param collisionCenter the center of the penetration test sphere
|
||||
/// \param collisionRadius the radius of the penetration test sphere
|
||||
/// \param collision[out] the details of the collision point
|
||||
/// \return whether or not the sphere collided
|
||||
virtual bool findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision);
|
||||
bool findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision);
|
||||
|
||||
/// Checks for collision between the a sphere and the avatar's skeleton (including hand capsules).
|
||||
/// \param collisionCenter the center of the penetration test sphere
|
||||
/// \param collisionRadius the radius of the penetration test sphere
|
||||
/// \param collision[out] the details of the collision point
|
||||
/// \return whether or not the sphere collided
|
||||
//bool findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision);
|
||||
|
||||
virtual bool isMyAvatar() { return false; }
|
||||
|
||||
|
@ -119,6 +126,15 @@ public:
|
|||
|
||||
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
|
||||
|
||||
float getHeight() const;
|
||||
|
||||
/// \param collision a data structure for storing info about collisions against Models
|
||||
/// \return true if the collision affects the Avatar models
|
||||
bool poke(ModelCollisionInfo& collision);
|
||||
|
||||
public slots:
|
||||
void updateCollisionFlags();
|
||||
|
||||
protected:
|
||||
Head _head;
|
||||
Hand _hand;
|
||||
|
@ -137,6 +153,8 @@ protected:
|
|||
bool _moving; ///< set when position is changing
|
||||
QWeakPointer<Node> _owningAvatarMixer;
|
||||
|
||||
uint32_t _collisionFlags;
|
||||
|
||||
// protected methods...
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
|
@ -144,7 +162,6 @@ protected:
|
|||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void setScale(float scale);
|
||||
|
||||
float getHeight() const;
|
||||
float getPelvisFloatingHeight() const;
|
||||
float getPelvisToHeadLength() const;
|
||||
|
||||
|
|
|
@ -67,9 +67,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
}
|
||||
|
||||
void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
return;
|
||||
}
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::renderAvatars()");
|
||||
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors);
|
||||
|
|
|
@ -90,9 +90,7 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
calculateGeometry();
|
||||
|
||||
if (isMine) {
|
||||
|
||||
// Iterate hand controllers, take actions as needed
|
||||
|
||||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
if (palm.isActive()) {
|
||||
|
@ -173,6 +171,7 @@ void Hand::updateCollisions() {
|
|||
int leftPalmIndex, rightPalmIndex;
|
||||
getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
|
||||
ModelCollisionList collisions;
|
||||
// check for collisions
|
||||
for (size_t i = 0; i < getNumPalms(); i++) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
|
@ -182,69 +181,76 @@ void Hand::updateCollisions() {
|
|||
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
|
||||
glm::vec3 totalPenetration;
|
||||
|
||||
// check other avatars
|
||||
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
if (avatar == _owningAvatar) {
|
||||
// don't collid with our own hands
|
||||
continue;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
||||
// Check for palm collisions
|
||||
glm::vec3 myPalmPosition = palm.getPosition();
|
||||
float palmCollisionDistance = 0.1f;
|
||||
bool wasColliding = palm.getIsCollidingWithPalm();
|
||||
palm.setIsCollidingWithPalm(false);
|
||||
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
|
||||
for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) {
|
||||
PalmData& otherPalm = avatar->getHand().getPalms()[j];
|
||||
if (!otherPalm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 otherPalmPosition = otherPalm.getPosition();
|
||||
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
|
||||
palm.setIsCollidingWithPalm(true);
|
||||
if (!wasColliding) {
|
||||
const float PALM_COLLIDE_VOLUME = 1.f;
|
||||
const float PALM_COLLIDE_FREQUENCY = 1000.f;
|
||||
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
|
||||
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
|
||||
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
|
||||
PALM_COLLIDE_FREQUENCY,
|
||||
PALM_COLLIDE_DURATION_MAX,
|
||||
PALM_COLLIDE_DECAY_PER_SAMPLE);
|
||||
// If the other person's palm is in motion, move mine downward to show I was hit
|
||||
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
|
||||
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
|
||||
// add slapback here
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) {
|
||||
// check other avatars
|
||||
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
if (avatar == _owningAvatar) {
|
||||
// don't collid with our own hands
|
||||
continue;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
||||
// Check for palm collisions
|
||||
glm::vec3 myPalmPosition = palm.getPosition();
|
||||
float palmCollisionDistance = 0.1f;
|
||||
bool wasColliding = palm.getIsCollidingWithPalm();
|
||||
palm.setIsCollidingWithPalm(false);
|
||||
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
|
||||
for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) {
|
||||
PalmData& otherPalm = avatar->getHand().getPalms()[j];
|
||||
if (!otherPalm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 otherPalmPosition = otherPalm.getPosition();
|
||||
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
|
||||
palm.setIsCollidingWithPalm(true);
|
||||
if (!wasColliding) {
|
||||
const float PALM_COLLIDE_VOLUME = 1.f;
|
||||
const float PALM_COLLIDE_FREQUENCY = 1000.f;
|
||||
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
|
||||
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
|
||||
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
|
||||
PALM_COLLIDE_FREQUENCY,
|
||||
PALM_COLLIDE_DURATION_MAX,
|
||||
PALM_COLLIDE_DECAY_PER_SAMPLE);
|
||||
// If the other person's palm is in motion, move mine downward to show I was hit
|
||||
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
|
||||
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
|
||||
// add slapback here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
glm::vec3 avatarPenetration;
|
||||
if (avatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, avatarPenetration)) {
|
||||
totalPenetration = addPenetrations(totalPenetration, avatarPenetration);
|
||||
// Check for collisions with the other avatar's leap palms
|
||||
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) {
|
||||
for (size_t j = 0; j < collisions.size(); ++j) {
|
||||
if (!avatar->poke(collisions[j])) {
|
||||
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) {
|
||||
// and the current avatar (ignoring everything below the parent of the parent of the last free joint)
|
||||
glm::vec3 owningPenetration;
|
||||
collisions.clear();
|
||||
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
|
||||
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
|
||||
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
|
||||
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
|
||||
if (_owningAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, owningPenetration, skipIndex)) {
|
||||
totalPenetration = addPenetrations(totalPenetration, owningPenetration);
|
||||
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions, skipIndex)) {
|
||||
for (size_t j = 0; j < collisions.size(); ++j) {
|
||||
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// un-penetrate
|
||||
palm.addToPosition(-totalPenetration);
|
||||
|
||||
// we recycle the collisions container, so we clear it for the next loop
|
||||
collisions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,14 +284,14 @@ void Hand::calculateGeometry() {
|
|||
FingerData& finger = palm.getFingers()[f];
|
||||
if (finger.isActive()) {
|
||||
const float standardBallRadius = FINGERTIP_COLLISION_RADIUS;
|
||||
_leapFingerTipBalls.resize(_leapFingerTipBalls.size() + 1);
|
||||
HandBall& ball = _leapFingerTipBalls.back();
|
||||
HandBall ball;
|
||||
ball.rotation = getBaseOrientation();
|
||||
ball.position = finger.getTipPosition();
|
||||
ball.radius = standardBallRadius;
|
||||
ball.touchForce = 0.0;
|
||||
ball.isCollidable = true;
|
||||
ball.isColliding = false;
|
||||
_leapFingerTipBalls.push_back(ball);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -300,14 +306,14 @@ void Hand::calculateGeometry() {
|
|||
FingerData& finger = palm.getFingers()[f];
|
||||
if (finger.isActive()) {
|
||||
const float standardBallRadius = 0.005f;
|
||||
_leapFingerRootBalls.resize(_leapFingerRootBalls.size() + 1);
|
||||
HandBall& ball = _leapFingerRootBalls.back();
|
||||
HandBall ball;
|
||||
ball.rotation = getBaseOrientation();
|
||||
ball.position = finger.getRootPosition();
|
||||
ball.radius = standardBallRadius;
|
||||
ball.touchForce = 0.0;
|
||||
ball.isCollidable = true;
|
||||
ball.isColliding = false;
|
||||
_leapFingerRootBalls.push_back(ball);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -473,8 +479,3 @@ void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -209,21 +209,26 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
_velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime);
|
||||
}
|
||||
|
||||
// Only collide if we are not moving to a target
|
||||
if (_isCollisionsOn && (glm::length(_moveTarget) < EPSILON)) {
|
||||
|
||||
if (_collisionFlags != 0) {
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
|
||||
float radius = getHeight() * COLLISION_RADIUS_SCALE;
|
||||
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
|
||||
_collisionRadius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f));
|
||||
_collisionRadius *= COLLISION_RADIUS_SCALAR;
|
||||
} else {
|
||||
_collisionRadius = getHeight() * COLLISION_RADIUS_SCALE;
|
||||
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f));
|
||||
radius *= COLLISION_RADIUS_SCALAR;
|
||||
}
|
||||
|
||||
updateCollisionWithEnvironment(deltaTime);
|
||||
updateCollisionWithVoxels(deltaTime);
|
||||
updateAvatarCollisions(deltaTime);
|
||||
if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
|
||||
updateCollisionWithEnvironment(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_VOXELS) {
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
||||
// Note, hand-vs-avatar collisions are done elsewhere
|
||||
// This is where we avatar-vs-avatar bounding capsule
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
// add thrust to velocity
|
||||
|
@ -476,7 +481,12 @@ void MyAvatar::renderDebugBodyPoints() {
|
|||
void MyAvatar::render(bool forceRenderHead) {
|
||||
|
||||
// render body
|
||||
renderBody(forceRenderHead);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
|
||||
_skeletonModel.renderCollisionProxies(1.f);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
renderBody(forceRenderHead);
|
||||
}
|
||||
|
||||
//renderDebugBodyPoints();
|
||||
|
||||
|
@ -878,10 +888,9 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::updateCollisionWithEnvironment(float deltaTime) {
|
||||
void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
|
||||
glm::vec3 up = getBodyUpDirection();
|
||||
float radius = _collisionRadius;
|
||||
const float ENVIRONMENT_SURFACE_ELASTICITY = 1.0f;
|
||||
const float ENVIRONMENT_SURFACE_ELASTICITY = 0.0f;
|
||||
const float ENVIRONMENT_SURFACE_DAMPING = 0.01f;
|
||||
const float ENVIRONMENT_COLLISION_FREQUENCY = 0.05f;
|
||||
glm::vec3 penetration;
|
||||
|
@ -896,8 +905,7 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime) {
|
|||
}
|
||||
|
||||
|
||||
void MyAvatar::updateCollisionWithVoxels(float deltaTime) {
|
||||
float radius = _collisionRadius;
|
||||
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
||||
const float VOXEL_ELASTICITY = 0.4f;
|
||||
const float VOXEL_DAMPING = 0.0f;
|
||||
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||
|
@ -917,8 +925,8 @@ void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity
|
|||
// Update the avatar in response to a hard collision. Position will be reset exactly
|
||||
// to outside the colliding surface. Velocity will be modified according to elasticity.
|
||||
//
|
||||
// if elasticity = 1.0, collision is inelastic.
|
||||
// if elasticity > 1.0, collision is elastic.
|
||||
// if elasticity = 0.0, collision is 100% inelastic.
|
||||
// if elasticity = 1.0, collision is elastic.
|
||||
//
|
||||
_position -= penetration;
|
||||
static float HALTING_VELOCITY = 0.2f;
|
||||
|
@ -927,7 +935,7 @@ void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity
|
|||
if (penetrationLength > EPSILON) {
|
||||
_elapsedTimeSinceCollision = 0.0f;
|
||||
glm::vec3 direction = penetration / penetrationLength;
|
||||
_velocity -= glm::dot(_velocity, direction) * direction * elasticity;
|
||||
_velocity -= glm::dot(_velocity, direction) * direction * (1.f + elasticity);
|
||||
_velocity *= glm::clamp(1.f - damping, 0.0f, 1.0f);
|
||||
if ((glm::length(_velocity) < HALTING_VELOCITY) && (glm::length(_thrust) == 0.f)) {
|
||||
// If moving really slowly after a collision, and not applying forces, stop altogether
|
||||
|
@ -966,11 +974,34 @@ void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTim
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::updateAvatarCollisions(float deltaTime) {
|
||||
const float DEFAULT_HAND_RADIUS = 0.1f;
|
||||
|
||||
void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
||||
// Reset detector for nearest avatar
|
||||
_distanceToNearestAvatar = std::numeric_limits<float>::max();
|
||||
// loop through all the other avatars for potential interactions
|
||||
const AvatarHash& avatars = Application::getInstance()->getAvatarManager().getAvatarHash();
|
||||
if (avatars.size() <= 1) {
|
||||
// no need to compute a bunch of stuff if we have one or fewer avatars
|
||||
return;
|
||||
}
|
||||
float myRadius = getHeight();
|
||||
|
||||
CollisionInfo collisionInfo;
|
||||
foreach (const AvatarSharedPointer& avatarPointer, avatars) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
if (static_cast<Avatar*>(this) == avatar) {
|
||||
// don't collide with ourselves
|
||||
continue;
|
||||
}
|
||||
float distance = glm::length(_position - avatar->getPosition());
|
||||
if (_distanceToNearestAvatar > distance) {
|
||||
_distanceToNearestAvatar = distance;
|
||||
}
|
||||
float theirRadius = avatar->getHeight();
|
||||
if (distance < myRadius + theirRadius) {
|
||||
// TODO: Andrew to make avatar-avatar capsule collisions work here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SortedAvatar {
|
||||
|
|
|
@ -113,7 +113,6 @@ private:
|
|||
bool _isCollisionsOn;
|
||||
bool _isThrustOn;
|
||||
float _thrustMultiplier;
|
||||
float _collisionRadius;
|
||||
glm::vec3 _moveTarget;
|
||||
int _moveTargetStepCounter;
|
||||
QWeakPointer<AvatarData> _lookAtTargetAvatar;
|
||||
|
@ -126,9 +125,9 @@ private:
|
|||
void renderBody(bool forceRenderHead);
|
||||
void updateThrust(float deltaTime);
|
||||
void updateHandMovementAndTouching(float deltaTime);
|
||||
void updateAvatarCollisions(float deltaTime);
|
||||
void updateCollisionWithEnvironment(float deltaTime);
|
||||
void updateCollisionWithVoxels(float deltaTime);
|
||||
void updateCollisionWithAvatars(float deltaTime);
|
||||
void updateCollisionWithEnvironment(float deltaTime, float radius);
|
||||
void updateCollisionWithVoxels(float deltaTime, float radius);
|
||||
void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping);
|
||||
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||
void updateChatCircle(float deltaTime);
|
||||
|
|
|
@ -70,10 +70,6 @@ bool SkeletonModel::render(float alpha) {
|
|||
|
||||
Model::render(alpha);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
|
||||
renderCollisionProxies(alpha);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,15 +98,6 @@ void SixenseManager::update(float deltaTime) {
|
|||
// Compute current velocity from position change
|
||||
glm::vec3 rawVelocity = (position - palm->getRawPosition()) / deltaTime / 1000.f;
|
||||
palm->setRawVelocity(rawVelocity); // meters/sec
|
||||
/*
|
||||
if (i == 0)
|
||||
{
|
||||
printf("ADEBUG rawVelocity = [%e, %e, %e]\n",
|
||||
rawVelocity.x,
|
||||
rawVelocity.y,
|
||||
rawVelocity.z);
|
||||
}
|
||||
*/
|
||||
palm->setRawPosition(position);
|
||||
|
||||
// use the velocity to determine whether there's any movement (if the hand isn't new)
|
||||
|
|
|
@ -1265,7 +1265,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
const glm::mat4& transform = geometry.joints.at(geometry.neckJointIndex).transform;
|
||||
geometry.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]);
|
||||
}
|
||||
|
||||
|
||||
geometry.bindExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
geometry.bindExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
|
||||
|
|
|
@ -437,11 +437,11 @@ bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Model::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
glm::vec3& penetration, float boneScale, int skipIndex) const {
|
||||
bool Model::findSphereCollision(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionInfo& collisionInfo, float boneScale, int skipIndex) const {
|
||||
int jointIndex = -1;
|
||||
const glm::vec3 relativeCenter = penetratorCenter - _translation;
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
bool didPenetrate = false;
|
||||
glm::vec3 totalPenetration;
|
||||
float radiusScale = extractUniformScale(_scale) * boneScale;
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
|
@ -468,12 +468,16 @@ bool Model::findSpherePenetration(const glm::vec3& penetratorCenter, float penet
|
|||
if (findSphereCapsuleConePenetration(relativeCenter, penetratorRadius, start, end,
|
||||
startRadius, endRadius, bonePenetration)) {
|
||||
totalPenetration = addPenetrations(totalPenetration, bonePenetration);
|
||||
didPenetrate = true;
|
||||
// TODO: Andrew to try to keep the joint furthest toward the root
|
||||
jointIndex = i;
|
||||
}
|
||||
outerContinue: ;
|
||||
}
|
||||
if (didPenetrate) {
|
||||
penetration = totalPenetration;
|
||||
if (jointIndex != -1) {
|
||||
// don't store collisionInfo._model at this stage, let the outer context do that
|
||||
collisionInfo._penetration = totalPenetration;
|
||||
collisionInfo._jointIndex = jointIndex;
|
||||
collisionInfo._contactPoint = penetratorCenter + penetratorRadius * glm::normalize(totalPenetration);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -548,6 +552,9 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int last
|
|||
glm::vec3 relativePosition = position - _translation;
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
||||
if (freeLineage.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (lastFreeIndex == -1) {
|
||||
lastFreeIndex = freeLineage.last();
|
||||
}
|
||||
|
@ -706,6 +713,37 @@ void Model::renderCollisionProxies(float alpha) {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
bool Model::poke(ModelCollisionInfo& collision) {
|
||||
// This needs work. At the moment it can wiggle joints that are free to move (such as arms)
|
||||
// but unmovable joints (such as torso) cannot be influenced at all.
|
||||
glm::vec3 jointPosition(0.f);
|
||||
if (getJointPosition(collision._jointIndex, jointPosition)) {
|
||||
int jointIndex = collision._jointIndex;
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
|
||||
if (joint.parentIndex != -1) {
|
||||
// compute the approximate distance (travel) that the joint needs to move
|
||||
glm::vec3 start;
|
||||
getJointPosition(joint.parentIndex, start);
|
||||
glm::vec3 contactPoint = collision._contactPoint - start;
|
||||
glm::vec3 penetrationEnd = contactPoint + collision._penetration;
|
||||
glm::vec3 axis = glm::cross(contactPoint, penetrationEnd);
|
||||
float travel = glm::length(axis);
|
||||
const float MIN_TRAVEL = 1.0e-8f;
|
||||
if (travel > MIN_TRAVEL) {
|
||||
// compute the new position of the joint
|
||||
float angle = asinf(travel / (glm::length(contactPoint) * glm::length(penetrationEnd)));
|
||||
axis = glm::normalize(axis);
|
||||
glm::vec3 end;
|
||||
getJointPosition(jointIndex, end);
|
||||
glm::vec3 newEnd = start + glm::angleAxis(glm::degrees(angle), axis) * (end - start);
|
||||
// try to move it
|
||||
return setJointPosition(jointIndex, newEnd, -1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Model::deleteGeometry() {
|
||||
foreach (Model* attachment, _attachments) {
|
||||
delete attachment;
|
||||
|
|
|
@ -17,6 +17,16 @@
|
|||
#include "ProgramObject.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
class Model;
|
||||
|
||||
// TODO: Andrew to move this into its own file
|
||||
class ModelCollisionInfo : public CollisionInfo {
|
||||
public:
|
||||
ModelCollisionInfo() : CollisionInfo(), _model(NULL), _jointIndex(-1) {}
|
||||
Model* _model;
|
||||
int _jointIndex;
|
||||
};
|
||||
|
||||
/// A generic 3D model displaying geometry loaded from a URL.
|
||||
class Model : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -149,8 +159,14 @@ public:
|
|||
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
glm::vec3& penetration, float boneScale = 1.0f, int skipIndex = -1) const;
|
||||
bool findSphereCollision(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionInfo& collision, float boneScale = 1.0f, int skipIndex = -1) const;
|
||||
|
||||
void renderCollisionProxies(float alpha);
|
||||
|
||||
/// \param collisionInfo info about the collision
|
||||
/// \return true if collision affects the Model
|
||||
bool poke(ModelCollisionInfo& collisionInfo);
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -209,8 +225,6 @@ protected:
|
|||
|
||||
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true);
|
||||
|
||||
void renderCollisionProxies(float alpha);
|
||||
|
||||
private:
|
||||
|
||||
void deleteGeometry();
|
||||
|
|
|
@ -63,7 +63,7 @@ QByteArray AvatarData::toByteArray() {
|
|||
if (!_headData) {
|
||||
_headData = new HeadData(this);
|
||||
}
|
||||
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
||||
// lazily allocate memory for HandData in case we're not an Avatar instance
|
||||
if (!_handData) {
|
||||
_handData = new HandData(this);
|
||||
}
|
||||
|
|
|
@ -134,16 +134,11 @@ public:
|
|||
|
||||
virtual const glm::vec3& getVelocity() const { return vec3Zero; }
|
||||
|
||||
/// Checks for penetration between the described sphere and the avatar.
|
||||
/// \param penetratorCenter the center of the penetration test sphere
|
||||
/// \param penetratorRadius the radius of the penetration test sphere
|
||||
/// \param penetration[out] the vector in which to store the penetration
|
||||
/// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model
|
||||
/// \return whether or not the sphere penetrated
|
||||
virtual bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
glm::vec3& penetration, int skeletonSkipIndex = -1) const { return false; }
|
||||
virtual bool findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
virtual bool findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -139,18 +139,22 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
|
|||
|
||||
// handle A particle
|
||||
particleA->setVelocity(particleA->getVelocity() - axialVelocity * (2.0f * massB / totalMass));
|
||||
particleA->setPosition(particleA->getPosition() - 0.5f * penetration);
|
||||
ParticleProperties propertiesA;
|
||||
ParticleID particleAid(particleA->getID());
|
||||
propertiesA.copyFromParticle(*particleA);
|
||||
propertiesA.setVelocity(particleA->getVelocity() * (float)TREE_SCALE);
|
||||
propertiesA.setPosition(particleA->getPosition() * (float)TREE_SCALE);
|
||||
_packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, particleAid, propertiesA);
|
||||
|
||||
// handle B particle
|
||||
particleB->setVelocity(particleB->getVelocity() + axialVelocity * (2.0f * massA / totalMass));
|
||||
particleA->setPosition(particleB->getPosition() + 0.5f * penetration);
|
||||
ParticleProperties propertiesB;
|
||||
ParticleID particleBid(particleB->getID());
|
||||
propertiesB.copyFromParticle(*particleB);
|
||||
propertiesB.setVelocity(particleB->getVelocity() * (float)TREE_SCALE);
|
||||
propertiesB.setPosition(particleB->getPosition() * (float)TREE_SCALE);
|
||||
_packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, particleBid, propertiesB);
|
||||
|
||||
_packetSender->releaseQueuedMessages();
|
||||
|
@ -182,7 +186,9 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
CollisionInfo collisionInfo;
|
||||
collisionInfo._damping = DAMPING;
|
||||
collisionInfo._elasticity = ELASTICITY;
|
||||
if (avatar->findSphereCollision(center, radius, collisionInfo)) {
|
||||
if (avatar->findSphereCollisionWithHands(center, radius, collisionInfo)) {
|
||||
// TODO: Andrew to resurrect particles-vs-avatar body collisions
|
||||
//avatar->findSphereCollisionWithSkeleton(center, radius, collisionInfo)) {
|
||||
collisionInfo._addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
|
||||
if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
|
||||
|
|
|
@ -11,22 +11,28 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
const uint32_t COLLISION_GROUP_ENVIRONMENT = 1U << 0;
|
||||
const uint32_t COLLISION_GROUP_AVATARS = 1U << 1;
|
||||
const uint32_t COLLISION_GROUP_VOXELS = 1U << 2;
|
||||
const uint32_t COLLISION_GROUP_PARTICLES = 1U << 3;
|
||||
|
||||
class CollisionInfo {
|
||||
public:
|
||||
CollisionInfo()
|
||||
: _damping(0.f),
|
||||
_elasticity(1.f),
|
||||
_contactPoint(0.f),
|
||||
_penetration(0.f),
|
||||
_addedVelocity(0.f) {
|
||||
}
|
||||
_addedVelocity(0.f) {
|
||||
}
|
||||
|
||||
~CollisionInfo() {}
|
||||
|
||||
//glm::vec3 _point;
|
||||
//glm::vec3 _normal;
|
||||
float _damping;
|
||||
float _elasticity;
|
||||
glm::vec3 _penetration; // depth that bodyA is penetrates bodyB
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue