mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 13:50:35 +02:00
258 lines
9.4 KiB
C++
258 lines
9.4 KiB
C++
//
|
|
// Hand.cpp
|
|
// interface/src/avatar
|
|
//
|
|
// Copyright 2013 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 <QImage>
|
|
|
|
#include <NodeList.h>
|
|
|
|
#include <GeometryUtil.h>
|
|
|
|
#include "Application.h"
|
|
#include "Avatar.h"
|
|
#include "Hand.h"
|
|
#include "Menu.h"
|
|
#include "Util.h"
|
|
#include "renderer/ProgramObject.h"
|
|
|
|
using namespace std;
|
|
|
|
const float FINGERTIP_COLLISION_RADIUS = 0.01f;
|
|
const float PALM_COLLISION_RADIUS = 0.03f;
|
|
|
|
|
|
Hand::Hand(Avatar* owningAvatar) :
|
|
HandData((AvatarData*)owningAvatar),
|
|
_owningAvatar(owningAvatar)
|
|
{
|
|
}
|
|
|
|
void Hand::simulate(float deltaTime, bool isMine) {
|
|
if (isMine) {
|
|
// Iterate hand controllers, take actions as needed
|
|
for (size_t i = 0; i < getNumPalms(); ++i) {
|
|
PalmData& palm = getPalms()[i];
|
|
palm.setLastControllerButtons(palm.getControllerButtons());
|
|
}
|
|
}
|
|
}
|
|
|
|
// We create a static CollisionList that is recycled for each collision test.
|
|
const float MAX_COLLISIONS_PER_AVATAR = 32;
|
|
static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR);
|
|
|
|
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
|
if (!avatar || avatar == _owningAvatar) {
|
|
// don't collide hands against ourself (that is done elsewhere)
|
|
return;
|
|
}
|
|
|
|
const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel();
|
|
int jointIndices[2];
|
|
jointIndices[0] = skeletonModel.getLeftHandJointIndex();
|
|
jointIndices[1] = skeletonModel.getRightHandJointIndex();
|
|
|
|
for (size_t i = 0; i < 2; i++) {
|
|
int jointIndex = jointIndices[i];
|
|
if (jointIndex < 0) {
|
|
continue;
|
|
}
|
|
|
|
handCollisions.clear();
|
|
QVector<const Shape*> shapes;
|
|
skeletonModel.getHandShapes(jointIndex, shapes);
|
|
|
|
if (avatar->findCollisions(shapes, handCollisions)) {
|
|
glm::vec3 totalPenetration(0.f);
|
|
glm::vec3 averageContactPoint;
|
|
for (int j = 0; j < handCollisions.size(); ++j) {
|
|
CollisionInfo* collision = handCollisions.getCollision(j);
|
|
totalPenetration += collision->_penetration;
|
|
averageContactPoint += collision->_contactPoint;
|
|
}
|
|
if (isMyHand) {
|
|
// our hand against other avatar
|
|
// TODO: resolve this penetration when we don't think the other avatar will yield
|
|
//palm.addToPenetration(averagePenetration);
|
|
} else {
|
|
// someone else's hand against MyAvatar
|
|
// TODO: submit collision info to MyAvatar which should lean accordingly
|
|
averageContactPoint /= (float)handCollisions.size();
|
|
avatar->applyCollision(averageContactPoint, totalPenetration);
|
|
|
|
CollisionInfo collision;
|
|
collision._penetration = totalPenetration;
|
|
collision._contactPoint = averageContactPoint;
|
|
emit avatar->collisionWithAvatar(avatar->getSessionUUID(), _owningAvatar->getSessionUUID(), collision);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Hand::collideAgainstOurself() {
|
|
if (!Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) {
|
|
return;
|
|
}
|
|
|
|
int leftPalmIndex, rightPalmIndex;
|
|
getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
|
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
|
|
|
|
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
|
|
for (int i = 0; i < int(getNumPalms()); i++) {
|
|
PalmData& palm = getPalms()[i];
|
|
if (!palm.isActive()) {
|
|
continue;
|
|
}
|
|
// ignoring everything below the parent of the parent of the last free joint
|
|
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
|
|
skeletonModel.getLastFreeJointIndex((int(i) == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
|
|
(int(i) == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
|
|
|
|
handCollisions.clear();
|
|
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions, skipIndex)) {
|
|
glm::vec3 totalPenetration;
|
|
for (int j = 0; j < handCollisions.size(); ++j) {
|
|
CollisionInfo* collision = handCollisions.getCollision(j);
|
|
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
|
}
|
|
// resolve penetration
|
|
palm.addToPenetration(totalPenetration);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Hand::resolvePenetrations() {
|
|
for (size_t i = 0; i < getNumPalms(); ++i) {
|
|
PalmData& palm = getPalms()[i];
|
|
palm.resolvePenetrations();
|
|
}
|
|
}
|
|
|
|
void Hand::render(bool isMine, Model::RenderMode renderMode) {
|
|
if (renderMode != Model::SHADOW_RENDER_MODE &&
|
|
Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
|
|
// draw a green sphere at hand joint location, which is actually near the wrist)
|
|
for (size_t 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 (renderMode != Model::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
|
|
renderHandTargets(isMine);
|
|
}
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glEnable(GL_RESCALE_NORMAL);
|
|
}
|
|
|
|
void Hand::renderHandTargets(bool isMine) {
|
|
glPushMatrix();
|
|
|
|
const float alpha = 1.0f;
|
|
const glm::vec3 handColor(1.0, 0.0, 0.0); // Color the hand targets red to be different than skin
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_TRUE);
|
|
|
|
if (isMine && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) {
|
|
for (size_t i = 0; i < getNumPalms(); ++i) {
|
|
PalmData& palm = getPalms()[i];
|
|
if (!palm.isActive()) {
|
|
continue;
|
|
}
|
|
glm::vec3 targetPosition;
|
|
palm.getBallHoldPosition(targetPosition);
|
|
glPushMatrix();
|
|
glTranslatef(targetPosition.x, targetPosition.y, targetPosition.z);
|
|
|
|
const float collisionRadius = 0.05f;
|
|
glColor4f(0.5f,0.5f,0.5f, alpha);
|
|
glutWireSphere(collisionRadius, 10.f, 10.f);
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
|
|
const float PALM_BALL_RADIUS = 0.03f;
|
|
const float PALM_DISK_RADIUS = 0.06f;
|
|
const float PALM_DISK_THICKNESS = 0.01f;
|
|
const float PALM_FINGER_ROD_RADIUS = 0.003f;
|
|
|
|
// Draw the palm ball and disk
|
|
for (size_t i = 0; i < getNumPalms(); ++i) {
|
|
PalmData& palm = getPalms()[i];
|
|
if (palm.isActive()) {
|
|
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
|
|
FingerData& finger = palm.getFingers()[f];
|
|
if (finger.isActive()) {
|
|
glColor4f(handColor.r, handColor.g, handColor.b, alpha);
|
|
glm::vec3 tip = finger.getTipPosition();
|
|
glm::vec3 root = finger.getRootPosition();
|
|
Avatar::renderJointConnectingCone(root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS);
|
|
// Render sphere at palm/finger root
|
|
glm::vec3 palmNormal = root + palm.getNormal() * PALM_DISK_THICKNESS;
|
|
Avatar::renderJointConnectingCone(root, palmNormal, PALM_DISK_RADIUS, 0.0f);
|
|
glPushMatrix();
|
|
glTranslatef(root.x, root.y, root.z);
|
|
glutSolidSphere(PALM_BALL_RADIUS, 20.0f, 20.0f);
|
|
glPopMatrix();
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Draw the hand paddles
|
|
int MAX_NUM_PADDLES = 2; // one for left and one for right
|
|
glColor4f(handColor.r, handColor.g, handColor.b, 0.3f);
|
|
for (int i = 0; i < MAX_NUM_PADDLES; i++) {
|
|
const PalmData* palm = getPalm(i);
|
|
if (palm) {
|
|
// compute finger axis
|
|
glm::vec3 fingerAxis(0.f);
|
|
for (size_t f = 0; f < palm->getNumFingers(); ++f) {
|
|
const FingerData& finger = (palm->getFingers())[f];
|
|
if (finger.isActive()) {
|
|
glm::vec3 fingerTip = finger.getTipPosition();
|
|
glm::vec3 fingerRoot = finger.getRootPosition();
|
|
fingerAxis = glm::normalize(fingerTip - fingerRoot);
|
|
break;
|
|
}
|
|
}
|
|
// compute paddle position
|
|
glm::vec3 handPosition;
|
|
if (i == SIXENSE_CONTROLLER_ID_LEFT_HAND) {
|
|
_owningAvatar->getSkeletonModel().getLeftHandPosition(handPosition);
|
|
} else if (i == SIXENSE_CONTROLLER_ID_RIGHT_HAND) {
|
|
_owningAvatar->getSkeletonModel().getRightHandPosition(handPosition);
|
|
}
|
|
glm::vec3 tip = handPosition + HAND_PADDLE_OFFSET * fingerAxis;
|
|
glm::vec3 root = tip + palm->getNormal() * HAND_PADDLE_THICKNESS;
|
|
// render a very shallow cone as the paddle
|
|
Avatar::renderJointConnectingCone(root, tip, HAND_PADDLE_RADIUS, 0.f);
|
|
}
|
|
}
|
|
*/
|
|
|
|
glDepthMask(GL_TRUE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glPopMatrix();
|
|
}
|
|
|