Merge pull request #162 from Ventrella/master

improved hand holding algorithm
This commit is contained in:
Philip Rosedale 2013-04-29 12:06:19 -07:00
commit da4d541ecb
5 changed files with 110 additions and 103 deletions

View file

@ -116,10 +116,7 @@ Avatar::Avatar(bool isMine) {
_sphere = NULL;
_interactingOther = NULL;
_interactingOtherIsNearby = false;
_handHolding.position = glm::vec3( 0.0, 0.0, 0.0 );
_handHolding.velocity = glm::vec3( 0.0, 0.0, 0.0 );
_handHolding.force = 10.0f;
_handHoldingPosition = glm::vec3( 0.0, 0.0, 0.0 );
initializeSkeleton();
@ -309,8 +306,7 @@ void Avatar::simulate(float deltaTime) {
if ( !_interactingOtherIsNearby ) {
//initialize _handHolding
_handHolding.position = _bone[ AVATAR_BONE_RIGHT_HAND ].position;
_handHolding.velocity = glm::vec3( 0.0, 0.0, 0.0 );
_handHoldingPosition = _bone[ AVATAR_BONE_RIGHT_HAND ].position;
}
_interactingOtherIsNearby = false;
@ -337,27 +333,20 @@ void Avatar::simulate(float deltaTime) {
float distance = glm::length( v );
if ( distance < _maxArmLength + _maxArmLength ) {
//if ( distance < closestDistance ) { // perhaps I don't need this if we want to allow multi-avatar interactions
{
closestDistance = distance;
_interactingOther = otherAvatar;
_interactingOtherIsNearby = true;
// if I am holding hands with another avatar, a force is applied
if (( _handState == 1 ) || ( _interactingOther->_handState == 1 )) {
glm::vec3 vectorToOtherHand = _interactingOther->_handPosition - _handHolding.position;
glm::vec3 vectorToMyHand = _bone[ AVATAR_BONE_RIGHT_HAND ].position - _handHolding.position;
_handHolding.velocity *= 0.7;
_handHolding.velocity += ( vectorToOtherHand + vectorToMyHand ) * _handHolding.force * deltaTime;
_handHolding.position += _handHolding.velocity;
_bone[ AVATAR_BONE_RIGHT_HAND ].position = _handHolding.position;
}
}
closestDistance = distance;
_interactingOther = otherAvatar;
_interactingOtherIsNearby = true;
_avatarTouch.setMyHandPosition( _bone[ AVATAR_BONE_RIGHT_HAND ].position );
_avatarTouch.setYourPosition( otherAvatar->getBonePosition( AVATAR_BONE_RIGHT_HAND ) );
// if I am holding hands with another avatar, a force is applied
if (( _handState == 1 ) || ( _interactingOther->_handState == 1 )) {
glm::vec3 vectorToOtherHand = _interactingOther->_handPosition - _handHoldingPosition;
glm::vec3 vectorToMyHand = _bone[ AVATAR_BONE_RIGHT_HAND ].position - _handHoldingPosition;
_handHoldingPosition += vectorToOtherHand * YOUR_HAND_HOLDING_PULL;
_handHoldingPosition += vectorToMyHand * MY_HAND_HOLDING_PULL;
_bone[ AVATAR_BONE_RIGHT_HAND ].position = _handHoldingPosition;
}
_avatarTouch.setYourHandPosition( _interactingOther->_handPosition );
}
}
}
@ -370,8 +359,13 @@ void Avatar::simulate(float deltaTime) {
}//if ( _isMine )
//constrain right arm length and re-adjust elbow position as it bends
updateArmIKAndConstraints( deltaTime );
if (_isMine) {
_avatarTouch.setMyHandPosition( _bone[ AVATAR_BONE_RIGHT_HAND ].position );
}
if (!_interactingOtherIsNearby) {
_interactingOther = NULL;
}
@ -545,21 +539,23 @@ void Avatar::updateCollisionWithSphere( glm::vec3 position, float radius, float
float distanceToBigSphere = glm::length(vectorFromMyBodyToBigSphere);
if ( distanceToBigSphere < myBodyApproximateBoundingRadius + radius ) {
for (int b = 0; b < NUM_AVATAR_BONES; b++) {
glm::vec3 vectorFromJointToBigSphereCenter(_bone[b].springyPosition - position);
float distanceToBigSphereCenter = glm::length(vectorFromJointToBigSphereCenter);
float combinedRadius = _bone[b].radius + radius;
if ( distanceToBigSphereCenter < combinedRadius ) {
jointCollision = true;
if (distanceToBigSphereCenter > 0.0) {
glm::vec3 directionVector = vectorFromJointToBigSphereCenter / distanceToBigSphereCenter;
float penetration = 1.0 - (distanceToBigSphereCenter / combinedRadius);
glm::vec3 collisionForce = vectorFromJointToBigSphereCenter * penetration;
_bone[b].springyVelocity += collisionForce * 30.0f * deltaTime;
_velocity += collisionForce * 100.0f * deltaTime;
_bone[b].springyPosition = position + directionVector * combinedRadius;
if ( _bone[b].isCollidable ) {
glm::vec3 vectorFromJointToBigSphereCenter(_bone[b].springyPosition - position);
float distanceToBigSphereCenter = glm::length(vectorFromJointToBigSphereCenter);
float combinedRadius = _bone[b].radius + radius;
if ( distanceToBigSphereCenter < combinedRadius ) {
jointCollision = true;
if (distanceToBigSphereCenter > 0.0) {
glm::vec3 directionVector = vectorFromJointToBigSphereCenter / distanceToBigSphereCenter;
float penetration = 1.0 - (distanceToBigSphereCenter / combinedRadius);
glm::vec3 collisionForce = vectorFromJointToBigSphereCenter * penetration;
_bone[b].springyVelocity += collisionForce * 30.0f * deltaTime;
_velocity += collisionForce * 100.0f * deltaTime;
_bone[b].springyPosition = position + directionVector * combinedRadius;
}
}
}
}
@ -583,27 +579,32 @@ void Avatar::updateCollisionWithOtherAvatar( Avatar * otherAvatar, float deltaTi
// loop through the bones of each avatar to check for every possible collision
for (int b=1; b<NUM_AVATAR_BONES; b++) {
for (int o=b+1; o<NUM_AVATAR_BONES; o++) {
if ( _bone[b].isCollidable ) {
for (int o=b+1; o<NUM_AVATAR_BONES; o++) {
if ( _bone[o].isCollidable ) {
glm::vec3 vectorBetweenJoints(_bone[b].springyPosition - otherAvatar->_bone[o].springyPosition);
float distanceBetweenJoints = glm::length(vectorBetweenJoints);
glm::vec3 vectorBetweenJoints(_bone[b].springyPosition - otherAvatar->_bone[o].springyPosition);
float distanceBetweenJoints = glm::length(vectorBetweenJoints);
// to avoid divide by zero
if ( distanceBetweenJoints > 0.0 ) {
float combinedRadius = _bone[b].radius + otherAvatar->_bone[o].radius;
// to avoid divide by zero
if ( distanceBetweenJoints > 0.0 ) {
float combinedRadius = _bone[b].radius + otherAvatar->_bone[o].radius;
// check for collision
if ( distanceBetweenJoints < combinedRadius * COLLISION_RADIUS_SCALAR) {
glm::vec3 directionVector = vectorBetweenJoints / distanceBetweenJoints;
// check for collision
if ( distanceBetweenJoints < combinedRadius * COLLISION_RADIUS_SCALAR) {
glm::vec3 directionVector = vectorBetweenJoints / distanceBetweenJoints;
// push ball away from colliding other ball and puch avatar body (_velocity) as well
_bone[b].springyVelocity += directionVector * COLLISION_BALL_FORCE * deltaTime;
_velocity += directionVector * COLLISION_BODY_FORCE * deltaTime;
// apply fruction to _velocity
float momentum = 1.0 - COLLISION_FRICTION * deltaTime;
if ( momentum < 0.0 ) { momentum = 0.0;}
_velocity *= momentum;
// push ball away from colliding other ball and puch avatar body (_velocity) as well
_bone[b].springyVelocity += directionVector * COLLISION_BALL_FORCE * deltaTime;
_velocity += directionVector * COLLISION_BODY_FORCE * deltaTime;
// apply fruction to _velocity
float momentum = 1.0 - COLLISION_FRICTION * deltaTime;
if ( momentum < 0.0 ) { momentum = 0.0;}
_velocity *= momentum;
}
}
}
}
}
@ -867,19 +868,6 @@ void Avatar::renderHead(bool lookingInMirror) {
glPopMatrix();
}
void Avatar::startHandMovement() {
if (!_usingBodySprings) {
initializeBodySprings();
_usingBodySprings = true;
}
}
void Avatar::stopHandMovement() {
//_usingBodySprings = false;
}
void Avatar::setHandMovementValues( glm::vec3 handOffset ) {
_movedHandOffset = handOffset;
}
@ -891,6 +879,7 @@ AvatarMode Avatar::getMode() {
void Avatar::initializeSkeleton() {
for (int b=0; b<NUM_AVATAR_BONES; b++) {
_bone[b].isCollidable = true;
_bone[b].parent = AVATAR_BONE_NULL;
_bone[b].position = glm::vec3( 0.0, 0.0, 0.0 );
_bone[b].defaultPosePosition = glm::vec3( 0.0, 0.0, 0.0 );
@ -906,9 +895,7 @@ void Avatar::initializeSkeleton() {
_bone[b].orientation.setToIdentity();
}
//----------------------------------------------------------------------------
// parental hierarchy
//----------------------------------------------------------------------------
// specify the parental hierarchy
_bone[ AVATAR_BONE_PELVIS_SPINE ].parent = AVATAR_BONE_NULL;
_bone[ AVATAR_BONE_MID_SPINE ].parent = AVATAR_BONE_PELVIS_SPINE;
_bone[ AVATAR_BONE_CHEST_SPINE ].parent = AVATAR_BONE_MID_SPINE;
@ -958,7 +945,7 @@ void Avatar::initializeSkeleton() {
_bone[ AVATAR_BONE_RIGHT_SHIN ].defaultPosePosition = glm::vec3( 0.0, -0.15, 0.0 );
_bone[ AVATAR_BONE_RIGHT_FOOT ].defaultPosePosition = glm::vec3( 0.0, 0.0, 0.04 );
// specify the radii of the bone positions
_bone[ AVATAR_BONE_PELVIS_SPINE ].radius = 0.05;
_bone[ AVATAR_BONE_MID_SPINE ].radius = 0.06;
_bone[ AVATAR_BONE_CHEST_SPINE ].radius = 0.03;
@ -982,7 +969,12 @@ void Avatar::initializeSkeleton() {
_bone[ AVATAR_BONE_RIGHT_THIGH ].radius = 0.02;
_bone[ AVATAR_BONE_RIGHT_SHIN ].radius = 0.015;
_bone[ AVATAR_BONE_RIGHT_FOOT ].radius = 0.02;
// to aid in hand-shaking and hand-holding, the right hand is not collidable
_bone[ AVATAR_BONE_RIGHT_UPPER_ARM ].isCollidable = false;
_bone[ AVATAR_BONE_RIGHT_FOREARM ].isCollidable = false;
_bone[ AVATAR_BONE_RIGHT_HAND ].isCollidable = false;
// calculate bone length
calculateBoneLengths();
@ -1135,7 +1127,7 @@ void Avatar::updateArmIKAndConstraints( float deltaTime ) {
// test to see if right hand is being dragged beyond maximum arm length
float distance = glm::length( armVector );
// if right hand is being dragged beyond maximum arm length...
// don't let right hand get dragged beyond maximum arm length...
if ( distance > _maxArmLength ) {
// reset right hand to be constrained to maximum arm length
_bone[ AVATAR_BONE_RIGHT_HAND ].position = _bone[ AVATAR_BONE_RIGHT_SHOULDER ].position;

View file

@ -36,10 +36,11 @@ const float COLLISION_RADIUS_SCALAR = 1.8;
const float COLLISION_BALL_FORCE = 0.1;
const float COLLISION_BODY_FORCE = 3.0;
const float MY_HAND_HOLDING_PULL = 0.2;
const float YOUR_HAND_HOLDING_PULL = 1.0;
enum eyeContactTargets {LEFT_EYE, RIGHT_EYE, MOUTH};
enum DriveKeys
{
FWD = 0,
@ -107,18 +108,10 @@ enum AvatarBoneID
NUM_AVATAR_BONES
};
struct AvatarHandHolding //think of this as one half of a distributed spring :)
{
glm::vec3 position;
glm::vec3 velocity;
float force;
};
struct AvatarBone
{
AvatarBoneID parent; // which bone is this bone connected to?
glm::vec3 position; // the position at the "end" of the bone
glm::vec3 position; // the position at the "end" of the bone - in global space
glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the "T-pose"
glm::vec3 springyPosition; // used for special effects (a 'flexible' variant of position)
glm::vec3 springyVelocity; // used for special effects ( the velocity of the springy position)
@ -130,6 +123,7 @@ struct AvatarBone
Orientation orientation; // three orthogonal normals determined by yaw, pitch, roll
float length; // the length of the bone
float radius; // used for detecting collisions for certain physical effects
bool isCollidable; // when false, the bone position will not register a collision
};
struct AvatarHead
@ -215,8 +209,6 @@ public:
void renderBody();
void renderHead(bool lookingInMirror);
void simulate(float);
void startHandMovement();
void stopHandMovement();
void setHandMovementValues( glm::vec3 movement );
void updateHandMovement( float deltaTime );
void updateArmIKAndConstraints( float deltaTime );
@ -257,7 +249,7 @@ private:
glm::quat _rotation; // the rotation of the avatar body as a whole expressed as a quaternion
AvatarBone _bone[ NUM_AVATAR_BONES ];
AvatarMode _mode;
AvatarHandHolding _handHolding;
glm::vec3 _handHoldingPosition;
glm::vec3 _velocity;
glm::vec3 _thrust;
float _speed;

View file

@ -3,41 +3,61 @@
// interface
//
// Created by Jeffrey Ventrella
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <iostream>
#include <glm/glm.hpp>
#include <SharedUtil.h>
#include "AvatarTouch.h"
#include "InterfaceConfig.h"
AvatarTouch::AvatarTouch() {
_myHandPosition = glm::vec3( 0.0f, 0.0f, 0.0f );
_yourHandPosition = glm::vec3( 0.0f, 0.0f, 0.0f );
for (int p=0; p<NUM_POINTS; p++) {
_point[p] = glm::vec3( 0.0, 0.0, 0.0 );
}
}
void AvatarTouch::setMyHandPosition( glm::vec3 position ) {
_myHandPosition = position;
}
void AvatarTouch::setYourPosition( glm::vec3 position ) {
void AvatarTouch::setYourHandPosition( glm::vec3 position ) {
_yourHandPosition = position;
}
void AvatarTouch::render() {
glm::vec3 v1( _myHandPosition );
glm::vec3 v2( _yourHandPosition );
glLineWidth( 8.0 );
glColor4f( 0.7f, 0.4f, 0.1f, 0.6 );
glLineWidth( 2.0 );
glColor4f( 0.7f, 0.4f, 0.1f, 0.3 );
glBegin( GL_LINE_STRIP );
glVertex3f( v1.x, v1.y, v1.z );
glVertex3f( v2.x, v2.y, v2.z );
glEnd();
glColor4f( 1.0f, 1.0f, 0.0f, 0.8 );
for (int p=0; p<NUM_POINTS; p++) {
glBegin(GL_POINTS);
glVertex3f(_point[p].x, _point[p].y, _point[p].z);
glEnd();
}
}
void AvatarTouch::simulate (float deltaTime) {
glm::vec3 v = _yourHandPosition - _myHandPosition;
for (int p=0; p<NUM_POINTS; p++) {
_point[p] = _myHandPosition + v * ( (float)p / (float)NUM_POINTS );
_point[p].x += randFloatInRange( -0.007, 0.007 );
_point[p].y += randFloatInRange( -0.007, 0.007 );
_point[p].z += randFloatInRange( -0.007, 0.007 );
}
}

View file

@ -15,12 +15,16 @@ class AvatarTouch {
public:
AvatarTouch();
void setMyHandPosition( glm::vec3 position );
void setYourPosition ( glm::vec3 position );
void setMyHandPosition ( glm::vec3 position );
void setYourHandPosition( glm::vec3 position );
void simulate(float deltaTime);
void render();
private:
static const int NUM_POINTS = 100;
glm::vec3 _point [NUM_POINTS];
glm::vec3 _myHandPosition;
glm::vec3 _yourHandPosition;
};

View file

@ -1464,8 +1464,7 @@ void idle(void) {
// tell my avatar if the mouse is being pressed...
if ( mousePressed == 1 ) {
myAvatar.setMousePressed( true );
}
else {
} else {
myAvatar.setMousePressed( false );
}
@ -1484,7 +1483,7 @@ void idle(void) {
networkReceive(0);
}
//loop through all the other avatars and simulate them...
//loop through all the remote avatars and simulate them...
AgentList* agentList = AgentList::getInstance();
for(AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL) {