Merge pull request #1332 from ey6es/master

Tweaks to hand rotation, basic hand/avatar collisions using "capsule cone" proxies.
This commit is contained in:
Philip Rosedale 2013-12-06 09:15:30 -08:00
commit 349b7b493b
19 changed files with 417 additions and 45 deletions

View file

@ -318,6 +318,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarAsBalls);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies);
addActionToQMenuAndActionHash(avatarOptionsMenu,
MenuOption::VoxelMode,

View file

@ -153,6 +153,7 @@ namespace MenuOption {
const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details";
const QString ChatCircling = "Chat Circling";
const QString CollisionProxies = "Collision Proxies";
const QString Collisions = "Collisions";
const QString CopyVoxels = "Copy";
const QString CoverageMap = "Render Coverage Map";

View file

@ -224,6 +224,18 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
0.5f * sqrtf(z2) * (upper[0][1] >= upper[1][0] ? 1.0f : -1.0f)));
}
glm::vec3 extractScale(const glm::mat4& matrix) {
return glm::vec3(glm::length(matrix[0]), glm::length(matrix[1]), glm::length(matrix[2]));
}
float extractUniformScale(const glm::mat4& matrix) {
return extractUniformScale(extractScale(matrix));
}
float extractUniformScale(const glm::vec3& scale) {
return (scale.x + scale.y + scale.z) / 3.0f;
}
// Draw a 3D vector floating in space
void drawVector(glm::vec3 * vector) {
glDisable(GL_LIGHTING);

View file

@ -61,6 +61,12 @@ void setTranslation(glm::mat4& matrix, const glm::vec3& translation);
glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal = false);
glm::vec3 extractScale(const glm::mat4& matrix);
float extractUniformScale(const glm::mat4& matrix);
float extractUniformScale(const glm::vec3& scale);
double diffclock(timeval *clock1,timeval *clock2);
void renderMouseVoxelGrid(const float& mouseVoxelX, const float& mouseVoxelY, const float& mouseVoxelZ, const float& mouseVoxelS);

View file

@ -16,6 +16,8 @@
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <GeometryUtil.h>
#include "Application.h"
#include "Avatar.h"
#include "DataServerClient.h"
@ -814,6 +816,28 @@ bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc
return true;
}
bool Avatar::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
glm::vec3& penetration, int skeletonSkipIndex) {
bool didPenetrate = false;
glm::vec3 totalPenetration;
glm::vec3 skeletonPenetration;
if (_skeletonModel.findSpherePenetration(penetratorCenter, penetratorRadius,
skeletonPenetration, 1.0f, skeletonSkipIndex)) {
totalPenetration = addPenetrations(totalPenetration, skeletonPenetration);
didPenetrate = true;
}
glm::vec3 facePenetration;
if (_head.getFaceModel().findSpherePenetration(penetratorCenter, penetratorRadius, facePenetration)) {
totalPenetration = addPenetrations(totalPenetration, facePenetration);
didPenetrate = true;
}
if (didPenetrate) {
penetration = totalPenetration;
return true;
}
return false;
}
int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) {
// change in position implies movement
glm::vec3 oldPosition = _position;

View file

@ -174,6 +174,15 @@ public:
/// \return whether or not the ray intersected
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
/// 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
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
glm::vec3& penetration, int skeletonSkipIndex = -1);
virtual int parseData(unsigned char* sourceBuffer, int numBytes);
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);

View file

@ -11,6 +11,7 @@
#include "Avatar.h"
#include "FaceModel.h"
#include "Head.h"
#include "Menu.h"
FaceModel::FaceModel(Head* owningHead) :
_owningHead(owningHead)
@ -46,6 +47,16 @@ void FaceModel::simulate(float deltaTime) {
Model::simulate(deltaTime);
}
bool FaceModel::render(float alpha) {
if (!Model::render(alpha)) {
return false;
}
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
renderCollisionProxies(alpha);
}
return true;
}
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(_rotation);

View file

@ -22,6 +22,7 @@ public:
FaceModel(Head* owningHead);
void simulate(float deltaTime);
bool render(float alpha);
protected:

View file

