mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
parabola picks, started implementing math
This commit is contained in:
parent
1f4346d221
commit
90091d11e4
50 changed files with 1057 additions and 60 deletions
43
interface/src/raypick/JointParabolaPick.cpp
Normal file
43
interface/src/raypick/JointParabolaPick.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Created by Sam Gondelman 7/2/2018
|
||||
// Copyright 2018 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 "JointParabolaPick.h"
|
||||
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
||||
JointParabolaPick::JointParabolaPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset,
|
||||
float speed, const glm::vec3& accelerationAxis, bool rotateWithAvatar, PickFilter& filter, float maxDistance, bool enabled) :
|
||||
ParabolaPick(speed, accelerationAxis, rotateWithAvatar, filter, maxDistance, enabled),
|
||||
_jointName(jointName),
|
||||
_posOffset(posOffset),
|
||||
_dirOffset(dirOffset)
|
||||
{
|
||||
}
|
||||
|
||||
PickParabola JointParabolaPick::getMathematicalPick() const {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
int jointIndex = myAvatar->getJointIndex(QString::fromStdString(_jointName));
|
||||
bool useAvatarHead = _jointName == "Avatar";
|
||||
const int INVALID_JOINT = -1;
|
||||
if (jointIndex != INVALID_JOINT || useAvatarHead) {
|
||||
glm::vec3 jointPos = useAvatarHead ? myAvatar->getHeadPosition() : myAvatar->getAbsoluteJointTranslationInObjectFrame(jointIndex);
|
||||
glm::quat jointRot = useAvatarHead ? myAvatar->getHeadOrientation() : myAvatar->getAbsoluteJointRotationInObjectFrame(jointIndex);
|
||||
glm::vec3 avatarPos = myAvatar->getWorldPosition();
|
||||
glm::quat avatarRot = myAvatar->getWorldOrientation();
|
||||
|
||||
glm::vec3 pos = useAvatarHead ? jointPos : avatarPos + (avatarRot * jointPos);
|
||||
glm::quat rot = useAvatarHead ? jointRot * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT) : avatarRot * jointRot;
|
||||
|
||||
// Apply offset
|
||||
pos = pos + (rot * (myAvatar->getSensorToWorldScale() * _posOffset));
|
||||
glm::vec3 dir = rot * glm::normalize(_dirOffset);
|
||||
|
||||
return PickParabola(pos, _speed * dir, getAcceleration());
|
||||
}
|
||||
|
||||
return PickParabola();
|
||||
}
|
31
interface/src/raypick/JointParabolaPick.h
Normal file
31
interface/src/raypick/JointParabolaPick.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Created by Sam Gondelman 7/2/2018
|
||||
// Copyright 2018 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_JointParabolaPick_h
|
||||
#define hifi_JointParabolaPick_h
|
||||
|
||||
#include "ParabolaPick.h"
|
||||
|
||||
class JointParabolaPick : public ParabolaPick {
|
||||
|
||||
public:
|
||||
JointParabolaPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset,
|
||||
float speed, const glm::vec3& accelerationAxis, bool rotateWithAvatar, PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
|
||||
|
||||
PickParabola getMathematicalPick() const override;
|
||||
|
||||
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"); }
|
||||
|
||||
private:
|
||||
std::string _jointName;
|
||||
glm::vec3 _posOffset;
|
||||
glm::vec3 _dirOffset;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_JointParabolaPick_h
|
|
@ -19,12 +19,13 @@
|
|||
#include "RayPick.h"
|
||||
|
||||
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 scaleWithAvatar, bool enabled) :
|
||||
const PointerTriggers& triggers, bool faceAvatar, bool followNormal, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled) :
|
||||
Pointer(DependencyManager::get<PickScriptingInterface>()->createRayPick(rayProps), enabled, hover),
|
||||
_triggers(triggers),
|
||||
_renderStates(renderStates),
|
||||
_defaultRenderStates(defaultRenderStates),
|
||||
_faceAvatar(faceAvatar),
|
||||
_followNormal(followNormal),
|
||||
_centerEndY(centerEndY),
|
||||
_lockEnd(lockEnd),
|
||||
_distanceScaleEnd(distanceScaleEnd),
|
||||
|
@ -78,6 +79,10 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta
|
|||
if (lineWidth.isValid()) {
|
||||
_renderStates[state].setLineWidth(lineWidth.toFloat());
|
||||
}
|
||||
QVariant rotation = pathProps.toMap()["rotation"];
|
||||
if (rotation.isValid()) {
|
||||
_renderStates[state].setEndRot(quatFromVariant(rotation));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -148,7 +153,7 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant&
|
|||
}
|
||||
}
|
||||
|
||||
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay) {
|
||||
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const glm::vec3& normal, const QUuid& objectID, const PickRay& pickRay) {
|
||||
if (!renderState.getStartID().isNull()) {
|
||||
QVariantMap startProps;
|
||||
startProps.insert("position", vec3toVariant(pickRay.origin));
|
||||
|
@ -182,10 +187,11 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
|
|||
endProps.insert("position", end);
|
||||
} else {
|
||||
glm::vec3 currentUpVector = faceAvatarRotation * Vectors::UP;
|
||||
endProps.insert("position", vec3toVariant(endVec + glm::vec3(currentUpVector.x * 0.5f * dim.y, currentUpVector.y * 0.5f * dim.y, currentUpVector.z * 0.5f * dim.y)));
|
||||
endProps.insert("position", vec3toVariant(endVec + currentUpVector * vec3(0.0f, 0.5f * dim.y, 0.0f)));
|
||||
}
|
||||
if (_faceAvatar) {
|
||||
endProps.insert("rotation", quatToVariant(faceAvatarRotation));
|
||||
glm::quat rotation = _faceAvatar ? faceAvatarRotation : renderState.getEndRot();
|
||||
endProps.insert("rotation", quatToVariant(rotation));
|
||||
}
|
||||
endProps.insert("visible", true);
|
||||
endProps.insert("ignoreRayIntersection", renderState.doesEndIgnoreRays());
|
||||
|
@ -223,12 +229,12 @@ void LaserPointer::updateVisuals(const PickResultPointer& pickResult) {
|
|||
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant): PickRay();
|
||||
QUuid uid = rayPickResult->objectID;
|
||||
float distance = _laserLength > 0.0f ? _laserLength : rayPickResult->distance;
|
||||
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay);
|
||||
updateRenderState(_renderStates[_currentRenderState], type, distance, rayPickResult->surfaceNormal, uid, pickRay);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
} else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant) : PickRay();
|
||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay);
|
||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, Vectors::UP, QUuid(), pickRay);
|
||||
} else if (!_currentRenderState.empty()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
|
@ -306,6 +312,7 @@ RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, cons
|
|||
}
|
||||
if (!_endID.isNull()) {
|
||||
_endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value);
|
||||
_endRot = quatFromVariant(qApp->getOverlays().getProperty(_endID, "rotation").value);
|
||||
_endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignoreRayIntersection").value.toBool();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,9 @@ public:
|
|||
void setEndDim(const glm::vec3& endDim) { _endDim = endDim; }
|
||||
const glm::vec3& getEndDim() const { return _endDim; }
|
||||
|
||||
void setEndRot(const glm::quat& endRot) { _endRot = endRot; }
|
||||
const glm::quat& getEndRot() const { return _endRot; }
|
||||
|
||||
void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
|
||||
const float& getLineWidth() const { return _lineWidth; }
|
||||
|
||||
|
@ -55,6 +58,7 @@ private:
|
|||
bool _endIgnoreRays;
|
||||
|
||||
glm::vec3 _endDim;
|
||||
glm::quat _endRot;
|
||||
float _lineWidth;
|
||||
};
|
||||
|
||||
|
@ -65,7 +69,7 @@ public:
|
|||
typedef std::unordered_map<std::string, std::pair<float, RenderState>> DefaultRenderStateMap;
|
||||
|
||||
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
|
||||
bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
|
||||
bool faceAvatar, bool followNormal, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
|
||||
~LaserPointer();
|
||||
|
||||
void setRenderState(const std::string& state) override;
|
||||
|
@ -96,6 +100,7 @@ private:
|
|||
RenderStateMap _renderStates;
|
||||
DefaultRenderStateMap _defaultRenderStates;
|
||||
bool _faceAvatar;
|
||||
bool _followNormal;
|
||||
bool _centerEndY;
|
||||
bool _lockEnd;
|
||||
bool _distanceScaleEnd;
|
||||
|
@ -103,7 +108,7 @@ private:
|
|||
LockEndObject _lockEndObject;
|
||||
|
||||
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
|
||||
void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay);
|
||||
void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const glm::vec3& normal, const QUuid& objectID, const PickRay& pickRay);
|
||||
void disableRenderState(const RenderState& renderState);
|
||||
|
||||
struct TriggerState {
|
||||
|
|
27
interface/src/raypick/MouseParabolaPick.cpp
Normal file
27
interface/src/raypick/MouseParabolaPick.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Created by Sam Gondelman 7/2/2018
|
||||
// Copyright 2018 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 "MouseParabolaPick.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "display-plugins/CompositorHelper.h"
|
||||
|
||||
MouseParabolaPick::MouseParabolaPick(float speed, const glm::vec3& accelerationAxis, bool rotateWithAvatar, const PickFilter& filter, float maxDistance, bool enabled) :
|
||||
ParabolaPick(speed, accelerationAxis, rotateWithAvatar, filter, maxDistance, enabled)
|
||||
{
|
||||
}
|
||||
|
||||
PickParabola MouseParabolaPick::getMathematicalPick() const {
|
||||
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
|
||||
if (position.isValid()) {
|
||||
QVariantMap posMap = position.toMap();
|
||||
PickRay pickRay = qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat());
|
||||
return PickParabola(pickRay.origin, _speed * pickRay.direction, getAcceleration());
|
||||
}
|
||||
|
||||
return PickParabola();
|
||||
}
|
23
interface/src/raypick/MouseParabolaPick.h
Normal file
23
interface/src/raypick/MouseParabolaPick.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Created by Sam Gondelman 7/2/2018
|
||||
// Copyright 2018 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_MouseParabolaPick_h
|
||||
#define hifi_MouseParabolaPick_h
|
||||
|
||||
#include "ParabolaPick.h"
|
||||
|
||||
class MouseParabolaPick : public ParabolaPick {
|
||||
|
||||
public:
|
||||
MouseParabolaPick(float speed, const glm::vec3& accelerationAxis, bool rotateWithAvatar, const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
|
||||
|
||||
PickParabola getMathematicalPick() const override;
|
||||
|
||||
bool isMouse() const override { return true; }
|
||||
};
|
||||
|
||||
#endif // hifi_MouseParabolaPick_h
|
57
interface/src/raypick/ParabolaPick.cpp
Normal file
57
interface/src/raypick/ParabolaPick.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// Created by Sam Gondelman 7/2/2018
|
||||
// Copyright 2018 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 "ParabolaPick.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "EntityScriptingInterface.h"
|
||||
#include "ui/overlays/Overlays.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
#include "DependencyManager.h"
|
||||
|
||||
PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) {
|
||||
ParabolaToEntityIntersectionResult entityRes =
|
||||
DependencyManager::get<EntityScriptingInterface>()->findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(),
|
||||
getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
|
||||
if (entityRes.intersects) {
|
||||
return std::make_shared<ParabolaPickResult>(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo);
|
||||
} else {
|
||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||
}
|
||||
}
|
||||
|
||||
PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) {
|
||||
/*ParabolaToOverlayIntersectionResult overlayRes =
|
||||
qApp->getOverlays().findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(),
|
||||
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
|
||||
if (overlayRes.intersects) {
|
||||
return std::make_shared<ParabolaPickResult>(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo);
|
||||
} else {*/
|
||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||
//}
|
||||
}
|
||||
|
||||
PickResultPointer ParabolaPick::getAvatarIntersection(const PickParabola& pick) {
|
||||
/*ParabolaToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findParabolaIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
||||
if (avatarRes.intersects) {
|
||||
return std::make_shared<ParabolaPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.parabolicDistance, avatarRes.intersection, pick, glm::vec3(NAN), avatarRes.extraInfo);
|
||||
} else {*/
|
||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||
//}
|
||||
}
|
||||
|
||||
PickResultPointer ParabolaPick::getHUDIntersection(const PickParabola& pick) {
|
||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||
//glm::vec3 hudRes = DependencyManager::get<HMDScriptingInterface>()->calculateParabolaUICollisionPoint(pick);
|
||||
//return std::make_shared<ParabolaPickResult>(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick);
|
||||
}
|
||||
|
||||
glm::vec3 ParabolaPick::getAcceleration() const {
|
||||
// TODO: use rotateWithAvatar
|
||||
return _accelerationAxis;
|
||||
}
|
94
interface/src/raypick/ParabolaPick.h
Normal file
94
interface/src/raypick/ParabolaPick.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// Created by Sam Gondelman 7/2/2018
|
||||
// Copyright 2018 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_ParabolaPick_h
|
||||
#define hifi_ParabolaPick_h
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <Pick.h>
|
||||
|
||||
class EntityItemID;
|
||||
class OverlayID;
|
||||
|
||||
class ParabolaPickResult : public PickResult {
|
||||
public:
|
||||
ParabolaPickResult() {}
|
||||
ParabolaPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {}
|
||||
ParabolaPickResult(const IntersectionType type, const QUuid& objectID, float distance, float parabolicDistance, const glm::vec3& intersection, const PickParabola& parabola,
|
||||
const glm::vec3& surfaceNormal = glm::vec3(NAN), const QVariantMap& extraInfo = QVariantMap()) :
|
||||
PickResult(parabola.toVariantMap()), type(type), intersects(type != NONE), objectID(objectID), distance(distance), parabolicDistance(parabolicDistance), intersection(intersection), surfaceNormal(surfaceNormal), extraInfo(extraInfo) {
|
||||
}
|
||||
|
||||
ParabolaPickResult(const ParabolaPickResult& parabolaPickResult) : PickResult(parabolaPickResult.pickVariant) {
|
||||
type = parabolaPickResult.type;
|
||||
intersects = parabolaPickResult.intersects;
|
||||
objectID = parabolaPickResult.objectID;
|
||||
distance = parabolaPickResult.distance;
|
||||
parabolicDistance = parabolaPickResult.parabolicDistance;
|
||||
intersection = parabolaPickResult.intersection;
|
||||
surfaceNormal = parabolaPickResult.surfaceNormal;
|
||||
extraInfo = parabolaPickResult.extraInfo;
|
||||
}
|
||||
|
||||
IntersectionType type { NONE };
|
||||
bool intersects { false };
|
||||
QUuid objectID;
|
||||
float distance { FLT_MAX };
|
||||
float parabolicDistance { FLT_MAX };
|
||||
glm::vec3 intersection { NAN };
|
||||
glm::vec3 surfaceNormal { NAN };
|
||||
QVariantMap extraInfo;
|
||||
|
||||
virtual QVariantMap toVariantMap() const override {
|
||||
QVariantMap toReturn;
|
||||
toReturn["type"] = type;
|
||||
toReturn["intersects"] = intersects;
|
||||
toReturn["objectID"] = objectID;
|
||||
toReturn["distance"] = distance;
|
||||
toReturn["parabolicDistance"] = parabolicDistance;
|
||||
toReturn["intersection"] = vec3toVariant(intersection);
|
||||
toReturn["surfaceNormal"] = vec3toVariant(surfaceNormal);
|
||||
toReturn["parabola"] = PickResult::toVariantMap();
|
||||
toReturn["extraInfo"] = extraInfo;
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
bool doesIntersect() const override { return intersects; }
|
||||
bool checkOrFilterAgainstMaxDistance(float maxDistance) override { return distance < maxDistance; }
|
||||
|
||||
PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override {
|
||||
auto newParabolaRes = std::static_pointer_cast<ParabolaPickResult>(newRes);
|
||||
if (newParabolaRes->distance < distance) {
|
||||
return std::make_shared<ParabolaPickResult>(*newParabolaRes);
|
||||
} else {
|
||||
return std::make_shared<ParabolaPickResult>(*this);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class ParabolaPick : public Pick<PickParabola> {
|
||||
|
||||
public:
|
||||
ParabolaPick(float speed, const glm::vec3& accelerationAxis, bool rotateWithAvatar, const PickFilter& filter, float maxDistance, bool enabled) :
|
||||
Pick(filter, maxDistance, enabled), _speed(speed), _accelerationAxis(accelerationAxis), _rotateWithAvatar(rotateWithAvatar) {}
|
||||
|
||||
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { return std::make_shared<ParabolaPickResult>(pickVariant); }
|
||||
PickResultPointer getEntityIntersection(const PickParabola& pick) override;
|
||||
PickResultPointer getOverlayIntersection(const PickParabola& pick) override;
|
||||
PickResultPointer getAvatarIntersection(const PickParabola& pick) override;
|
||||
PickResultPointer getHUDIntersection(const PickParabola& pick) override;
|
||||
|
||||
protected:
|
||||
float _speed;
|
||||
glm::vec3 _accelerationAxis;
|
||||
bool _rotateWithAvatar;
|
||||
|
||||
glm::vec3 getAcceleration() const;
|
||||
};
|
||||
|
||||
#endif // hifi_ParabolaPick_h
|
|
@ -17,6 +17,9 @@
|
|||
#include "JointRayPick.h"
|
||||
#include "MouseRayPick.h"
|
||||
#include "StylusPick.h"
|
||||
#include "StaticParabolaPick.h"
|
||||
#include "JointParabolaPick.h"
|
||||
#include "MouseParabolaPick.h"
|
||||
|
||||
#include <ScriptEngine.h>
|
||||
|
||||
|
@ -26,6 +29,8 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type,
|
|||
return createRayPick(properties);
|
||||
case PickQuery::PickType::Stylus:
|
||||
return createStylusPick(properties);
|
||||
case PickQuery::PickType::Parabola:
|
||||
return createParabolaPick(properties);
|
||||
default:
|
||||
return PickManager::INVALID_PICK_ID;
|
||||
}
|
||||
|
@ -134,6 +139,91 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties
|
|||
return DependencyManager::get<PickManager>()->addPick(PickQuery::Stylus, std::make_shared<StylusPick>(side, filter, maxDistance, enabled));
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Picks.createPick} to create a new Parabola Pick.
|
||||
* @typedef {object} Picks.ParabolaPickProperties
|
||||
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
|
||||
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
|
||||
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
|
||||
* @property {string} [joint] Only for Joint or Mouse Parabola Picks. If "Mouse", it will create a Parabola Pick that follows the system mouse, in desktop or HMD.
|
||||
* If "Avatar", it will create a Joint Parabola Pick that follows your avatar's head. Otherwise, it will create a Joint Parabola Pick that follows the given joint, if it
|
||||
* exists on your current avatar.
|
||||
* @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Parabola Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral
|
||||
* @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Parabola Picks. A local joint direction offset. x = upward, y = forward, z = lateral
|
||||
* @property {Vec3} [position] Only for Static Parabola Picks. The world-space origin of the parabola segment.
|
||||
* @property {Vec3} [direction=-Vec3.FRONT] Only for Static Parabola Picks. The world-space direction of the parabola segment.
|
||||
* @property {number} [speed=1] The initial speed of the parabola, i.e. the initial speed of the projectile whose trajectory defines the parabola.
|
||||
* @property {Vec3} [accelerationAxis=-Vec3.UP] The acceleration of the parabola, i.e. the acceleration of the projectile whose trajectory defines the parabola, both magnitude and direction.
|
||||
* @property {boolean} [rotateWithAvatar=true] Whether or not the acceleration axis should rotate with your avatar's local Y axis.
|
||||
*/
|
||||
unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properties) {
|
||||
QVariantMap propMap = properties.toMap();
|
||||
|
||||
bool enabled = false;
|
||||
if (propMap["enabled"].isValid()) {
|
||||
enabled = propMap["enabled"].toBool();
|
||||
}
|
||||
|
||||
PickFilter filter = PickFilter();
|
||||
if (propMap["filter"].isValid()) {
|
||||
filter = PickFilter(propMap["filter"].toUInt());
|
||||
}
|
||||
|
||||
float maxDistance = 0.0f;
|
||||
if (propMap["maxDistance"].isValid()) {
|
||||
maxDistance = propMap["maxDistance"].toFloat();
|
||||
}
|
||||
|
||||
float speed = 1.0f;
|
||||
if (propMap["speed"].isValid()) {
|
||||
speed = propMap["speed"].toFloat();
|
||||
}
|
||||
|
||||
glm::vec3 accelerationAxis = -Vectors::UP;
|
||||
if (propMap["accelerationAxis"].isValid()) {
|
||||
accelerationAxis = vec3FromVariant(propMap["accelerationAxis"]);
|
||||
}
|
||||
|
||||
bool rotateWithAvatar = true;
|
||||
if (propMap["rotateWithAvatar"].isValid()) {
|
||||
rotateWithAvatar = propMap["rotateWithAvatar"].toBool();
|
||||
}
|
||||
|
||||
if (propMap["joint"].isValid()) {
|
||||
std::string jointName = propMap["joint"].toString().toStdString();
|
||||
|
||||
if (jointName != "Mouse") {
|
||||
// x = upward, y = forward, z = lateral
|
||||
glm::vec3 posOffset = Vectors::ZERO;
|
||||
if (propMap["posOffset"].isValid()) {
|
||||
posOffset = vec3FromVariant(propMap["posOffset"]);
|
||||
}
|
||||
|
||||
glm::vec3 dirOffset = Vectors::UP;
|
||||
if (propMap["dirOffset"].isValid()) {
|
||||
dirOffset = vec3FromVariant(propMap["dirOffset"]);
|
||||
}
|
||||
|
||||
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, std::make_shared<JointParabolaPick>(jointName, posOffset, dirOffset,
|
||||
speed, accelerationAxis, rotateWithAvatar, filter, maxDistance, enabled));
|
||||
|
||||
} else {
|
||||
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, std::make_shared<MouseParabolaPick>(speed, accelerationAxis, rotateWithAvatar, filter, maxDistance, enabled));
|
||||
}
|
||||
} else if (propMap["position"].isValid()) {
|
||||
glm::vec3 position = vec3FromVariant(propMap["position"]);
|
||||
|
||||
glm::vec3 direction = -Vectors::FRONT;
|
||||
if (propMap["direction"].isValid()) {
|
||||
direction = vec3FromVariant(propMap["direction"]);
|
||||
}
|
||||
|
||||
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, std::make_shared<StaticParabolaPick>(position, direction, speed, accelerationAxis, rotateWithAvatar, filter, maxDistance, enabled));
|
||||
}
|
||||
|
||||
return PickManager::INVALID_PICK_ID;
|
||||
}
|
||||
|
||||
void PickScriptingInterface::enablePick(unsigned int uid) {
|
||||
DependencyManager::get<PickManager>()->enablePick(uid);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ class PickScriptingInterface : public QObject, public Dependency {
|
|||
public:
|
||||
unsigned int createRayPick(const QVariant& properties);
|
||||
unsigned int createStylusPick(const QVariant& properties);
|
||||
unsigned int createParabolaPick(const QVariant& properties);
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
|
@ -71,7 +72,7 @@ public:
|
|||
* with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick.
|
||||
* @function Picks.createPick
|
||||
* @param {PickType} type A PickType that specifies the method of picking to use
|
||||
* @param {Picks.RayPickProperties|Picks.StylusPickProperties} properties A PickProperties object, containing all the properties for initializing this Pick
|
||||
* @param {Picks.RayPickProperties|Picks.StylusPickProperties|Picks.ParabolaPickProperties} properties A PickProperties object, containing all the properties for initializing this Pick
|
||||
* @returns {number} The ID of the created Pick. Used for managing the Pick. 0 if invalid.
|
||||
*/
|
||||
Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties);
|
||||
|
@ -125,6 +126,21 @@ public:
|
|||
* @property {StylusTip} stylusTip The StylusTip that was used. Valid even if there was no intersection.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* An intersection result for a Parabola Pick.
|
||||
*
|
||||
* @typedef {object} ParabolaPickResult
|
||||
* @property {number} type The intersection type.
|
||||
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
|
||||
* @property {number} distance The distance to the intersection point from the origin of the parabola, not along the parabola.
|
||||
* @property {number} parabolicDistance The distance to the intersection point from the origin of the parabola, along the parabola.
|
||||
* @property {Vec3} intersection The intersection point in world-space.
|
||||
* @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD.
|
||||
* @property {Variant} extraInfo Additional intersection details when available for Model objects.
|
||||
* @property {StylusTip} parabola The PickParabola that was used. Valid even if there was no intersection.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Get the most recent pick result from this Pick. This will be updated as long as the Pick is enabled.
|
||||
* @function Picks.getPrevPickResult
|
||||
|
@ -162,7 +178,7 @@ public:
|
|||
* Check if a Pick is associated with the left hand.
|
||||
* @function Picks.isLeftHand
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @returns {boolean} True if the Pick is a Joint Ray Pick with joint == "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a Stylus Pick with hand == 0.
|
||||
* @returns {boolean} True if the Pick is a Joint Ray or Parabola Pick with joint == "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a Stylus Pick with hand == 0.
|
||||
*/
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid);
|
||||
|
||||
|
@ -170,7 +186,7 @@ public:
|
|||
* Check if a Pick is associated with the right hand.
|
||||
* @function Picks.isRightHand
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @returns {boolean} True if the Pick is a Joint Ray Pick with joint == "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a Stylus Pick with hand == 1.
|
||||
* @returns {boolean} True if the Pick is a Joint Ray or Parabola Pick with joint == "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a Stylus Pick with hand == 1.
|
||||
*/
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid);
|
||||
|
||||
|
@ -178,7 +194,7 @@ public:
|
|||
* Check if a Pick is associated with the system mouse.
|
||||
* @function Picks.isMouse
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @returns {boolean} True if the Pick is a Mouse Ray Pick, false otherwise.
|
||||
* @returns {boolean} True if the Pick is a Mouse Ray or Parabola Pick, false otherwise.
|
||||
*/
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid);
|
||||
|
||||
|
|
|
@ -134,6 +134,11 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
|
|||
scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool();
|
||||
}
|
||||
|
||||
bool followNormal = true;
|
||||
if (propertyMap["followNormal"].isValid()) {
|
||||
followNormal = propertyMap["followNormal"].toBool();
|
||||
}
|
||||
|
||||
bool enabled = false;
|
||||
if (propertyMap["enabled"].isValid()) {
|
||||
enabled = propertyMap["enabled"].toBool();
|
||||
|
@ -192,7 +197,8 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
|
|||
}
|
||||
|
||||
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<LaserPointer>(properties, renderStates, defaultRenderStates, hover, triggers,
|
||||
faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled));
|
||||
faceAvatar, followNormal, centerEndY, lockEnd, distanceScaleEnd,
|
||||
scaleWithAvatar, enabled));
|
||||
}
|
||||
|
||||
void PointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const {
|
||||
|
|
19
interface/src/raypick/StaticParabolaPick.cpp
Normal file
19
interface/src/raypick/StaticParabolaPick.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Created by Sam Gondelman 7/2/2018
|
||||
// Copyright 2018 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 "StaticParabolaPick.h"
|
||||
|
||||
StaticParabolaPick::StaticParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& accelerationAxis, bool rotateWithAvatar,
|
||||
const PickFilter& filter, float maxDistance, bool enabled) :
|
||||
ParabolaPick(speed, accelerationAxis, rotateWithAvatar, filter, maxDistance, enabled),
|
||||
_position(position), _velocity(speed * direction)
|
||||
{
|
||||
}
|
||||
|
||||
PickParabola StaticParabolaPick::getMathematicalPick() const {
|
||||
return PickParabola(_position, _velocity, getAcceleration());
|
||||
}
|
26
interface/src/raypick/StaticParabolaPick.h
Normal file
26
interface/src/raypick/StaticParabolaPick.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// Created by Sam Gondelman 7/2/2018
|
||||
// Copyright 2018 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_StaticParabolaPick_h
|
||||
#define hifi_StaticParabolaPick_h
|
||||
|
||||
#include "ParabolaPick.h"
|
||||
|
||||
class StaticParabolaPick : public ParabolaPick {
|
||||
|
||||
public:
|
||||
StaticParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& accelerationAxis, bool rotateWithAvatar,
|
||||
const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
|
||||
|
||||
PickParabola getMathematicalPick() const override;
|
||||
|
||||
private:
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _velocity;
|
||||
};
|
||||
|
||||
#endif // hifi_StaticParabolaPick_h
|
|
@ -65,13 +65,22 @@ public:
|
|||
glm::vec3 intersection;
|
||||
QVariantMap extraInfo;
|
||||
};
|
||||
|
||||
|
||||
Q_DECLARE_METATYPE(RayToOverlayIntersectionResult);
|
||||
|
||||
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value);
|
||||
void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value);
|
||||
|
||||
class ParabolaToOverlayIntersectionResult {
|
||||
public:
|
||||
bool intersects { false };
|
||||
OverlayID overlayID { UNKNOWN_OVERLAY_ID };
|
||||
float distance { 0 };
|
||||
float parabolicDistance { 0 };
|
||||
BoxFace face { UNKNOWN_FACE };
|
||||
glm::vec3 surfaceNormal;
|
||||
glm::vec3 intersection;
|
||||
QVariantMap extraInfo;
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to
|
||||
* yourself and that aren't persisted to the domain. They are used for UI.
|
||||
|
|
|
@ -1525,19 +1525,26 @@ void registerAvatarTypes(QScriptEngine* engine);
|
|||
|
||||
class RayToAvatarIntersectionResult {
|
||||
public:
|
||||
RayToAvatarIntersectionResult() : intersects(false), avatarID(), distance(0) {}
|
||||
bool intersects;
|
||||
bool intersects { false };
|
||||
QUuid avatarID;
|
||||
float distance;
|
||||
float distance { 0.0f };
|
||||
glm::vec3 intersection;
|
||||
QVariantMap extraInfo;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(RayToAvatarIntersectionResult)
|
||||
|
||||
QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& results);
|
||||
void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& results);
|
||||
|
||||
class ParabolaToAvatarIntersectionResult {
|
||||
public:
|
||||
bool intersects { false };
|
||||
QUuid avatarID;
|
||||
float distance { 0.0f };
|
||||
float parabolicDistance { 0.0f };
|
||||
glm::vec3 intersection;
|
||||
QVariantMap extraInfo;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AvatarEntityMap)
|
||||
|
||||
QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value);
|
||||
|
|
|
@ -278,7 +278,7 @@ EntityItemProperties RenderableModelEntityItem::getProperties(EntityPropertyFlag
|
|||
return properties;
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::supportsDetailedRayIntersection() const {
|
||||
bool RenderableModelEntityItem::supportsDetailedIntersection() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
void doInitialModelSimulation();
|
||||
void updateModelBounds();
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const override;
|
||||
virtual bool supportsDetailedIntersection() const override;
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
|
||||
int getOnCount() const override { return _onCount; }
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
|
|
|
@ -159,11 +159,15 @@ public:
|
|||
|
||||
virtual void debugDump() const;
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const { return false; }
|
||||
virtual bool supportsDetailedIntersection() const { return false; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const { return true; }
|
||||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const { return true; }
|
||||
|
||||
// attributes applicable to all entity types
|
||||
EntityTypes::EntityType getType() const { return _type; }
|
||||
|
|
|
@ -871,6 +871,30 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
|
|||
return result;
|
||||
}
|
||||
|
||||
ParabolaToEntityIntersectionResult EntityScriptingInterface::findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking,
|
||||
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return findParabolaIntersectionWorker(parabola, Octree::Lock, precisionPicking, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly);
|
||||
}
|
||||
|
||||
ParabolaToEntityIntersectionResult EntityScriptingInterface::findParabolaIntersectionWorker(const PickParabola& parabola,
|
||||
Octree::lockType lockType, bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude,
|
||||
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
|
||||
|
||||
|
||||
ParabolaToEntityIntersectionResult result;
|
||||
if (_entityTree) {
|
||||
OctreeElementPointer element;
|
||||
result.entityID = _entityTree->findParabolaIntersection(parabola,
|
||||
entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking,
|
||||
element, result.intersection, result.distance, result.parabolicDistance, result.face, result.surfaceNormal,
|
||||
result.extraInfo, lockType, &result.accurate);
|
||||
result.intersects = !result.entityID.isNull();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) {
|
||||
auto client = DependencyManager::get<EntityScriptClient>();
|
||||
return client->reloadServerScript(entityID);
|
||||
|
@ -1025,15 +1049,6 @@ bool EntityScriptingInterface::getDrawZoneBoundaries() const {
|
|||
return ZoneEntityItem::getDrawZoneBoundaries();
|
||||
}
|
||||
|
||||
RayToEntityIntersectionResult::RayToEntityIntersectionResult() :
|
||||
intersects(false),
|
||||
accurate(true), // assume it's accurate
|
||||
entityID(),
|
||||
distance(0),
|
||||
face()
|
||||
{
|
||||
}
|
||||
|
||||
QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
|
|
|
@ -71,11 +71,10 @@ private:
|
|||
// "accurate" is currently always true because the ray intersection is always performed with an Octree::Lock.
|
||||
class RayToEntityIntersectionResult {
|
||||
public:
|
||||
RayToEntityIntersectionResult();
|
||||
bool intersects;
|
||||
bool accurate;
|
||||
bool intersects { false };
|
||||
bool accurate { true };
|
||||
QUuid entityID;
|
||||
float distance;
|
||||
float distance { 0.0f };
|
||||
BoxFace face;
|
||||
glm::vec3 intersection;
|
||||
glm::vec3 surfaceNormal;
|
||||
|
@ -87,6 +86,18 @@ Q_DECLARE_METATYPE(RayToEntityIntersectionResult)
|
|||
QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& results);
|
||||
void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& results);
|
||||
|
||||
class ParabolaToEntityIntersectionResult {
|
||||
public:
|
||||
bool intersects { false };
|
||||
bool accurate { true };
|
||||
QUuid entityID;
|
||||
float distance { 0.0f };
|
||||
float parabolicDistance { 0.0f };
|
||||
BoxFace face;
|
||||
glm::vec3 intersection;
|
||||
glm::vec3 surfaceNormal;
|
||||
QVariantMap extraInfo;
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* The Entities API provides facilities to create and interact with entities. Entities are 2D and 3D objects that are visible
|
||||
|
@ -131,6 +142,12 @@ public:
|
|||
|
||||
void resetActivityTracking();
|
||||
ActivityTracking getActivityTracking() const { return _activityTracking; }
|
||||
|
||||
// TODO: expose to script?
|
||||
ParabolaToEntityIntersectionResult findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking,
|
||||
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
|
||||
bool visibleOnly, bool collidableOnly);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1895,6 +1912,11 @@ private:
|
|||
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
|
||||
bool visibleOnly = false, bool collidableOnly = false);
|
||||
|
||||
/// actually does the work of finding the parabola intersection, can be called in locking mode or tryLock mode
|
||||
ParabolaToEntityIntersectionResult findParabolaIntersectionWorker(const PickParabola& parabola, Octree::lockType lockType,
|
||||
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
|
||||
bool visibleOnly = false, bool collidableOnly = false);
|
||||
|
||||
EntityTreePointer _entityTree;
|
||||
|
||||
std::recursive_mutex _entitiesScriptEngineLock;
|
||||
|
|
|
@ -63,6 +63,27 @@ public:
|
|||
EntityItemID entityID;
|
||||
};
|
||||
|
||||
class ParabolaArgs {
|
||||
public:
|
||||
// Inputs
|
||||
glm::vec3 origin;
|
||||
glm::vec3 velocity;
|
||||
glm::vec3 acceleration;
|
||||
const QVector<EntityItemID>& entityIdsToInclude;
|
||||
const QVector<EntityItemID>& entityIdsToDiscard;
|
||||
bool visibleOnly;
|
||||
bool collidableOnly;
|
||||
bool precisionPicking;
|
||||
|
||||
// Outputs
|
||||
OctreeElementPointer& element;
|
||||
float& parabolicDistance;
|
||||
BoxFace& face;
|
||||
glm::vec3& surfaceNormal;
|
||||
QVariantMap& extraInfo;
|
||||
EntityItemID entityID;
|
||||
};
|
||||
|
||||
|
||||
EntityTree::EntityTree(bool shouldReaverage) :
|
||||
Octree(shouldReaverage)
|
||||
|
@ -820,8 +841,7 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm:
|
|||
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
|
||||
Octree::lockType lockType, bool* accurateResult) {
|
||||
RayArgs args = { origin, direction, entityIdsToInclude, entityIdsToDiscard,
|
||||
visibleOnly, collidableOnly, precisionPicking,
|
||||
element, distance, face, surfaceNormal, extraInfo, EntityItemID() };
|
||||
visibleOnly, collidableOnly, precisionPicking, element, distance, face, surfaceNormal, extraInfo, EntityItemID() };
|
||||
distance = FLT_MAX;
|
||||
|
||||
bool requireLock = lockType == Octree::Lock;
|
||||
|
@ -836,6 +856,47 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm:
|
|||
return args.entityID;
|
||||
}
|
||||
|
||||
bool findParabolaIntersectionOp(const OctreeElementPointer& element, void* extraData) {
|
||||
ParabolaArgs* args = static_cast<ParabolaArgs*>(extraData);
|
||||
bool keepSearching = true;
|
||||
EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast<EntityTreeElement>(element);
|
||||
EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration, keepSearching,
|
||||
args->element, args->parabolicDistance, args->face, args->surfaceNormal, args->entityIdsToInclude,
|
||||
args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking);
|
||||
if (!entityID.isNull()) {
|
||||
args->entityID = entityID;
|
||||
}
|
||||
return keepSearching;
|
||||
}
|
||||
|
||||
EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola,
|
||||
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
|
||||
bool visibleOnly, bool collidableOnly, bool precisionPicking,
|
||||
OctreeElementPointer& element, glm::vec3& intersection, float& distance, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
|
||||
Octree::lockType lockType, bool* accurateResult) {
|
||||
ParabolaArgs args = { parabola.origin, parabola.velocity, parabola.acceleration, entityIdsToInclude, entityIdsToDiscard,
|
||||
visibleOnly, collidableOnly, precisionPicking, element, parabolicDistance, face, surfaceNormal, extraInfo, EntityItemID() };
|
||||
parabolicDistance = FLT_MAX;
|
||||
distance = FLT_MAX;
|
||||
|
||||
bool requireLock = lockType == Octree::Lock;
|
||||
bool lockResult = withReadLock([&] {
|
||||
recurseTreeWithOperation(findParabolaIntersectionOp, &args);
|
||||
}, requireLock);
|
||||
|
||||
if (accurateResult) {
|
||||
*accurateResult = lockResult; // if user asked to accuracy or result, let them know this is accurate
|
||||
}
|
||||
|
||||
if (!args.entityID.isNull()) {
|
||||
intersection = parabola.origin + parabola.velocity * parabolicDistance + 0.5f * parabola.acceleration * parabolicDistance * parabolicDistance;
|
||||
distance = glm::distance(intersection, parabola.origin);
|
||||
}
|
||||
|
||||
return args.entityID;
|
||||
}
|
||||
|
||||
|
||||
EntityItemPointer EntityTree::findClosestEntity(const glm::vec3& position, float targetRadius) {
|
||||
FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX };
|
||||
|
|
|
@ -95,7 +95,14 @@ public:
|
|||
virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
|
||||
bool visibleOnly, bool collidableOnly, bool precisionPicking,
|
||||
OctreeElementPointer& node, float& distance,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
|
||||
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
|
||||
|
||||
virtual EntityItemID findParabolaIntersection(const PickParabola& parabola,
|
||||
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
|
||||
bool visibleOnly, bool collidableOnly, bool precisionPicking,
|
||||
OctreeElementPointer& element, glm::vec3& intersection, float& distance, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
|
||||
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con
|
|||
}
|
||||
|
||||
// by default, we only allow intersections with leaves with content
|
||||
if (!canRayIntersect()) {
|
||||
if (!canPickIntersect()) {
|
||||
return result; // we don't intersect with non-leaves, and we keep searching
|
||||
}
|
||||
|
||||
|
@ -232,7 +232,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori
|
|||
localFace, localSurfaceNormal)) {
|
||||
if (entityFrameBox.contains(entityFrameOrigin) || localDistance < distance) {
|
||||
// now ask the entity if we actually intersect
|
||||
if (entity->supportsDetailedRayIntersection()) {
|
||||
if (entity->supportsDetailedIntersection()) {
|
||||
QVariantMap localExtraInfo;
|
||||
if (entity->findDetailedRayIntersection(origin, direction, element, localDistance,
|
||||
localFace, localSurfaceNormal, localExtraInfo, precisionPicking)) {
|
||||
|
@ -250,7 +250,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori
|
|||
if (localDistance < distance && entity->getType() != EntityTypes::ParticleEffect) {
|
||||
distance = localDistance;
|
||||
face = localFace;
|
||||
surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 1.0f));
|
||||
surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f));
|
||||
entityID = entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
|
@ -287,6 +287,134 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad
|
|||
return result;
|
||||
}
|
||||
|
||||
EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, bool& keepSearching, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
|
||||
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
|
||||
QVariantMap& extraInfo, bool precisionPicking) {
|
||||
|
||||
EntityItemID result;
|
||||
float distanceToElementCube = std::numeric_limits<float>::max();
|
||||
BoxFace localFace;
|
||||
glm::vec3 localSurfaceNormal;
|
||||
|
||||
// if the parabola doesn't intersect with our cube OR the distance to element is less than current best distance
|
||||
// we can stop searching!
|
||||
bool hit = _cube.findParabolaIntersection(origin, velocity, acceleration, distanceToElementCube, localFace, localSurfaceNormal);
|
||||
if (!hit || (!_cube.contains(origin) && distanceToElementCube > parabolicDistance)) {
|
||||
keepSearching = false; // no point in continuing to search
|
||||
return result; // we did not intersect
|
||||
}
|
||||
|
||||
// by default, we only allow intersections with leaves with content
|
||||
if (!canPickIntersect()) {
|
||||
return result; // we don't intersect with non-leaves, and we keep searching
|
||||
}
|
||||
|
||||
// if the distance to the element cube is not less than the current best distance, then it's not possible
|
||||
// for any details inside the cube to be closer so we don't need to consider them.
|
||||
QVariantMap localExtraInfo;
|
||||
float distanceToElementDetails = parabolicDistance;
|
||||
EntityItemID entityID = findDetailedParabolaIntersection(origin, velocity, acceleration, element, distanceToElementDetails,
|
||||
face, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly,
|
||||
localExtraInfo, precisionPicking);
|
||||
if (!entityID.isNull() && distanceToElementDetails < parabolicDistance) {
|
||||
parabolicDistance = distanceToElementDetails;
|
||||
face = localFace;
|
||||
surfaceNormal = localSurfaceNormal;
|
||||
extraInfo = localExtraInfo;
|
||||
result = entityID;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIDsToDiscard,
|
||||
bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) {
|
||||
|
||||
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
||||
int entityNumber = 0;
|
||||
EntityItemID entityID;
|
||||
forEachEntity([&](EntityItemPointer entity) {
|
||||
// use simple line-sphere for broadphase check
|
||||
// (this is faster and more likely to cull results than the filter check below so we do it first)
|
||||
bool success;
|
||||
AABox entityBox = entity->getAABox(success);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Instead of checking parabolaInstersectsBoundingSphere here, we are just going to check if the plane
|
||||
// defined by the parabola slices the sphere. The solution to parabolaIntersectsBoundingSphere is cubic,
|
||||
// the solution to which is more computationally expensive than the quadratic AABox::findParabolaIntersection
|
||||
// below
|
||||
if (!entityBox.parabolaPlaneIntersectsBoundingSphere(origin, velocity, acceleration)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check RayPick filter settings
|
||||
if ((visibleOnly && !entity->isVisible())
|
||||
|| (collidableOnly && (entity->getCollisionless() || entity->getShapeType() == SHAPE_TYPE_NONE))
|
||||
|| (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID()))
|
||||
|| (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// extents is the entity relative, scaled, centered extents of the entity
|
||||
glm::mat4 rotation = glm::mat4_cast(entity->getWorldOrientation());
|
||||
glm::mat4 translation = glm::translate(entity->getWorldPosition());
|
||||
glm::mat4 entityToWorldMatrix = translation * rotation;
|
||||
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
|
||||
|
||||
glm::vec3 dimensions = entity->getRaycastDimensions();
|
||||
glm::vec3 registrationPoint = entity->getRegistrationPoint();
|
||||
glm::vec3 corner = -(dimensions * registrationPoint);
|
||||
|
||||
AABox entityFrameBox(corner, dimensions);
|
||||
|
||||
glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
|
||||
glm::vec3 entityFrameVelocity = glm::vec3(worldToEntityMatrix * glm::vec4(velocity, 0.0f));
|
||||
glm::vec3 entityFrameAcceleration = glm::vec3(worldToEntityMatrix * glm::vec4(acceleration, 0.0f));
|
||||
|
||||
// we can use the AABox's ray intersection by mapping our origin and direction into the entity frame
|
||||
// and testing intersection there.
|
||||
float localDistance;
|
||||
BoxFace localFace;
|
||||
glm::vec3 localSurfaceNormal;
|
||||
if (entityFrameBox.findParabolaIntersection(entityFrameOrigin, entityFrameVelocity, entityFrameAcceleration, localDistance,
|
||||
localFace, localSurfaceNormal)) {
|
||||
if (entityFrameBox.contains(entityFrameOrigin) || localDistance < parabolicDistance) {
|
||||
// now ask the entity if we actually intersect
|
||||
if (entity->supportsDetailedIntersection()) {
|
||||
QVariantMap localExtraInfo;
|
||||
if (entity->findDetailedParabolaIntersection(origin, velocity, acceleration, element, localDistance,
|
||||
localFace, localSurfaceNormal, localExtraInfo, precisionPicking)) {
|
||||
if (localDistance < parabolicDistance) {
|
||||
parabolicDistance = localDistance;
|
||||
face = localFace;
|
||||
surfaceNormal = localSurfaceNormal;
|
||||
extraInfo = localExtraInfo;
|
||||
entityID = entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if the entity type doesn't support a detailed intersection, then just return the non-AABox results
|
||||
// Never intersect with particle entities
|
||||
if (localDistance < parabolicDistance && entity->getType() != EntityTypes::ParticleEffect) {
|
||||
parabolicDistance = localDistance;
|
||||
face = localFace;
|
||||
surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f));
|
||||
entityID = entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
entityNumber++;
|
||||
});
|
||||
return entityID;
|
||||
}
|
||||
|
||||
EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const {
|
||||
EntityItemPointer closestEntity = NULL;
|
||||
float closestEntityDistance = FLT_MAX;
|
||||
|
|
|
@ -134,9 +134,9 @@ public:
|
|||
virtual bool isRendered() const override { return getShouldRender(); }
|
||||
virtual bool deleteApproved() const override { return !hasEntities(); }
|
||||
|
||||
virtual bool canRayIntersect() const override { return hasEntities(); }
|
||||
virtual bool canPickIntersect() const override { return hasEntities(); }
|
||||
virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& node, float& distance,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
|
||||
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
|
||||
QVariantMap& extraInfo, bool precisionPicking = false);
|
||||
|
@ -148,6 +148,16 @@ public:
|
|||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const override;
|
||||
|
||||
virtual EntityItemID findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, bool& keepSearching, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
|
||||
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
|
||||
QVariantMap& extraInfo, bool precisionPicking = false);
|
||||
virtual EntityItemID findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
|
||||
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
|
||||
QVariantMap& extraInfo, bool precisionPicking);
|
||||
|
||||
template <typename F>
|
||||
void forEachEntity(F f) const {
|
||||
|
|
|
@ -309,3 +309,14 @@ bool LightEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
|
|||
return _lightsArePickable;
|
||||
}
|
||||
|
||||
bool LightEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
// TODO: consider if this is really what we want to do. We've made it so that "lights are pickable" is a global state
|
||||
// this is probably reasonable since there's typically only one tree you'd be picking on at a time. Technically we could
|
||||
// be on the clipboard and someone might be trying to use the parabola intersection API there. Anyway... if you ever try to
|
||||
// do parabola intersection testing off of trees other than the main tree of the main entity renderer, then we'll need to
|
||||
// fix this mechanism.
|
||||
return _lightsArePickable;
|
||||
}
|
||||
|
|
|
@ -84,11 +84,15 @@ public:
|
|||
bool lightPropertiesChanged() const { return _lightPropertiesChanged; }
|
||||
void resetLightPropertiesChanged();
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
|
||||
private:
|
||||
// properties of a light
|
||||
|
|
|
@ -58,12 +58,17 @@ class LineEntityItem : public EntityItem {
|
|||
virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; }
|
||||
|
||||
// never have a ray intersection pick a LineEntityItem.
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo,
|
||||
bool precisionPicking) const override { return false; }
|
||||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo,
|
||||
bool precisionPicking) const override { return false; }
|
||||
bool pointsChanged() const { return _pointsChanged; }
|
||||
void resetPointsChanged();
|
||||
virtual void debugDump() const override;
|
||||
|
|
|
@ -314,7 +314,7 @@ public:
|
|||
bool getEmitterShouldTrail() const { return _particleProperties.emission.shouldTrail; }
|
||||
void setEmitterShouldTrail(bool emitterShouldTrail);
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const override { return false; }
|
||||
virtual bool supportsDetailedIntersection() const override { return false; }
|
||||
|
||||
particle::Properties getParticleProperties() const;
|
||||
|
||||
|
|
|
@ -89,11 +89,15 @@ class PolyLineEntityItem : public EntityItem {
|
|||
|
||||
|
||||
// never have a ray intersection pick a PolyLineEntityItem.
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override { return false; }
|
||||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const { return false; }
|
||||
|
||||
// disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain
|
||||
virtual void setRegistrationPoint(const glm::vec3& value) override {}; // FIXME: this is suspicious!
|
||||
|
|
|
@ -42,11 +42,15 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
bool& somethingChanged) override;
|
||||
|
||||
// never have a ray intersection pick a PolyVoxEntityItem.
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override { return false; }
|
||||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const { return false; }
|
||||
|
||||
virtual void debugDump() const override;
|
||||
|
||||
|
|
|
@ -250,7 +250,7 @@ void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ShapeEntityItem::supportsDetailedRayIntersection() const {
|
||||
bool ShapeEntityItem::supportsDetailedIntersection() const {
|
||||
return _shape == entity::Sphere;
|
||||
}
|
||||
|
||||
|
@ -282,6 +282,14 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ShapeEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShapeEntityItem::debugDump() const {
|
||||
quint64 now = usecTimestampNow();
|
||||
qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------";
|
||||
|
|
|
@ -90,11 +90,15 @@ public:
|
|||
|
||||
bool shouldBePhysical() const override { return !isDead(); }
|
||||
|
||||
bool supportsDetailedRayIntersection() const override;
|
||||
bool supportsDetailedIntersection() const override;
|
||||
bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
|
||||
void debugDump() const override;
|
||||
|
||||
|
|
|
@ -140,6 +140,14 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
|
|||
return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance);
|
||||
}
|
||||
|
||||
bool TextEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextEntityItem::setText(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
_text = value;
|
||||
|
|
|
@ -45,11 +45,15 @@ public:
|
|||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
|
||||
static const QString DEFAULT_TEXT;
|
||||
void setText(const QString& value);
|
||||
|
|
|
@ -121,6 +121,14 @@ bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const g
|
|||
}
|
||||
}
|
||||
|
||||
bool WebEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebEntityItem::setSourceUrl(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
if (_sourceUrl != value) {
|
||||
|
|
|
@ -44,11 +44,15 @@ public:
|
|||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
|
||||
virtual void setSourceUrl(const QString& value);
|
||||
QString getSourceUrl() const;
|
||||
|
|
|
@ -294,10 +294,16 @@ void ZoneEntityItem::setCompoundShapeURL(const QString& url) {
|
|||
}
|
||||
|
||||
bool ZoneEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
return _zonesArePickable;
|
||||
}
|
||||
|
||||
bool ZoneEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
return _zonesArePickable;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,11 +102,15 @@ public:
|
|||
|
||||
void resetRenderingPropertiesChanged();
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
|
||||
virtual void debugDump() const override;
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ public:
|
|||
|
||||
virtual bool deleteApproved() const { return true; }
|
||||
|
||||
virtual bool canRayIntersect() const { return isLeaf(); }
|
||||
virtual bool canPickIntersect() const { return isLeaf(); }
|
||||
/// \param center center of sphere in meters
|
||||
/// \param radius radius of sphere in meters
|
||||
/// \param[out] penetration pointing into cube from sphere
|
||||
|
|
|
@ -161,6 +161,7 @@ public:
|
|||
enum PickType {
|
||||
Ray = 0,
|
||||
Stylus,
|
||||
Parabola,
|
||||
|
||||
NUM_PICK_TYPES
|
||||
};
|
||||
|
|
|
@ -100,6 +100,7 @@ void PickManager::update() {
|
|||
// and the rayPicks updae will ALWAYS update at least one ray even when there is no budget
|
||||
_stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false);
|
||||
_rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD);
|
||||
_parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD);
|
||||
}
|
||||
|
||||
bool PickManager::isLeftHand(unsigned int uid) {
|
||||
|
|
|
@ -59,12 +59,13 @@ protected:
|
|||
|
||||
std::shared_ptr<PickQuery> findPick(unsigned int uid) const;
|
||||
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> _picks;
|
||||
unsigned int _nextPickToUpdate[PickQuery::NUM_PICK_TYPES] { 0, 0 };
|
||||
unsigned int _nextPickToUpdate[PickQuery::NUM_PICK_TYPES] { 0, 0, 0 };
|
||||
std::unordered_map<unsigned int, PickQuery::PickType> _typeMap;
|
||||
unsigned int _nextPickID { INVALID_PICK_ID + 1 };
|
||||
|
||||
PickCacheOptimizer<PickRay> _rayPickCacheOptimizer;
|
||||
PickCacheOptimizer<StylusTip> _stylusPickCacheOptimizer;
|
||||
PickCacheOptimizer<PickParabola> _parabolaPickCacheOptimizer;
|
||||
|
||||
static const unsigned int DEFAULT_PER_FRAME_TIME_BUDGET = 2 * USECS_PER_MSEC;
|
||||
unsigned int _perFrameTimeBudget { DEFAULT_PER_FRAME_TIME_BUDGET };
|
||||
|
|
|
@ -287,6 +287,68 @@ bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct
|
|||
return false;
|
||||
}
|
||||
|
||||
void AABox::checkPossibleParabolicIntersection(float t, int i, float& minDistance,
|
||||
const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, bool& hit) const {
|
||||
if (t < minDistance && t > 0.0f &&
|
||||
isWithin(origin[(i + 1) % 3] + velocity[(i + 1) % 3] * t + 0.5f * acceleration[(i + 1) % 3] * t * t, _corner[(i + 1) % 3], _scale[(i + 1) % 3]) &&
|
||||
isWithin(origin[(i + 2) % 3] + velocity[(i + 2) % 3] * t + 0.5f * acceleration[(i + 2) % 3] * t * t, _corner[(i + 2) % 3], _scale[(i + 2) % 3])) {
|
||||
minDistance = t;
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AABox::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
|
||||
float minDistance = FLT_MAX;
|
||||
BoxFace minFace;
|
||||
glm::vec3 minNormal;
|
||||
std::pair<float, float> possibleDistances;
|
||||
float a, b, c;
|
||||
|
||||
// Solve the intersection for each face of the cube. As we go, keep track of the smallest, positive, real distance
|
||||
// that is within the bounds of the other two dimensions
|
||||
for (int i = 0; i < 3; i++) {
|
||||
a = 0.5f * acceleration[i];
|
||||
b = velocity[i];
|
||||
{ // min
|
||||
c = origin[i] - _corner[i];
|
||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||
bool hit = false;
|
||||
checkPossibleParabolicIntersection(possibleDistances.first, i, minDistance, origin, velocity, acceleration, hit);
|
||||
checkPossibleParabolicIntersection(possibleDistances.second, i, minDistance, origin, velocity, acceleration, hit);
|
||||
if (hit) {
|
||||
minFace = BoxFace(2 * i);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = -1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
{ // max
|
||||
c = origin[i] - (_corner[i] + _scale[i]);
|
||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||
bool hit = false;
|
||||
checkPossibleParabolicIntersection(possibleDistances.first, i, minDistance, origin, velocity, acceleration, hit);
|
||||
checkPossibleParabolicIntersection(possibleDistances.second, i, minDistance, origin, velocity, acceleration, hit);
|
||||
if (hit) {
|
||||
minFace = BoxFace(2 * i + 1);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minDistance < FLT_MAX) {
|
||||
parabolicDistance = minDistance;
|
||||
face = minFace;
|
||||
surfaceNormal = minNormal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AABox::rayHitsBoundingSphere(const glm::vec3& origin, const glm::vec3& direction) const {
|
||||
glm::vec3 localCenter = calcCenter() - origin;
|
||||
float distance = glm::dot(localCenter, direction);
|
||||
|
@ -296,6 +358,28 @@ bool AABox::rayHitsBoundingSphere(const glm::vec3& origin, const glm::vec3& dire
|
|||
|| (glm::abs(distance) > 0.0f && glm::distance2(distance * direction, localCenter) < radiusSquared));
|
||||
}
|
||||
|
||||
bool AABox::parabolaPlaneIntersectsBoundingSphere(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration) const {
|
||||
glm::vec3 localCenter = calcCenter() - origin;
|
||||
const float ONE_OVER_TWO_SQUARED = 0.25f;
|
||||
float radiusSquared = ONE_OVER_TWO_SQUARED * glm::length2(_scale);
|
||||
|
||||
// origin is inside the sphere
|
||||
if (glm::length2(localCenter) < radiusSquared) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the normal of the plane, the cross product of two vectors on the plane
|
||||
// Assumes: velocity and acceleration != 0 and normalize(velocity) != normalize(acceleration)
|
||||
glm::vec3 normal = glm::normalize(glm::cross(velocity, acceleration));
|
||||
|
||||
// Project vector from plane to sphere center onto the normal
|
||||
float distance = glm::dot(localCenter, normal);
|
||||
if (distance * distance < radiusSquared) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AABox::touchesSphere(const glm::vec3& center, float radius) const {
|
||||
// Avro's algorithm from this paper: http://www.mrtc.mdh.se/projects/3Dgraphics/paperF.pdf
|
||||
glm::vec3 e = glm::max(_corner - center, Vectors::ZERO) + glm::max(center - _corner - _scale, Vectors::ZERO);
|
||||
|
|
|
@ -70,8 +70,11 @@ public:
|
|||
bool expandedContains(const glm::vec3& point, float expansion) const;
|
||||
bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const;
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) const;
|
||||
BoxFace& face, glm::vec3& surfaceNormal) const;
|
||||
bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const;
|
||||
bool rayHitsBoundingSphere(const glm::vec3& origin, const glm::vec3& direction) const;
|
||||
bool parabolaPlaneIntersectsBoundingSphere(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration) const;
|
||||
bool touchesSphere(const glm::vec3& center, float radius) const; // fast but may generate false positives
|
||||
bool touchesAAEllipsoid(const glm::vec3& center, const glm::vec3& radials) const;
|
||||
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
|
||||
|
@ -136,6 +139,9 @@ private:
|
|||
|
||||
static BoxFace getOppositeFace(BoxFace face);
|
||||
|
||||
void checkPossibleParabolicIntersection(float t, int i, float& minDistance,
|
||||
const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, bool& hit) const;
|
||||
|
||||
glm::vec3 _corner;
|
||||
glm::vec3 _scale;
|
||||
};
|
||||
|
|
|
@ -283,6 +283,68 @@ bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
return false;
|
||||
}
|
||||
|
||||
void AACube::checkPossibleParabolicIntersection(float t, int i, float& minDistance,
|
||||
const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, bool& hit) const {
|
||||
if (t < minDistance && t > 0.0f &&
|
||||
isWithin(origin[(i + 1) % 3] + velocity[(i + 1) % 3] * t + 0.5f * acceleration[(i + 1) % 3] * t * t, _corner[(i + 1) % 3], _scale) &&
|
||||
isWithin(origin[(i + 2) % 3] + velocity[(i + 2) % 3] * t + 0.5f * acceleration[(i + 2) % 3] * t * t, _corner[(i + 2) % 3], _scale)) {
|
||||
minDistance = t;
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AACube::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
|
||||
float minDistance = FLT_MAX;
|
||||
BoxFace minFace;
|
||||
glm::vec3 minNormal;
|
||||
std::pair<float, float> possibleDistances;
|
||||
float a, b, c;
|
||||
|
||||
// Solve the intersection for each face of the cube. As we go, keep track of the smallest, positive, real distance
|
||||
// that is within the bounds of the other two dimensions
|
||||
for (int i = 0; i < 3; i++) {
|
||||
a = 0.5f * acceleration[i];
|
||||
b = velocity[i];
|
||||
{ // min
|
||||
c = origin[i] - _corner[i];
|
||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||
bool hit = false;
|
||||
checkPossibleParabolicIntersection(possibleDistances.first, i, minDistance, origin, velocity, acceleration, hit);
|
||||
checkPossibleParabolicIntersection(possibleDistances.second, i, minDistance, origin, velocity, acceleration, hit);
|
||||
if (hit) {
|
||||
minFace = BoxFace(2 * i);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = -1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
{ // max
|
||||
c = origin[i] - (_corner[i] + _scale);
|
||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||
bool hit = false;
|
||||
checkPossibleParabolicIntersection(possibleDistances.first, i, minDistance, origin, velocity, acceleration, hit);
|
||||
checkPossibleParabolicIntersection(possibleDistances.second, i, minDistance, origin, velocity, acceleration, hit);
|
||||
if (hit) {
|
||||
minFace = BoxFace(2 * i + 1);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minDistance < FLT_MAX) {
|
||||
parabolicDistance = minDistance;
|
||||
face = minFace;
|
||||
surfaceNormal = minNormal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AACube::touchesSphere(const glm::vec3& center, float radius) const {
|
||||
// Avro's algorithm from this paper: http://www.mrtc.mdh.se/projects/3Dgraphics/paperF.pdf
|
||||
glm::vec3 e = glm::max(_corner - center, Vectors::ZERO) + glm::max(center - _corner - glm::vec3(_scale), Vectors::ZERO);
|
||||
|
|
|
@ -58,6 +58,8 @@ public:
|
|||
bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const;
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) const;
|
||||
bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const;
|
||||
bool touchesSphere(const glm::vec3& center, float radius) const;
|
||||
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
|
||||
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const;
|
||||
|
@ -76,6 +78,9 @@ private:
|
|||
|
||||
static BoxFace getOppositeFace(BoxFace face);
|
||||
|
||||
void checkPossibleParabolicIntersection(float t, int i, float& minDistance,
|
||||
const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, bool& hit) const;
|
||||
|
||||
glm::vec3 _corner;
|
||||
float _scale;
|
||||
};
|
||||
|
|
|
@ -941,3 +941,17 @@ void generateBoundryLinesForDop14(const std::vector<float>& dots, const glm::vec
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool computeRealQuadraticRoots(float a, float b, float c, std::pair<float, float>& roots) {
|
||||
float discriminant = b * b - 4.0f * a * c;
|
||||
if (discriminant < 0.0f) {
|
||||
return false;
|
||||
} else if (discriminant == 0.0f) {
|
||||
roots.first = (-b + sqrtf(discriminant)) / (2.0f * a);
|
||||
} else {
|
||||
float discriminantRoot = sqrtf(discriminant);
|
||||
roots.first = (-b + discriminantRoot) / (2.0f * a);
|
||||
roots.second = (-b - discriminantRoot) / (2.0f * a);
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -178,4 +178,6 @@ bool findIntersectionOfThreePlanes(const glm::vec4& planeA, const glm::vec4& pla
|
|||
|
||||
void generateBoundryLinesForDop14(const std::vector<float>& dots, const glm::vec3& center, std::vector<glm::vec3>& linesOut);
|
||||
|
||||
bool computeRealQuadraticRoots(float a, float b, float c, std::pair<float, float>& roots);
|
||||
|
||||
#endif // hifi_GeometryUtil_h
|
||||
|
|
|
@ -219,6 +219,37 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* A PickParabola defines a parabola with a starting point, intitial velocity, and acceleration.
|
||||
*
|
||||
* @typedef {object} PickParabola
|
||||
* @property {Vec3} origin - The starting position of the PickParabola.
|
||||
* @property {Vec3} velocity - The starting velocity of the parabola.
|
||||
* @property {Vec3} acceleration - The acceleration that the parabola experiences.
|
||||
*/
|
||||
class PickParabola : public MathPick {
|
||||
public:
|
||||
PickParabola() : origin(NAN), velocity(NAN), acceleration(NAN) { }
|
||||
PickParabola(const QVariantMap& pickVariant) : origin(vec3FromVariant(pickVariant["origin"])), velocity(vec3FromVariant(pickVariant["velocity"])), acceleration(vec3FromVariant(pickVariant["acceleration"])) {}
|
||||
PickParabola(const glm::vec3& origin, const glm::vec3 velocity, const glm::vec3 acceleration) : origin(origin), velocity(velocity), acceleration(acceleration) {}
|
||||
glm::vec3 origin;
|
||||
glm::vec3 velocity;
|
||||
glm::vec3 acceleration;
|
||||
|
||||
operator bool() const override {
|
||||
return !(glm::any(glm::isnan(origin)) || glm::any(glm::isnan(velocity)) || glm::any(glm::isnan(acceleration)));
|
||||
}
|
||||
bool operator==(const PickParabola& other) const {
|
||||
return (origin == other.origin && velocity == other.velocity && acceleration == other.acceleration);
|
||||
}
|
||||
QVariantMap toVariantMap() const override {
|
||||
QVariantMap pickParabola;
|
||||
pickParabola["origin"] = vec3toVariant(origin);
|
||||
pickParabola["velocity"] = vec3toVariant(velocity);
|
||||
pickParabola["acceleration"] = vec3toVariant(acceleration);
|
||||
return pickParabola;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
inline void hash_combine(std::size_t& seed) { }
|
||||
|
@ -273,6 +304,15 @@ namespace std {
|
|||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<PickParabola> {
|
||||
size_t operator()(const PickParabola& a) const {
|
||||
size_t result = 0;
|
||||
hash_combine(result, a.origin, a.velocity, a.acceleration);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<QString> {
|
||||
size_t operator()(const QString& a) const {
|
||||
|
|
Loading…
Reference in a new issue