mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-18 13:40:28 +02:00
Better head vs camera checks for avatar head cauterization
* cameraInsideHead() check now uses detailed avatar collision when possible. * head is now more constantly hidden in first person camera mode * getEyeModelPositions() uses a better estimate when avatar eye joints are missing. * moved findPointKDopDisplacement from Rig.cpp into AnimUtil.cpp * added isPlayingOverrideAnimation() method to Rig class
This commit is contained in:
parent
c902e8392c
commit
02d5769991
7 changed files with 112 additions and 95 deletions
|
@ -3180,17 +3180,40 @@ int MyAvatar::sendAvatarDataPacket(bool sendAll) {
|
||||||
return bytesSent;
|
return bytesSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
|
|
||||||
|
|
||||||
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
|
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
|
||||||
|
if (!_skeletonModel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// transform cameraPosition into rig coordinates
|
||||||
|
AnimPose rigToWorld = AnimPose(getWorldOrientation() * Quaternions::Y_180, getWorldPosition());
|
||||||
|
AnimPose worldToRig = rigToWorld.inverse();
|
||||||
|
glm::vec3 rigCameraPosition = worldToRig * cameraPosition;
|
||||||
|
|
||||||
|
// use head k-dop shape to determine if camera is inside head.
|
||||||
|
const Rig& rig = _skeletonModel->getRig();
|
||||||
|
int headJointIndex = rig.indexOfJoint("Head");
|
||||||
|
if (headJointIndex >= 0) {
|
||||||
|
const HFMModel& hfmModel = _skeletonModel->getHFMModel();
|
||||||
|
AnimPose headPose;
|
||||||
|
if (rig.getAbsoluteJointPoseInRigFrame(headJointIndex, headPose)) {
|
||||||
|
glm::vec3 displacement;
|
||||||
|
const HFMJointShapeInfo& headShapeInfo = hfmModel.joints[headJointIndex].shapeInfo;
|
||||||
|
return findPointKDopDisplacement(rigCameraPosition, headPose, headShapeInfo, displacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fall back to simple distance check.
|
||||||
|
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
|
||||||
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale());
|
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
||||||
bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE;
|
bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE;
|
||||||
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
|
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
|
||||||
|
bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false;
|
||||||
bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition());
|
bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition());
|
||||||
return !defaultMode || !firstPerson || !insideHead;
|
return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setHasScriptedBlendshapes(bool hasScriptedBlendshapes) {
|
void MyAvatar::setHasScriptedBlendshapes(bool hasScriptedBlendshapes) {
|
||||||
|
|
|
@ -142,3 +142,72 @@ glm::quat computeBodyFacingFromHead(const glm::quat& headRot, const glm::vec3& u
|
||||||
|
|
||||||
return glmExtractRotation(bodyMat);
|
return glmExtractRotation(bodyMat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const float INV_SQRT_3 = 1.0f / sqrtf(3.0f);
|
||||||
|
const int DOP14_COUNT = 14;
|
||||||
|
const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = {
|
||||||
|
Vectors::UNIT_X,
|
||||||
|
-Vectors::UNIT_X,
|
||||||
|
Vectors::UNIT_Y,
|
||||||
|
-Vectors::UNIT_Y,
|
||||||
|
Vectors::UNIT_Z,
|
||||||
|
-Vectors::UNIT_Z,
|
||||||
|
glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
|
||||||
|
-glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
|
||||||
|
glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
|
||||||
|
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
|
||||||
|
glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
|
||||||
|
-glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
|
||||||
|
glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3),
|
||||||
|
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)
|
||||||
|
};
|
||||||
|
|
||||||
|
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
|
||||||
|
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
|
||||||
|
// such that it lies on the surface of the kdop.
|
||||||
|
bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) {
|
||||||
|
|
||||||
|
// transform point into local space of jointShape.
|
||||||
|
glm::vec3 localPoint = shapePose.inverse().xformPoint(point);
|
||||||
|
|
||||||
|
// Only works for 14-dop shape infos.
|
||||||
|
if (shapeInfo.dots.size() != DOP14_COUNT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 minDisplacement(FLT_MAX);
|
||||||
|
float minDisplacementLen = FLT_MAX;
|
||||||
|
glm::vec3 p = localPoint - shapeInfo.avgPoint;
|
||||||
|
float pLen = glm::length(p);
|
||||||
|
if (pLen > 0.0f) {
|
||||||
|
int slabCount = 0;
|
||||||
|
for (int i = 0; i < DOP14_COUNT; i++) {
|
||||||
|
float dot = glm::dot(p, DOP14_NORMALS[i]);
|
||||||
|
if (dot > 0.0f && dot < shapeInfo.dots[i]) {
|
||||||
|
slabCount++;
|
||||||
|
float distToPlane = pLen * (shapeInfo.dots[i] / dot);
|
||||||
|
float displacementLen = distToPlane - pLen;
|
||||||
|
|
||||||
|
// keep track of the smallest displacement
|
||||||
|
if (displacementLen < minDisplacementLen) {
|
||||||
|
minDisplacementLen = displacementLen;
|
||||||
|
minDisplacement = (p / pLen) * displacementLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) {
|
||||||
|
// we are within the k-dop so push the point along the minimum displacement found
|
||||||
|
displacementOut = shapePose.xformVectorFast(minDisplacement);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// point is outside of kdop
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// point is directly on top of shapeInfo.avgPoint.
|
||||||
|
// push the point out along the x axis.
|
||||||
|
displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -128,4 +128,10 @@ protected:
|
||||||
bool _snapshotValid { false };
|
bool _snapshotValid { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
|
||||||
|
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
|
||||||
|
// such that it lies on the surface of the kdop.
|
||||||
|
bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1521,74 +1521,6 @@ void Rig::updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headPos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const float INV_SQRT_3 = 1.0f / sqrtf(3.0f);
|
|
||||||
const int DOP14_COUNT = 14;
|
|
||||||
const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = {
|
|
||||||
Vectors::UNIT_X,
|
|
||||||
-Vectors::UNIT_X,
|
|
||||||
Vectors::UNIT_Y,
|
|
||||||
-Vectors::UNIT_Y,
|
|
||||||
Vectors::UNIT_Z,
|
|
||||||
-Vectors::UNIT_Z,
|
|
||||||
glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
|
|
||||||
-glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
|
|
||||||
glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
|
|
||||||
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
|
|
||||||
glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
|
|
||||||
-glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
|
|
||||||
glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3),
|
|
||||||
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)
|
|
||||||
};
|
|
||||||
|
|
||||||
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
|
|
||||||
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
|
|
||||||
// such that it lies on the surface of the kdop.
|
|
||||||
static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) {
|
|
||||||
|
|
||||||
// transform point into local space of jointShape.
|
|
||||||
glm::vec3 localPoint = shapePose.inverse().xformPoint(point);
|
|
||||||
|
|
||||||
// Only works for 14-dop shape infos.
|
|
||||||
if (shapeInfo.dots.size() != DOP14_COUNT) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 minDisplacement(FLT_MAX);
|
|
||||||
float minDisplacementLen = FLT_MAX;
|
|
||||||
glm::vec3 p = localPoint - shapeInfo.avgPoint;
|
|
||||||
float pLen = glm::length(p);
|
|
||||||
if (pLen > 0.0f) {
|
|
||||||
int slabCount = 0;
|
|
||||||
for (int i = 0; i < DOP14_COUNT; i++) {
|
|
||||||
float dot = glm::dot(p, DOP14_NORMALS[i]);
|
|
||||||
if (dot > 0.0f && dot < shapeInfo.dots[i]) {
|
|
||||||
slabCount++;
|
|
||||||
float distToPlane = pLen * (shapeInfo.dots[i] / dot);
|
|
||||||
float displacementLen = distToPlane - pLen;
|
|
||||||
|
|
||||||
// keep track of the smallest displacement
|
|
||||||
if (displacementLen < minDisplacementLen) {
|
|
||||||
minDisplacementLen = displacementLen;
|
|
||||||
minDisplacement = (p / pLen) * displacementLen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) {
|
|
||||||
// we are within the k-dop so push the point along the minimum displacement found
|
|
||||||
displacementOut = shapePose.xformVectorFast(minDisplacement);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// point is outside of kdop
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// point is directly on top of shapeInfo.avgPoint.
|
|
||||||
// push the point out along the x axis.
|
|
||||||
displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo,
|
glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo,
|
||||||
const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const {
|
const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const {
|
||||||
glm::vec3 position = handPosition;
|
glm::vec3 position = handPosition;
|
||||||
|
|
|
@ -116,6 +116,7 @@ public:
|
||||||
void destroyAnimGraph();
|
void destroyAnimGraph();
|
||||||
|
|
||||||
void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||||
|
bool isPlayingOverrideAnimation() const { return _userAnimState.clipNodeEnum != UserAnimState::None; };
|
||||||
void restoreAnimation();
|
void restoreAnimation();
|
||||||
|
|
||||||
void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||||
|
|
|
@ -270,28 +270,13 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3&
|
||||||
getJointPosition(_rig.indexOfJoint("RightEye"), secondEyePosition)) {
|
getJointPosition(_rig.indexOfJoint("RightEye"), secondEyePosition)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// no eye joints; try to estimate based on head/neck joints
|
|
||||||
glm::vec3 neckPosition, headPosition;
|
glm::vec3 headPosition;
|
||||||
if (getJointPosition(_rig.indexOfJoint("Neck"), neckPosition) &&
|
if (getJointPosition(_rig.indexOfJoint("Head"), headPosition)) {
|
||||||
getJointPosition(_rig.indexOfJoint("Head"), headPosition)) {
|
float heightRatio = _rig.getUnscaledEyeHeight() / DEFAULT_AVATAR_EYE_HEIGHT;
|
||||||
const float EYE_PROPORTION = 0.6f;
|
glm::vec3 ipdOffset = glm::vec3(DEFAULT_AVATAR_IPD / 2.0f, 0.0f, 0.0f);
|
||||||
glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION);
|
firstEyePosition = headPosition + heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET + ipdOffset);
|
||||||
glm::quat headRotation;
|
secondEyePosition = headPosition + heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET - ipdOffset);
|
||||||
getJointRotation(_rig.indexOfJoint("Head"), headRotation);
|
|
||||||
const float EYES_FORWARD = 0.25f;
|
|
||||||
const float EYE_SEPARATION = 0.1f;
|
|
||||||
float headHeight = glm::distance(neckPosition, headPosition);
|
|
||||||
firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight;
|
|
||||||
secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight;
|
|
||||||
return true;
|
|
||||||
} else if (getJointPosition(_rig.indexOfJoint("Head"), headPosition)) {
|
|
||||||
glm::vec3 baseEyePosition = headPosition;
|
|
||||||
glm::quat headRotation;
|
|
||||||
getJointRotation(_rig.indexOfJoint("Head"), headRotation);
|
|
||||||
const float EYES_FORWARD_HEAD_ONLY = 0.30f;
|
|
||||||
const float EYE_SEPARATION = 0.1f;
|
|
||||||
firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY);
|
|
||||||
secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -42,6 +42,7 @@ const float DEFAULT_AVATAR_HIPS_MASS = 40.0f;
|
||||||
const float DEFAULT_AVATAR_HEAD_MASS = 20.0f;
|
const float DEFAULT_AVATAR_HEAD_MASS = 20.0f;
|
||||||
const float DEFAULT_AVATAR_LEFTHAND_MASS = 2.0f;
|
const float DEFAULT_AVATAR_LEFTHAND_MASS = 2.0f;
|
||||||
const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f;
|
const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f;
|
||||||
|
const float DEFAULT_AVATAR_IPD = 0.064f;
|
||||||
|
|
||||||
// Used when avatar is missing joints... (avatar space)
|
// Used when avatar is missing joints... (avatar space)
|
||||||
const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };
|
const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };
|
||||||
|
|
Loading…
Reference in a new issue