@ -8,6 +8,8 @@
#include <NodeList.h>
#include <GeometryUtil.h>
#include "Application.h"
#include "Avatar.h"
#include "Hand.h"
@ -15,7 +17,6 @@
#include "Util.h"
#include "renderer/ProgramObject.h"
using namespace std;
Hand::Hand(Avatar* owningAvatar) :
@ -50,6 +51,16 @@ void Hand::simulate(float deltaTime, bool isMine) {
_collisionAge += deltaTime;
}
const glm::vec3 leapHandsOffsetFromFace(0.0, -0.2, -0.3); // place the hand in front of the face where we can see it
Head& head = _owningAvatar->getHead();
_baseOrientation = _owningAvatar->getOrientation();
_basePosition = head.calculateAverageEyePosition() + _baseOrientation * leapHandsOffsetFromFace * head.getScale();
if (isMine) {
updateCollisions();
}
calculateGeometry();
if (isMine) {
@ -110,6 +121,49 @@ void Hand::simulate(float deltaTime, bool isMine) {
}
}
const float PALM_COLLISION_RADIUS = 0.03f;
void Hand::updateCollisions() {
// use position to obtain the left and right palm indices
int leftPalmIndex, rightPalmIndex;
getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
// check for collisions
for (int i = 0; i < getNumPalms(); i++) {
PalmData& palm = getPalms()[i];
if (!palm.isActive()) {
continue;
}
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
glm::vec3 totalPenetration;
// check other avatars
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
Avatar* otherAvatar = (Avatar*)node->getLinkedData();
glm::vec3 avatarPenetration;
if (otherAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, avatarPenetration)) {
totalPenetration = addPenetrations(totalPenetration, avatarPenetration);
}
}
}
// and the current avatar (ignoring everything below the parent of the parent of the last free joint)
glm::vec3 owningPenetration;
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);
}
// un-penetrate
palm.addToPosition(-totalPenetration);
}
}
void Hand::handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime) {
// Collision between finger and a voxel plays sound
const float LOWEST_FREQUENCY = 100.f;
@ -131,12 +185,6 @@ void Hand::handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPositi
}
void Hand::calculateGeometry() {
const glm::vec3 leapHandsOffsetFromFace(0.0, -0.2, -0.3); // place the hand in front of the face where we can see it
Head& head = _owningAvatar->getHead();
_baseOrientation = _owningAvatar->getOrientation();
_basePosition = head.calculateAverageEyePosition() + _baseOrientation * leapHandsOffsetFromFace * head.getScale();
// generate finger tip balls....
_leapFingerTipBalls.clear();
for (size_t i = 0; i < getNumPalms(); ++i) {
@ -186,6 +234,21 @@ void Hand::render( bool isMine) {
_renderAlpha = 1.0;
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
for (int i = 0; i < getNumPalms(); i++) {
PalmData& palm = getPalms()[i];
if (!palm.isActive()) {
continue;
}
glm::vec3 position = palm.getPosition();
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glColor3f(0.0f, 1.0f, 0.0f);
glutSolidSphere(PALM_COLLISION_RADIUS * _owningAvatar->getScale(), 10, 10);
glPopMatrix();
}
}
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayLeapHands)) {
renderLeapHands();
}

View file

@ -80,6 +80,8 @@ private:
void renderLeapHands();
void renderLeapFingerTrails();
void updateCollisions();
void calculateGeometry();
void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime);

View file

@ -10,6 +10,7 @@
#include "Application.h"
#include "Avatar.h"
#include "Menu.h"
#include "SkeletonModel.h"
SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
@ -29,24 +30,9 @@ void SkeletonModel::simulate(float deltaTime) {
Model::simulate(deltaTime);
// find the left and rightmost active Leap palms
HandData& hand = _owningAvatar->getHand();
int leftPalmIndex = -1;
float leftPalmX = FLT_MAX;
int rightPalmIndex = -1;
float rightPalmX = -FLT_MAX;
for (int i = 0; i < hand.getNumPalms(); i++) {
if (hand.getPalms()[i].isActive()) {
float x = hand.getPalms()[i].getRawPosition().x;
if (x < leftPalmX) {
leftPalmIndex = i;
leftPalmX = x;
}
if (x > rightPalmX) {
rightPalmIndex = i;
rightPalmX = x;
}
}
}
int leftPalmIndex, rightPalmIndex;
HandData& hand = _owningAvatar->getHand();
hand.getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
const float HAND_RESTORATION_RATE = 0.25f;
@ -121,6 +107,10 @@ bool SkeletonModel::render(float alpha) {
Model::render(alpha);
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
renderCollisionProxies(alpha);
}
return true;
}
@ -141,7 +131,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
glm::quat palmRotation;
getJointRotation(jointIndex, palmRotation, true);
applyRotationDelta(jointIndex, rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()));
applyRotationDelta(jointIndex, rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()), false);
getJointRotation(jointIndex, palmRotation, true);
// sort the finger indices by raw x, get the average direction
@ -163,7 +153,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
float directionLength = glm::length(direction);
const int MIN_ROTATION_FINGERS = 3;
if (directionLength > EPSILON && palm.getNumFingers() >= MIN_ROTATION_FINGERS) {
applyRotationDelta(jointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction));
applyRotationDelta(jointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction), false);
getJointRotation(jointIndex, palmRotation, true);
}

