306 lines · 13.7 KB · C++
RawDownload
////  LaserPointer.cpp//  interface/src/raypick////  Created by Sam Gondelman 7/11/2017//  Copyright 2017 High Fidelity, Inc.//  Copyright 2023 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//#include "LaserPointer.h" #include "Application.h"#include "avatar/AvatarManager.h" #include <DependencyManager.h>#include "PickManager.h"#include "RayPick.h" #include "PolyLineEntityItem.h" LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,                           const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalTime, bool centerEndY, bool lockEnd,                           bool distanceScaleEnd, bool scaleWithParent, bool enabled) :    PathPointer(PickQuery::Ray, rayProps, renderStates, defaultRenderStates, hover, triggers, faceAvatar, followNormal, followNormalTime,                centerEndY, lockEnd, distanceScaleEnd, scaleWithParent, enabled){} PickQuery::PickType LaserPointer::getType() const {    return PickQuery::PickType::Ray;} void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& pathProps) {    auto renderState = std::static_pointer_cast<RenderState>(_renderStates[state]);    if (renderState) {        updateRenderState(renderState->getPathID(), pathProps);        QVariantMap pathPropsMap = pathProps.toMap();        QVariant lineWidth = pathPropsMap["lineWidth"];        if (lineWidth.isValid()) {            renderState->setLineWidth(lineWidth.toFloat());        }         if (pathPropsMap.contains("linePoints")) {            QVariantList linePoints = pathPropsMap["linePoints"].toList();            renderState->setNumPoints(linePoints.length());        }    }} PickResultPointer LaserPointer::getPickResultCopy(const PickResultPointer& pickResult) const {    auto rayPickResult = std::dynamic_pointer_cast<RayPickResult>(pickResult);    if (!rayPickResult) {        return std::make_shared<RayPickResult>();    }    return std::make_shared<RayPickResult>(*rayPickResult.get());} QVariantMap LaserPointer::toVariantMap() const {    QVariantMap qVariantMap = Parent::toVariantMap();     QVariantMap qRenderStates;    for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) {        auto renderState = iter->second;        QVariantMap qRenderState;        qRenderState["start"] = renderState->getStartID();        qRenderState["path"] = std::static_pointer_cast<RenderState>(renderState)->getPathID();        qRenderState["end"] = renderState->getEndID();        qRenderStates[iter->first.c_str()] = qRenderState;    }    qVariantMap["renderStates"] = qRenderStates;     QVariantMap qDefaultRenderStates;    for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) {        float distance = iter->second.first;        auto defaultRenderState = iter->second.second;        QVariantMap qDefaultRenderState;         qDefaultRenderState["distance"] = distance;        qDefaultRenderState["start"] = defaultRenderState->getStartID();        qDefaultRenderState["path"] = std::static_pointer_cast<RenderState>(defaultRenderState)->getPathID();        qDefaultRenderState["end"] = defaultRenderState->getEndID();        qDefaultRenderStates[iter->first.c_str()] = qDefaultRenderState;    }    qVariantMap["defaultRenderStates"] = qDefaultRenderStates;     return qVariantMap;} glm::vec3 LaserPointer::getPickOrigin(const PickResultPointer& pickResult) const {    auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);    return (rayPickResult ? vec3FromVariant(rayPickResult->pickVariant["origin"]) : glm::vec3(0.0f));} glm::vec3 LaserPointer::getPickEnd(const PickResultPointer& pickResult, float distance) const {    auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);    if (!rayPickResult) {        return glm::vec3(0.0f);    }    if (distance > 0.0f) {        PickRay pick = PickRay(rayPickResult->pickVariant);        return pick.origin + distance * pick.direction;    } else {        return rayPickResult->intersection;    }} glm::vec3 LaserPointer::getPickedObjectNormal(const PickResultPointer& pickResult) const {    auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);    return (rayPickResult ? rayPickResult->surfaceNormal : glm::vec3(0.0f));} IntersectionType LaserPointer::getPickedObjectType(const PickResultPointer& pickResult) const {    auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);    return (rayPickResult ? rayPickResult->type : IntersectionType::NONE);} QUuid LaserPointer::getPickedObjectID(const PickResultPointer& pickResult) const {    auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);    return (rayPickResult ? rayPickResult->objectID : QUuid());} void LaserPointer::setVisualPickResultInternal(PickResultPointer pickResult, IntersectionType type, const QUuid& id,                                               const glm::vec3& intersection, float distance, const glm::vec3& surfaceNormal) {    auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);    if (rayPickResult) {        rayPickResult->type = type;        rayPickResult->objectID = id;        rayPickResult->intersection = intersection;        rayPickResult->distance = distance;        rayPickResult->surfaceNormal = surfaceNormal;        rayPickResult->pickVariant["direction"] = vec3toVariant(-surfaceNormal);    }} LaserPointer::RenderState::RenderState(const QUuid& startID, const QUuid& pathID, const QUuid& endID) :    StartEndRenderState(startID, endID), _pathID(pathID){    if (!getPathID().isNull()) {        EntityPropertyFlags desiredProperties;        desiredProperties += PROP_IGNORE_PICK_INTERSECTION;        desiredProperties += PROP_LINE_POINTS;        desiredProperties += PROP_STROKE_WIDTHS;        auto properties = DependencyManager::get<EntityScriptingInterface>()->getEntityPropertiesInternal(getPathID(), desiredProperties, false);         auto widths = properties.getStrokeWidths();        _lineWidth = widths.length() == 0 ? PolyLineEntityItem::DEFAULT_LINE_WIDTH : widths[0];         setNumPoints(properties.getLinePoints().length());         _pathIgnorePicks = properties.getIgnorePickIntersection();    }} void LaserPointer::RenderState::cleanup() {    StartEndRenderState::cleanup();    if (!getPathID().isNull()) {        DependencyManager::get<EntityScriptingInterface>()->deleteEntity(getPathID());    }} void LaserPointer::RenderState::disable() {    StartEndRenderState::disable();    if (!getPathID().isNull()) {        EntityItemProperties properties;        properties.setVisible(false);        properties.setIgnorePickIntersection(true);        DependencyManager::get<EntityScriptingInterface>()->editEntity(getPathID(), properties);    }} void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,                                       bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {    StartEndRenderState::update(origin, end, surfaceNormal, parentScale, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);    if (!getPathID().isNull()) {        EntityItemProperties properties;        QVector<glm::vec3> points;        const size_t numPoints = getNumPoints();        points.append(glm::vec3(0.0f));        const glm::vec3 endPoint = end - origin;        if (numPoints > 2) {            EntityPropertyFlags desiredProperties;            desiredProperties += PROP_VISIBLE;            auto oldProperties = DependencyManager::get<EntityScriptingInterface>()->getEntityPropertiesInternal(getPathID(), desiredProperties, false);             bool hasUnmodifiedEndPoint = false;            glm::vec3 unmodifiedEndPoint;            float directionLength = glm::length(endPoint);            auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);            if (rayPickResult && rayPickResult->pickVariant.contains("unmodifiedDirection")) {                unmodifiedEndPoint = directionLength * vec3FromVariant(rayPickResult->pickVariant["unmodifiedDirection"]);                hasUnmodifiedEndPoint = true;            }             // Segment points are evenly spaced between origin and end            for (size_t i = 1; i < numPoints - 1; i++) {                const float frac = ((float)i / (numPoints - 1));                if (!oldProperties.getVisible() || !_hasSetLinePoints || !hasUnmodifiedEndPoint) {                    points.append(frac * endPoint);                } else {                    points.append(frac * directionLength * glm::normalize(glm::mix(unmodifiedEndPoint, endPoint, frac)));                }            }            _hasSetLinePoints = true;        }        points.append(endPoint);        properties.setPosition(origin);        properties.setRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f));        properties.setLinePoints(points);        properties.setVisible(true);        properties.setIgnorePickIntersection(doesPathIgnorePicks());        QVector<glm::vec3> normals;        normals.fill(glm::vec3(0.0f, 0.0f, 1.0f), (int)numPoints);        properties.setNormals(normals);        QVector<float> widths;        float width = getLineWidth() * parentScale;        widths.fill(width, (int)numPoints);        properties.setStrokeWidths(widths);        DependencyManager::get<EntityScriptingInterface>()->editEntity(getPathID(), properties);    }} std::shared_ptr<StartEndRenderState> LaserPointer::buildRenderState(const QVariantMap& propMap, const QList<EntityItemProperties> &entityProperties) {    QUuid startID;    if (propMap["startPropertyIndex"].isValid()) {        int startPropertyIndex = propMap["startPropertyIndex"].toInt();        if (startPropertyIndex >= 0 && startPropertyIndex < entityProperties.length()) {            EntityItemProperties startProperties(entityProperties[startPropertyIndex]);            startProperties.getGrab().setGrabbable(false);            startID = DependencyManager::get<EntityScriptingInterface>()->addEntityInternal(startProperties, entity::HostType::LOCAL);        }    }     QUuid pathID;    if (propMap["pathPropertyIndex"].isValid()) {        int pathPropertyIndex = propMap["pathPropertyIndex"].toInt();        if (pathPropertyIndex >= 0 && pathPropertyIndex < entityProperties.length()) {            EntityItemProperties pathProperties(entityProperties[pathPropertyIndex]);            // laser paths must be PolyLine            pathProperties.setType(EntityTypes::EntityType::PolyLine);            pathProperties.getGrab().setGrabbable(false);            pathID = DependencyManager::get<EntityScriptingInterface>()->addEntityInternal(pathProperties, entity::HostType::LOCAL);        }    }     QUuid endID;    if (propMap["endPropertyIndex"].isValid()) {        int endPropertyIndex = propMap["endPropertyIndex"].toInt();        if (endPropertyIndex >= 0 && endPropertyIndex < entityProperties.length()) {            EntityItemProperties endProperties(entityProperties[endPropertyIndex]);            endProperties.getGrab().setGrabbable(false);            endID = DependencyManager::get<EntityScriptingInterface>()->addEntityInternal(endProperties, entity::HostType::LOCAL);        }    }     return std::make_shared<RenderState>(startID, pathID, endID);} PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button, bool hover) {    QUuid pickedID;    glm::vec3 intersection, surfaceNormal, direction, origin;    auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);    if (rayPickResult) {        intersection = rayPickResult->intersection;        surfaceNormal = rayPickResult->surfaceNormal;        const QVariantMap& searchRay = rayPickResult->pickVariant;        direction = vec3FromVariant(searchRay["direction"]);        origin = vec3FromVariant(searchRay["origin"]);        pickedID = rayPickResult->objectID;    }     if (pickedID != target.objectID) {        intersection = findIntersection(target, origin, direction);    }    glm::vec2 pos2D = findPos2D(target, intersection);     // If we just started triggering and we haven't moved too much, don't update intersection and pos2D    TriggerState& state = hover ? _latestState : _states[button];    auto avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();    if (avatar) {        float sensorToWorldScale = avatar->getSensorToWorldScale();        float deadspotSquared = TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED * sensorToWorldScale * sensorToWorldScale;        bool withinDeadspot = usecTimestampNow() - state.triggerStartTime < POINTER_MOVE_DELAY && glm::distance2(pos2D, state.triggerPos2D) < deadspotSquared;        if ((state.triggering || state.wasTriggering) && !state.deadspotExpired && withinDeadspot) {            pos2D = state.triggerPos2D;            intersection = state.intersection;            surfaceNormal = state.surfaceNormal;        }        if (!withinDeadspot) {            state.deadspotExpired = true;        }    }     return PointerEvent(pos2D, intersection, surfaceNormal, direction);} glm::vec3 LaserPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) {    switch (pickedObject.type) {        case ENTITY:        case LOCAL_ENTITY:            return RayPick::intersectRayWithEntityXYPlane(pickedObject.objectID, origin, direction);        default:            return glm::vec3(NAN);    }}