mirror of
https://github.com/overte-org/overte.git
synced 2025-06-22 03:00:26 +02:00
merge with pointers branch
This commit is contained in:
commit
f970eb2302
46 changed files with 1119 additions and 606 deletions
4
cmake/externals/wasapi/CMakeLists.txt
vendored
4
cmake/externals/wasapi/CMakeLists.txt
vendored
|
@ -6,8 +6,8 @@ if (WIN32)
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
ExternalProject_Add(
|
ExternalProject_Add(
|
||||||
${EXTERNAL_NAME}
|
${EXTERNAL_NAME}
|
||||||
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip
|
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi10.zip
|
||||||
URL_MD5 94f4765bdbcd53cd099f349ae031e769
|
URL_MD5 4f40e49715a420fb67b45b9cee19052c
|
||||||
CONFIGURE_COMMAND ""
|
CONFIGURE_COMMAND ""
|
||||||
BUILD_COMMAND ""
|
BUILD_COMMAND ""
|
||||||
INSTALL_COMMAND ""
|
INSTALL_COMMAND ""
|
||||||
|
|
|
@ -213,8 +213,8 @@ Rectangle {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
peak: model.peak;
|
peak: model.peak;
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: (bar.currentIndex === 1 && selectedHMD && isVR) ||
|
visible: ((bar.currentIndex === 1 && isVR) ||
|
||||||
(bar.currentIndex === 0 && selectedDesktop && !isVR) &&
|
(bar.currentIndex === 0 && !isVR)) &&
|
||||||
Audio.devices.input.peakValuesAvailable;
|
Audio.devices.input.peakValuesAvailable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1825,14 +1825,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([&](unsigned int rayPickID) {
|
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([&](unsigned int rayPickID) {
|
||||||
RayToEntityIntersectionResult entityResult;
|
RayToEntityIntersectionResult entityResult;
|
||||||
entityResult.intersects = false;
|
entityResult.intersects = false;
|
||||||
QVariantMap result = DependencyManager::get<PickManager>()->getPrevPickResult(rayPickID);
|
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResultTyped<RayPickResult>(rayPickID);
|
||||||
if (result["type"].isValid()) {
|
if (pickResult) {
|
||||||
entityResult.intersects = result["type"] != PickScriptingInterface::INTERSECTED_NONE();
|
entityResult.intersects = pickResult->type != IntersectionType::NONE;
|
||||||
if (entityResult.intersects) {
|
if (entityResult.intersects) {
|
||||||
entityResult.intersection = vec3FromVariant(result["intersection"]);
|
entityResult.intersection = pickResult->intersection;
|
||||||
entityResult.distance = result["distance"].toFloat();
|
entityResult.distance = pickResult->distance;
|
||||||
entityResult.surfaceNormal = vec3FromVariant(result["surfaceNormal"]);
|
entityResult.surfaceNormal = pickResult->surfaceNormal;
|
||||||
entityResult.entityID = result["objectID"].toUuid();
|
entityResult.entityID = pickResult->objectID;
|
||||||
entityResult.entity = DependencyManager::get<EntityTreeRenderer>()->getTree()->findEntityByID(entityResult.entityID);
|
entityResult.entity = DependencyManager::get<EntityTreeRenderer>()->getTree()->findEntityByID(entityResult.entityID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4963,7 +4963,7 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(app, "PointerManager");
|
PROFILE_RANGE(app, "PointerManager");
|
||||||
DependencyManager::get<PointerManager>()->update();
|
DependencyManager::get<PointerManager>()->update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -90,7 +90,7 @@ void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, cons
|
||||||
signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure);
|
signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
|
bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint) {
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
if (!accountManager->isLoggedIn()) {
|
if (!accountManager->isLoggedIn()) {
|
||||||
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
|
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
|
||||||
|
@ -99,7 +99,13 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
|
||||||
return false; // We know right away that we will fail, so tell the caller.
|
return false; // We know right away that we will fail, so tell the caller.
|
||||||
}
|
}
|
||||||
|
|
||||||
signedSend("public_key", hfc_key.toUtf8(), old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
|
QJsonObject transaction;
|
||||||
|
transaction["hfc_key"] = hfc_key;
|
||||||
|
transaction["machine_fingerprint"] = machine_fingerprint;
|
||||||
|
QJsonDocument transactionDoc{ transaction };
|
||||||
|
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||||
|
|
||||||
|
signedSend("transaction", transactionString, old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
|
||||||
return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in.
|
return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Ledger : public QObject, public Dependency {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false);
|
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false);
|
||||||
bool receiveAt(const QString& hfc_key, const QString& old_key);
|
bool receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint);
|
||||||
void balance(const QStringList& keys);
|
void balance(const QStringList& keys);
|
||||||
void inventory(const QStringList& keys);
|
void inventory(const QStringList& keys);
|
||||||
void history(const QStringList& keys);
|
void history(const QStringList& keys);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "ui/ImageProvider.h"
|
#include "ui/ImageProvider.h"
|
||||||
#include "scripting/HMDScriptingInterface.h"
|
#include "scripting/HMDScriptingInterface.h"
|
||||||
|
|
||||||
|
#include <FingerprintUtils.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <OffscreenUi.h>
|
#include <OffscreenUi.h>
|
||||||
#include <AccountManager.h>
|
#include <AccountManager.h>
|
||||||
|
@ -541,7 +542,8 @@ bool Wallet::generateKeyPair() {
|
||||||
// 2. It is maximally private, and we can step back from that later if desired.
|
// 2. It is maximally private, and we can step back from that later if desired.
|
||||||
// 3. It maximally exercises all the machinery, so we are most likely to surface issues now.
|
// 3. It maximally exercises all the machinery, so we are most likely to surface issues now.
|
||||||
auto ledger = DependencyManager::get<Ledger>();
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
return ledger->receiveAt(key, oldKey);
|
QString machineFingerprint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||||
|
return ledger->receiveAt(key, oldKey, machineFingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Wallet::listPublicKeys() {
|
QStringList Wallet::listPublicKeys() {
|
||||||
|
|
|
@ -20,7 +20,7 @@ JointRayPick::JointRayPick(const std::string& jointName, const glm::vec3& posOff
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const PickRay JointRayPick::getMathematicalPick() const {
|
PickRay JointRayPick::getMathematicalPick() const {
|
||||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
int jointIndex = myAvatar->getJointIndex(QString::fromStdString(_jointName));
|
int jointIndex = myAvatar->getJointIndex(QString::fromStdString(_jointName));
|
||||||
bool useAvatarHead = _jointName == "Avatar";
|
bool useAvatarHead = _jointName == "Avatar";
|
||||||
|
|
|
@ -18,7 +18,7 @@ class JointRayPick : public RayPick {
|
||||||
public:
|
public:
|
||||||
JointRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const PickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
JointRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const PickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
||||||
|
|
||||||
const PickRay getMathematicalPick() const override;
|
PickRay getMathematicalPick() const override;
|
||||||
|
|
||||||
bool isLeftHand() const override { return (_jointName == "_CONTROLLER_LEFTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); }
|
bool isLeftHand() const override { return (_jointName == "_CONTROLLER_LEFTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); }
|
||||||
bool isRightHand() const override { return (_jointName == "_CONTROLLER_RIGHTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"); }
|
bool isRightHand() const override { return (_jointName == "_CONTROLLER_RIGHTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"); }
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <pointers/PickManager.h>
|
#include <pointers/PickManager.h>
|
||||||
#include "PickScriptingInterface.h"
|
#include "PickScriptingInterface.h"
|
||||||
|
#include "RayPick.h"
|
||||||
|
|
||||||
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,
|
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,
|
||||||
const PointerTriggers& triggers, bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled) :
|
const PointerTriggers& triggers, bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled) :
|
||||||
|
@ -177,17 +178,20 @@ void LaserPointer::disableRenderState(const RenderState& renderState) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaserPointer::updateVisuals(const QVariantMap& prevRayPickResult) {
|
void LaserPointer::updateVisuals(const PickResultPointer& pickResult) {
|
||||||
IntersectionType type = IntersectionType(prevRayPickResult["type"].toInt());
|
auto rayPickResult = std::static_pointer_cast<const RayPickResult>(pickResult);
|
||||||
PickRay pickRay = PickRay(prevRayPickResult["searchRay"].toMap());
|
|
||||||
QUuid uid = prevRayPickResult["objectID"].toUuid();
|
IntersectionType type = rayPickResult ? rayPickResult->type : IntersectionType::NONE;
|
||||||
if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
|
if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
|
||||||
(type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) {
|
(type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) {
|
||||||
float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult["distance"].toFloat();
|
PickRay pickRay{ rayPickResult->pickVariant };
|
||||||
|
QUuid uid = rayPickResult->objectID;
|
||||||
|
float distance = _laserLength > 0.0f ? _laserLength : rayPickResult->distance;
|
||||||
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay, false);
|
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay, false);
|
||||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||||
} else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
} else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||||
disableRenderState(_renderStates[_currentRenderState]);
|
disableRenderState(_renderStates[_currentRenderState]);
|
||||||
|
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant) : PickRay();
|
||||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay, true);
|
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay, true);
|
||||||
} else if (!_currentRenderState.empty()) {
|
} else if (!_currentRenderState.empty()) {
|
||||||
disableRenderState(_renderStates[_currentRenderState]);
|
disableRenderState(_renderStates[_currentRenderState]);
|
||||||
|
@ -195,8 +199,12 @@ void LaserPointer::updateVisuals(const QVariantMap& prevRayPickResult) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pointer::PickedObject LaserPointer::getHoveredObject(const QVariantMap& pickResult) {
|
Pointer::PickedObject LaserPointer::getHoveredObject(const PickResultPointer& pickResult) {
|
||||||
return Pointer::PickedObject(pickResult["objectID"].toUuid(), IntersectionType(pickResult["type"].toUInt()));
|
auto rayPickResult = std::static_pointer_cast<const RayPickResult>(pickResult);
|
||||||
|
if (!rayPickResult) {
|
||||||
|
return PickedObject();
|
||||||
|
}
|
||||||
|
return PickedObject(rayPickResult->objectID, rayPickResult->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pointer::Buttons LaserPointer::getPressedButtons() {
|
Pointer::Buttons LaserPointer::getPressedButtons() {
|
||||||
|
@ -281,15 +289,21 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) {
|
||||||
return RenderState(startID, pathID, endID);
|
return RenderState(startID, pathID, endID);
|
||||||
}
|
}
|
||||||
|
|
||||||
PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const {
|
PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const {
|
||||||
glm::vec3 intersection = vec3FromVariant(pickResult["intersection"]);
|
QUuid pickedID;
|
||||||
glm::vec3 surfaceNormal = vec3FromVariant(pickResult["surfaceNormal"]);
|
glm::vec3 intersection, surfaceNormal, direction, origin;
|
||||||
QVariantMap searchRay = pickResult["searchRay"].toMap();
|
if (target.type != NONE) {
|
||||||
glm::vec3 direction = vec3FromVariant(searchRay["direction"]);
|
auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);
|
||||||
QUuid pickedID = pickResult["objectID"].toUuid();
|
intersection = rayPickResult->intersection;
|
||||||
|
surfaceNormal = rayPickResult->surfaceNormal;
|
||||||
|
const QVariantMap& searchRay = rayPickResult->pickVariant;
|
||||||
|
direction = vec3FromVariant(searchRay["direction"]);
|
||||||
|
origin = vec3FromVariant(searchRay["origin"]);
|
||||||
|
pickedID = rayPickResult->objectID;;
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec2 pos2D;
|
glm::vec2 pos2D;
|
||||||
if (pickedID != target.objectID) {
|
if (pickedID != target.objectID) {
|
||||||
glm::vec3 origin = vec3FromVariant(searchRay["origin"]);
|
|
||||||
if (target.type == ENTITY) {
|
if (target.type == ENTITY) {
|
||||||
intersection = intersectRayWithEntityXYPlane(target.objectID, origin, direction);
|
intersection = intersectRayWithEntityXYPlane(target.objectID, origin, direction);
|
||||||
} else if (target.type == OVERLAY) {
|
} else if (target.type == OVERLAY) {
|
||||||
|
|
|
@ -65,15 +65,15 @@ public:
|
||||||
void setLength(float length) override;
|
void setLength(float length) override;
|
||||||
void setLockEndUUID(const QUuid& objectID, bool isOverlay) override;
|
void setLockEndUUID(const QUuid& objectID, bool isOverlay) override;
|
||||||
|
|
||||||
void updateVisuals(const QVariantMap& prevRayPickResult) override;
|
void updateVisuals(const PickResultPointer& prevRayPickResult) override;
|
||||||
|
|
||||||
PickedObject getHoveredObject(const QVariantMap& pickResult) override;
|
|
||||||
Pointer::Buttons getPressedButtons() override;
|
|
||||||
|
|
||||||
static RenderState buildRenderState(const QVariantMap& propMap);
|
static RenderState buildRenderState(const QVariantMap& propMap);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const override;
|
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const override;
|
||||||
|
|
||||||
|
PickedObject getHoveredObject(const PickResultPointer& pickResult) override;
|
||||||
|
Pointer::Buttons getPressedButtons() override;
|
||||||
|
|
||||||
bool shouldHover() override { return _currentRenderState != ""; }
|
bool shouldHover() override { return _currentRenderState != ""; }
|
||||||
bool shouldTrigger() override { return _currentRenderState != ""; }
|
bool shouldTrigger() override { return _currentRenderState != ""; }
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
void LaserPointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const {
|
void LaserPointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const {
|
||||||
DependencyManager::get<PointerManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
DependencyManager::get<PointerManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaserPointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const {
|
void LaserPointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const {
|
||||||
DependencyManager::get<PointerManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
DependencyManager::get<PointerManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||||
}
|
}
|
||||||
|
@ -28,3 +29,7 @@ unsigned int LaserPointerScriptingInterface::createLaserPointer(const QVariant&
|
||||||
void LaserPointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const {
|
void LaserPointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const {
|
||||||
DependencyManager::get<PointerScriptingInterface>()->editRenderState(uid, renderState, properties);
|
DependencyManager::get<PointerScriptingInterface>()->editRenderState(uid, renderState, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantMap LaserPointerScriptingInterface::getPrevRayPickResult(unsigned int uid) const {
|
||||||
|
return DependencyManager::get<PointerScriptingInterface>()->getPrevPickResult(uid);
|
||||||
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
Q_INVOKABLE void removeLaserPointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
Q_INVOKABLE void removeLaserPointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
||||||
Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const;
|
Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const;
|
||||||
Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
||||||
Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid) const { return DependencyManager::get<PointerManager>()->getPrevPickResult(uid); }
|
Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid) const;
|
||||||
|
|
||||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
||||||
Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
|
Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
|
||||||
|
|
|
@ -18,7 +18,7 @@ MouseRayPick::MouseRayPick(const PickFilter& filter, const float maxDistance, co
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const PickRay MouseRayPick::getMathematicalPick() const {
|
PickRay MouseRayPick::getMathematicalPick() const {
|
||||||
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
|
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
|
||||||
if (position.isValid()) {
|
if (position.isValid()) {
|
||||||
QVariantMap posMap = position.toMap();
|
QVariantMap posMap = position.toMap();
|
||||||
|
|
|
@ -18,7 +18,7 @@ class MouseRayPick : public RayPick {
|
||||||
public:
|
public:
|
||||||
MouseRayPick(const PickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
MouseRayPick(const PickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
||||||
|
|
||||||
const PickRay getMathematicalPick() const override;
|
PickRay getMathematicalPick() const override;
|
||||||
|
|
||||||
bool isMouse() const override { return true; }
|
bool isMouse() const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -94,10 +94,15 @@ void PickScriptingInterface::removePick(unsigned int uid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap PickScriptingInterface::getPrevPickResult(unsigned int uid) {
|
QVariantMap PickScriptingInterface::getPrevPickResult(unsigned int uid) {
|
||||||
return DependencyManager::get<PickManager>()->getPrevPickResult(uid);
|
QVariantMap result;
|
||||||
|
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResult(uid);
|
||||||
|
if (pickResult) {
|
||||||
|
result = pickResult->toVariantMap();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PickScriptingInterface::setPrecisionPicking(unsigned int uid, const bool precisionPicking) {
|
void PickScriptingInterface::setPrecisionPicking(unsigned int uid, bool precisionPicking) {
|
||||||
DependencyManager::get<PickManager>()->setPrecisionPicking(uid, precisionPicking);
|
DependencyManager::get<PickManager>()->setPrecisionPicking(uid, precisionPicking);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ public:
|
||||||
Q_INVOKABLE void removePick(unsigned int uid);
|
Q_INVOKABLE void removePick(unsigned int uid);
|
||||||
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid);
|
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid);
|
||||||
|
|
||||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, const bool precisionPicking);
|
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking);
|
||||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities);
|
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities);
|
||||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities);
|
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities);
|
||||||
|
|
||||||
|
|
|
@ -10,26 +10,54 @@
|
||||||
|
|
||||||
#include <QtCore/QVariant>
|
#include <QtCore/QVariant>
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
|
#include <shared/QtHelpers.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "LaserPointer.h"
|
#include "LaserPointer.h"
|
||||||
|
#include "StylusPointer.h"
|
||||||
|
|
||||||
void PointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const {
|
void PointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const {
|
||||||
DependencyManager::get<PointerManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
DependencyManager::get<PointerManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const {
|
void PointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const {
|
||||||
DependencyManager::get<PointerManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
DependencyManager::get<PointerManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& type, const QVariant& properties) const {
|
unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& type, const QVariant& properties) {
|
||||||
|
// Interaction with managers should always happen on the main thread
|
||||||
|
if (QThread::currentThread() != qApp->thread()) {
|
||||||
|
unsigned int result;
|
||||||
|
BLOCKING_INVOKE_METHOD(this, "createPointer", Q_RETURN_ARG(unsigned int, result), Q_ARG(PickQuery::PickType, type), Q_ARG(QVariant, properties));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PickQuery::PickType::Ray:
|
case PickQuery::PickType::Ray:
|
||||||
return createLaserPointer(properties);
|
return createLaserPointer(properties);
|
||||||
|
case PickQuery::PickType::Stylus:
|
||||||
|
return createStylus(properties);
|
||||||
default:
|
default:
|
||||||
return PointerEvent::INVALID_POINTER_ID;
|
return PointerEvent::INVALID_POINTER_ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) const {
|
||||||
|
bilateral::Side side = bilateral::Side::Invalid;
|
||||||
|
{
|
||||||
|
QVariant handVar = properties.toMap()["hand"];
|
||||||
|
if (handVar.isValid()) {
|
||||||
|
side = bilateral::side(handVar.toInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bilateral::Side::Invalid == side) {
|
||||||
|
return PointerEvent::INVALID_POINTER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<StylusPointer>(side));
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& properties) const {
|
unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& properties) const {
|
||||||
QVariantMap propertyMap = properties.toMap();
|
QVariantMap propertyMap = properties.toMap();
|
||||||
|
|
||||||
|
@ -134,3 +162,12 @@ void PointerScriptingInterface::editRenderState(unsigned int uid, const QString&
|
||||||
|
|
||||||
DependencyManager::get<PointerManager>()->editRenderState(uid, renderState.toStdString(), startProps, pathProps, endProps);
|
DependencyManager::get<PointerManager>()->editRenderState(uid, renderState.toStdString(), startProps, pathProps, endProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const {
|
||||||
|
QVariantMap result;
|
||||||
|
auto pickResult = DependencyManager::get<PointerManager>()->getPrevPickResult(uid);
|
||||||
|
if (pickResult) {
|
||||||
|
result = pickResult->toVariantMap();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -20,14 +20,15 @@ class PointerScriptingInterface : public QObject, public Dependency {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
unsigned int createLaserPointer(const QVariant& properties) const;
|
unsigned int createLaserPointer(const QVariant& properties) const;
|
||||||
|
unsigned int createStylus(const QVariant& properties) const;
|
||||||
|
|
||||||
Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties) const;
|
Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties);
|
||||||
Q_INVOKABLE void enablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
|
Q_INVOKABLE void enablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
|
||||||
Q_INVOKABLE void disablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
|
Q_INVOKABLE void disablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
|
||||||
Q_INVOKABLE void removePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
Q_INVOKABLE void removePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
||||||
Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const;
|
Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const;
|
||||||
Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
||||||
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid) const { return DependencyManager::get<PointerManager>()->getPrevPickResult(uid); }
|
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid) const;
|
||||||
|
|
||||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
||||||
Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
|
Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
|
||||||
|
|
|
@ -53,7 +53,7 @@ public:
|
||||||
bool doesIntersect() const override { return intersects; }
|
bool doesIntersect() const override { return intersects; }
|
||||||
bool checkOrFilterAgainstMaxDistance(float maxDistance) override { return distance < maxDistance; }
|
bool checkOrFilterAgainstMaxDistance(float maxDistance) override { return distance < maxDistance; }
|
||||||
|
|
||||||
PickResultPointer compareAndProcessNewResult(const PickResultPointer newRes) override {
|
PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override {
|
||||||
auto newRayRes = std::static_pointer_cast<RayPickResult>(newRes);
|
auto newRayRes = std::static_pointer_cast<RayPickResult>(newRes);
|
||||||
if (newRayRes->distance < distance) {
|
if (newRayRes->distance < distance) {
|
||||||
return std::make_shared<RayPickResult>(*newRayRes);
|
return std::make_shared<RayPickResult>(*newRayRes);
|
||||||
|
|
|
@ -20,35 +20,40 @@
|
||||||
#include "JointRayPick.h"
|
#include "JointRayPick.h"
|
||||||
#include "MouseRayPick.h"
|
#include "MouseRayPick.h"
|
||||||
|
|
||||||
uint32_t RayPickScriptingInterface::createRayPick(const QVariant& properties) {
|
unsigned int RayPickScriptingInterface::createRayPick(const QVariant& properties) {
|
||||||
return DependencyManager::get<PickScriptingInterface>()->createRayPick(properties);
|
return DependencyManager::get<PickScriptingInterface>()->createRayPick(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RayPickScriptingInterface::enableRayPick(uint32_t uid) {
|
void RayPickScriptingInterface::enableRayPick(unsigned int uid) {
|
||||||
DependencyManager::get<PickManager>()->enablePick(uid);
|
DependencyManager::get<PickManager>()->enablePick(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RayPickScriptingInterface::disableRayPick(uint32_t uid) {
|
void RayPickScriptingInterface::disableRayPick(unsigned int uid) {
|
||||||
DependencyManager::get<PickManager>()->disablePick(uid);
|
DependencyManager::get<PickManager>()->disablePick(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RayPickScriptingInterface::removeRayPick(uint32_t uid) {
|
void RayPickScriptingInterface::removeRayPick(unsigned int uid) {
|
||||||
DependencyManager::get<PickManager>()->removePick(uid);
|
DependencyManager::get<PickManager>()->removePick(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap RayPickScriptingInterface::getPrevRayPickResult(uint32_t uid) {
|
QVariantMap RayPickScriptingInterface::getPrevRayPickResult(unsigned int uid) {
|
||||||
return DependencyManager::get<PickManager>()->getPrevPickResult(uid);
|
QVariantMap result;
|
||||||
|
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResult(uid);
|
||||||
|
if (pickResult) {
|
||||||
|
result = pickResult->toVariantMap();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RayPickScriptingInterface::setPrecisionPicking(uint32_t uid, const bool precisionPicking) {
|
void RayPickScriptingInterface::setPrecisionPicking(unsigned int uid, bool precisionPicking) {
|
||||||
DependencyManager::get<PickManager>()->setPrecisionPicking(uid, precisionPicking);
|
DependencyManager::get<PickManager>()->setPrecisionPicking(uid, precisionPicking);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RayPickScriptingInterface::setIgnoreItems(uint32_t uid, const QScriptValue& ignoreItems) {
|
void RayPickScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) {
|
||||||
DependencyManager::get<PickManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
DependencyManager::get<PickManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RayPickScriptingInterface::setIncludeItems(uint32_t uid, const QScriptValue& includeItems) {
|
void RayPickScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) {
|
||||||
DependencyManager::get<PickManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
DependencyManager::get<PickManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ public:
|
||||||
Q_INVOKABLE void removeRayPick(unsigned int uid);
|
Q_INVOKABLE void removeRayPick(unsigned int uid);
|
||||||
Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid);
|
Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid);
|
||||||
|
|
||||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, const bool precisionPicking);
|
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking);
|
||||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities);
|
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities);
|
||||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities);
|
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,6 @@ StaticRayPick::StaticRayPick(const glm::vec3& position, const glm::vec3& directi
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const PickRay StaticRayPick::getMathematicalPick() const {
|
PickRay StaticRayPick::getMathematicalPick() const {
|
||||||
return _pickRay;
|
return _pickRay;
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ class StaticRayPick : public RayPick {
|
||||||
public:
|
public:
|
||||||
StaticRayPick(const glm::vec3& position, const glm::vec3& direction, const PickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
StaticRayPick(const glm::vec3& position, const glm::vec3& direction, const PickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
||||||
|
|
||||||
const PickRay getMathematicalPick() const override;
|
PickRay getMathematicalPick() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PickRay _pickRay;
|
PickRay _pickRay;
|
||||||
|
|
627
interface/src/raypick/StylusPointer.cpp
Normal file
627
interface/src/raypick/StylusPointer.cpp
Normal file
|
@ -0,0 +1,627 @@
|
||||||
|
//
|
||||||
|
// 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 "StylusPointer.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
#include <pointers/PickManager.h>
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
#include <Transform.h>
|
||||||
|
#include <shared/QtHelpers.h>
|
||||||
|
#include <controllers/StandardControls.h>
|
||||||
|
#include <controllers/UserInputMapper.h>
|
||||||
|
#include <RegisteredMetaTypes.h>
|
||||||
|
#include <MessagesClient.h>
|
||||||
|
#include <EntityItemID.h>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
#include "avatar/AvatarManager.h"
|
||||||
|
#include "avatar/MyAvatar.h"
|
||||||
|
|
||||||
|
#include "scripting/HMDScriptingInterface.h"
|
||||||
|
#include "ui/overlays/Web3DOverlay.h"
|
||||||
|
#include "ui/overlays/Sphere3DOverlay.h"
|
||||||
|
#include "avatar/AvatarManager.h"
|
||||||
|
#include "InterfaceLogging.h"
|
||||||
|
#include "PickScriptingInterface.h"
|
||||||
|
|
||||||
|
using namespace controller;
|
||||||
|
using namespace bilateral;
|
||||||
|
|
||||||
|
static Setting::Handle<double> USE_FINGER_AS_STYLUS("preferAvatarFingerOverStylus", false);
|
||||||
|
static const float WEB_STYLUS_LENGTH = 0.2f;
|
||||||
|
static const float WEB_TOUCH_Y_OFFSET = 0.105f; // how far forward (or back with a negative number) to slide stylus in hand
|
||||||
|
static const vec3 TIP_OFFSET{ 0.0f, WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f };
|
||||||
|
static const float TABLET_MIN_HOVER_DISTANCE = 0.01f;
|
||||||
|
static const float TABLET_MAX_HOVER_DISTANCE = 0.1f;
|
||||||
|
static const float TABLET_MIN_TOUCH_DISTANCE = -0.05f;
|
||||||
|
static const float TABLET_MAX_TOUCH_DISTANCE = TABLET_MIN_HOVER_DISTANCE;
|
||||||
|
static const float EDGE_BORDER = 0.075f;
|
||||||
|
|
||||||
|
static const float HOVER_HYSTERESIS = 0.01f;
|
||||||
|
static const float NEAR_HYSTERESIS = 0.05f;
|
||||||
|
static const float TOUCH_HYSTERESIS = 0.002f;
|
||||||
|
|
||||||
|
// triggered when stylus presses a web overlay/entity
|
||||||
|
static const float HAPTIC_STYLUS_STRENGTH = 1.0f;
|
||||||
|
static const float HAPTIC_STYLUS_DURATION = 20.0f;
|
||||||
|
static const float POINTER_PRESS_TO_MOVE_DELAY = 0.33f; // seconds
|
||||||
|
|
||||||
|
static const float WEB_DISPLAY_STYLUS_DISTANCE = 0.5f;
|
||||||
|
static const float TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481f;
|
||||||
|
static const float TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED = TOUCH_PRESS_TO_MOVE_DEADSPOT * TOUCH_PRESS_TO_MOVE_DEADSPOT;
|
||||||
|
|
||||||
|
std::array<StylusPointer*, 2> STYLUSES;
|
||||||
|
|
||||||
|
static OverlayID getHomeButtonID() {
|
||||||
|
return DependencyManager::get<HMDScriptingInterface>()->getCurrentHomeButtonID();
|
||||||
|
}
|
||||||
|
|
||||||
|
static OverlayID getTabletScreenID() {
|
||||||
|
return DependencyManager::get<HMDScriptingInterface>()->getCurrentTabletScreenID();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SideData {
|
||||||
|
QString avatarJoint;
|
||||||
|
QString cameraJoint;
|
||||||
|
controller::StandardPoseChannel channel;
|
||||||
|
controller::Hand hand;
|
||||||
|
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",
|
||||||
|
StandardPoseChannel::LEFT_HAND,
|
||||||
|
Hand::LEFT,
|
||||||
|
{ -0.04f, 0.13f, 0.039f } },
|
||||||
|
{ "RightHandIndex4",
|
||||||
|
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND",
|
||||||
|
StandardPoseChannel::RIGHT_HAND,
|
||||||
|
Hand::RIGHT,
|
||||||
|
{ 0.04f, 0.13f, 0.039f } } } };
|
||||||
|
|
||||||
|
static StylusTip getFingerWorldLocation(Side side) {
|
||||||
|
const auto& sideData = SIDES[index(side)];
|
||||||
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
|
auto fingerJointIndex = myAvatar->getJointIndex(sideData.avatarJoint);
|
||||||
|
|
||||||
|
if (-1 == fingerJointIndex) {
|
||||||
|
return StylusTip();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fingerPosition = myAvatar->getAbsoluteJointTranslationInObjectFrame(fingerJointIndex);
|
||||||
|
auto fingerRotation = myAvatar->getAbsoluteJointRotationInObjectFrame(fingerJointIndex);
|
||||||
|
auto avatarOrientation = myAvatar->getOrientation();
|
||||||
|
auto avatarPosition = myAvatar->getPosition();
|
||||||
|
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, float sensorToWorldScale) {
|
||||||
|
static const std::array<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();
|
||||||
|
auto controllerJointIndex = myAvatar->getJointIndex(sideData.cameraJoint);
|
||||||
|
|
||||||
|
const auto avatarOrientation = myAvatar->getOrientation();
|
||||||
|
const auto avatarPosition = myAvatar->getPosition();
|
||||||
|
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 * sensorToWorldScale);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StylusPickResult::isNormalized() const {
|
||||||
|
return valid && (normalizedPosition == glm::clamp(normalizedPosition, vec3(0), vec3(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StylusPickResult::isNearNormal(float min, float max, float hystersis) const {
|
||||||
|
return valid && (distance == glm::clamp(distance, min - hystersis, max + hystersis));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StylusPickResult::isNear2D(float border, float hystersis) const {
|
||||||
|
return valid && position2D == glm::clamp(position2D, vec2(0) - border - hystersis, vec2(dimensions) + border + hystersis);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StylusPickResult::isNear(float min, float max, float border, float hystersis) const {
|
||||||
|
// check to see if the projected stylusTip is within within the 2d border
|
||||||
|
return isNearNormal(min, max, hystersis) && isNear2D(border, hystersis);
|
||||||
|
}
|
||||||
|
|
||||||
|
StylusPickResult::operator bool() const {
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StylusPickResult::hasKeyboardFocus() const {
|
||||||
|
if (!overlayID.isNull()) {
|
||||||
|
return qApp->getOverlays().getKeyboardFocusOverlay() == overlayID;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
if (!entityID.isNull()) {
|
||||||
|
return qApp->getKeyboardFocusEntity() == entityID;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPickResult::setKeyboardFocus() const {
|
||||||
|
if (!overlayID.isNull()) {
|
||||||
|
qApp->getOverlays().setKeyboardFocusOverlay(overlayID);
|
||||||
|
qApp->setKeyboardFocusEntity(EntityItemID());
|
||||||
|
#if 0
|
||||||
|
} else if (!entityID.isNull()) {
|
||||||
|
qApp->getOverlays().setKeyboardFocusOverlay(OverlayID());
|
||||||
|
qApp->setKeyboardFocusEntity(entityID);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPickResult::sendHoverOverEvent() const {
|
||||||
|
if (!overlayID.isNull()) {
|
||||||
|
qApp->getOverlays().hoverOverOverlay(overlayID, PointerEvent{ PointerEvent::Move, deviceId(), position2D, position,
|
||||||
|
normal, -normal });
|
||||||
|
}
|
||||||
|
// FIXME support entity
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPickResult::sendHoverEnterEvent() const {
|
||||||
|
if (!overlayID.isNull()) {
|
||||||
|
qApp->getOverlays().hoverEnterOverlay(overlayID, PointerEvent{ PointerEvent::Move, deviceId(), position2D, position,
|
||||||
|
normal, -normal });
|
||||||
|
}
|
||||||
|
// FIXME support entity
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPickResult::sendTouchStartEvent() const {
|
||||||
|
if (!overlayID.isNull()) {
|
||||||
|
qApp->getOverlays().sendMousePressOnOverlay(overlayID, PointerEvent{ PointerEvent::Press, deviceId(), position2D, position,
|
||||||
|
normal, -normal, PointerEvent::PrimaryButton,
|
||||||
|
PointerEvent::PrimaryButton });
|
||||||
|
}
|
||||||
|
// FIXME support entity
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPickResult::sendTouchEndEvent() const {
|
||||||
|
if (!overlayID.isNull()) {
|
||||||
|
qApp->getOverlays().sendMouseReleaseOnOverlay(overlayID,
|
||||||
|
PointerEvent{ PointerEvent::Release, deviceId(), position2D, position, normal,
|
||||||
|
-normal, PointerEvent::PrimaryButton });
|
||||||
|
}
|
||||||
|
// FIXME support entity
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPickResult::sendTouchMoveEvent() const {
|
||||||
|
if (!overlayID.isNull()) {
|
||||||
|
qApp->getOverlays().sendMouseMoveOnOverlay(overlayID, PointerEvent{ PointerEvent::Move, deviceId(), position2D, position,
|
||||||
|
normal, -normal, PointerEvent::PrimaryButton,
|
||||||
|
PointerEvent::PrimaryButton });
|
||||||
|
}
|
||||||
|
// FIXME support entity
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StylusPickResult::doesIntersect() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for example: if we want the closest result, compare based on distance
|
||||||
|
// if we want all results, combine them
|
||||||
|
// must return a new pointer
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if this result contains any valid results with distance < maxDistance
|
||||||
|
// can also filter out results with distance >= maxDistance
|
||||||
|
bool StylusPickResult::checkOrFilterAgainstMaxDistance(float maxDistance) {
|
||||||
|
return distance < maxDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t StylusPickResult::deviceId() const {
|
||||||
|
// 0 is reserved for hardware mouse
|
||||||
|
return index(tip.side) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StylusPick::StylusPick(Side side)
|
||||||
|
: Pick(PickFilter(PickScriptingInterface::PICK_OVERLAYS()), FLT_MAX, true)
|
||||||
|
, _side(side) {
|
||||||
|
}
|
||||||
|
|
||||||
|
StylusTip StylusPick::getMathematicalPick() const {
|
||||||
|
StylusTip result;
|
||||||
|
if (_useFingerInsteadOfStylus) {
|
||||||
|
result = getFingerWorldLocation(_side);
|
||||||
|
} else {
|
||||||
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
|
float sensorScaleFactor = myAvatar->getSensorToWorldScale();
|
||||||
|
result = getControllerWorldLocation(_side, sensorScaleFactor);
|
||||||
|
result.position += result.orientation * (TIP_OFFSET * sensorScaleFactor);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PickResultPointer StylusPick::getDefaultResult(const QVariantMap& pickVariant) const {
|
||||||
|
return std::make_shared<StylusPickResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) {
|
||||||
|
return PickResultPointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
PickResultPointer StylusPick::getOverlayIntersection(const StylusTip& pick) {
|
||||||
|
if (!getFilter().doesPickOverlays()) {
|
||||||
|
return PickResultPointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<StylusPickResult> results;
|
||||||
|
for (const auto& target : getIncludeItems()) {
|
||||||
|
if (target.isNull()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto overlay = qApp->getOverlays().getOverlay(target);
|
||||||
|
// Don't interact with non-3D or invalid overlays
|
||||||
|
if (!overlay || !overlay->is3D()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overlay->getVisible() && !getFilter().doesPickInvisible()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto overlayType = overlay->getType();
|
||||||
|
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(overlay);
|
||||||
|
const auto overlayRotation = overlay3D->getRotation();
|
||||||
|
const auto overlayPosition = overlay3D->getPosition();
|
||||||
|
|
||||||
|
StylusPickResult result;
|
||||||
|
result.tip = pick;
|
||||||
|
result.overlayID = target;
|
||||||
|
result.normal = overlayRotation * Vectors::UNIT_Z;
|
||||||
|
result.distance = glm::dot(pick.position - overlayPosition, result.normal);
|
||||||
|
result.position = pick.position - (result.normal * result.distance);
|
||||||
|
if (overlayType == Web3DOverlay::TYPE) {
|
||||||
|
result.dimensions = vec3(std::static_pointer_cast<Web3DOverlay>(overlay3D)->getSize(), 0.01f);
|
||||||
|
} else if (overlayType == Sphere3DOverlay::TYPE) {
|
||||||
|
result.dimensions = std::static_pointer_cast<Sphere3DOverlay>(overlay3D)->getDimensions();
|
||||||
|
} else {
|
||||||
|
result.dimensions = vec3(0.01f);
|
||||||
|
}
|
||||||
|
auto tipRelativePosition = result.position - overlayPosition;
|
||||||
|
auto localPos = glm::inverse(overlayRotation) * tipRelativePosition;
|
||||||
|
auto normalizedPosition = localPos / result.dimensions;
|
||||||
|
result.normalizedPosition = normalizedPosition + 0.5f;
|
||||||
|
result.position2D = { result.normalizedPosition.x * result.dimensions.x,
|
||||||
|
(1.0f - result.normalizedPosition.y) * result.dimensions.y };
|
||||||
|
result.valid = true;
|
||||||
|
results.push_back(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
StylusPickResult nearestTarget;
|
||||||
|
for (const auto& result : results) {
|
||||||
|
if (result && result.isNormalized() && result.distance < nearestTarget.distance) {
|
||||||
|
nearestTarget = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_shared<StylusPickResult>(nearestTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
PickResultPointer StylusPick::getAvatarIntersection(const StylusTip& pick) {
|
||||||
|
return PickResultPointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
PickResultPointer StylusPick::getHUDIntersection(const StylusTip& pick) {
|
||||||
|
return PickResultPointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
StylusPointer::StylusPointer(Side side)
|
||||||
|
: Pointer(DependencyManager::get<PickManager>()->addPick(PickQuery::Stylus, std::make_shared<StylusPick>(side)),
|
||||||
|
false,
|
||||||
|
true)
|
||||||
|
, _side(side)
|
||||||
|
, _sideData(SIDES[index(side)]) {
|
||||||
|
setIncludeItems({ { getHomeButtonID(), getTabletScreenID() } });
|
||||||
|
STYLUSES[index(_side)] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
StylusPointer::~StylusPointer() {
|
||||||
|
if (!_stylusOverlay.isNull()) {
|
||||||
|
qApp->getOverlays().deleteOverlay(_stylusOverlay);
|
||||||
|
}
|
||||||
|
STYLUSES[index(_side)] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
StylusPointer* StylusPointer::getOtherStylus() {
|
||||||
|
return STYLUSES[((index(_side) + 1) % 2)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::enable() {
|
||||||
|
Parent::enable();
|
||||||
|
withWriteLock([&] { _renderingEnabled = true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::disable() {
|
||||||
|
Parent::disable();
|
||||||
|
withWriteLock([&] { _renderingEnabled = false; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::updateStylusTarget() {
|
||||||
|
const float minNearDistance = TABLET_MIN_TOUCH_DISTANCE * _sensorScaleFactor;
|
||||||
|
const float maxNearDistance = WEB_DISPLAY_STYLUS_DISTANCE * _sensorScaleFactor;
|
||||||
|
const float edgeBorder = EDGE_BORDER * _sensorScaleFactor;
|
||||||
|
|
||||||
|
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResultTyped<StylusPickResult>(_pickUID);
|
||||||
|
|
||||||
|
if (pickResult) {
|
||||||
|
_state.target = *pickResult;
|
||||||
|
float hystersis = 0.0f;
|
||||||
|
// If we're already near the target, add hystersis to ensure we don't rapidly toggle between near and not near
|
||||||
|
// but only for the current near target
|
||||||
|
if (_previousState.nearTarget && pickResult->overlayID == _previousState.target.overlayID) {
|
||||||
|
hystersis = _nearHysteresis;
|
||||||
|
}
|
||||||
|
_state.nearTarget = pickResult->isNear(minNearDistance, maxNearDistance, edgeBorder, hystersis);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not near anything, short circuit the rest
|
||||||
|
if (!_state.nearTarget) {
|
||||||
|
relinquishTouchFocus();
|
||||||
|
hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
show();
|
||||||
|
|
||||||
|
auto minTouchDistance = TABLET_MIN_TOUCH_DISTANCE * _sensorScaleFactor;
|
||||||
|
auto maxTouchDistance = TABLET_MAX_TOUCH_DISTANCE * _sensorScaleFactor;
|
||||||
|
auto maxHoverDistance = TABLET_MAX_HOVER_DISTANCE * _sensorScaleFactor;
|
||||||
|
|
||||||
|
float hystersis = 0.0f;
|
||||||
|
if (_previousState.nearTarget && _previousState.target.overlayID == _previousState.target.overlayID) {
|
||||||
|
hystersis = _nearHysteresis;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're in hover distance (calculated as the normal distance from the XY plane of the overlay)
|
||||||
|
if ((getOtherStylus() && getOtherStylus()->_state.touchingTarget) || !_state.target.isNearNormal(minTouchDistance, maxHoverDistance, hystersis)) {
|
||||||
|
relinquishTouchFocus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestTouchFocus(_state.target);
|
||||||
|
|
||||||
|
if (!_state.target.hasKeyboardFocus()) {
|
||||||
|
_state.target.setKeyboardFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTouchFocus(_state.target) && !_previousState.touchingTarget) {
|
||||||
|
_state.target.sendHoverOverEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
hystersis = 0.0f;
|
||||||
|
if (_previousState.touchingTarget && _previousState.target.overlayID == _state.target.overlayID) {
|
||||||
|
hystersis = _touchHysteresis;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're in touch distance
|
||||||
|
if (_state.target.isNearNormal(minTouchDistance, maxTouchDistance, _touchHysteresis) && _state.target.isNormalized()) {
|
||||||
|
_state.touchingTarget = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::update(unsigned int pointerID, float deltaTime) {
|
||||||
|
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||||
|
withReadLock([&] {
|
||||||
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
|
|
||||||
|
// Store and reset the state
|
||||||
|
{
|
||||||
|
_previousState = _state;
|
||||||
|
_state = State();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Update finger as stylus setting
|
||||||
|
{
|
||||||
|
useFingerInsteadOfStylus = (USE_FINGER_AS_STYLUS.get() && myAvatar->getJointIndex(sideData.avatarJoint) != -1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Update scale factor
|
||||||
|
{
|
||||||
|
_sensorScaleFactor = myAvatar->getSensorToWorldScale();
|
||||||
|
_hoverHysteresis = HOVER_HYSTERESIS * _sensorScaleFactor;
|
||||||
|
_nearHysteresis = NEAR_HYSTERESIS * _sensorScaleFactor;
|
||||||
|
_touchHysteresis = TOUCH_HYSTERESIS * _sensorScaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify the current near or touching target
|
||||||
|
updateStylusTarget();
|
||||||
|
|
||||||
|
// If we stopped touching, or if the target overlay ID changed, send a touching exit to the previous touch target
|
||||||
|
if (_previousState.touchingTarget &&
|
||||||
|
(!_state.touchingTarget || _state.target.overlayID != _previousState.target.overlayID)) {
|
||||||
|
stylusTouchingExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle new or continuing touch
|
||||||
|
if (_state.touchingTarget) {
|
||||||
|
// If we were previously not touching, or we were touching a different overlay, add a touch enter
|
||||||
|
if (!_previousState.touchingTarget || _previousState.target.overlayID != _state.target.overlayID) {
|
||||||
|
stylusTouchingEnter();
|
||||||
|
} else {
|
||||||
|
_touchingEnterTimer += deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
stylusTouching();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setIncludeItems({ { getHomeButtonID(), getTabletScreenID() } });
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::show() {
|
||||||
|
if (!_stylusOverlay.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
|
// FIXME perhaps instantiate a stylus and use show / hide instead of create / destroy
|
||||||
|
// however, the current design doesn't really allow for this because it assumes that
|
||||||
|
// hide / show are idempotent and low cost, but constantly querying the visibility
|
||||||
|
QVariantMap overlayProperties;
|
||||||
|
overlayProperties["name"] = "stylus";
|
||||||
|
overlayProperties["url"] = PathUtils::resourcesPath() + "/meshes/tablet-stylus-fat.fbx";
|
||||||
|
overlayProperties["loadPriority"] = 10.0f;
|
||||||
|
overlayProperties["dimensions"] = vec3toVariant(_sensorScaleFactor * vec3(0.01f, 0.01f, WEB_STYLUS_LENGTH));
|
||||||
|
overlayProperties["solid"] = true;
|
||||||
|
overlayProperties["visible"] = true;
|
||||||
|
overlayProperties["ignoreRayIntersection"] = true;
|
||||||
|
overlayProperties["drawInFront"] = false;
|
||||||
|
overlayProperties["parentID"] = AVATAR_SELF_ID;
|
||||||
|
overlayProperties["parentJointIndex"] = myAvatar->getJointIndex(_sideData.cameraJoint);
|
||||||
|
|
||||||
|
static const glm::quat X_ROT_NEG_90{ 0.70710678f, -0.70710678f, 0.0f, 0.0f };
|
||||||
|
auto modelOrientation = _state.target.tip.orientation * X_ROT_NEG_90;
|
||||||
|
auto modelPositionOffset = modelOrientation * (vec3(0.0f, 0.0f, -WEB_STYLUS_LENGTH / 2.0f) * _sensorScaleFactor);
|
||||||
|
overlayProperties["position"] = vec3toVariant(_state.target.tip.position + modelPositionOffset);
|
||||||
|
overlayProperties["rotation"] = quatToVariant(modelOrientation);
|
||||||
|
_stylusOverlay = qApp->getOverlays().addOverlay("model", overlayProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::hide() {
|
||||||
|
if (_stylusOverlay.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qApp->getOverlays().deleteOverlay(_stylusOverlay);
|
||||||
|
_stylusOverlay = OverlayID();
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
void pointFinger(bool value) {
|
||||||
|
static const QString HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index";
|
||||||
|
static const std::array<QString, 2> KEYS{ { "pointLeftIndex", "pointLeftIndex" } };
|
||||||
|
if (fingerPointing != value) {
|
||||||
|
QString message = QJsonDocument(QJsonObject{ { KEYS[index(side)], value } }).toJson();
|
||||||
|
DependencyManager::get<MessagesClient>()->sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, message);
|
||||||
|
fingerPointing = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
void StylusPointer::requestTouchFocus(const StylusPickResult& pickResult) {
|
||||||
|
if (!pickResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// send hover events to target if we can.
|
||||||
|
// record the entity or overlay we are hovering over.
|
||||||
|
if (!pickResult.overlayID.isNull() && pickResult.overlayID != _hoverOverlay &&
|
||||||
|
getOtherStylus() && pickResult.overlayID != getOtherStylus()->_hoverOverlay) {
|
||||||
|
_hoverOverlay = pickResult.overlayID;
|
||||||
|
pickResult.sendHoverEnterEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StylusPointer::hasTouchFocus(const StylusPickResult& pickResult) {
|
||||||
|
return (!pickResult.overlayID.isNull() && pickResult.overlayID == _hoverOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::relinquishTouchFocus() {
|
||||||
|
// send hover leave event.
|
||||||
|
if (!_hoverOverlay.isNull()) {
|
||||||
|
PointerEvent pointerEvent{ PointerEvent::Move, (uint32_t)(index(_side) + 1) };
|
||||||
|
auto& overlays = qApp->getOverlays();
|
||||||
|
overlays.sendMouseMoveOnOverlay(_hoverOverlay, pointerEvent);
|
||||||
|
overlays.sendHoverOverOverlay(_hoverOverlay, pointerEvent);
|
||||||
|
overlays.sendHoverLeaveOverlay(_hoverOverlay, pointerEvent);
|
||||||
|
_hoverOverlay = OverlayID();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void StylusPointer::stealTouchFocus() {
|
||||||
|
// send hover events to target
|
||||||
|
if (getOtherStylus() && _state.target.overlayID == getOtherStylus()->_hoverOverlay) {
|
||||||
|
getOtherStylus()->relinquishTouchFocus();
|
||||||
|
}
|
||||||
|
requestTouchFocus(_state.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::stylusTouchingEnter() {
|
||||||
|
stealTouchFocus();
|
||||||
|
_state.target.sendTouchStartEvent();
|
||||||
|
DependencyManager::get<UserInputMapper>()->triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION,
|
||||||
|
_sideData.hand);
|
||||||
|
_touchingEnterTimer = 0;
|
||||||
|
_touchingEnterPosition = _state.target.position2D;
|
||||||
|
_deadspotExpired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::stylusTouchingExit() {
|
||||||
|
if (!_previousState.target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case to handle home button.
|
||||||
|
if (_previousState.target.overlayID == getHomeButtonID()) {
|
||||||
|
DependencyManager::get<MessagesClient>()->sendLocalMessage("home", _previousState.target.overlayID.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// send touch end event
|
||||||
|
_state.target.sendTouchEndEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylusPointer::stylusTouching() {
|
||||||
|
qDebug() << "QQQ " << __FUNCTION__;
|
||||||
|
if (_state.target.overlayID.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_deadspotExpired) {
|
||||||
|
_deadspotExpired =
|
||||||
|
(_touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY) ||
|
||||||
|
glm::distance2(_state.target.position2D, _touchingEnterPosition) > TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only send moves if the target moves more than the deadspot position or if we've timed out the deadspot
|
||||||
|
if (_deadspotExpired) {
|
||||||
|
_state.target.sendTouchMoveEvent();
|
||||||
|
}
|
||||||
|
}
|
147
interface/src/raypick/StylusPointer.h
Normal file
147
interface/src/raypick/StylusPointer.h
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
#ifndef hifi_StylusPointer_h
|
||||||
|
#define hifi_StylusPointer_h
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include <pointers/Pointer.h>
|
||||||
|
#include <pointers/Pick.h>
|
||||||
|
#include <shared/Bilateral.h>
|
||||||
|
#include <RegisteredMetaTypes.h>
|
||||||
|
#include <pointers/Pick.h>
|
||||||
|
|
||||||
|
#include "ui/overlays/Overlay.h"
|
||||||
|
|
||||||
|
|
||||||
|
class StylusPick : public Pick<StylusTip> {
|
||||||
|
using Side = bilateral::Side;
|
||||||
|
public:
|
||||||
|
StylusPick(Side side);
|
||||||
|
|
||||||
|
StylusTip getMathematicalPick() const override;
|
||||||
|
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override;
|
||||||
|
PickResultPointer getEntityIntersection(const StylusTip& pick) override;
|
||||||
|
PickResultPointer getOverlayIntersection(const StylusTip& pick) override;
|
||||||
|
PickResultPointer getAvatarIntersection(const StylusTip& pick) override;
|
||||||
|
PickResultPointer getHUDIntersection(const StylusTip& pick) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Side _side;
|
||||||
|
const bool _useFingerInsteadOfStylus{ false };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SideData;
|
||||||
|
|
||||||
|
struct StylusPickResult : public PickResult {
|
||||||
|
using Side = bilateral::Side;
|
||||||
|
// FIXME make into a single ID
|
||||||
|
OverlayID overlayID;
|
||||||
|
// FIXME restore entity functionality
|
||||||
|
#if 0
|
||||||
|
EntityItemID entityID;
|
||||||
|
#endif
|
||||||
|
StylusTip tip;
|
||||||
|
float distance{ FLT_MAX };
|
||||||
|
vec3 position;
|
||||||
|
vec2 position2D;
|
||||||
|
vec3 normal;
|
||||||
|
vec3 normalizedPosition;
|
||||||
|
vec3 dimensions;
|
||||||
|
bool valid{ false };
|
||||||
|
|
||||||
|
virtual bool doesIntersect() const override;
|
||||||
|
virtual std::shared_ptr<PickResult> compareAndProcessNewResult(const std::shared_ptr<PickResult>& newRes) override;
|
||||||
|
virtual bool checkOrFilterAgainstMaxDistance(float maxDistance) override;
|
||||||
|
|
||||||
|
bool isNormalized() const;
|
||||||
|
bool isNearNormal(float min, float max, float hystersis) const;
|
||||||
|
bool isNear2D(float border, float hystersis) const;
|
||||||
|
bool isNear(float min, float max, float border, float hystersis) const;
|
||||||
|
operator bool() const;
|
||||||
|
bool hasKeyboardFocus() const;
|
||||||
|
void setKeyboardFocus() const;
|
||||||
|
void sendHoverOverEvent() const;
|
||||||
|
void sendHoverEnterEvent() const;
|
||||||
|
void sendTouchStartEvent() const;
|
||||||
|
void sendTouchEndEvent() const;
|
||||||
|
void sendTouchMoveEvent() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t deviceId() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class StylusPointer : public Pointer {
|
||||||
|
using Parent = Pointer;
|
||||||
|
using Side = bilateral::Side;
|
||||||
|
using Ptr = std::shared_ptr<StylusPointer>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StylusPointer(Side side);
|
||||||
|
~StylusPointer();
|
||||||
|
|
||||||
|
void enable() override;
|
||||||
|
void disable() override;
|
||||||
|
void update(unsigned int pointerID, float deltaTime) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
virtual void setRenderState(const std::string& state) override {}
|
||||||
|
virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override {}
|
||||||
|
virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) override { return PickedObject(); }
|
||||||
|
virtual Buttons getPressedButtons() override { return {}; }
|
||||||
|
bool shouldHover() override { return true; }
|
||||||
|
bool shouldTrigger() override { return true; }
|
||||||
|
virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const override { return PointerEvent(); }
|
||||||
|
|
||||||
|
|
||||||
|
StylusPointer* getOtherStylus();
|
||||||
|
|
||||||
|
void updateStylusTarget();
|
||||||
|
void requestTouchFocus(const StylusPickResult& pickResult);
|
||||||
|
bool hasTouchFocus(const StylusPickResult& pickResult);
|
||||||
|
void relinquishTouchFocus();
|
||||||
|
void stealTouchFocus();
|
||||||
|
void stylusTouchingEnter();
|
||||||
|
void stylusTouchingExit();
|
||||||
|
void stylusTouching();
|
||||||
|
void show();
|
||||||
|
void hide();
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
StylusPickResult target;
|
||||||
|
bool nearTarget{ false };
|
||||||
|
bool touchingTarget{ false };
|
||||||
|
};
|
||||||
|
|
||||||
|
State _state;
|
||||||
|
State _previousState;
|
||||||
|
|
||||||
|
float _nearHysteresis{ 0.0f };
|
||||||
|
float _touchHysteresis{ 0.0f };
|
||||||
|
float _hoverHysteresis{ 0.0f };
|
||||||
|
|
||||||
|
float _sensorScaleFactor{ 1.0f };
|
||||||
|
float _touchingEnterTimer{ 0 };
|
||||||
|
vec2 _touchingEnterPosition;
|
||||||
|
bool _deadspotExpired{ false };
|
||||||
|
|
||||||
|
bool _renderingEnabled;
|
||||||
|
OverlayID _stylusOverlay;
|
||||||
|
OverlayID _hoverOverlay;
|
||||||
|
const Side _side;
|
||||||
|
const SideData& _sideData;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_StylusPointer_h
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -93,27 +93,41 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
|
||||||
|
|
||||||
QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0);
|
QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0);
|
||||||
|
|
||||||
bool success;
|
OctreeElement::AppendState encodeResult = OctreeElement::PARTIAL; // start the loop assuming there's more to send
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
EntityPropertyFlags didntFitProperties;
|
||||||
|
EntityItemProperties propertiesCopy = properties;
|
||||||
|
|
||||||
if (properties.parentIDChanged() && properties.getParentID() == AVATAR_SELF_ID) {
|
if (properties.parentIDChanged() && properties.getParentID() == AVATAR_SELF_ID) {
|
||||||
EntityItemProperties propertiesCopy = properties;
|
|
||||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||||
propertiesCopy.setParentID(myNodeID);
|
propertiesCopy.setParentID(myNodeID);
|
||||||
success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut);
|
|
||||||
} else {
|
|
||||||
success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, properties, bufferOut);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
EntityPropertyFlags requestedProperties = propertiesCopy.getChangedProperties();
|
||||||
#ifdef WANT_DEBUG
|
|
||||||
qCDebug(entities) << "calling queueOctreeEditMessage()...";
|
while (encodeResult == OctreeElement::PARTIAL) {
|
||||||
qCDebug(entities) << " id:" << entityItemID;
|
encodeResult = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut, requestedProperties, didntFitProperties);
|
||||||
qCDebug(entities) << " properties:" << properties;
|
|
||||||
#endif
|
if (encodeResult != OctreeElement::NONE) {
|
||||||
queueOctreeEditMessage(type, bufferOut);
|
#ifdef WANT_DEBUG
|
||||||
if (type == PacketType::EntityAdd && !properties.getCertificateID().isEmpty()) {
|
qCDebug(entities) << "calling queueOctreeEditMessage()...";
|
||||||
emit addingEntityWithCertificate(properties.getCertificateID(), DependencyManager::get<AddressManager>()->getPlaceName());
|
qCDebug(entities) << " id:" << entityItemID;
|
||||||
|
qCDebug(entities) << " properties:" << properties;
|
||||||
|
#endif
|
||||||
|
queueOctreeEditMessage(type, bufferOut);
|
||||||
|
if (type == PacketType::EntityAdd && !properties.getCertificateID().isEmpty()) {
|
||||||
|
emit addingEntityWithCertificate(properties.getCertificateID(), DependencyManager::get<AddressManager>()->getPlaceName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we still have properties to send, switch the message type to edit, and request only the packets that didn't fit
|
||||||
|
if (encodeResult != OctreeElement::COMPLETED) {
|
||||||
|
type = PacketType::EntityEdit;
|
||||||
|
requestedProperties = didntFitProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferOut.resize(NLPacket::maxPayloadSize(type)); // resize our output buffer for the next packet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
||||||
requestedProperties += PROP_ANGULAR_VELOCITY;
|
requestedProperties += PROP_ANGULAR_VELOCITY;
|
||||||
requestedProperties += PROP_ACCELERATION;
|
requestedProperties += PROP_ACCELERATION;
|
||||||
|
|
||||||
requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete
|
requestedProperties += PROP_DIMENSIONS;
|
||||||
requestedProperties += PROP_DENSITY;
|
requestedProperties += PROP_DENSITY;
|
||||||
requestedProperties += PROP_GRAVITY;
|
requestedProperties += PROP_GRAVITY;
|
||||||
requestedProperties += PROP_DAMPING;
|
requestedProperties += PROP_DAMPING;
|
||||||
|
@ -241,7 +241,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getLocalAngularVelocity());
|
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getLocalAngularVelocity());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ACCELERATION, getAcceleration());
|
APPEND_ENTITY_PROPERTY(PROP_ACCELERATION, getAcceleration());
|
||||||
|
|
||||||
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions()); // NOTE: PROP_RADIUS obsolete
|
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity());
|
APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, getGravity());
|
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, getGravity());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_DAMPING, getDamping());
|
APPEND_ENTITY_PROPERTY(PROP_DAMPING, getDamping());
|
||||||
|
|
|
@ -1221,8 +1221,9 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
||||||
//
|
//
|
||||||
// TODO: Implement support for script and visible properties.
|
// TODO: Implement support for script and visible properties.
|
||||||
//
|
//
|
||||||
bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
|
OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
|
||||||
QByteArray& buffer) {
|
QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties) {
|
||||||
|
|
||||||
OctreePacketData ourDataPacket(false, buffer.size()); // create a packetData object to add out packet details too.
|
OctreePacketData ourDataPacket(false, buffer.size()); // create a packetData object to add out packet details too.
|
||||||
OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro
|
OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro
|
||||||
|
|
||||||
|
@ -1264,17 +1265,8 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
||||||
QByteArray encodedUpdateDelta = updateDeltaCoder;
|
QByteArray encodedUpdateDelta = updateDeltaCoder;
|
||||||
|
|
||||||
EntityPropertyFlags propertyFlags(PROP_LAST_ITEM);
|
EntityPropertyFlags propertyFlags(PROP_LAST_ITEM);
|
||||||
EntityPropertyFlags requestedProperties = properties.getChangedProperties();
|
|
||||||
EntityPropertyFlags propertiesDidntFit = requestedProperties;
|
EntityPropertyFlags propertiesDidntFit = requestedProperties;
|
||||||
|
|
||||||
// TODO: we need to handle the multi-pass form of this, similar to how we handle entity data
|
|
||||||
//
|
|
||||||
// If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item,
|
|
||||||
// then our modelTreeElementExtraEncodeData should include data about which properties we need to append.
|
|
||||||
//if (modelTreeElementExtraEncodeData && modelTreeElementExtraEncodeData->includedItems.contains(getEntityItemID())) {
|
|
||||||
// requestedProperties = modelTreeElementExtraEncodeData->includedItems.value(getEntityItemID());
|
|
||||||
//}
|
|
||||||
|
|
||||||
LevelDetails entityLevel = packetData->startLevel();
|
LevelDetails entityLevel = packetData->startLevel();
|
||||||
|
|
||||||
// Last Edited quint64 always first, before any other details, which allows us easy access to adjusting this
|
// Last Edited quint64 always first, before any other details, which allows us easy access to adjusting this
|
||||||
|
@ -1302,7 +1294,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
||||||
int propertyCount = 0;
|
int propertyCount = 0;
|
||||||
|
|
||||||
bool headerFits = successIDFits && successTypeFits && successLastEditedFits
|
bool headerFits = successIDFits && successTypeFits && successLastEditedFits
|
||||||
&& successLastUpdatedFits && successPropertyFlagsFits;
|
&& successLastUpdatedFits && successPropertyFlagsFits;
|
||||||
|
|
||||||
int startOfEntityItemData = packetData->getUncompressedByteOffset();
|
int startOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||||
|
|
||||||
|
@ -1316,7 +1308,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
||||||
|
|
||||||
APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, properties._simulationOwner.toByteArray());
|
APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, properties._simulationOwner.toByteArray());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition());
|
APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete
|
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ROTATION, properties.getRotation());
|
APPEND_ENTITY_PROPERTY(PROP_ROTATION, properties.getRotation());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_DENSITY, properties.getDensity());
|
APPEND_ENTITY_PROPERTY(PROP_DENSITY, properties.getDensity());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, properties.getVelocity());
|
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, properties.getVelocity());
|
||||||
|
@ -1472,6 +1464,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
||||||
properties.getType() == EntityTypes::Sphere) {
|
properties.getType() == EntityTypes::Sphere) {
|
||||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape());
|
APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape());
|
||||||
}
|
}
|
||||||
|
|
||||||
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
|
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL());
|
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData());
|
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData());
|
||||||
|
@ -1522,12 +1515,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
||||||
|
|
||||||
// If any part of the model items didn't fit, then the element is considered partial
|
// If any part of the model items didn't fit, then the element is considered partial
|
||||||
if (appendState != OctreeElement::COMPLETED) {
|
if (appendState != OctreeElement::COMPLETED) {
|
||||||
// TODO: handle mechanism for handling partial fitting data!
|
didntFitProperties = propertiesDidntFit;
|
||||||
// add this item into our list for the next appendElementData() pass
|
|
||||||
//modelTreeElementExtraEncodeData->includedItems.insert(getEntityItemID(), propertiesDidntFit);
|
|
||||||
|
|
||||||
// for now, if it's not complete, it's not successful
|
|
||||||
success = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1543,11 +1531,15 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "ERROR - encoded edit message doesn't fit in output buffer.";
|
qCDebug(entities) << "ERROR - encoded edit message doesn't fit in output buffer.";
|
||||||
success = false;
|
success = false;
|
||||||
|
appendState = OctreeElement::NONE; // if we got here, then we didn't include the item
|
||||||
|
// maybe we should assert!!!
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
packetData->discardSubTree();
|
packetData->discardSubTree();
|
||||||
}
|
}
|
||||||
return success;
|
|
||||||
|
|
||||||
|
return appendState;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray EntityItemProperties::getPackedNormals() const {
|
QByteArray EntityItemProperties::getPackedNormals() const {
|
||||||
|
@ -1673,7 +1665,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATION_OWNER, QByteArray, setSimulationOwner);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATION_OWNER, QByteArray, setSimulationOwner);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DENSITY, float, setDensity);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DENSITY, float, setDensity);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity);
|
||||||
|
|
|
@ -262,8 +262,8 @@ public:
|
||||||
float getLocalRenderAlpha() const { return _localRenderAlpha; }
|
float getLocalRenderAlpha() const { return _localRenderAlpha; }
|
||||||
void setLocalRenderAlpha(float value) { _localRenderAlpha = value; _localRenderAlphaChanged = true; }
|
void setLocalRenderAlpha(float value) { _localRenderAlpha = value; _localRenderAlphaChanged = true; }
|
||||||
|
|
||||||
static bool encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
|
static OctreeElement::AppendState encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
|
||||||
QByteArray& buffer);
|
QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties);
|
||||||
|
|
||||||
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer);
|
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer);
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,7 @@ enum EntityPropertyList {
|
||||||
// these properties are supported by the EntityItem base class
|
// these properties are supported by the EntityItem base class
|
||||||
PROP_VISIBLE,
|
PROP_VISIBLE,
|
||||||
PROP_POSITION,
|
PROP_POSITION,
|
||||||
PROP_RADIUS, // NOTE: PROP_RADIUS is obsolete and only included in old format streams
|
PROP_DIMENSIONS,
|
||||||
PROP_DIMENSIONS = PROP_RADIUS,
|
|
||||||
PROP_ROTATION,
|
PROP_ROTATION,
|
||||||
PROP_DENSITY,
|
PROP_DENSITY,
|
||||||
PROP_VELOCITY,
|
PROP_VELOCITY,
|
||||||
|
@ -47,13 +46,13 @@ enum EntityPropertyList {
|
||||||
PROP_ANGULAR_VELOCITY,
|
PROP_ANGULAR_VELOCITY,
|
||||||
PROP_ANGULAR_DAMPING,
|
PROP_ANGULAR_DAMPING,
|
||||||
PROP_COLLISIONLESS,
|
PROP_COLLISIONLESS,
|
||||||
PROP_DYNAMIC,
|
PROP_DYNAMIC, // 24
|
||||||
|
|
||||||
// property used by Light entity
|
// property used by Light entity
|
||||||
PROP_IS_SPOTLIGHT,
|
PROP_IS_SPOTLIGHT,
|
||||||
PROP_DIFFUSE_COLOR,
|
PROP_DIFFUSE_COLOR,
|
||||||
PROP_AMBIENT_COLOR_UNUSED,
|
PROP_AMBIENT_COLOR_UNUSED, // FIXME - No longer used, can remove and bump protocol
|
||||||
PROP_SPECULAR_COLOR_UNUSED,
|
PROP_SPECULAR_COLOR_UNUSED, // FIXME - No longer used, can remove and bump protocol
|
||||||
PROP_INTENSITY, // Previously PROP_CONSTANT_ATTENUATION
|
PROP_INTENSITY, // Previously PROP_CONSTANT_ATTENUATION
|
||||||
PROP_LINEAR_ATTENUATION_UNUSED,
|
PROP_LINEAR_ATTENUATION_UNUSED,
|
||||||
PROP_QUADRATIC_ATTENUATION_UNUSED,
|
PROP_QUADRATIC_ATTENUATION_UNUSED,
|
||||||
|
@ -61,30 +60,30 @@ enum EntityPropertyList {
|
||||||
PROP_CUTOFF,
|
PROP_CUTOFF,
|
||||||
|
|
||||||
// available to all entities
|
// available to all entities
|
||||||
PROP_LOCKED,
|
PROP_LOCKED, // 34
|
||||||
|
|
||||||
PROP_TEXTURES, // used by Model entities
|
PROP_TEXTURES, // used by Model entities
|
||||||
PROP_ANIMATION_SETTINGS, // used by Model entities
|
PROP_ANIMATION_SETTINGS_UNUSED, // FIXME - No longer used, can remove and bump protocol
|
||||||
PROP_USER_DATA, // all entities
|
PROP_USER_DATA, // all entities -- 37
|
||||||
PROP_SHAPE_TYPE, // used by Model + zones entities
|
PROP_SHAPE_TYPE, // used by Model + zones entities
|
||||||
|
|
||||||
// used by ParticleEffect entities
|
// used by ParticleEffect entities
|
||||||
PROP_MAX_PARTICLES,
|
PROP_MAX_PARTICLES, // 39
|
||||||
PROP_LIFESPAN,
|
PROP_LIFESPAN, // 40 -- used by all entities
|
||||||
PROP_EMIT_RATE,
|
PROP_EMIT_RATE,
|
||||||
PROP_EMIT_SPEED,
|
PROP_EMIT_SPEED,
|
||||||
PROP_EMIT_STRENGTH,
|
PROP_EMIT_STRENGTH,
|
||||||
PROP_EMIT_ACCELERATION,
|
PROP_EMIT_ACCELERATION, // FIXME - doesn't seem to get set in mark all changed????
|
||||||
PROP_PARTICLE_RADIUS,
|
PROP_PARTICLE_RADIUS, // 45!!
|
||||||
|
|
||||||
PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities
|
PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities
|
||||||
PROP_MARKETPLACE_ID, // all entities
|
PROP_MARKETPLACE_ID, // all entities
|
||||||
PROP_ACCELERATION, // all entities
|
PROP_ACCELERATION, // all entities
|
||||||
PROP_SIMULATION_OWNER, // formerly known as PROP_SIMULATOR_ID
|
PROP_SIMULATION_OWNER, // formerly known as PROP_SIMULATOR_ID
|
||||||
PROP_NAME, // all entities
|
PROP_NAME, // all entities -- 50
|
||||||
PROP_COLLISION_SOUND_URL,
|
PROP_COLLISION_SOUND_URL,
|
||||||
PROP_RESTITUTION,
|
PROP_RESTITUTION,
|
||||||
PROP_FRICTION,
|
PROP_FRICTION, // 53
|
||||||
|
|
||||||
PROP_VOXEL_VOLUME_SIZE,
|
PROP_VOXEL_VOLUME_SIZE,
|
||||||
PROP_VOXEL_DATA,
|
PROP_VOXEL_DATA,
|
||||||
|
@ -96,7 +95,7 @@ enum EntityPropertyList {
|
||||||
|
|
||||||
// used by hyperlinks
|
// used by hyperlinks
|
||||||
PROP_HREF,
|
PROP_HREF,
|
||||||
PROP_DESCRIPTION,
|
PROP_DESCRIPTION, // 61
|
||||||
|
|
||||||
PROP_FACE_CAMERA,
|
PROP_FACE_CAMERA,
|
||||||
PROP_SCRIPT_TIMESTAMP,
|
PROP_SCRIPT_TIMESTAMP,
|
||||||
|
|
|
@ -68,8 +68,8 @@ bool OctreePacketData::append(const unsigned char* data, int length) {
|
||||||
_dirty = true;
|
_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool wantDebug = false;
|
#ifdef WANT_DEBUG
|
||||||
if (wantDebug && !success) {
|
if (!success) {
|
||||||
qCDebug(octree) << "OctreePacketData::append(const unsigned char* data, int length) FAILING....";
|
qCDebug(octree) << "OctreePacketData::append(const unsigned char* data, int length) FAILING....";
|
||||||
qCDebug(octree) << " length=" << length;
|
qCDebug(octree) << " length=" << length;
|
||||||
qCDebug(octree) << " _bytesAvailable=" << _bytesAvailable;
|
qCDebug(octree) << " _bytesAvailable=" << _bytesAvailable;
|
||||||
|
@ -77,6 +77,7 @@ bool OctreePacketData::append(const unsigned char* data, int length) {
|
||||||
qCDebug(octree) << " _targetSize=" << _targetSize;
|
qCDebug(octree) << " _targetSize=" << _targetSize;
|
||||||
qCDebug(octree) << " _bytesReserved=" << _bytesReserved;
|
qCDebug(octree) << " _bytesReserved=" << _bytesReserved;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,6 +648,13 @@ void OctreePacketData::debugContent() {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OctreePacketData::debugBytes() {
|
||||||
|
qCDebug(octree) << " _bytesAvailable=" << _bytesAvailable;
|
||||||
|
qCDebug(octree) << " _bytesInUse=" << _bytesInUse;
|
||||||
|
qCDebug(octree) << " _targetSize=" << _targetSize;
|
||||||
|
qCDebug(octree) << " _bytesReserved=" << _bytesReserved;
|
||||||
|
}
|
||||||
|
|
||||||
int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QString& result) {
|
int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QString& result) {
|
||||||
uint16_t length;
|
uint16_t length;
|
||||||
memcpy(&length, dataBytes, sizeof(length));
|
memcpy(&length, dataBytes, sizeof(length));
|
||||||
|
|
|
@ -240,6 +240,7 @@ public:
|
||||||
|
|
||||||
/// displays contents for debugging
|
/// displays contents for debugging
|
||||||
void debugContent();
|
void debugContent();
|
||||||
|
void debugBytes();
|
||||||
|
|
||||||
static quint64 getCompressContentTime() { return _compressContentTime; } /// total time spent compressing content
|
static quint64 getCompressContentTime() { return _compressContentTime; } /// total time spent compressing content
|
||||||
static quint64 getCompressContentCalls() { return _compressContentCalls; } /// total calls to compress content
|
static quint64 getCompressContentCalls() { return _compressContentCalls; } /// total calls to compress content
|
||||||
|
|
|
@ -118,7 +118,7 @@ public:
|
||||||
// for example: if we want the closest result, compare based on distance
|
// for example: if we want the closest result, compare based on distance
|
||||||
// if we want all results, combine them
|
// if we want all results, combine them
|
||||||
// must return a new pointer
|
// must return a new pointer
|
||||||
virtual std::shared_ptr<PickResult> compareAndProcessNewResult(const std::shared_ptr<PickResult> newRes) = 0;
|
virtual std::shared_ptr<PickResult> compareAndProcessNewResult(const std::shared_ptr<PickResult>& newRes) = 0;
|
||||||
|
|
||||||
// returns true if this result contains any valid results with distance < maxDistance
|
// returns true if this result contains any valid results with distance < maxDistance
|
||||||
// can also filter out results with distance >= maxDistance
|
// can also filter out results with distance >= maxDistance
|
||||||
|
@ -202,7 +202,7 @@ class Pick : public PickQuery {
|
||||||
public:
|
public:
|
||||||
Pick(const PickFilter& filter, const float maxDistance, const bool enabled) : PickQuery(filter, maxDistance, enabled) {}
|
Pick(const PickFilter& filter, const float maxDistance, const bool enabled) : PickQuery(filter, maxDistance, enabled) {}
|
||||||
|
|
||||||
virtual const T getMathematicalPick() const = 0;
|
virtual T getMathematicalPick() const = 0;
|
||||||
virtual PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const = 0;
|
virtual PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const = 0;
|
||||||
virtual PickResultPointer getEntityIntersection(const T& pick) = 0;
|
virtual PickResultPointer getEntityIntersection(const T& pick) = 0;
|
||||||
virtual PickResultPointer getOverlayIntersection(const T& pick) = 0;
|
virtual PickResultPointer getOverlayIntersection(const T& pick) = 0;
|
||||||
|
|
|
@ -70,7 +70,6 @@ template<typename T>
|
||||||
void PickCacheOptimizer<T>::update(std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>& picks, bool shouldPickHUD) {
|
void PickCacheOptimizer<T>::update(std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>& picks, bool shouldPickHUD) {
|
||||||
PickCache results;
|
PickCache results;
|
||||||
for (const auto& pickPair : picks) {
|
for (const auto& pickPair : picks) {
|
||||||
unsigned int uid = pickPair.first;
|
|
||||||
std::shared_ptr<Pick<T>> pick = std::static_pointer_cast<Pick<T>>(pickPair.second);
|
std::shared_ptr<Pick<T>> pick = std::static_pointer_cast<Pick<T>>(pickPair.second);
|
||||||
|
|
||||||
T mathematicalPick = pick->getMathematicalPick();
|
T mathematicalPick = pick->getMathematicalPick();
|
||||||
|
@ -85,7 +84,9 @@ void PickCacheOptimizer<T>::update(std::unordered_map<unsigned int, std::shared_
|
||||||
PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
||||||
if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) {
|
if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) {
|
||||||
PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick);
|
PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick);
|
||||||
cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick);
|
if (entityRes) {
|
||||||
|
cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +94,9 @@ void PickCacheOptimizer<T>::update(std::unordered_map<unsigned int, std::shared_
|
||||||
PickCacheKey overlayKey = { pick->getFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
PickCacheKey overlayKey = { pick->getFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
||||||
if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) {
|
if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) {
|
||||||
PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick);
|
PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick);
|
||||||
cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick);
|
if (overlayRes) {
|
||||||
|
cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +104,9 @@ void PickCacheOptimizer<T>::update(std::unordered_map<unsigned int, std::shared_
|
||||||
PickCacheKey avatarKey = { pick->getFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
PickCacheKey avatarKey = { pick->getFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
||||||
if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) {
|
if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) {
|
||||||
PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick);
|
PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick);
|
||||||
cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick);
|
if (avatarRes) {
|
||||||
|
cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +115,9 @@ void PickCacheOptimizer<T>::update(std::unordered_map<unsigned int, std::shared_
|
||||||
PickCacheKey hudKey = { pick->getFilter().getHUDFlags(), QVector<QUuid>(), QVector<QUuid>() };
|
PickCacheKey hudKey = { pick->getFilter().getHUDFlags(), QVector<QUuid>(), QVector<QUuid>() };
|
||||||
if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) {
|
if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) {
|
||||||
PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick);
|
PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick);
|
||||||
cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick);
|
if (hudRes) {
|
||||||
|
cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,12 +45,12 @@ void PickManager::removePick(unsigned int uid) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap PickManager::getPrevPickResult(unsigned int uid) const {
|
PickResultPointer PickManager::getPrevPickResult(unsigned int uid) const {
|
||||||
auto pick = findPick(uid);
|
auto pick = findPick(uid);
|
||||||
if (pick && pick->getPrevPickResult()) {
|
if (pick) {
|
||||||
return pick->getPrevPickResult()->toVariantMap();
|
return pick->getPrevPickResult();
|
||||||
}
|
}
|
||||||
return QVariantMap();
|
return PickResultPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PickManager::enablePick(unsigned int uid) const {
|
void PickManager::enablePick(unsigned int uid) const {
|
||||||
|
@ -96,6 +96,7 @@ void PickManager::update() {
|
||||||
|
|
||||||
bool shouldPickHUD = _shouldPickHUDOperator();
|
bool shouldPickHUD = _shouldPickHUDOperator();
|
||||||
_rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], shouldPickHUD);
|
_rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], shouldPickHUD);
|
||||||
|
_stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PickManager::isLeftHand(unsigned int uid) {
|
bool PickManager::isLeftHand(unsigned int uid) {
|
||||||
|
|
|
@ -27,7 +27,12 @@ public:
|
||||||
void enablePick(unsigned int uid) const;
|
void enablePick(unsigned int uid) const;
|
||||||
void disablePick(unsigned int uid) const;
|
void disablePick(unsigned int uid) const;
|
||||||
|
|
||||||
QVariantMap getPrevPickResult(unsigned int uid) const;
|
PickResultPointer getPrevPickResult(unsigned int uid) const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::shared_ptr<T> getPrevPickResultTyped(unsigned int uid) const {
|
||||||
|
return std::static_pointer_cast<T>(getPrevPickResult(uid));
|
||||||
|
}
|
||||||
|
|
||||||
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
|
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
|
||||||
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const;
|
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const;
|
||||||
|
@ -53,6 +58,7 @@ protected:
|
||||||
unsigned int _nextPickID { INVALID_PICK_ID + 1 };
|
unsigned int _nextPickID { INVALID_PICK_ID + 1 };
|
||||||
|
|
||||||
PickCacheOptimizer<PickRay> _rayPickCacheOptimizer;
|
PickCacheOptimizer<PickRay> _rayPickCacheOptimizer;
|
||||||
|
PickCacheOptimizer<StylusTip> _stylusPickCacheOptimizer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_PickManager_h
|
#endif // hifi_PickManager_h
|
|
@ -30,7 +30,7 @@ void Pointer::disable() {
|
||||||
DependencyManager::get<PickManager>()->disablePick(_pickUID);
|
DependencyManager::get<PickManager>()->disablePick(_pickUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVariantMap Pointer::getPrevPickResult() {
|
PickResultPointer Pointer::getPrevPickResult() {
|
||||||
return DependencyManager::get<PickManager>()->getPrevPickResult(_pickUID);
|
return DependencyManager::get<PickManager>()->getPrevPickResult(_pickUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,17 +58,17 @@ bool Pointer::isMouse() const {
|
||||||
return DependencyManager::get<PickManager>()->isMouse(_pickUID);
|
return DependencyManager::get<PickManager>()->isMouse(_pickUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pointer::update(unsigned int pointerID) {
|
void Pointer::update(unsigned int pointerID, float deltaTime) {
|
||||||
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
QVariantMap pickResult = getPrevPickResult();
|
auto pickResult = getPrevPickResult();
|
||||||
updateVisuals(pickResult);
|
updateVisuals(pickResult);
|
||||||
generatePointerEvents(pointerID, pickResult);
|
generatePointerEvents(pointerID, pickResult);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pointer::generatePointerEvents(unsigned int pointerID, const QVariantMap& pickResult) {
|
void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult) {
|
||||||
// TODO: avatars?
|
// TODO: avatars/HUD?
|
||||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||||
|
|
||||||
// NOTE: After this loop: _prevButtons = buttons that were removed
|
// NOTE: After this loop: _prevButtons = buttons that were removed
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include <shared/ReadWriteLockable.h>
|
#include <shared/ReadWriteLockable.h>
|
||||||
|
#include "Pick.h"
|
||||||
|
|
||||||
#include <controllers/impl/Endpoint.h>
|
#include <controllers/impl/Endpoint.h>
|
||||||
#include "PointerEvent.h"
|
#include "PointerEvent.h"
|
||||||
|
@ -44,7 +45,7 @@ public:
|
||||||
|
|
||||||
virtual void enable();
|
virtual void enable();
|
||||||
virtual void disable();
|
virtual void disable();
|
||||||
virtual const QVariantMap getPrevPickResult();
|
virtual PickResultPointer getPrevPickResult();
|
||||||
|
|
||||||
virtual void setRenderState(const std::string& state) = 0;
|
virtual void setRenderState(const std::string& state) = 0;
|
||||||
virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0;
|
virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0;
|
||||||
|
@ -61,13 +62,12 @@ public:
|
||||||
virtual void setLength(float length) {}
|
virtual void setLength(float length) {}
|
||||||
virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay) {}
|
virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay) {}
|
||||||
|
|
||||||
void update(unsigned int pointerID);
|
virtual void update(unsigned int pointerID, float deltaTime);
|
||||||
virtual void updateVisuals(const QVariantMap& pickResult) = 0;
|
virtual void updateVisuals(const PickResultPointer& pickResult) {}
|
||||||
void generatePointerEvents(unsigned int pointerID, const QVariantMap& pickResult);
|
void generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult);
|
||||||
|
|
||||||
struct PickedObject {
|
struct PickedObject {
|
||||||
PickedObject() {}
|
PickedObject(const QUuid& objectID = QUuid(), IntersectionType type = IntersectionType::NONE) : objectID(objectID), type(type) {}
|
||||||
PickedObject(const QUuid& objectID, IntersectionType type) : objectID(objectID), type(type) {}
|
|
||||||
|
|
||||||
QUuid objectID;
|
QUuid objectID;
|
||||||
IntersectionType type;
|
IntersectionType type;
|
||||||
|
@ -82,9 +82,9 @@ protected:
|
||||||
bool _enabled;
|
bool _enabled;
|
||||||
bool _hover;
|
bool _hover;
|
||||||
|
|
||||||
virtual PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const = 0;
|
virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const = 0;
|
||||||
|
|
||||||
virtual PickedObject getHoveredObject(const QVariantMap& pickResult) = 0;
|
virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) = 0;
|
||||||
virtual Buttons getPressedButtons() = 0;
|
virtual Buttons getPressedButtons() = 0;
|
||||||
|
|
||||||
virtual bool shouldHover() = 0;
|
virtual bool shouldHover() = 0;
|
||||||
|
|
|
@ -68,21 +68,22 @@ void PointerManager::editRenderState(unsigned int uid, const std::string& state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVariantMap PointerManager::getPrevPickResult(unsigned int uid) const {
|
PickResultPointer PointerManager::getPrevPickResult(unsigned int uid) const {
|
||||||
|
PickResultPointer result;
|
||||||
auto pointer = find(uid);
|
auto pointer = find(uid);
|
||||||
if (pointer) {
|
if (pointer) {
|
||||||
return pointer->getPrevPickResult();
|
result = pointer->getPrevPickResult();
|
||||||
}
|
}
|
||||||
return QVariantMap();
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PointerManager::update() {
|
void PointerManager::update(float deltaTime) {
|
||||||
auto cachedPointers = resultWithReadLock<std::unordered_map<unsigned int, std::shared_ptr<Pointer>>>([&] {
|
auto cachedPointers = resultWithReadLock<std::unordered_map<unsigned int, std::shared_ptr<Pointer>>>([&] {
|
||||||
return _pointers;
|
return _pointers;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const auto& pointerPair : cachedPointers) {
|
for (const auto& pointerPair : cachedPointers) {
|
||||||
pointerPair.second->update(pointerPair.first);
|
pointerPair.second->update(pointerPair.first, deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
void disablePointer(unsigned int uid) const;
|
void disablePointer(unsigned int uid) const;
|
||||||
void setRenderState(unsigned int uid, const std::string& renderState) const;
|
void setRenderState(unsigned int uid, const std::string& renderState) const;
|
||||||
void editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const;
|
void editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const;
|
||||||
const QVariantMap getPrevPickResult(unsigned int uid) const;
|
PickResultPointer getPrevPickResult(unsigned int uid) const;
|
||||||
|
|
||||||
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
|
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
|
||||||
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignoreEntities) const;
|
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignoreEntities) const;
|
||||||
|
@ -38,7 +38,7 @@ public:
|
||||||
void setLength(unsigned int uid, float length) const;
|
void setLength(unsigned int uid, float length) const;
|
||||||
void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const;
|
void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const;
|
||||||
|
|
||||||
void update();
|
void update(float deltaTime);
|
||||||
|
|
||||||
bool isLeftHand(unsigned int uid);
|
bool isLeftHand(unsigned int uid);
|
||||||
bool isRightHand(unsigned int uid);
|
bool isRightHand(unsigned int uid);
|
||||||
|
|
|
@ -69,6 +69,7 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable
|
||||||
#ifdef THREAD_DEBUGGING
|
#ifdef THREAD_DEBUGGING
|
||||||
qCDebug(scriptengine) << "ScriptCache::getScriptContents() on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
|
qCDebug(scriptengine) << "ScriptCache::getScriptContents() on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
|
||||||
#endif
|
#endif
|
||||||
|
forceDownload = true;
|
||||||
QUrl unnormalizedURL(scriptOrURL);
|
QUrl unnormalizedURL(scriptOrURL);
|
||||||
QUrl url = DependencyManager::get<ResourceManager>()->normalizeURL(unnormalizedURL);
|
QUrl url = DependencyManager::get<ResourceManager>()->normalizeURL(unnormalizedURL);
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ public:
|
||||||
PointerEvent(EventType type, uint32_t id,
|
PointerEvent(EventType type, uint32_t id,
|
||||||
const glm::vec2& pos2D, const glm::vec3& pos3D,
|
const glm::vec2& pos2D, const glm::vec3& pos3D,
|
||||||
const glm::vec3& normal, const glm::vec3& direction,
|
const glm::vec3& normal, const glm::vec3& direction,
|
||||||
Button button, uint32_t buttons, Qt::KeyboardModifiers keyboardModifiers);
|
Button button = NoButtons, uint32_t buttons = NoButtons, Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier);
|
||||||
|
|
||||||
static QScriptValue toScriptValue(QScriptEngine* engine, const PointerEvent& event);
|
static QScriptValue toScriptValue(QScriptEngine* engine, const PointerEvent& event);
|
||||||
static void fromScriptValue(const QScriptValue& object, PointerEvent& event);
|
static void fromScriptValue(const QScriptValue& object, PointerEvent& event);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "AACube.h"
|
#include "AACube.h"
|
||||||
#include "SharedUtil.h"
|
#include "SharedUtil.h"
|
||||||
|
#include "shared/Bilateral.h"
|
||||||
|
|
||||||
class QColor;
|
class QColor;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
@ -152,17 +153,73 @@ public:
|
||||||
return pickRay;
|
return pickRay;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StylusTip : public MathPick {
|
||||||
|
bilateral::Side side{ bilateral::Side::Invalid };
|
||||||
|
glm::vec3 position;
|
||||||
|
glm::quat orientation;
|
||||||
|
glm::vec3 velocity;
|
||||||
|
virtual operator bool() const override { return side != bilateral::Side::Invalid; }
|
||||||
|
QVariantMap toVariantMap() const override {
|
||||||
|
QVariantMap pickRay;
|
||||||
|
pickRay["position"] = vec3toVariant(position);
|
||||||
|
pickRay["orientation"] = quatToVariant(orientation);
|
||||||
|
pickRay["velocity"] = vec3toVariant(velocity);
|
||||||
|
return pickRay;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
|
inline void hash_combine(std::size_t& seed) { }
|
||||||
|
|
||||||
|
template <typename T, typename... Rest>
|
||||||
|
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
|
||||||
|
std::hash<T> hasher;
|
||||||
|
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
||||||
|
hash_combine(seed, rest...);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct hash<glm::vec3> {
|
struct hash<bilateral::Side> {
|
||||||
size_t operator()(const glm::vec3& a) const {
|
size_t operator()(const bilateral::Side& a) const {
|
||||||
return ((hash<float>()(a.x) ^ (hash<float>()(a.y) << 1)) >> 1) ^ (hash<float>()(a.z) << 1);
|
return std::hash<int>()((int)a);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <>
|
|
||||||
|
template <>
|
||||||
|
struct hash<glm::vec3> {
|
||||||
|
size_t operator()(const glm::vec3& a) const {
|
||||||
|
size_t result = 0;
|
||||||
|
hash_combine(result, a.x, a.y, a.z);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<glm::quat> {
|
||||||
|
size_t operator()(const glm::quat& a) const {
|
||||||
|
size_t result = 0;
|
||||||
|
hash_combine(result, a.x, a.y, a.z, a.w);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
struct hash<PickRay> {
|
struct hash<PickRay> {
|
||||||
size_t operator()(const PickRay& a) const {
|
size_t operator()(const PickRay& a) const {
|
||||||
return (hash<glm::vec3>()(a.origin) ^ (hash<glm::vec3>()(a.direction) << 1));
|
size_t result = 0;
|
||||||
|
hash_combine(result, a.origin, a.direction);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<StylusTip> {
|
||||||
|
size_t operator()(const StylusTip& a) const {
|
||||||
|
size_t result = 0;
|
||||||
|
hash_combine(result, a.side, a.position, a.orientation, a.velocity);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
namespace bilateral {
|
namespace bilateral {
|
||||||
enum class Side {
|
enum class Side {
|
||||||
Left = 0,
|
Left = 0,
|
||||||
Right = 1
|
Right = 1,
|
||||||
|
Invalid = -1
|
||||||
};
|
};
|
||||||
|
|
||||||
using Indices = Side;
|
using Indices = Side;
|
||||||
|
@ -27,8 +28,10 @@ namespace bilateral {
|
||||||
return 0x01;
|
return 0x01;
|
||||||
case Side::Right:
|
case Side::Right:
|
||||||
return 0x02;
|
return 0x02;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return std::numeric_limits<uint8_t>::max();
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint8_t index(Side side) {
|
inline uint8_t index(Side side) {
|
||||||
|
@ -37,10 +40,24 @@ namespace bilateral {
|
||||||
return 0;
|
return 0;
|
||||||
case Side::Right:
|
case Side::Right:
|
||||||
return 1;
|
return 1;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return std::numeric_limits<uint8_t>::max();
|
return std::numeric_limits<uint8_t>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Side side(int index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
return Side::Left;
|
||||||
|
case 1:
|
||||||
|
return Side::Right;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return Side::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void for_each_side(F f) {
|
void for_each_side(F f) {
|
||||||
f(Side::Left);
|
f(Side::Left);
|
||||||
|
|
|
@ -16,88 +16,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
Script.include("/~/system/libraries/controllers.js");
|
Script.include("/~/system/libraries/controllers.js");
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var TouchEventUtils = Script.require("/~/system/libraries/touchEventUtils.js");
|
function TabletStylusInput(hand) {
|
||||||
// triggered when stylus presses a web overlay/entity
|
|
||||||
var HAPTIC_STYLUS_STRENGTH = 1.0;
|
|
||||||
var HAPTIC_STYLUS_DURATION = 20.0;
|
|
||||||
|
|
||||||
var WEB_DISPLAY_STYLUS_DISTANCE = 0.5;
|
|
||||||
var WEB_STYLUS_LENGTH = 0.2;
|
|
||||||
var WEB_TOUCH_Y_OFFSET = 0.105; // how far forward (or back with a negative number) to slide stylus in hand
|
|
||||||
|
|
||||||
function isNearStylusTarget(stylusTargets, edgeBorder, minNormalDistance, maxNormalDistance) {
|
|
||||||
for (var i = 0; i < stylusTargets.length; i++) {
|
|
||||||
var stylusTarget = stylusTargets[i];
|
|
||||||
|
|
||||||
// check to see if the projected stylusTip is within within the 2d border
|
|
||||||
var borderMin = {x: -edgeBorder, y: -edgeBorder};
|
|
||||||
var borderMax = {x: stylusTarget.dimensions.x + edgeBorder, y: stylusTarget.dimensions.y + edgeBorder};
|
|
||||||
if (stylusTarget.distance >= minNormalDistance && stylusTarget.distance <= maxNormalDistance &&
|
|
||||||
stylusTarget.position2D.x >= borderMin.x && stylusTarget.position2D.y >= borderMin.y &&
|
|
||||||
stylusTarget.position2D.x <= borderMax.x && stylusTarget.position2D.y <= borderMax.y) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateNearestStylusTarget(stylusTargets) {
|
|
||||||
var nearestStylusTarget;
|
|
||||||
|
|
||||||
for (var i = 0; i < stylusTargets.length; i++) {
|
|
||||||
var stylusTarget = stylusTargets[i];
|
|
||||||
|
|
||||||
if ((!nearestStylusTarget || stylusTarget.distance < nearestStylusTarget.distance) &&
|
|
||||||
stylusTarget.normalizedPosition.x >= 0 && stylusTarget.normalizedPosition.y >= 0 &&
|
|
||||||
stylusTarget.normalizedPosition.x <= 1 && stylusTarget.normalizedPosition.y <= 1) {
|
|
||||||
nearestStylusTarget = stylusTarget;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nearestStylusTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFingerWorldLocation(hand) {
|
|
||||||
var fingerJointName = (hand === RIGHT_HAND) ? "RightHandIndex4" : "LeftHandIndex4";
|
|
||||||
|
|
||||||
var fingerJointIndex = MyAvatar.getJointIndex(fingerJointName);
|
|
||||||
var fingerPosition = MyAvatar.getAbsoluteJointTranslationInObjectFrame(fingerJointIndex);
|
|
||||||
var fingerRotation = MyAvatar.getAbsoluteJointRotationInObjectFrame(fingerJointIndex);
|
|
||||||
var worldFingerRotation = Quat.multiply(MyAvatar.orientation, fingerRotation);
|
|
||||||
var worldFingerPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, fingerPosition));
|
|
||||||
|
|
||||||
return {
|
|
||||||
position: worldFingerPosition,
|
|
||||||
orientation: worldFingerRotation,
|
|
||||||
rotation: worldFingerRotation,
|
|
||||||
valid: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function distance2D(a, b) {
|
|
||||||
var dx = (a.x - b.x);
|
|
||||||
var dy = (a.y - b.y);
|
|
||||||
return Math.sqrt(dx * dx + dy * dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
function TabletStylusInput(hand) {
|
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
this.previousStylusTouchingTarget = false;
|
|
||||||
this.stylusTouchingTarget = false;
|
|
||||||
|
|
||||||
this.useFingerInsteadOfStylus = false;
|
|
||||||
this.fingerPointing = false;
|
|
||||||
|
|
||||||
// initialize stylus tip
|
|
||||||
var DEFAULT_STYLUS_TIP = {
|
|
||||||
position: {x: 0, y: 0, z: 0},
|
|
||||||
orientation: {x: 0, y: 0, z: 0, w: 0},
|
|
||||||
rotation: {x: 0, y: 0, z: 0, w: 0},
|
|
||||||
velocity: {x: 0, y: 0, z: 0},
|
|
||||||
valid: false
|
|
||||||
};
|
|
||||||
this.stylusTip = DEFAULT_STYLUS_TIP;
|
|
||||||
|
|
||||||
|
|
||||||
this.parameters = makeDispatcherModuleParameters(
|
this.parameters = makeDispatcherModuleParameters(
|
||||||
100,
|
100,
|
||||||
|
@ -105,167 +25,7 @@ Script.include("/~/system/libraries/controllers.js");
|
||||||
[],
|
[],
|
||||||
100);
|
100);
|
||||||
|
|
||||||
this.getOtherHandController = function() {
|
this.pointer = Pointers.createPointer(PickType.Stylus, { hand: this.hand });
|
||||||
return (this.hand === RIGHT_HAND) ? leftTabletStylusInput : rightTabletStylusInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.handToController = function() {
|
|
||||||
return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.updateFingerAsStylusSetting = function () {
|
|
||||||
var DEFAULT_USE_FINGER_AS_STYLUS = false;
|
|
||||||
var USE_FINGER_AS_STYLUS = Settings.getValue("preferAvatarFingerOverStylus");
|
|
||||||
if (USE_FINGER_AS_STYLUS === "") {
|
|
||||||
USE_FINGER_AS_STYLUS = DEFAULT_USE_FINGER_AS_STYLUS;
|
|
||||||
}
|
|
||||||
if (USE_FINGER_AS_STYLUS && MyAvatar.getJointIndex("LeftHandIndex4") !== -1) {
|
|
||||||
this.useFingerInsteadOfStylus = true;
|
|
||||||
} else {
|
|
||||||
this.useFingerInsteadOfStylus = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.updateStylusTip = function() {
|
|
||||||
if (this.useFingerInsteadOfStylus) {
|
|
||||||
this.stylusTip = getFingerWorldLocation(this.hand);
|
|
||||||
} else {
|
|
||||||
this.stylusTip = getControllerWorldLocation(this.handToController(), true);
|
|
||||||
|
|
||||||
// translate tip forward according to constant.
|
|
||||||
var TIP_OFFSET = Vec3.multiply(MyAvatar.sensorToWorldScale, {x: 0, y: WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, z: 0});
|
|
||||||
this.stylusTip.position = Vec3.sum(this.stylusTip.position,
|
|
||||||
Vec3.multiplyQbyV(this.stylusTip.orientation, TIP_OFFSET));
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute tip velocity from hand controller motion, it is more accurate than computing it from previous positions.
|
|
||||||
var pose = Controller.getPoseValue(this.handToController());
|
|
||||||
if (pose.valid) {
|
|
||||||
var worldControllerPos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation));
|
|
||||||
var worldControllerLinearVel = Vec3.multiplyQbyV(MyAvatar.orientation, pose.velocity);
|
|
||||||
var worldControllerAngularVel = Vec3.multiplyQbyV(MyAvatar.orientation, pose.angularVelocity);
|
|
||||||
var tipVelocity = Vec3.sum(worldControllerLinearVel, Vec3.cross(worldControllerAngularVel,
|
|
||||||
Vec3.subtract(this.stylusTip.position, worldControllerPos)));
|
|
||||||
this.stylusTip.velocity = tipVelocity;
|
|
||||||
} else {
|
|
||||||
this.stylusTip.velocity = {x: 0, y: 0, z: 0};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.showStylus = function() {
|
|
||||||
if (this.stylus) {
|
|
||||||
var X_ROT_NEG_90 = { x: -0.70710678, y: 0, z: 0, w: 0.70710678 };
|
|
||||||
var modelOrientation = Quat.multiply(this.stylusTip.orientation, X_ROT_NEG_90);
|
|
||||||
var modelOrientationAngles = Quat.safeEulerAngles(modelOrientation);
|
|
||||||
|
|
||||||
var rotation = Overlays.getProperty(this.stylus, "rotation");
|
|
||||||
var rotationAngles = Quat.safeEulerAngles(rotation);
|
|
||||||
|
|
||||||
if(!Vec3.withinEpsilon(modelOrientationAngles, rotationAngles, 1)) {
|
|
||||||
var modelPositionOffset = Vec3.multiplyQbyV(modelOrientation, { x: 0, y: 0, z: MyAvatar.sensorToWorldScale * -WEB_STYLUS_LENGTH / 2 });
|
|
||||||
|
|
||||||
var updatedStylusProperties = {
|
|
||||||
position: Vec3.sum(this.stylusTip.position, modelPositionOffset),
|
|
||||||
rotation: modelOrientation,
|
|
||||||
dimensions: Vec3.multiply(MyAvatar.sensorToWorldScale, { x: 0.01, y: 0.01, z: WEB_STYLUS_LENGTH }),
|
|
||||||
};
|
|
||||||
|
|
||||||
Overlays.editOverlay(this.stylus, updatedStylusProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var X_ROT_NEG_90 = { x: -0.70710678, y: 0, z: 0, w: 0.70710678 };
|
|
||||||
var modelOrientation = Quat.multiply(this.stylusTip.orientation, X_ROT_NEG_90);
|
|
||||||
var modelPositionOffset = Vec3.multiplyQbyV(modelOrientation, { x: 0, y: 0, z: MyAvatar.sensorToWorldScale * -WEB_STYLUS_LENGTH / 2 });
|
|
||||||
|
|
||||||
var stylusProperties = {
|
|
||||||
name: "stylus",
|
|
||||||
url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx",
|
|
||||||
loadPriority: 10.0,
|
|
||||||
position: Vec3.sum(this.stylusTip.position, modelPositionOffset),
|
|
||||||
rotation: modelOrientation,
|
|
||||||
dimensions: Vec3.multiply(MyAvatar.sensorToWorldScale, { x: 0.01, y: 0.01, z: WEB_STYLUS_LENGTH }),
|
|
||||||
solid: true,
|
|
||||||
visible: true,
|
|
||||||
ignoreRayIntersection: true,
|
|
||||||
drawInFront: false,
|
|
||||||
parentID: MyAvatar.SELF_ID,
|
|
||||||
parentJointIndex: MyAvatar.getJointIndex(this.hand === RIGHT_HAND ?
|
|
||||||
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
|
|
||||||
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND")
|
|
||||||
};
|
|
||||||
this.stylus = Overlays.addOverlay("model", stylusProperties);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hideStylus = function() {
|
|
||||||
if (!this.stylus) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Overlays.deleteOverlay(this.stylus);
|
|
||||||
this.stylus = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.stealTouchFocus = function(stylusTarget) {
|
|
||||||
// send hover events to target
|
|
||||||
// record the entity or overlay we are hovering over.
|
|
||||||
if ((stylusTarget.entityID === this.getOtherHandController().hoverEntity) ||
|
|
||||||
(stylusTarget.overlayID === this.getOtherHandController().hoverOverlay)) {
|
|
||||||
this.getOtherHandController().relinquishTouchFocus();
|
|
||||||
}
|
|
||||||
this.requestTouchFocus(stylusTarget);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.requestTouchFocus = function(stylusTarget) {
|
|
||||||
|
|
||||||
// send hover events to target if we can.
|
|
||||||
// record the entity or overlay we are hovering over.
|
|
||||||
if (stylusTarget.entityID &&
|
|
||||||
stylusTarget.entityID !== this.hoverEntity &&
|
|
||||||
stylusTarget.entityID !== this.getOtherHandController().hoverEntity) {
|
|
||||||
this.hoverEntity = stylusTarget.entityID;
|
|
||||||
TouchEventUtils.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget);
|
|
||||||
} else if (stylusTarget.overlayID &&
|
|
||||||
stylusTarget.overlayID !== this.hoverOverlay &&
|
|
||||||
stylusTarget.overlayID !== this.getOtherHandController().hoverOverlay) {
|
|
||||||
this.hoverOverlay = stylusTarget.overlayID;
|
|
||||||
TouchEventUtils.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hasTouchFocus = function(stylusTarget) {
|
|
||||||
return ((stylusTarget.entityID && stylusTarget.entityID === this.hoverEntity) ||
|
|
||||||
(stylusTarget.overlayID && stylusTarget.overlayID === this.hoverOverlay));
|
|
||||||
};
|
|
||||||
|
|
||||||
this.relinquishTouchFocus = function() {
|
|
||||||
// send hover leave event.
|
|
||||||
var pointerEvent = { type: "Move", id: this.hand + 1 };
|
|
||||||
if (this.hoverEntity) {
|
|
||||||
Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent);
|
|
||||||
this.hoverEntity = null;
|
|
||||||
} else if (this.hoverOverlay) {
|
|
||||||
Overlays.sendMouseMoveOnOverlay(this.hoverOverlay, pointerEvent);
|
|
||||||
Overlays.sendHoverOverOverlay(this.hoverOverlay, pointerEvent);
|
|
||||||
Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent);
|
|
||||||
this.hoverOverlay = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.pointFinger = function(value) {
|
|
||||||
var HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index";
|
|
||||||
if (this.fingerPointing !== value) {
|
|
||||||
var message;
|
|
||||||
if (this.hand === RIGHT_HAND) {
|
|
||||||
message = { pointRightIndex: value };
|
|
||||||
} else {
|
|
||||||
message = { pointLeftIndex: value };
|
|
||||||
}
|
|
||||||
Messages.sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, JSON.stringify(message), true);
|
|
||||||
this.fingerPointing = value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.otherModuleNeedsToRun = function(controllerData) {
|
this.otherModuleNeedsToRun = function(controllerData) {
|
||||||
var grabOverlayModuleName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay";
|
var grabOverlayModuleName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay";
|
||||||
|
@ -277,189 +37,6 @@ Script.include("/~/system/libraries/controllers.js");
|
||||||
return grabOverlayModuleReady.active || farGrabModuleReady.active;
|
return grabOverlayModuleReady.active || farGrabModuleReady.active;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.processStylus = function(controllerData) {
|
|
||||||
this.updateStylusTip();
|
|
||||||
|
|
||||||
if (!this.stylusTip.valid || this.overlayLaserActive(controllerData) || this.otherModuleNeedsToRun(controllerData)) {
|
|
||||||
this.pointFinger(false);
|
|
||||||
this.hideStylus();
|
|
||||||
this.stylusTouchingTarget = false;
|
|
||||||
this.relinquishTouchFocus();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.useFingerInsteadOfStylus) {
|
|
||||||
this.hideStylus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// build list of stylus targets, near the stylusTip
|
|
||||||
var stylusTargets = [];
|
|
||||||
var candidateEntities = controllerData.nearbyEntityProperties;
|
|
||||||
var i, props, stylusTarget;
|
|
||||||
for (i = 0; i < candidateEntities.length; i++) {
|
|
||||||
props = candidateEntities[i];
|
|
||||||
if (props && props.type === "Web") {
|
|
||||||
stylusTarget = TouchEventUtils.calculateTouchTargetFromEntity(this.stylusTip, candidateEntities[i]);
|
|
||||||
if (stylusTarget) {
|
|
||||||
stylusTargets.push(stylusTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the tabletScreen, if it is valid
|
|
||||||
if (HMD.tabletScreenID && HMD.tabletScreenID !== Uuid.NULL &&
|
|
||||||
Overlays.getProperty(HMD.tabletScreenID, "visible")) {
|
|
||||||
stylusTarget = TouchEventUtils.calculateTouchTargetFromOverlay(this.stylusTip, HMD.tabletScreenID);
|
|
||||||
if (stylusTarget) {
|
|
||||||
stylusTargets.push(stylusTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the tablet home button.
|
|
||||||
if (HMD.homeButtonID && HMD.homeButtonID !== Uuid.NULL &&
|
|
||||||
Overlays.getProperty(HMD.homeButtonID, "visible")) {
|
|
||||||
stylusTarget = TouchEventUtils.calculateTouchTargetFromOverlay(this.stylusTip, HMD.homeButtonID);
|
|
||||||
if (stylusTarget) {
|
|
||||||
stylusTargets.push(stylusTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var TABLET_MIN_HOVER_DISTANCE = 0.01;
|
|
||||||
var TABLET_MAX_HOVER_DISTANCE = 0.1;
|
|
||||||
var TABLET_MIN_TOUCH_DISTANCE = -0.05;
|
|
||||||
var TABLET_MAX_TOUCH_DISTANCE = TABLET_MIN_HOVER_DISTANCE;
|
|
||||||
var EDGE_BORDER = 0.075;
|
|
||||||
|
|
||||||
var hysteresisOffset = 0.0;
|
|
||||||
if (this.isNearStylusTarget) {
|
|
||||||
hysteresisOffset = 0.05;
|
|
||||||
}
|
|
||||||
|
|
||||||
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
|
|
||||||
this.isNearStylusTarget = isNearStylusTarget(stylusTargets,
|
|
||||||
(EDGE_BORDER + hysteresisOffset) * sensorScaleFactor,
|
|
||||||
(TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset) * sensorScaleFactor,
|
|
||||||
(WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset) * sensorScaleFactor);
|
|
||||||
|
|
||||||
if (this.isNearStylusTarget) {
|
|
||||||
if (!this.useFingerInsteadOfStylus) {
|
|
||||||
this.showStylus();
|
|
||||||
} else {
|
|
||||||
this.pointFinger(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.hideStylus();
|
|
||||||
this.pointFinger(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
var nearestStylusTarget = calculateNearestStylusTarget(stylusTargets);
|
|
||||||
|
|
||||||
var SCALED_TABLET_MIN_TOUCH_DISTANCE = TABLET_MIN_TOUCH_DISTANCE * sensorScaleFactor;
|
|
||||||
var SCALED_TABLET_MAX_TOUCH_DISTANCE = TABLET_MAX_TOUCH_DISTANCE * sensorScaleFactor;
|
|
||||||
var SCALED_TABLET_MAX_HOVER_DISTANCE = TABLET_MAX_HOVER_DISTANCE * sensorScaleFactor;
|
|
||||||
|
|
||||||
if (nearestStylusTarget && nearestStylusTarget.distance > SCALED_TABLET_MIN_TOUCH_DISTANCE &&
|
|
||||||
nearestStylusTarget.distance < SCALED_TABLET_MAX_HOVER_DISTANCE && !this.getOtherHandController().stylusTouchingTarget) {
|
|
||||||
|
|
||||||
this.requestTouchFocus(nearestStylusTarget);
|
|
||||||
|
|
||||||
if (!TouchEventUtils.touchTargetHasKeyboardFocus(nearestStylusTarget)) {
|
|
||||||
TouchEventUtils.setKeyboardFocusOnTouchTarget(nearestStylusTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hasTouchFocus(nearestStylusTarget) && !this.stylusTouchingTarget) {
|
|
||||||
TouchEventUtils.sendHoverOverEventToTouchTarget(this.hand, nearestStylusTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter out presses when tip is moving away from tablet.
|
|
||||||
// ensure that stylus is within bounding box by checking normalizedPosition
|
|
||||||
if (nearestStylusTarget.valid && nearestStylusTarget.distance > SCALED_TABLET_MIN_TOUCH_DISTANCE &&
|
|
||||||
nearestStylusTarget.distance < SCALED_TABLET_MAX_TOUCH_DISTANCE &&
|
|
||||||
Vec3.dot(this.stylusTip.velocity, nearestStylusTarget.normal) < 0 &&
|
|
||||||
nearestStylusTarget.normalizedPosition.x >= 0 && nearestStylusTarget.normalizedPosition.x <= 1 &&
|
|
||||||
nearestStylusTarget.normalizedPosition.y >= 0 && nearestStylusTarget.normalizedPosition.y <= 1) {
|
|
||||||
|
|
||||||
this.stylusTarget = nearestStylusTarget;
|
|
||||||
this.stylusTouchingTarget = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.relinquishTouchFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.homeButtonTouched = false;
|
|
||||||
|
|
||||||
if (this.isNearStylusTarget) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
this.pointFinger(false);
|
|
||||||
this.hideStylus();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.stylusTouchingEnter = function () {
|
|
||||||
this.stealTouchFocus(this.stylusTarget);
|
|
||||||
TouchEventUtils.sendTouchStartEventToTouchTarget(this.hand, this.stylusTarget);
|
|
||||||
Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand);
|
|
||||||
|
|
||||||
this.touchingEnterTimer = 0;
|
|
||||||
this.touchingEnterStylusTarget = this.stylusTarget;
|
|
||||||
this.deadspotExpired = false;
|
|
||||||
|
|
||||||
var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481;
|
|
||||||
this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.stylusTouchingExit = function () {
|
|
||||||
|
|
||||||
if (this.stylusTarget === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// special case to handle home button.
|
|
||||||
if (this.stylusTarget.overlayID === HMD.homeButtonID) {
|
|
||||||
Messages.sendLocalMessage("home", this.stylusTarget.overlayID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// send press event
|
|
||||||
if (this.deadspotExpired) {
|
|
||||||
TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.stylusTarget);
|
|
||||||
} else {
|
|
||||||
TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.touchingEnterStylusTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.stylusTouching = function (controllerData, dt) {
|
|
||||||
|
|
||||||
this.touchingEnterTimer += dt;
|
|
||||||
|
|
||||||
if (this.stylusTarget.entityID) {
|
|
||||||
this.stylusTarget = TouchEventUtils.calculateTouchTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps);
|
|
||||||
} else if (this.stylusTarget.overlayID) {
|
|
||||||
this.stylusTarget = TouchEventUtils.calculateTouchTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID);
|
|
||||||
}
|
|
||||||
|
|
||||||
var TABLET_MIN_TOUCH_DISTANCE = -0.1;
|
|
||||||
var TABLET_MAX_TOUCH_DISTANCE = 0.01;
|
|
||||||
|
|
||||||
if (this.stylusTarget) {
|
|
||||||
if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE &&
|
|
||||||
this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) {
|
|
||||||
var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds
|
|
||||||
if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY ||
|
|
||||||
distance2D(this.stylusTarget.position2D,
|
|
||||||
this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) {
|
|
||||||
TouchEventUtils.sendTouchMoveEventToTouchTarget(this.hand, this.stylusTarget);
|
|
||||||
this.deadspotExpired = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.stylusTouchingTarget = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.stylusTouchingTarget = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.overlayLaserActive = function(controllerData) {
|
this.overlayLaserActive = function(controllerData) {
|
||||||
var rightOverlayLaserModule = getEnabledModuleByName("RightOverlayLaserInput");
|
var rightOverlayLaserModule = getEnabledModuleByName("RightOverlayLaserInput");
|
||||||
var leftOverlayLaserModule = getEnabledModuleByName("LeftOverlayLaserInput");
|
var leftOverlayLaserModule = getEnabledModuleByName("LeftOverlayLaserInput");
|
||||||
|
@ -469,7 +46,7 @@ Script.include("/~/system/libraries/controllers.js");
|
||||||
};
|
};
|
||||||
|
|
||||||
this.isReady = function (controllerData) {
|
this.isReady = function (controllerData) {
|
||||||
if (this.processStylus(controllerData)) {
|
if (!this.overlayLaserActive(controllerData) && !this.otherModuleNeedsToRun(controllerData)) {
|
||||||
return makeRunningValues(true, [], []);
|
return makeRunningValues(true, [], []);
|
||||||
} else {
|
} else {
|
||||||
return makeRunningValues(false, [], []);
|
return makeRunningValues(false, [], []);
|
||||||
|
@ -477,28 +54,11 @@ Script.include("/~/system/libraries/controllers.js");
|
||||||
};
|
};
|
||||||
|
|
||||||
this.run = function (controllerData, deltaTime) {
|
this.run = function (controllerData, deltaTime) {
|
||||||
this.updateFingerAsStylusSetting();
|
return this.isReady(controllerData);
|
||||||
|
|
||||||
if (!this.previousStylusTouchingTarget && this.stylusTouchingTarget) {
|
|
||||||
this.stylusTouchingEnter();
|
|
||||||
}
|
|
||||||
if (this.previousStylusTouchingTarget && !this.stylusTouchingTarget) {
|
|
||||||
this.stylusTouchingExit();
|
|
||||||
}
|
|
||||||
this.previousStylusTouchingTarget = this.stylusTouchingTarget;
|
|
||||||
|
|
||||||
if (this.stylusTouchingTarget) {
|
|
||||||
this.stylusTouching(controllerData, deltaTime);
|
|
||||||
}
|
|
||||||
if (this.processStylus(controllerData)) {
|
|
||||||
return makeRunningValues(true, [], []);
|
|
||||||
} else {
|
|
||||||
return makeRunningValues(false, [], []);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.cleanup = function () {
|
this.cleanup = function () {
|
||||||
this.hideStylus();
|
Pointers.createPointer(this.pointer);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ void OctreeTests::propertyFlagsTests() {
|
||||||
EntityPropertyFlags props;
|
EntityPropertyFlags props;
|
||||||
props.setHasProperty(PROP_VISIBLE);
|
props.setHasProperty(PROP_VISIBLE);
|
||||||
props.setHasProperty(PROP_POSITION);
|
props.setHasProperty(PROP_POSITION);
|
||||||
props.setHasProperty(PROP_RADIUS);
|
props.setHasProperty(PROP_DIMENSIONS);
|
||||||
props.setHasProperty(PROP_MODEL_URL);
|
props.setHasProperty(PROP_MODEL_URL);
|
||||||
props.setHasProperty(PROP_COMPOUND_SHAPE_URL);
|
props.setHasProperty(PROP_COMPOUND_SHAPE_URL);
|
||||||
props.setHasProperty(PROP_ROTATION);
|
props.setHasProperty(PROP_ROTATION);
|
||||||
|
|
Loading…
Reference in a new issue