View file

@ -20,6 +20,7 @@
#include <OctalCode.h>
#include <GeometryUtil.h>
#include <VoxelTree.h>
#include "FBXReader.h"
@ -1142,6 +1143,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
joint.distanceToParent = glm::distance(extractTranslation(parentJoint.transform),
extractTranslation(joint.transform));
}
joint.boneRadius = 0.0f;
joint.inverseBindRotation = joint.inverseDefaultRotation;
geometry.joints.append(joint);
geometry.jointIndices.insert(model.name, geometry.joints.size() - 1);
@ -1274,7 +1276,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
// whether we're skinned depends on how many clusters are attached
int maxJointIndex = extracted.mesh.clusters.at(0).jointIndex;
const FBXCluster& firstFBXCluster = extracted.mesh.clusters.at(0);
int maxJointIndex = firstFBXCluster.jointIndex;
glm::mat4 inverseModelTransform = glm::inverse(modelTransform);
if (clusterIDs.size() > 1) {
extracted.mesh.clusterIndices.resize(extracted.mesh.vertices.size());
extracted.mesh.clusterWeights.resize(extracted.mesh.vertices.size());
@ -1282,6 +1286,21 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
for (int i = 0; i < clusterIDs.size(); i++) {
QString clusterID = clusterIDs.at(i);
const Cluster& cluster = clusters[clusterID];
const FBXCluster& fbxCluster = extracted.mesh.clusters.at(i);
int jointIndex = fbxCluster.jointIndex;
FBXJoint& joint = geometry.joints[jointIndex];
glm::vec3 boneEnd = extractTranslation(inverseModelTransform * joint.bindTransform);
glm::vec3 boneDirection;
float boneLength;
if (joint.parentIndex != -1) {
boneDirection = boneEnd - extractTranslation(inverseModelTransform *
geometry.joints[joint.parentIndex].bindTransform);
boneLength = glm::length(boneDirection);
if (boneLength > EPSILON) {
boneDirection /= boneLength;
}
}
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
float totalWeight = 0.0f;
for (int j = 0; j < cluster.indices.size(); j++) {
int oldIndex = cluster.indices.at(j);
@ -1289,9 +1308,19 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
totalWeight += weight;
for (QMultiHash<int, int>::const_iterator it = extracted.newIndices.constFind(oldIndex);
it != extracted.newIndices.end() && it.key() == oldIndex; it++) {
glm::vec4& weights = extracted.mesh.clusterWeights[it.value()];
// expand the bone radius for vertices with at least 1/4 weight
const float EXPANSION_WEIGHT_THRESHOLD = 0.25f;
if (weight > EXPANSION_WEIGHT_THRESHOLD) {
const glm::vec3& vertex = extracted.mesh.vertices.at(it.value());
float proj = glm::dot(boneDirection, vertex - boneEnd);
if (proj < 0.0f && proj > -boneLength) {
joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(
vertex, boneEnd + boneDirection * proj));
}
}
// look for an unused slot in the weights vector
glm::vec4& weights = extracted.mesh.clusterWeights[it.value()];
for (int k = 0; k < 4; k++) {
if (weights[k] == 0.0f) {
extracted.mesh.clusterIndices[it.value()][k] = i;
@ -1303,7 +1332,29 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
if (totalWeight > maxWeight) {
maxWeight = totalWeight;
maxJointIndex = extracted.mesh.clusters.at(i).jointIndex;
maxJointIndex = jointIndex;
}
}
} else {
int jointIndex = maxJointIndex;
FBXJoint& joint = geometry.joints[jointIndex];
glm::vec3 boneEnd = extractTranslation(inverseModelTransform * joint.bindTransform);
glm::vec3 boneDirection;
float boneLength;
if (joint.parentIndex != -1) {
boneDirection = boneEnd - extractTranslation(inverseModelTransform *
geometry.joints[joint.parentIndex].bindTransform);
boneLength = glm::length(boneDirection);
if (boneLength > EPSILON) {
boneDirection /= boneLength;
}
}
float radiusScale = extractUniformScale(joint.transform * firstFBXCluster.inverseBindMatrix);
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
float proj = glm::dot(boneDirection, vertex - boneEnd);
if (proj < 0.0f && proj > -boneLength) {
joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(
vertex, boneEnd + boneDirection * proj));
}
}
}

View file

@ -47,6 +47,7 @@ public:
QVector<int> freeLineage;
int parentIndex;
float distanceToParent;
float boneRadius;
glm::mat4 preTransform;
glm::quat preRotation;
glm::quat rotation;

View file

@ -8,6 +8,8 @@
#include <glm/gtx/transform.hpp>
#include <GeometryUtil.h>
#include "Application.h"
#include "Model.h"
@ -452,6 +454,14 @@ bool Model::render(float alpha) {
return true;
}
int Model::getParentJointIndex(int jointIndex) const {
return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).parentIndex : -1;
}
int Model::getLastFreeJointIndex(int jointIndex) const {
return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1;
}
bool Model::getHeadPosition(glm::vec3& headPosition) const {
return isActive() && getJointPosition(_geometry->getFBXGeometry().headJointIndex, headPosition);
}
@ -474,35 +484,35 @@ bool Model::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePos
}
bool Model::setLeftHandPosition(const glm::vec3& position) {
return isActive() && setJointPosition(_geometry->getFBXGeometry().leftHandJointIndex, position);
return setJointPosition(getLeftHandJointIndex(), position);
}
bool Model::restoreLeftHandPosition(float percent) {
return isActive() && restoreJointPosition(_geometry->getFBXGeometry().leftHandJointIndex, percent);
return restoreJointPosition(getLeftHandJointIndex(), percent);
}
bool Model::setLeftHandRotation(const glm::quat& rotation) {
return isActive() && setJointRotation(_geometry->getFBXGeometry().leftHandJointIndex, rotation);
return setJointRotation(getLeftHandJointIndex(), rotation);
}
float Model::getLeftArmLength() const {
return isActive() ? getLimbLength(_geometry->getFBXGeometry().leftHandJointIndex) : 0.0f;
return getLimbLength(getLeftHandJointIndex());
}
bool Model::setRightHandPosition(const glm::vec3& position) {
return isActive() && setJointPosition(_geometry->getFBXGeometry().rightHandJointIndex, position);
return setJointPosition(getRightHandJointIndex(), position);
}
bool Model::restoreRightHandPosition(float percent) {
return isActive() && restoreJointPosition(_geometry->getFBXGeometry().rightHandJointIndex, percent);
return restoreJointPosition(getRightHandJointIndex(), percent);
}
bool Model::setRightHandRotation(const glm::quat& rotation) {
return isActive() && setJointRotation(_geometry->getFBXGeometry().rightHandJointIndex, rotation);
return setJointRotation(getRightHandJointIndex(), rotation);
}
float Model::getRightArmLength() const {
return isActive() ? getLimbLength(_geometry->getFBXGeometry().rightHandJointIndex) : 0.0f;
return getLimbLength(getRightHandJointIndex());
}
void Model::setURL(const QUrl& url) {
@ -523,6 +533,48 @@ glm::vec4 Model::computeAverageColor() const {
return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
}
bool Model::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
glm::vec3& penetration, float boneScale, int skipIndex) const {
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++) {
const FBXJoint& joint = geometry.joints[i];
glm::vec3 end = extractTranslation(_jointStates[i].transform);
float endRadius = joint.boneRadius * radiusScale;
glm::vec3 start = end;
float startRadius = joint.boneRadius * radiusScale;
glm::vec3 bonePenetration;
if (joint.parentIndex != -1) {
if (skipIndex != -1) {
int ancestorIndex = joint.parentIndex;
do {
if (ancestorIndex == skipIndex) {
goto outerContinue;
}
ancestorIndex = geometry.joints[ancestorIndex].parentIndex;
} while (ancestorIndex != -1);
}
start = extractTranslation(_jointStates[joint.parentIndex].transform);
startRadius = geometry.joints[joint.parentIndex].boneRadius * radiusScale;
}
if (findSphereCapsuleConePenetration(relativeCenter, penetratorRadius, start, end,
startRadius, endRadius, bonePenetration)) {
totalPenetration = addPenetrations(totalPenetration, bonePenetration);
didPenetrate = true;
}
outerContinue: ;
}
if (didPenetrate) {
penetration = totalPenetration;
return true;
}
return false;
}
void Model::updateJointState(int index) {
JointState& state = _jointStates[index];
const FBXGeometry& geometry = _geometry->getFBXGeometry();
@ -680,10 +732,11 @@ float Model::getLimbLength(int jointIndex) const {
return length;
}
void Model::applyRotationDelta(int jointIndex, const glm::quat& delta) {
void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain) {
JointState& state = _jointStates[jointIndex];
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
if (joint.rotationMin == glm::vec3(-180.0f, -180.0f, -180.0f) && joint.rotationMax == glm::vec3(180.0f, 180.0f, 180.0f)) {
if (!constrain || (joint.rotationMin == glm::vec3(-180.0f, -180.0f, -180.0f) &&
joint.rotationMax == glm::vec3(180.0f, 180.0f, 180.0f))) {
// no constraints
state.rotation = state.rotation * glm::inverse(state.combinedRotation) * delta * state.combinedRotation;
state.combinedRotation = delta * state.combinedRotation;
@ -695,6 +748,40 @@ void Model::applyRotationDelta(int jointIndex, const glm::quat& delta) {
state.rotation = newRotation;
}
void Model::renderCollisionProxies(float alpha) {
glPushMatrix();
Application::getInstance()->loadTranslatedViewMatrix(_translation);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
float uniformScale = extractUniformScale(_scale);
for (int i = 0; i < _jointStates.size(); i++) {
glPushMatrix();
glm::vec3 position = extractTranslation(_jointStates[i].transform);
glTranslatef(position.x, position.y, position.z);
glm::quat rotation;
getJointRotation(i, rotation);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
glColor4f(0.75f, 0.75f, 0.75f, alpha);
float scaledRadius = geometry.joints[i].boneRadius * uniformScale;
const int BALL_SUBDIVISIONS = 10;
glutSolidSphere(scaledRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS);
glPopMatrix();
int parentIndex = geometry.joints[i].parentIndex;
if (parentIndex != -1) {
Avatar::renderJointConnectingCone(extractTranslation(_jointStates[parentIndex].transform), position,
geometry.joints[parentIndex].boneRadius * uniformScale, scaledRadius);
}
}
glPopMatrix();
}
void Model::setJointTranslation(int jointIndex, int parentIndex, int childIndex, const glm::vec3& translation) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
JointState& state = _jointStates[jointIndex];

View file

@ -54,6 +54,18 @@ public:
Q_INVOKABLE void setURL(const QUrl& url);
const QUrl& getURL() const { return _url; }
/// Returns the index of the left hand joint, or -1 if not found.
int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; }
/// Returns the index of the right hand joint, or -1 if not found.
int getRightHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().rightHandJointIndex : -1; }
/// Returns the index of the parent of the indexed joint, or -1 if not found.
int getParentJointIndex(int jointIndex) const;
/// Returns the index of the last free ancestor or the indexed joint, or -1 if not found.
int getLastFreeJointIndex(int jointIndex) const;
/// Returns the position of the head joint.
/// \return whether or not the head was found
bool getHeadPosition(glm::vec3& headPosition) const;
@ -105,6 +117,9 @@ public:
/// Returns the average color of all meshes in the geometry.
glm::vec4 computeAverageColor() const;
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
glm::vec3& penetration, float boneScale = 1.0f, int skipIndex = -1) const;
protected:
QSharedPointer<NetworkGeometry> _geometry;
@ -157,7 +172,9 @@ protected:
/// first free ancestor.
float getLimbLength(int jointIndex) const;
void applyRotationDelta(int jointIndex, const glm::quat& delta);
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true);
void renderCollisionProxies(float alpha);
private:

