mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-19 22:22:11 +02:00
218 lines
8.9 KiB
C++
218 lines
8.9 KiB
C++
//
|
|
// Created by Bradley Austin Davis on 2017/10/24
|
|
// Copyright 2013-2017 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 "StylusPick.h"
|
|
|
|
#include "RayPick.h"
|
|
|
|
#include <glm/glm.hpp>
|
|
|
|
#include "Application.h"
|
|
#include <DependencyManager.h>
|
|
#include "avatar/AvatarManager.h"
|
|
|
|
#include <controllers/StandardControls.h>
|
|
#include <controllers/UserInputMapper.h>
|
|
|
|
#include <EntityTreeElement.h>
|
|
|
|
using namespace bilateral;
|
|
float StylusPick::WEB_STYLUS_LENGTH = 0.2f;
|
|
|
|
struct SideData {
|
|
QString avatarJoint;
|
|
QString cameraJoint;
|
|
controller::StandardPoseChannel channel;
|
|
controller::Hand hand;
|
|
glm::vec3 grabPointSphereOffset;
|
|
|
|
int getJointIndex(bool finger) {
|
|
const auto& jointName = finger ? avatarJoint : cameraJoint;
|
|
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getJointIndex(jointName);
|
|
}
|
|
};
|
|
|
|
static const std::array<SideData, 2> SIDES{ { { "LeftHandIndex4",
|
|
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
|
|
controller::StandardPoseChannel::LEFT_HAND,
|
|
controller::Hand::LEFT,
|
|
{ -0.04f, 0.13f, 0.039f } },
|
|
{ "RightHandIndex4",
|
|
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND",
|
|
controller::StandardPoseChannel::RIGHT_HAND,
|
|
controller::Hand::RIGHT,
|
|
{ 0.04f, 0.13f, 0.039f } } } };
|
|
|
|
std::shared_ptr<PickResult> StylusPickResult::compareAndProcessNewResult(const std::shared_ptr<PickResult>& newRes) {
|
|
auto newStylusResult = std::static_pointer_cast<StylusPickResult>(newRes);
|
|
if (newStylusResult && newStylusResult->distance < distance) {
|
|
return std::make_shared<StylusPickResult>(*newStylusResult);
|
|
} else {
|
|
return std::make_shared<StylusPickResult>(*this);
|
|
}
|
|
}
|
|
|
|
bool StylusPickResult::checkOrFilterAgainstMaxDistance(float maxDistance) {
|
|
return distance < maxDistance;
|
|
}
|
|
|
|
StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled, const glm::vec3& tipOffset) :
|
|
Pick(StylusTip(side, tipOffset), filter, maxDistance, enabled)
|
|
{
|
|
}
|
|
|
|
static StylusTip getFingerWorldLocation(Side side) {
|
|
const auto& sideData = SIDES[index(side)];
|
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
|
auto fingerJointIndex = myAvatar->getJointIndex(sideData.avatarJoint);
|
|
|
|
if (fingerJointIndex == -1) {
|
|
return StylusTip();
|
|
}
|
|
|
|
auto fingerPosition = myAvatar->getAbsoluteJointTranslationInObjectFrame(fingerJointIndex);
|
|
auto fingerRotation = myAvatar->getAbsoluteJointRotationInObjectFrame(fingerJointIndex);
|
|
auto avatarOrientation = myAvatar->getWorldOrientation();
|
|
auto avatarPosition = myAvatar->getWorldPosition();
|
|
StylusTip result;
|
|
result.side = side;
|
|
result.orientation = avatarOrientation * fingerRotation;
|
|
result.position = avatarPosition + (avatarOrientation * fingerPosition);
|
|
return result;
|
|
}
|
|
|
|
// controllerWorldLocation is where the controller would be, in-world, with an added offset
|
|
static StylusTip getControllerWorldLocation(Side side, const glm::vec3& tipOffset) {
|
|
static const std::array<controller::Input, 2> INPUTS{ { UserInputMapper::makeStandardInput(SIDES[0].channel),
|
|
UserInputMapper::makeStandardInput(SIDES[1].channel) } };
|
|
const auto sideIndex = index(side);
|
|
const auto& input = INPUTS[sideIndex];
|
|
|
|
const auto pose = DependencyManager::get<UserInputMapper>()->getPose(input);
|
|
const auto& valid = pose.valid;
|
|
StylusTip result;
|
|
if (valid) {
|
|
result.side = side;
|
|
const auto& sideData = SIDES[sideIndex];
|
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
|
float sensorScaleFactor = myAvatar->getSensorToWorldScale();
|
|
auto controllerJointIndex = myAvatar->getJointIndex(sideData.cameraJoint);
|
|
|
|
const auto avatarOrientation = myAvatar->getWorldOrientation();
|
|
const auto avatarPosition = myAvatar->getWorldPosition();
|
|
result.orientation = avatarOrientation * myAvatar->getAbsoluteJointRotationInObjectFrame(controllerJointIndex);
|
|
|
|
result.position = avatarPosition + (avatarOrientation * myAvatar->getAbsoluteJointTranslationInObjectFrame(controllerJointIndex));
|
|
// add to the real position so the grab-point is out in front of the hand, a bit
|
|
result.position += result.orientation * (sideData.grabPointSphereOffset * sensorScaleFactor);
|
|
// move the stylus forward a bit
|
|
result.position += result.orientation * (tipOffset * sensorScaleFactor);
|
|
|
|
auto worldControllerPos = avatarPosition + avatarOrientation * pose.translation;
|
|
// compute tip velocity from hand controller motion, it is more accurate than computing it from previous positions.
|
|
auto worldControllerLinearVel = avatarOrientation * pose.velocity;
|
|
auto worldControllerAngularVel = avatarOrientation * pose.angularVelocity;
|
|
result.velocity = worldControllerLinearVel + glm::cross(worldControllerAngularVel, result.position - worldControllerPos);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
StylusTip StylusPick::getMathematicalPick() const {
|
|
StylusTip result;
|
|
if (qApp->getPreferAvatarFingerOverStylus()) {
|
|
result = getFingerWorldLocation(_mathPick.side);
|
|
} else {
|
|
result = getControllerWorldLocation(_mathPick.side, _mathPick.tipOffset);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
PickResultPointer StylusPick::getDefaultResult(const QVariantMap& pickVariant) const {
|
|
return std::make_shared<StylusPickResult>(pickVariant);
|
|
}
|
|
|
|
PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) {
|
|
auto entityTree = qApp->getEntities()->getTree();
|
|
StylusPickResult nearestTarget(pick.toVariantMap());
|
|
for (const auto& target : getIncludeItems()) {
|
|
if (target.isNull()) {
|
|
continue;
|
|
}
|
|
|
|
auto entity = entityTree->findEntityByEntityItemID(target);
|
|
if (!entity) {
|
|
continue;
|
|
}
|
|
|
|
if (!EntityTreeElement::checkFilterSettings(entity, getFilter())) {
|
|
continue;
|
|
}
|
|
|
|
const auto entityRotation = entity->getWorldOrientation();
|
|
const auto entityPosition = entity->getWorldPosition();
|
|
const auto entityType = entity->getType();
|
|
glm::vec3 normal;
|
|
|
|
// TODO: Use the xz projection method for Sphere and Quad.
|
|
if (entityType == EntityTypes::Gizmo) {
|
|
normal = entityRotation * Vectors::UNIT_Y;
|
|
} else {
|
|
normal = entityRotation * Vectors::UNIT_Z;
|
|
}
|
|
float distance = glm::dot(pick.position - entityPosition, normal);
|
|
if (distance < nearestTarget.distance) {
|
|
const auto entityDimensions = entity->getScaledDimensions();
|
|
const auto entityRegistrationPoint = entity->getRegistrationPoint();
|
|
glm::vec3 intersection = pick.position - (normal * distance);
|
|
glm::vec2 pos2D;
|
|
|
|
|
|
auto entityType = entity->getType();
|
|
|
|
if (entityType == EntityTypes::Gizmo) {
|
|
pos2D = RayPick::projectOntoXZPlane(intersection, entityPosition, entityRotation,
|
|
entityDimensions, entityRegistrationPoint, false);
|
|
} else {
|
|
pos2D = RayPick::projectOntoXYPlane(intersection, entityPosition, entityRotation,
|
|
entityDimensions, entityRegistrationPoint, false);
|
|
}
|
|
|
|
if (pos2D == glm::clamp(pos2D, glm::vec2(0), glm::vec2(1))) {
|
|
IntersectionType type = IntersectionType::ENTITY;
|
|
if (getFilter().doesPickLocalEntities()) {
|
|
if (entity->getEntityHostType() == entity::HostType::LOCAL) {
|
|
type = IntersectionType::LOCAL_ENTITY;
|
|
}
|
|
}
|
|
nearestTarget = StylusPickResult(type, target, distance, intersection, pick, normal);
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_shared<StylusPickResult>(nearestTarget);
|
|
}
|
|
|
|
PickResultPointer StylusPick::getAvatarIntersection(const StylusTip& pick) {
|
|
return std::make_shared<StylusPickResult>(pick.toVariantMap());
|
|
}
|
|
|
|
PickResultPointer StylusPick::getHUDIntersection(const StylusTip& pick) {
|
|
return std::make_shared<StylusPickResult>(pick.toVariantMap());
|
|
}
|
|
|
|
Transform StylusPick::getResultTransform() const {
|
|
PickResultPointer result = getPrevPickResult();
|
|
if (!result) {
|
|
return Transform();
|
|
}
|
|
|
|
auto stylusResult = std::static_pointer_cast<StylusPickResult>(result);
|
|
Transform transform;
|
|
transform.setTranslation(stylusResult->intersection);
|
|
return transform;
|
|
}
|