mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 12:04:18 +02:00
Merge pull request #1452 from ey6es/master
First cut at orbit behavior (plus ray/capsule intersection test for avatar picking).
This commit is contained in:
commit
607d8332e3
11 changed files with 170 additions and 22 deletions
|
@ -1105,9 +1105,24 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
}
|
||||
|
||||
if (activeWindow() == _window) {
|
||||
int deltaX = event->x() - _mouseX;
|
||||
int deltaY = event->y() - _mouseY;
|
||||
|
||||
_mouseX = event->x();
|
||||
_mouseY = event->y();
|
||||
|
||||
// orbit behavior
|
||||
if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) {
|
||||
if (_lookatTargetAvatar) {
|
||||
_myAvatar.orbit(_lookatTargetAvatar->getPosition(), deltaX, deltaY);
|
||||
return;
|
||||
}
|
||||
if (_isHoverVoxel) {
|
||||
_myAvatar.orbit(glm::vec3(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z) * (float)TREE_SCALE, deltaX, deltaY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// detect drag
|
||||
glm::vec3 mouseVoxelPos(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z);
|
||||
if (!_justEditedVoxel && mouseVoxelPos != _lastMouseVoxelPos) {
|
||||
|
@ -1151,7 +1166,8 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
}
|
||||
|
||||
if (!_palette.isActive() && (!_isHoverVoxel || _lookatTargetAvatar)) {
|
||||
_pieMenu.mousePressEvent(_mouseX, _mouseY);
|
||||
// disable for now
|
||||
// _pieMenu.mousePressEvent(_mouseX, _mouseY);
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && _pasteMode) {
|
||||
|
@ -1952,7 +1968,9 @@ void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, cons
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()");
|
||||
|
||||
_lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF);
|
||||
if (!_mousePressed) {
|
||||
_lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF);
|
||||
}
|
||||
}
|
||||
|
||||
Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
|
||||
|
@ -1961,17 +1979,15 @@ Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, con
|
|||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* avatar = (Avatar *) node->getLinkedData();
|
||||
glm::vec3 headPosition = avatar->getHead().getPosition();
|
||||
Avatar* avatar = (Avatar*)node->getLinkedData();
|
||||
float distance;
|
||||
if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition,
|
||||
HEAD_SPHERE_RADIUS * avatar->getHead().getScale(), distance)) {
|
||||
if (avatar->findRayIntersection(mouseRayOrigin, mouseRayDirection, distance)) {
|
||||
// rescale to compensate for head embiggening
|
||||
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) *
|
||||
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot();
|
||||
|
||||
_lookatIndicatorScale = avatar->getHead().getScale();
|
||||
_lookatOtherPosition = headPosition;
|
||||
_lookatOtherPosition = avatar->getHead().getPosition();
|
||||
nodeUUID = avatar->getOwningNode()->getUUID();
|
||||
return avatar;
|
||||
}
|
||||
|
@ -2213,7 +2229,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
|
|||
glm::vec4 oldVoxel(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s);
|
||||
// only do this work if MAKE_SOUND_ON_VOXEL_HOVER or MAKE_SOUND_ON_VOXEL_CLICK is enabled,
|
||||
// and make sure the tree is not already busy... because otherwise you'll have to wait.
|
||||
if (!_voxels.treeIsBusy()) {
|
||||
if (!(_voxels.treeIsBusy() || _mousePressed)) {
|
||||
{
|
||||
PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()");
|
||||
_isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face);
|
||||
|
|
|
@ -190,10 +190,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
// update avatar skeleton
|
||||
_skeleton.update(deltaTime, getOrientation(), _position);
|
||||
|
||||
|
||||
// if this is not my avatar, then hand position comes from transmitted data
|
||||
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition;
|
||||
|
||||
_hand.simulate(deltaTime, false);
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
|
@ -340,8 +336,24 @@ void Avatar::getSkinColors(glm::vec3& lighter, glm::vec3& darker) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
float minDistance = FLT_MAX;
|
||||
float modelDistance;
|
||||
if (_skeletonModel.findRayIntersection(origin, direction, modelDistance)) {
|
||||
minDistance = qMin(minDistance, modelDistance);
|
||||
}
|
||||
if (_head.getFaceModel().findRayIntersection(origin, direction, modelDistance)) {
|
||||
minDistance = qMin(minDistance, modelDistance);
|
||||
}
|
||||
if (minDistance < FLT_MAX) {
|
||||
distance = minDistance;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Avatar::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
glm::vec3& penetration, int skeletonSkipIndex) {
|
||||
glm::vec3& penetration, int skeletonSkipIndex) const {
|
||||
bool didPenetrate = false;
|
||||
glm::vec3 totalPenetration;
|
||||
glm::vec3 skeletonPenetration;
|
||||
|
|
|
@ -160,6 +160,8 @@ public:
|
|||
|
||||
void getSkinColors(glm::vec3& lighter, glm::vec3& darker);
|
||||
|
||||
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
|
||||
|
@ -167,7 +169,7 @@ public:
|
|||
/// \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);
|
||||
glm::vec3& penetration, int skeletonSkipIndex = -1) const;
|
||||
|
||||
virtual int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ public:
|
|||
|
||||
VideoFace& getVideoFace() { return _videoFace; }
|
||||
FaceModel& getFaceModel() { return _faceModel; }
|
||||
const FaceModel& getFaceModel() const { return _faceModel; }
|
||||
|
||||
const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
|
||||
float getAverageLoudness() const { return _averageLoudness; }
|
||||
|
|
|
@ -542,6 +542,16 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
settings->endGroup();
|
||||
}
|
||||
|
||||
void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
|
||||
glm::vec3 vector = getPosition() - position;
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
const float ANGULAR_SCALE = 0.5f;
|
||||
glm::quat rotation = glm::angleAxis(deltaX * -ANGULAR_SCALE, up);
|
||||
const float LINEAR_SCALE = 0.01f;
|
||||
setPosition(position + rotation * vector + up * (deltaY * LINEAR_SCALE * _scale));
|
||||
setOrientation(rotation * orientation);
|
||||
}
|
||||
|
||||
float MyAvatar::getAbsoluteHeadYaw() const {
|
||||
return glm::yaw(_head.getOrientation());
|
||||
|
@ -757,15 +767,11 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
|
|||
glm::vec3 farVector = _mouseRayOrigin + pointDirection * (float)TREE_SCALE - shoulderPosition;
|
||||
const float ARM_RETRACTION = 0.75f;
|
||||
float retractedLength = _skeletonModel.getRightArmLength() * ARM_RETRACTION;
|
||||
_skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position = shoulderPosition +
|
||||
glm::normalize(farVector) * retractedLength;
|
||||
setHandPosition(shoulderPosition + glm::normalize(farVector) * retractedLength);
|
||||
pointing = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Set right hand position and state to be transmitted, and also tell AvatarTouch about it
|
||||
setHandPosition(_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position);
|
||||
|
||||
if (_mousePressed) {
|
||||
_handState = HAND_STATE_GRASPING;
|
||||
} else if (pointing) {
|
||||
|
@ -919,8 +925,9 @@ void MyAvatar::updateChatCircle(float deltaTime) {
|
|||
// remove members whose accumulated circles are too far away to influence us
|
||||
const float CIRCUMFERENCE_PER_MEMBER = 0.5f;
|
||||
const float CIRCLE_INFLUENCE_SCALE = 2.0f;
|
||||
const float MIN_RADIUS = 0.3f;
|
||||
for (int i = sortedAvatars.size() - 1; i >= 0; i--) {
|
||||
float radius = (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO;
|
||||
float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO);
|
||||
if (glm::distance(_position, sortedAvatars[i].accumulatedCenter) > radius * CIRCLE_INFLUENCE_SCALE) {
|
||||
sortedAvatars.remove(i);
|
||||
} else {
|
||||
|
@ -931,7 +938,7 @@ void MyAvatar::updateChatCircle(float deltaTime) {
|
|||
return;
|
||||
}
|
||||
center = sortedAvatars.last().accumulatedCenter;
|
||||
float radius = (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO;
|
||||
float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO);
|
||||
|
||||
// compute the average up vector
|
||||
glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP;
|
||||
|
|
|
@ -69,6 +69,8 @@ public:
|
|||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
void orbit(const glm::vec3& position, int deltaX, int deltaY);
|
||||
|
||||
private:
|
||||
bool _mousePressed;
|
||||
float _bodyPitchDelta;
|
||||
|
|
|
@ -546,6 +546,35 @@ glm::vec4 Model::computeAverageColor() const {
|
|||
return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
const glm::vec3 relativeOrigin = origin - _translation;
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
float minDistance = FLT_MAX;
|
||||
float radiusScale = extractUniformScale(_scale);
|
||||
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;
|
||||
if (joint.parentIndex != -1) {
|
||||
start = extractTranslation(_jointStates[joint.parentIndex].transform);
|
||||
startRadius = geometry.joints[joint.parentIndex].boneRadius * radiusScale;
|
||||
}
|
||||
// for now, use average of start and end radii
|
||||
float capsuleDistance;
|
||||
if (findRayCapsuleIntersection(relativeOrigin, direction, start, end,
|
||||
(startRadius + endRadius) / 2.0f, capsuleDistance)) {
|
||||
minDistance = qMin(minDistance, capsuleDistance);
|
||||
}
|
||||
}
|
||||
if (minDistance < FLT_MAX) {
|
||||
distance = minDistance;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Model::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
glm::vec3& penetration, float boneScale, int skipIndex) const {
|
||||
const glm::vec3 relativeCenter = penetratorCenter - _translation;
|
||||
|
|
|
@ -125,6 +125,8 @@ public:
|
|||
/// Returns the average color of all meshes in the geometry.
|
||||
glm::vec4 computeAverageColor() const;
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
/// \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) { return false; }
|
||||
glm::vec3& penetration, int skeletonSkipIndex = -1) const { return false; }
|
||||
|
||||
protected:
|
||||
QUuid _uuid;
|
||||
|
|
|
@ -154,6 +154,77 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3&
|
|||
newPenetration - (currentDirection * directionalComponent);
|
||||
}
|
||||
|
||||
bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const glm::vec3& center, float radius, float& distance) {
|
||||
glm::vec3 relativeOrigin = origin - center;
|
||||
float c = glm::dot(relativeOrigin, relativeOrigin) - radius * radius;
|
||||
if (c < 0.0f) {
|
||||
distance = 0.0f;
|
||||
return true; // starts inside the sphere
|
||||
}
|
||||
float b = glm::dot(direction, relativeOrigin);
|
||||
float radicand = b * b - c;
|
||||
if (radicand < 0.0f) {
|
||||
return false; // doesn't hit the sphere
|
||||
}
|
||||
float t = -b - sqrtf(radicand);
|
||||
if (t < 0.0f) {
|
||||
return false; // doesn't hit the sphere
|
||||
}
|
||||
distance = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const glm::vec3& start, const glm::vec3& end, float radius, float& distance) {
|
||||
if (start == end) {
|
||||
return findRaySphereIntersection(origin, direction, start, radius, distance); // handle degenerate case
|
||||
}
|
||||
glm::vec3 relativeOrigin = origin - start;
|
||||
glm::vec3 relativeEnd = end - start;
|
||||
float capsuleLength = glm::length(relativeEnd);
|
||||
relativeEnd /= capsuleLength;
|
||||
float originProjection = glm::dot(relativeEnd, relativeOrigin);
|
||||
glm::vec3 constant = relativeOrigin - relativeEnd * originProjection;
|
||||
float c = glm::dot(constant, constant) - radius * radius;
|
||||
if (c < 0.0f) { // starts inside cylinder
|
||||
if (originProjection < 0.0f) { // below start
|
||||
return findRaySphereIntersection(origin, direction, start, radius, distance);
|
||||
|
||||
} else if (originProjection > capsuleLength) { // above end
|
||||
return findRaySphereIntersection(origin, direction, end, radius, distance);
|
||||
|
||||
} else { // between start and end
|
||||
distance = 0.0f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
glm::vec3 coefficient = direction - relativeEnd * glm::dot(relativeEnd, direction);
|
||||
float a = glm::dot(coefficient, coefficient);
|
||||
if (a == 0.0f) {
|
||||
return false; // parallel to enclosing cylinder
|
||||
}
|
||||
float b = 2.0f * glm::dot(constant, coefficient);
|
||||
float radicand = b * b - 4.0f * a * c;
|
||||
if (radicand < 0.0f) {
|
||||
return false; // doesn't hit the enclosing cylinder
|
||||
}
|
||||
float t = (-b - sqrtf(radicand)) / (2.0f * a);
|
||||
if (t < 0.0f) {
|
||||
return false; // doesn't hit the enclosing cylinder
|
||||
}
|
||||
glm::vec3 intersection = relativeOrigin + direction * t;
|
||||
float intersectionProjection = glm::dot(relativeEnd, intersection);
|
||||
if (intersectionProjection < 0.0f) { // below start
|
||||
return findRaySphereIntersection(origin, direction, start, radius, distance);
|
||||
|
||||
} else if (intersectionProjection > capsuleLength) { // above end
|
||||
return findRaySphereIntersection(origin, direction, end, radius, distance);
|
||||
}
|
||||
distance = t; // between start and end
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect?
|
||||
// from: http://ptspts.blogspot.com/2010/06/how-to-determine-if-two-line-segments.html
|
||||
bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2) {
|
||||
|
|
|
@ -49,6 +49,12 @@ bool findCapsulePlanePenetration(const glm::vec3& penetratorStart, const glm::ve
|
|||
|
||||
glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration);
|
||||
|
||||
bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const glm::vec3& center, float radius, float& distance);
|
||||
|
||||
bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const glm::vec3& start, const glm::vec3& end, float radius, float& distance);
|
||||
|
||||
bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2);
|
||||
bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk);
|
||||
int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk);
|
||||
|
|
Loading…
Reference in a new issue