View file

@ -24,11 +24,40 @@ HandData::HandData(AvatarData* owningAvatar) :
addNewPalm();
}
glm::vec3 HandData::worldPositionToLeapPosition(const glm::vec3& worldPosition) const {
return glm::inverse(_baseOrientation) * (worldPosition - _basePosition) / LEAP_UNIT_SCALE;
}
glm::vec3 HandData::worldVectorToLeapVector(const glm::vec3& worldVector) const {
return glm::inverse(_baseOrientation) * worldVector / LEAP_UNIT_SCALE;
}
PalmData& HandData::addNewPalm() {
_palms.push_back(PalmData(this));
return _palms.back();
}
void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const {
leftPalmIndex = -1;
float leftPalmX = FLT_MAX;
rightPalmIndex = -1;
float rightPalmX = -FLT_MAX;
for (int i = 0; i < _palms.size(); i++) {
const PalmData& palm = _palms[i];
if (palm.isActive()) {
float x = palm.getRawPosition().x;
if (x < leftPalmX) {
leftPalmIndex = i;
leftPalmX = x;
}
if (x > rightPalmX) {
rightPalmIndex = i;
rightPalmX = x;
}
}
}
}
PalmData::PalmData(HandData* owningHandData) :
_rawPosition(0, 0, 0),
_rawNormal(0, 1, 0),
@ -46,6 +75,19 @@ _isCollidingWithVoxel(false)
}
}
void PalmData::addToPosition(const glm::vec3& delta) {
// convert to Leap coordinates, then add to palm and finger positions
glm::vec3 leapDelta = _owningHandData->worldVectorToLeapVector(delta);
_rawPosition += leapDelta;
for (int i = 0; i < getNumFingers(); i++) {
FingerData& finger = _fingers[i];
if (finger.isActive()) {
finger.setRawTipPosition(finger.getTipRawPosition() + leapDelta);
finger.setRawRootPosition(finger.getRootRawPosition() + leapDelta);
}
}
}
FingerData::FingerData(PalmData* owningPalmData, HandData* owningHandData) :
_tipRawPosition(0, 0, 0),
_rootRawPosition(0, 0, 0),

View file

@ -32,6 +32,8 @@ const int BUTTON_3 = 8;
const int BUTTON_4 = 16;
const int BUTTON_FWD = 128;
const float LEAP_UNIT_SCALE = 0.001f; ///< convert mm to meters
class HandData {
public:
HandData(AvatarData* owningAvatar);
@ -39,20 +41,25 @@ public:
// These methods return the positions in Leap-relative space.
// To convert to world coordinates, use Hand::leapPositionToWorldPosition.
// position conversion
glm::vec3 leapPositionToWorldPosition(const glm::vec3& leapPosition) {
const float unitScale = 0.001; // convert mm to meters
return _basePosition + _baseOrientation * (leapPosition * unitScale);
return _basePosition + _baseOrientation * (leapPosition * LEAP_UNIT_SCALE);
}
glm::vec3 leapDirectionToWorldDirection(const glm::vec3& leapDirection) {
return glm::normalize(_baseOrientation * leapDirection);
}
glm::vec3 worldPositionToLeapPosition(const glm::vec3& worldPosition) const;
glm::vec3 worldVectorToLeapVector(const glm::vec3& worldVector) const;
std::vector<PalmData>& getPalms() { return _palms; }
size_t getNumPalms() { return _palms.size(); }
PalmData& addNewPalm();
/// Finds the indices of the left and right palms according to their locations, or -1 if either or
/// both is not found.
void getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const;
void setFingerTrailLength(unsigned int length);
void updateFingerTrails();
@ -134,6 +141,8 @@ public:
void setVelocity(const glm::vec3& velocity) { _velocity = velocity; }
const glm::vec3& getVelocity() const { return _velocity; }
void addToPosition(const glm::vec3& delta);
void incrementFramesWithoutData() { _numFramesWithoutData++; }
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
int getFramesWithoutData() const { return _numFramesWithoutData; }

View file

@ -52,6 +52,12 @@ bool findSpherePointPenetration(const glm::vec3& penetratorCenter, float penetra
penetratorRadius, penetration);
}
bool findPointSpherePenetration(const glm::vec3& penetratorLocation, const glm::vec3& penetrateeCenter,
float penetrateeRadius, glm::vec3& penetration) {
return findSpherePenetration(penetrateeCenter - penetratorLocation, glm::vec3(0.0f, -1.0f, 0.0f),
penetrateeRadius, penetration);
}
bool findSphereSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
const glm::vec3& penetrateeCenter, float penetrateeRadius, glm::vec3& penetration) {
return findSpherePointPenetration(penetratorCenter, penetratorRadius + penetrateeRadius, penetrateeCenter, penetration);
@ -69,6 +75,35 @@ bool findSphereCapsulePenetration(const glm::vec3& penetratorCenter, float penet
penetrateeStart, penetrateeEnd, penetration);
}
bool findPointCapsuleConePenetration(const glm::vec3& penetratorLocation, const glm::vec3& penetrateeStart,
const glm::vec3& penetrateeEnd, float penetrateeStartRadius, float penetrateeEndRadius, glm::vec3& penetration) {
// compute the projection of the point vector onto the segment vector
glm::vec3 segmentVector = penetrateeEnd - penetrateeStart;
float lengthSquared = glm::dot(segmentVector, segmentVector);
if (lengthSquared < EPSILON) { // start and end the same
return findPointSpherePenetration(penetratorLocation, penetrateeStart,
glm::max(penetrateeStartRadius, penetrateeEndRadius), penetration);
}
float proj = glm::dot(penetratorLocation - penetrateeStart, segmentVector) / lengthSquared;
if (proj <= 0.0f) { // closest to the start
return findPointSpherePenetration(penetratorLocation, penetrateeStart, penetrateeStartRadius, penetration);
} else if (proj >= 1.0f) { // closest to the end
return findPointSpherePenetration(penetratorLocation, penetrateeEnd, penetrateeEndRadius, penetration);
} else { // closest to the middle
return findPointSpherePenetration(penetratorLocation, penetrateeStart + segmentVector * proj,
glm::mix(penetrateeStartRadius, penetrateeEndRadius, proj), penetration);
}
}
bool findSphereCapsuleConePenetration(const glm::vec3& penetratorCenter,
float penetratorRadius, const glm::vec3& penetrateeStart, const glm::vec3& penetrateeEnd,
float penetrateeStartRadius, float penetrateeEndRadius, glm::vec3& penetration) {
return findPointCapsuleConePenetration(penetratorCenter, penetrateeStart, penetrateeEnd,
penetrateeStartRadius + penetratorRadius, penetrateeEndRadius + penetratorRadius, penetration);
}
bool findSpherePlanePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
const glm::vec4& penetrateePlane, glm::vec3& penetration) {
float distance = glm::dot(penetrateePlane, glm::vec4(penetratorCenter, 1.0f)) - penetratorRadius;

View file

@ -19,6 +19,9 @@ bool findSpherePenetration(const glm::vec3& penetratorToPenetratee, const glm::v
bool findSpherePointPenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
const glm::vec3& penetrateeLocation, glm::vec3& penetration);
bool findPointSpherePenetration(const glm::vec3& penetratorLocation, const glm::vec3& penetrateeCenter,
float penetrateeRadius, glm::vec3& penetration);
bool findSphereSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
const glm::vec3& penetrateeCenter, float penetrateeRadius, glm::vec3& penetration);
@ -27,6 +30,13 @@ bool findSphereSegmentPenetration(const glm::vec3& penetratorCenter, float penet
bool findSphereCapsulePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, const glm::vec3& penetrateeStart,
const glm::vec3& penetrateeEnd, float penetrateeRadius, glm::vec3& penetration);
bool findPointCapsuleConePenetration(const glm::vec3& penetratorLocation, const glm::vec3& penetrateeStart,
const glm::vec3& penetrateeEnd, float penetrateeStartRadius, float penetrateeEndRadius, glm::vec3& penetration);
bool findSphereCapsuleConePenetration(const glm::vec3& penetratorCenter,
float penetratorRadius, const glm::vec3& penetrateeStart, const glm::vec3& penetrateeEnd,
float penetrateeStartRadius, float penetrateeEndRadius, glm::vec3& penetration);
bool findSpherePlanePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
const glm::vec4& penetrateePlane, glm::vec3& penetration);