mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 21:12:53 +02:00
parabola-overlay and -avatar intersection, handle case where acceleration == 0
This commit is contained in:
parent
ca5ce888f4
commit
845ddda695
41 changed files with 816 additions and 275 deletions
|
@ -5253,7 +5253,7 @@ void Application::setKeyboardFocusHighlight(const glm::vec3& position, const glm
|
||||||
_keyboardFocusHighlight->setPulseMin(0.5);
|
_keyboardFocusHighlight->setPulseMin(0.5);
|
||||||
_keyboardFocusHighlight->setPulseMax(1.0);
|
_keyboardFocusHighlight->setPulseMax(1.0);
|
||||||
_keyboardFocusHighlight->setColorPulse(1.0);
|
_keyboardFocusHighlight->setColorPulse(1.0);
|
||||||
_keyboardFocusHighlight->setIgnoreRayIntersection(true);
|
_keyboardFocusHighlight->setIgnorePickIntersection(true);
|
||||||
_keyboardFocusHighlight->setDrawInFront(false);
|
_keyboardFocusHighlight->setDrawInFront(false);
|
||||||
_keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);
|
_keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);
|
||||||
}
|
}
|
||||||
|
|
|
@ -573,6 +573,77 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector(const PickParabola& pick,
|
||||||
|
const QVector<EntityItemID>& avatarsToInclude,
|
||||||
|
const QVector<EntityItemID>& avatarsToDiscard) {
|
||||||
|
ParabolaToAvatarIntersectionResult result;
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findParabolaIntersectionVector",
|
||||||
|
Q_RETURN_ARG(ParabolaToAvatarIntersectionResult, result),
|
||||||
|
Q_ARG(const PickParabola&, pick),
|
||||||
|
Q_ARG(const QVector<EntityItemID>&, avatarsToInclude),
|
||||||
|
Q_ARG(const QVector<EntityItemID>&, avatarsToDiscard));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto avatarHashCopy = getHashCopy();
|
||||||
|
for (auto avatarData : avatarHashCopy) {
|
||||||
|
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
|
||||||
|
if ((avatarsToInclude.size() > 0 && !avatarsToInclude.contains(avatar->getID())) ||
|
||||||
|
(avatarsToDiscard.size() > 0 && avatarsToDiscard.contains(avatar->getID()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float parabolicDistance;
|
||||||
|
BoxFace face;
|
||||||
|
glm::vec3 surfaceNormal;
|
||||||
|
|
||||||
|
SkeletonModelPointer avatarModel = avatar->getSkeletonModel();
|
||||||
|
|
||||||
|
// It's better to intersect the parabola against the avatar's actual mesh, but this is currently difficult to
|
||||||
|
// do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code
|
||||||
|
// intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking
|
||||||
|
// against the avatar is sort-of right, but you likely wont be able to pick against the arms.
|
||||||
|
|
||||||
|
// TODO -- find a way to extract transformed avatar mesh data from the rendering engine.
|
||||||
|
|
||||||
|
// if we weren't picking against the capsule, we would want to pick against the avatarBounds...
|
||||||
|
// AABox avatarBounds = avatarModel->getRenderableMeshBound();
|
||||||
|
// if (!avatarBounds.findParabolaIntersection(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal)) {
|
||||||
|
// // parabola doesn't intersect avatar's bounding-box
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
glm::vec3 start;
|
||||||
|
glm::vec3 end;
|
||||||
|
float radius;
|
||||||
|
avatar->getCapsule(start, end, radius);
|
||||||
|
bool intersects = findParabolaCapsuleIntersection(pick.origin, pick.velocity, pick.acceleration, start, end, radius, avatar->getWorldOrientation(), parabolicDistance);
|
||||||
|
if (!intersects) {
|
||||||
|
// ray doesn't intersect avatar's capsule
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap extraInfo;
|
||||||
|
intersects = avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration,
|
||||||
|
parabolicDistance, face, surfaceNormal, extraInfo, true);
|
||||||
|
|
||||||
|
if (intersects && (!result.intersects || parabolicDistance < result.parabolicDistance)) {
|
||||||
|
result.intersects = true;
|
||||||
|
result.avatarID = avatar->getID();
|
||||||
|
result.parabolicDistance = parabolicDistance;
|
||||||
|
result.extraInfo = extraInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.intersects) {
|
||||||
|
result.intersection = pick.origin + pick.velocity * result.parabolicDistance + 0.5f * pick.acceleration * result.parabolicDistance * result.parabolicDistance;
|
||||||
|
result.distance = glm::distance(pick.origin, result.intersection);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// HACK
|
// HACK
|
||||||
float AvatarManager::getAvatarSortCoefficient(const QString& name) {
|
float AvatarManager::getAvatarSortCoefficient(const QString& name) {
|
||||||
if (name == "size") {
|
if (name == "size") {
|
||||||
|
|
|
@ -141,6 +141,10 @@ public:
|
||||||
const QVector<EntityItemID>& avatarsToInclude,
|
const QVector<EntityItemID>& avatarsToInclude,
|
||||||
const QVector<EntityItemID>& avatarsToDiscard);
|
const QVector<EntityItemID>& avatarsToDiscard);
|
||||||
|
|
||||||
|
Q_INVOKABLE ParabolaToAvatarIntersectionResult findParabolaIntersectionVector(const PickParabola& pick,
|
||||||
|
const QVector<EntityItemID>& avatarsToInclude,
|
||||||
|
const QVector<EntityItemID>& avatarsToDiscard);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function AvatarManager.getAvatarSortCoefficient
|
* @function AvatarManager.getAvatarSortCoefficient
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
|
|
|
@ -26,29 +26,29 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick)
|
||||||
}
|
}
|
||||||
|
|
||||||
PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) {
|
PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) {
|
||||||
/*ParabolaToOverlayIntersectionResult overlayRes =
|
ParabolaToOverlayIntersectionResult overlayRes =
|
||||||
qApp->getOverlays().findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(),
|
qApp->getOverlays().findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(),
|
||||||
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
|
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
|
||||||
if (overlayRes.intersects) {
|
if (overlayRes.intersects) {
|
||||||
return std::make_shared<ParabolaPickResult>(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo);
|
return std::make_shared<ParabolaPickResult>(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo);
|
||||||
} else {*/
|
} else {
|
||||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PickResultPointer ParabolaPick::getAvatarIntersection(const PickParabola& pick) {
|
PickResultPointer ParabolaPick::getAvatarIntersection(const PickParabola& pick) {
|
||||||
/*ParabolaToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findParabolaIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
ParabolaToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findParabolaIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
||||||
if (avatarRes.intersects) {
|
if (avatarRes.intersects) {
|
||||||
return std::make_shared<ParabolaPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.parabolicDistance, avatarRes.intersection, pick, glm::vec3(NAN), avatarRes.extraInfo);
|
return std::make_shared<ParabolaPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.parabolicDistance, avatarRes.intersection, pick, glm::vec3(NAN), avatarRes.extraInfo);
|
||||||
} else {*/
|
} else {
|
||||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PickResultPointer ParabolaPick::getHUDIntersection(const PickParabola& pick) {
|
PickResultPointer ParabolaPick::getHUDIntersection(const PickParabola& pick) {
|
||||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
float parabolicDistance;
|
||||||
//glm::vec3 hudRes = DependencyManager::get<HMDScriptingInterface>()->calculateParabolaUICollisionPoint(pick);
|
glm::vec3 hudRes = DependencyManager::get<HMDScriptingInterface>()->calculateParabolaUICollisionPoint(pick.origin, pick.velocity, pick.acceleration, parabolicDistance);
|
||||||
//return std::make_shared<ParabolaPickResult>(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick);
|
return std::make_shared<ParabolaPickResult>(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), parabolicDistance, hudRes, pick);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 ParabolaPick::getAcceleration() const {
|
glm::vec3 ParabolaPick::getAcceleration() const {
|
||||||
|
|
|
@ -58,11 +58,11 @@ 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 parabolicDistance < maxDistance; }
|
||||||
|
|
||||||
PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override {
|
PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override {
|
||||||
auto newParabolaRes = std::static_pointer_cast<ParabolaPickResult>(newRes);
|
auto newParabolaRes = std::static_pointer_cast<ParabolaPickResult>(newRes);
|
||||||
if (newParabolaRes->distance < distance) {
|
if (newParabolaRes->parabolicDistance < parabolicDistance) {
|
||||||
return std::make_shared<ParabolaPickResult>(*newParabolaRes);
|
return std::make_shared<ParabolaPickResult>(*newParabolaRes);
|
||||||
} else {
|
} else {
|
||||||
return std::make_shared<ParabolaPickResult>(*this);
|
return std::make_shared<ParabolaPickResult>(*this);
|
||||||
|
|
|
@ -35,6 +35,12 @@ glm::vec3 HMDScriptingInterface::calculateRayUICollisionPoint(const glm::vec3& p
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 HMDScriptingInterface::calculateParabolaUICollisionPoint(const glm::vec3& position, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance) const {
|
||||||
|
glm::vec3 result;
|
||||||
|
qApp->getApplicationCompositor().calculateParabolaUICollisionPoint(position, velocity, acceleration, result, parabolicDistance);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec2 HMDScriptingInterface::overlayFromWorldPoint(const glm::vec3& position) const {
|
glm::vec2 HMDScriptingInterface::overlayFromWorldPoint(const glm::vec3& position) const {
|
||||||
return qApp->getApplicationCompositor().overlayFromSphereSurface(position);
|
return qApp->getApplicationCompositor().overlayFromSphereSurface(position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,8 @@ public:
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const;
|
Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const;
|
||||||
|
|
||||||
|
glm::vec3 calculateParabolaUICollisionPoint(const glm::vec3& position, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance) const;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Get the 2D HUD overlay coordinates of a 3D point on the HUD overlay.
|
* Get the 2D HUD overlay coordinates of a 3D point on the HUD overlay.
|
||||||
* 2D HUD overlay coordinates are pixels with the origin at the top left of the overlay.
|
* 2D HUD overlay coordinates are pixels with the origin at the top left of the overlay.
|
||||||
|
|
|
@ -23,7 +23,7 @@ Base3DOverlay::Base3DOverlay() :
|
||||||
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
||||||
_isSolid(DEFAULT_IS_SOLID),
|
_isSolid(DEFAULT_IS_SOLID),
|
||||||
_isDashedLine(DEFAULT_IS_DASHED_LINE),
|
_isDashedLine(DEFAULT_IS_DASHED_LINE),
|
||||||
_ignoreRayIntersection(false),
|
_ignorePickIntersection(false),
|
||||||
_drawInFront(false),
|
_drawInFront(false),
|
||||||
_drawHUDLayer(false)
|
_drawHUDLayer(false)
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,7 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
||||||
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
||||||
_isSolid(base3DOverlay->_isSolid),
|
_isSolid(base3DOverlay->_isSolid),
|
||||||
_isDashedLine(base3DOverlay->_isDashedLine),
|
_isDashedLine(base3DOverlay->_isDashedLine),
|
||||||
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
|
_ignorePickIntersection(base3DOverlay->_ignorePickIntersection),
|
||||||
_drawInFront(base3DOverlay->_drawInFront),
|
_drawInFront(base3DOverlay->_drawInFront),
|
||||||
_drawHUDLayer(base3DOverlay->_drawHUDLayer),
|
_drawHUDLayer(base3DOverlay->_drawHUDLayer),
|
||||||
_isGrabbable(base3DOverlay->_isGrabbable),
|
_isGrabbable(base3DOverlay->_isGrabbable),
|
||||||
|
@ -183,8 +183,10 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
||||||
if (properties["dashed"].isValid()) {
|
if (properties["dashed"].isValid()) {
|
||||||
setIsDashedLine(properties["dashed"].toBool());
|
setIsDashedLine(properties["dashed"].toBool());
|
||||||
}
|
}
|
||||||
if (properties["ignoreRayIntersection"].isValid()) {
|
if (properties["ignorePickIntersection"].isValid()) {
|
||||||
setIgnoreRayIntersection(properties["ignoreRayIntersection"].toBool());
|
setIgnorePickIntersection(properties["ignorePickIntersection"].toBool());
|
||||||
|
} else if (properties["ignoreRayIntersection"].isValid()) {
|
||||||
|
setIgnorePickIntersection(properties["ignoreRayIntersection"].toBool());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties["parentID"].isValid()) {
|
if (properties["parentID"].isValid()) {
|
||||||
|
@ -224,8 +226,7 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
@ -260,8 +261,8 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
|
||||||
if (property == "isDashedLine" || property == "dashed") {
|
if (property == "isDashedLine" || property == "dashed") {
|
||||||
return _isDashedLine;
|
return _isDashedLine;
|
||||||
}
|
}
|
||||||
if (property == "ignoreRayIntersection") {
|
if (property == "ignorePickIntersection" || property == "ignoreRayIntersection") {
|
||||||
return _ignoreRayIntersection;
|
return _ignorePickIntersection;
|
||||||
}
|
}
|
||||||
if (property == "drawInFront") {
|
if (property == "drawInFront") {
|
||||||
return _drawInFront;
|
return _drawInFront;
|
||||||
|
@ -282,11 +283,6 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
|
||||||
return Overlay::getProperty(property);
|
return Overlay::getProperty(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Base3DOverlay::locationChanged(bool tellPhysics) {
|
void Base3DOverlay::locationChanged(bool tellPhysics) {
|
||||||
SpatiallyNestable::locationChanged(tellPhysics);
|
SpatiallyNestable::locationChanged(tellPhysics);
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ public:
|
||||||
bool getIsSolid() const { return _isSolid; }
|
bool getIsSolid() const { return _isSolid; }
|
||||||
bool getIsDashedLine() const { return _isDashedLine; }
|
bool getIsDashedLine() const { return _isDashedLine; }
|
||||||
bool getIsSolidLine() const { return !_isDashedLine; }
|
bool getIsSolidLine() const { return !_isDashedLine; }
|
||||||
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
|
bool getIgnorePickIntersection() const { return _ignorePickIntersection; }
|
||||||
bool getDrawInFront() const { return _drawInFront; }
|
bool getDrawInFront() const { return _drawInFront; }
|
||||||
bool getDrawHUDLayer() const { return _drawHUDLayer; }
|
bool getDrawHUDLayer() const { return _drawHUDLayer; }
|
||||||
bool getIsGrabbable() const { return _isGrabbable; }
|
bool getIsGrabbable() const { return _isGrabbable; }
|
||||||
|
@ -53,7 +53,7 @@ public:
|
||||||
|
|
||||||
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
|
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
|
||||||
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
|
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
|
||||||
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
|
void setIgnorePickIntersection(bool value) { _ignorePickIntersection = value; }
|
||||||
virtual void setDrawInFront(bool value) { _drawInFront = value; }
|
virtual void setDrawInFront(bool value) { _drawInFront = value; }
|
||||||
virtual void setDrawHUDLayer(bool value) { _drawHUDLayer = value; }
|
virtual void setDrawHUDLayer(bool value) { _drawHUDLayer = value; }
|
||||||
void setIsGrabbable(bool value) { _isGrabbable = value; }
|
void setIsGrabbable(bool value) { _isGrabbable = value; }
|
||||||
|
@ -69,13 +69,21 @@ public:
|
||||||
virtual QVariant getProperty(const QString& property) override;
|
virtual QVariant getProperty(const QString& property) override;
|
||||||
|
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false);
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) { return false; }
|
||||||
|
|
||||||
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) {
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) {
|
||||||
return findRayIntersection(origin, direction, distance, face, surfaceNormal, precisionPicking);
|
return findRayIntersection(origin, direction, distance, face, surfaceNormal, precisionPicking);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance,
|
||||||
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) { return false; }
|
||||||
|
|
||||||
|
virtual bool findParabolaIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) {
|
||||||
|
return findParabolaIntersection(origin, velocity, acceleration, parabolicDistance, face, surfaceNormal, precisionPicking);
|
||||||
|
}
|
||||||
|
|
||||||
virtual SpatialParentTree* getParentTree() const override;
|
virtual SpatialParentTree* getParentTree() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -91,7 +99,7 @@ protected:
|
||||||
|
|
||||||
bool _isSolid;
|
bool _isSolid;
|
||||||
bool _isDashedLine;
|
bool _isDashedLine;
|
||||||
bool _ignoreRayIntersection;
|
bool _ignorePickIntersection;
|
||||||
bool _drawInFront;
|
bool _drawInFront;
|
||||||
bool _drawHUDLayer;
|
bool _drawHUDLayer;
|
||||||
bool _isGrabbable { false };
|
bool _isGrabbable { false };
|
||||||
|
|
|
@ -397,8 +397,7 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
@ -520,22 +519,65 @@ QVariant Circle3DOverlay::getProperty(const QString& property) {
|
||||||
|
|
||||||
bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||||
|
|
||||||
// Scale the dimensions by the diameter
|
// Scale the dimensions by the diameter
|
||||||
glm::vec2 dimensions = getOuterRadius() * 2.0f * getDimensions();
|
glm::vec2 dimensions = getOuterRadius() * 2.0f * getDimensions();
|
||||||
bool intersects = findRayRectangleIntersection(origin, direction, getWorldOrientation(), getWorldPosition(), dimensions, distance);
|
glm::quat rotation = getWorldOrientation();
|
||||||
|
|
||||||
if (intersects) {
|
if (findRayRectangleIntersection(origin, direction, rotation, getWorldPosition(), dimensions, distance)) {
|
||||||
glm::vec3 hitPosition = origin + (distance * direction);
|
glm::vec3 hitPosition = origin + (distance * direction);
|
||||||
glm::vec3 localHitPosition = glm::inverse(getWorldOrientation()) * (hitPosition - getWorldPosition());
|
glm::vec3 localHitPosition = glm::inverse(getWorldOrientation()) * (hitPosition - getWorldPosition());
|
||||||
localHitPosition.x /= getDimensions().x;
|
localHitPosition.x /= getDimensions().x;
|
||||||
localHitPosition.y /= getDimensions().y;
|
localHitPosition.y /= getDimensions().y;
|
||||||
float distanceToHit = glm::length(localHitPosition);
|
float distanceToHit = glm::length(localHitPosition);
|
||||||
|
|
||||||
intersects = getInnerRadius() <= distanceToHit && distanceToHit <= getOuterRadius();
|
if (getInnerRadius() <= distanceToHit && distanceToHit <= getOuterRadius()) {
|
||||||
|
glm::vec3 forward = rotation * Vectors::FRONT;
|
||||||
|
if (glm::dot(forward, direction) > 0.0f) {
|
||||||
|
face = MAX_Z_FACE;
|
||||||
|
surfaceNormal = -forward;
|
||||||
|
} else {
|
||||||
|
face = MIN_Z_FACE;
|
||||||
|
surfaceNormal = forward;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intersects;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Circle3DOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||||
|
// Scale the dimensions by the diameter
|
||||||
|
glm::vec2 xyDimensions = getOuterRadius() * 2.0f * getDimensions();
|
||||||
|
glm::quat rotation = getWorldOrientation();
|
||||||
|
glm::vec3 position = getWorldPosition();
|
||||||
|
|
||||||
|
glm::quat inverseRot = glm::inverse(rotation);
|
||||||
|
glm::vec3 localOrigin = inverseRot * (origin - position);
|
||||||
|
glm::vec3 localVelocity = inverseRot * velocity;
|
||||||
|
glm::vec3 localAcceleration = inverseRot * acceleration;
|
||||||
|
|
||||||
|
if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, xyDimensions, parabolicDistance)) {
|
||||||
|
glm::vec3 localHitPosition = localOrigin + localVelocity * parabolicDistance + 0.5f * localAcceleration * parabolicDistance * parabolicDistance;
|
||||||
|
localHitPosition.x /= getDimensions().x;
|
||||||
|
localHitPosition.y /= getDimensions().y;
|
||||||
|
float distanceToHit = glm::length(localHitPosition);
|
||||||
|
|
||||||
|
if (getInnerRadius() <= distanceToHit && distanceToHit <= getOuterRadius()) {
|
||||||
|
float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance;
|
||||||
|
if (localIntersectionVelocityZ > 0.0f) {
|
||||||
|
face = MIN_Z_FACE;
|
||||||
|
surfaceNormal = rotation * Vectors::FRONT;
|
||||||
|
} else {
|
||||||
|
face = MAX_Z_FACE;
|
||||||
|
surfaceNormal = rotation * -Vectors::FRONT;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Circle3DOverlay* Circle3DOverlay::createClone() const {
|
Circle3DOverlay* Circle3DOverlay::createClone() const {
|
||||||
|
|
|
@ -54,8 +54,10 @@ public:
|
||||||
void setMajorTickMarksColor(const xColor& value) { _majorTickMarksColor = value; }
|
void setMajorTickMarksColor(const xColor& value) { _majorTickMarksColor = value; }
|
||||||
void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; }
|
void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; }
|
||||||
|
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
|
virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
|
|
||||||
virtual Circle3DOverlay* createClone() const override;
|
virtual Circle3DOverlay* createClone() const override;
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
|
||||||
_contextOverlay->setPulseMin(CONTEXT_OVERLAY_UNHOVERED_PULSEMIN);
|
_contextOverlay->setPulseMin(CONTEXT_OVERLAY_UNHOVERED_PULSEMIN);
|
||||||
_contextOverlay->setPulseMax(CONTEXT_OVERLAY_UNHOVERED_PULSEMAX);
|
_contextOverlay->setPulseMax(CONTEXT_OVERLAY_UNHOVERED_PULSEMAX);
|
||||||
_contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE);
|
_contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE);
|
||||||
_contextOverlay->setIgnoreRayIntersection(false);
|
_contextOverlay->setIgnorePickIntersection(false);
|
||||||
_contextOverlay->setDrawInFront(true);
|
_contextOverlay->setDrawInFront(true);
|
||||||
_contextOverlay->setURL(PathUtils::resourcesUrl() + "images/inspect-icon.png");
|
_contextOverlay->setURL(PathUtils::resourcesUrl() + "images/inspect-icon.png");
|
||||||
_contextOverlay->setIsFacingAvatar(true);
|
_contextOverlay->setIsFacingAvatar(true);
|
||||||
|
|
|
@ -160,8 +160,7 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
|
|
@ -145,8 +145,7 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
|
|
@ -35,7 +35,10 @@ public:
|
||||||
virtual Grid3DOverlay* createClone() const override;
|
virtual Grid3DOverlay* createClone() const override;
|
||||||
|
|
||||||
// Grids are UI tools, and may not be intersected (pickable)
|
// Grids are UI tools, and may not be intersected (pickable)
|
||||||
virtual bool findRayIntersection(const glm::vec3&, const glm::vec3&, float&, BoxFace&, glm::vec3&, bool precisionPicking = false) override { return false; }
|
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face,
|
||||||
|
glm::vec3& surfaceNormal, bool precisionPicking = false) override { return false; }
|
||||||
|
virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override { return false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Transform evalRenderTransform() override;
|
Transform evalRenderTransform() override;
|
||||||
|
|
|
@ -216,8 +216,7 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
@ -260,10 +259,7 @@ void Image3DOverlay::setURL(const QString& url) {
|
||||||
bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||||
if (_texture && _texture->isLoaded()) {
|
if (_texture && _texture->isLoaded()) {
|
||||||
// Make sure position and rotation is updated.
|
|
||||||
Transform transform = getTransform();
|
Transform transform = getTransform();
|
||||||
|
|
||||||
// Don't call applyTransformTo() or setTransform() here because this code runs too frequently.
|
|
||||||
|
|
||||||
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
|
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
|
||||||
bool isNull = _fromImage.isNull();
|
bool isNull = _fromImage.isNull();
|
||||||
|
@ -271,12 +267,54 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec
|
||||||
float height = isNull ? _texture->getHeight() : _fromImage.height();
|
float height = isNull ? _texture->getHeight() : _fromImage.height();
|
||||||
float maxSize = glm::max(width, height);
|
float maxSize = glm::max(width, height);
|
||||||
glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize);
|
glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize);
|
||||||
|
glm::quat rotation = transform.getRotation();
|
||||||
|
|
||||||
// FIXME - face and surfaceNormal not being set
|
if (findRayRectangleIntersection(origin, direction, rotation, transform.getTranslation(), dimensions, distance)) {
|
||||||
return findRayRectangleIntersection(origin, direction,
|
glm::vec3 forward = rotation * Vectors::FRONT;
|
||||||
transform.getRotation(),
|
if (glm::dot(forward, direction) > 0.0f) {
|
||||||
transform.getTranslation(),
|
face = MAX_Z_FACE;
|
||||||
dimensions, distance);
|
surfaceNormal = -forward;
|
||||||
|
} else {
|
||||||
|
face = MIN_Z_FACE;
|
||||||
|
surfaceNormal = forward;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Image3DOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||||
|
if (_texture && _texture->isLoaded()) {
|
||||||
|
Transform transform = getTransform();
|
||||||
|
|
||||||
|
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
|
||||||
|
bool isNull = _fromImage.isNull();
|
||||||
|
float width = isNull ? _texture->getWidth() : _fromImage.width();
|
||||||
|
float height = isNull ? _texture->getHeight() : _fromImage.height();
|
||||||
|
float maxSize = glm::max(width, height);
|
||||||
|
glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize);
|
||||||
|
glm::quat rotation = transform.getRotation();
|
||||||
|
glm::vec3 position = getWorldPosition();
|
||||||
|
|
||||||
|
glm::quat inverseRot = glm::inverse(rotation);
|
||||||
|
glm::vec3 localOrigin = inverseRot * (origin - position);
|
||||||
|
glm::vec3 localVelocity = inverseRot * velocity;
|
||||||
|
glm::vec3 localAcceleration = inverseRot * acceleration;
|
||||||
|
|
||||||
|
if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, dimensions, parabolicDistance)) {
|
||||||
|
float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance;
|
||||||
|
if (localIntersectionVelocityZ > 0.0f) {
|
||||||
|
face = MIN_Z_FACE;
|
||||||
|
surfaceNormal = rotation * Vectors::FRONT;
|
||||||
|
} else {
|
||||||
|
face = MAX_Z_FACE;
|
||||||
|
surfaceNormal = rotation * -Vectors::FRONT;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -42,8 +42,10 @@ public:
|
||||||
QVariant getProperty(const QString& property) override;
|
QVariant getProperty(const QString& property) override;
|
||||||
bool isTransparent() override { return Base3DOverlay::isTransparent() || _alphaTexture; }
|
bool isTransparent() override { return Base3DOverlay::isTransparent() || _alphaTexture; }
|
||||||
|
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
|
virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance,
|
||||||
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
|
|
||||||
virtual Image3DOverlay* createClone() const override;
|
virtual Image3DOverlay* createClone() const override;
|
||||||
|
|
||||||
|
|
|
@ -288,8 +288,7 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
|
|
@ -373,8 +373,7 @@ vectorType ModelOverlay::mapJoints(mapFunction<itemType> function) const {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} isGroupCulled=false - If <code>true</code>, the mesh parts of the model are LOD culled as a group.
|
* @property {boolean} isGroupCulled=false - If <code>true</code>, the mesh parts of the model are LOD culled as a group.
|
||||||
|
@ -510,17 +509,26 @@ QVariant ModelOverlay::getProperty(const QString& property) {
|
||||||
|
|
||||||
bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||||
|
|
||||||
QVariantMap extraInfo;
|
QVariantMap extraInfo;
|
||||||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking);
|
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) {
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) {
|
||||||
|
|
||||||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking);
|
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ModelOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||||
|
QVariantMap extraInfo;
|
||||||
|
return _model->findParabolaIntersectionAgainstSubMeshes(origin, velocity, acceleration, parabolicDistance, face, surfaceNormal, extraInfo, precisionPicking);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModelOverlay::findParabolaIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) {
|
||||||
|
return _model->findParabolaIntersectionAgainstSubMeshes(origin, velocity, acceleration, parabolicDistance, face, surfaceNormal, extraInfo, precisionPicking);
|
||||||
|
}
|
||||||
|
|
||||||
ModelOverlay* ModelOverlay::createClone() const {
|
ModelOverlay* ModelOverlay::createClone() const {
|
||||||
return new ModelOverlay(this);
|
return new ModelOverlay(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,11 @@ public:
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) override;
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) override;
|
||||||
|
virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance,
|
||||||
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
|
virtual bool findParabolaIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance,
|
||||||
|
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) override;
|
||||||
|
|
||||||
virtual ModelOverlay* createClone() const override;
|
virtual ModelOverlay* createClone() const override;
|
||||||
|
|
||||||
|
|
|
@ -546,7 +546,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thisOverlay && thisOverlay->getVisible() && !thisOverlay->getIgnoreRayIntersection() && thisOverlay->isLoaded()) {
|
if (thisOverlay && thisOverlay->getVisible() && !thisOverlay->getIgnorePickIntersection() && thisOverlay->isLoaded()) {
|
||||||
float thisDistance;
|
float thisDistance;
|
||||||
BoxFace thisFace;
|
BoxFace thisFace;
|
||||||
glm::vec3 thisSurfaceNormal;
|
glm::vec3 thisSurfaceNormal;
|
||||||
|
@ -573,6 +573,54 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParabolaToOverlayIntersectionResult Overlays::findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking,
|
||||||
|
const QVector<OverlayID>& overlaysToInclude,
|
||||||
|
const QVector<OverlayID>& overlaysToDiscard,
|
||||||
|
bool visibleOnly, bool collidableOnly) {
|
||||||
|
float bestDistance = std::numeric_limits<float>::max();
|
||||||
|
bool bestIsFront = false;
|
||||||
|
|
||||||
|
QMutexLocker locker(&_mutex);
|
||||||
|
ParabolaToOverlayIntersectionResult result;
|
||||||
|
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
OverlayID thisID = i.key();
|
||||||
|
auto thisOverlay = std::dynamic_pointer_cast<Base3DOverlay>(i.value());
|
||||||
|
|
||||||
|
if ((overlaysToDiscard.size() > 0 && overlaysToDiscard.contains(thisID)) ||
|
||||||
|
(overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thisOverlay && thisOverlay->getVisible() && !thisOverlay->getIgnorePickIntersection() && thisOverlay->isLoaded()) {
|
||||||
|
float thisDistance;
|
||||||
|
BoxFace thisFace;
|
||||||
|
glm::vec3 thisSurfaceNormal;
|
||||||
|
QVariantMap thisExtraInfo;
|
||||||
|
if (thisOverlay->findParabolaIntersectionExtraInfo(parabola.origin, parabola.velocity, parabola.acceleration, thisDistance,
|
||||||
|
thisFace, thisSurfaceNormal, thisExtraInfo, precisionPicking)) {
|
||||||
|
bool isDrawInFront = thisOverlay->getDrawInFront();
|
||||||
|
if ((bestIsFront && isDrawInFront && thisDistance < bestDistance)
|
||||||
|
|| (!bestIsFront && (isDrawInFront || thisDistance < bestDistance))) {
|
||||||
|
|
||||||
|
bestIsFront = isDrawInFront;
|
||||||
|
bestDistance = thisDistance;
|
||||||
|
result.intersects = true;
|
||||||
|
result.parabolicDistance = thisDistance;
|
||||||
|
result.face = thisFace;
|
||||||
|
result.surfaceNormal = thisSurfaceNormal;
|
||||||
|
result.overlayID = thisID;
|
||||||
|
result.intersection = parabola.origin + parabola.velocity * thisDistance + 0.5f * parabola.acceleration * thisDistance * thisDistance;
|
||||||
|
result.distance = glm::distance(result.intersection, parabola.origin);
|
||||||
|
result.extraInfo = thisExtraInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
|
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
|
||||||
auto obj = engine->newObject();
|
auto obj = engine->newObject();
|
||||||
obj.setProperty("intersects", value.intersects);
|
obj.setProperty("intersects", value.intersects);
|
||||||
|
@ -1046,7 +1094,8 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
||||||
i.next();
|
i.next();
|
||||||
OverlayID thisID = i.key();
|
OverlayID thisID = i.key();
|
||||||
auto overlay = std::dynamic_pointer_cast<Volume3DOverlay>(i.value());
|
auto overlay = std::dynamic_pointer_cast<Volume3DOverlay>(i.value());
|
||||||
if (overlay && overlay->getVisible() && !overlay->getIgnoreRayIntersection() && overlay->isLoaded()) {
|
// FIXME: this ignores overlays with ignorePickIntersection == true, which seems wrong
|
||||||
|
if (overlay && overlay->getVisible() && !overlay->getIgnorePickIntersection() && overlay->isLoaded()) {
|
||||||
// get AABox in frame of overlay
|
// get AABox in frame of overlay
|
||||||
glm::vec3 dimensions = overlay->getDimensions();
|
glm::vec3 dimensions = overlay->getDimensions();
|
||||||
glm::vec3 low = dimensions * -0.5f;
|
glm::vec3 low = dimensions * -0.5f;
|
||||||
|
|
|
@ -119,6 +119,11 @@ public:
|
||||||
const QVector<OverlayID>& overlaysToDiscard,
|
const QVector<OverlayID>& overlaysToDiscard,
|
||||||
bool visibleOnly = false, bool collidableOnly = false);
|
bool visibleOnly = false, bool collidableOnly = false);
|
||||||
|
|
||||||
|
ParabolaToOverlayIntersectionResult findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking,
|
||||||
|
const QVector<OverlayID>& overlaysToInclude,
|
||||||
|
const QVector<OverlayID>& overlaysToDiscard,
|
||||||
|
bool visibleOnly = false, bool collidableOnly = false);
|
||||||
|
|
||||||
bool mousePressEvent(QMouseEvent* event);
|
bool mousePressEvent(QMouseEvent* event);
|
||||||
bool mouseDoublePressEvent(QMouseEvent* event);
|
bool mouseDoublePressEvent(QMouseEvent* event);
|
||||||
bool mouseReleaseEvent(QMouseEvent* event);
|
bool mouseReleaseEvent(QMouseEvent* event);
|
||||||
|
|
|
@ -72,8 +72,47 @@ QVariant Planar3DOverlay::getProperty(const QString& property) {
|
||||||
|
|
||||||
bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||||
// FIXME - face and surfaceNormal not being returned
|
glm::vec2 xyDimensions = getDimensions();
|
||||||
return findRayRectangleIntersection(origin, direction, getWorldOrientation(), getWorldPosition(), getDimensions(), distance);
|
glm::quat rotation = getWorldOrientation();
|
||||||
|
glm::vec3 position = getWorldPosition();
|
||||||
|
|
||||||
|
if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) {
|
||||||
|
glm::vec3 forward = rotation * Vectors::FRONT;
|
||||||
|
if (glm::dot(forward, direction) > 0.0f) {
|
||||||
|
face = MAX_Z_FACE;
|
||||||
|
surfaceNormal = -forward;
|
||||||
|
} else {
|
||||||
|
face = MIN_Z_FACE;
|
||||||
|
surfaceNormal = forward;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Planar3DOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||||
|
glm::vec2 xyDimensions = getDimensions();
|
||||||
|
glm::quat rotation = getWorldOrientation();
|
||||||
|
glm::vec3 position = getWorldPosition();
|
||||||
|
|
||||||
|
glm::quat inverseRot = glm::inverse(rotation);
|
||||||
|
glm::vec3 localOrigin = inverseRot * (origin - position);
|
||||||
|
glm::vec3 localVelocity = inverseRot * velocity;
|
||||||
|
glm::vec3 localAcceleration = inverseRot * acceleration;
|
||||||
|
|
||||||
|
if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, xyDimensions, parabolicDistance)) {
|
||||||
|
float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance;
|
||||||
|
if (localIntersectionVelocityZ > 0.0f) {
|
||||||
|
face = MIN_Z_FACE;
|
||||||
|
surfaceNormal = rotation * Vectors::FRONT;
|
||||||
|
} else {
|
||||||
|
face = MAX_Z_FACE;
|
||||||
|
surfaceNormal = rotation * -Vectors::FRONT;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform Planar3DOverlay::evalRenderTransform() {
|
Transform Planar3DOverlay::evalRenderTransform() {
|
||||||
|
|
|
@ -32,6 +32,8 @@ public:
|
||||||
|
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
|
virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
glm::vec2 _dimensions;
|
glm::vec2 _dimensions;
|
||||||
|
|
|
@ -140,8 +140,7 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
|
|
@ -160,8 +160,7 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
|
|
@ -60,8 +60,7 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
|
|
@ -229,8 +229,7 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
|
|
@ -91,6 +91,23 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve
|
||||||
return _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face, surfaceNormal);
|
return _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face, surfaceNormal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Volume3DOverlay::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||||
|
// extents is the entity relative, scaled, centered extents of the entity
|
||||||
|
glm::mat4 worldToEntityMatrix;
|
||||||
|
Transform transform = getTransform();
|
||||||
|
transform.setScale(1.0f); // ignore any inherited scale from SpatiallyNestable
|
||||||
|
transform.getInverseMatrix(worldToEntityMatrix);
|
||||||
|
|
||||||
|
glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
|
||||||
|
glm::vec3 overlayFrameVelocity = glm::vec3(worldToEntityMatrix * glm::vec4(velocity, 0.0f));
|
||||||
|
glm::vec3 overlayFrameAcceleration = glm::vec3(worldToEntityMatrix * glm::vec4(acceleration, 0.0f));
|
||||||
|
|
||||||
|
// we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame
|
||||||
|
// and testing intersection there.
|
||||||
|
return _localBoundingBox.findParabolaIntersection(overlayFrameOrigin, overlayFrameVelocity, overlayFrameAcceleration, parabolicDistance, face, surfaceNormal);
|
||||||
|
}
|
||||||
|
|
||||||
Transform Volume3DOverlay::evalRenderTransform() {
|
Transform Volume3DOverlay::evalRenderTransform() {
|
||||||
Transform transform = getTransform();
|
Transform transform = getTransform();
|
||||||
#ifndef USE_SN_SCALE
|
#ifndef USE_SN_SCALE
|
||||||
|
|
|
@ -32,6 +32,8 @@ public:
|
||||||
|
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
|
virtual bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance,
|
||||||
|
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Centered local bounding box
|
// Centered local bounding box
|
||||||
|
|
|
@ -539,8 +539,7 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||||
* <code>dashed</code>.
|
* <code>dashed</code>.
|
||||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
|
||||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||||
|
@ -623,20 +622,6 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
|
||||||
glm::vec2 dimensions = getDimensions();
|
|
||||||
glm::quat rotation = getWorldOrientation();
|
|
||||||
glm::vec3 position = getWorldPosition();
|
|
||||||
|
|
||||||
if (findRayRectangleIntersection(origin, direction, rotation, position, dimensions, distance)) {
|
|
||||||
surfaceNormal = rotation * Vectors::UNIT_Z;
|
|
||||||
face = glm::dot(surfaceNormal, direction) > 0 ? MIN_Z_FACE : MAX_Z_FACE;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Web3DOverlay* Web3DOverlay::createClone() const {
|
Web3DOverlay* Web3DOverlay::createClone() const {
|
||||||
return new Web3DOverlay(this);
|
return new Web3DOverlay(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,9 +52,6 @@ public:
|
||||||
void setProperties(const QVariantMap& properties) override;
|
void setProperties(const QVariantMap& properties) override;
|
||||||
QVariant getProperty(const QString& property) override;
|
QVariant getProperty(const QString& property) override;
|
||||||
|
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
|
||||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
|
||||||
|
|
||||||
virtual Web3DOverlay* createClone() const override;
|
virtual Web3DOverlay* createClone() const override;
|
||||||
|
|
||||||
enum InputMode {
|
enum InputMode {
|
||||||
|
|
|
@ -1563,6 +1563,7 @@ void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
|
void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
|
||||||
|
// FIXME: this doesn't take into account Avatar rotation
|
||||||
ShapeInfo shapeInfo;
|
ShapeInfo shapeInfo;
|
||||||
computeShapeInfo(shapeInfo);
|
computeShapeInfo(shapeInfo);
|
||||||
glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight
|
glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include <CursorManager.h>
|
#include <CursorManager.h>
|
||||||
#include <gl/GLWidget.h>
|
#include <gl/GLWidget.h>
|
||||||
|
|
||||||
|
#include "GeometryUtil.h"
|
||||||
|
|
||||||
// Used to animate the magnification windows
|
// Used to animate the magnification windows
|
||||||
|
|
||||||
//static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
//static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
||||||
|
@ -357,9 +359,9 @@ bool CompositorHelper::calculateRayUICollisionPoint(const glm::vec3& position, c
|
||||||
glm::vec3 localDirection = glm::normalize(transformVectorFast(worldToUi, direction));
|
glm::vec3 localDirection = glm::normalize(transformVectorFast(worldToUi, direction));
|
||||||
|
|
||||||
const float UI_RADIUS = 1.0f;
|
const float UI_RADIUS = 1.0f;
|
||||||
float instersectionDistance;
|
float intersectionDistance;
|
||||||
if (raySphereIntersect(localDirection, localPosition, UI_RADIUS, &instersectionDistance)) {
|
if (raySphereIntersect(localDirection, localPosition, UI_RADIUS, &intersectionDistance)) {
|
||||||
result = transformPoint(uiToWorld, localPosition + localDirection * instersectionDistance);
|
result = transformPoint(uiToWorld, localPosition + localDirection * intersectionDistance);
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
DebugDraw::getInstance().drawRay(position, result, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
|
DebugDraw::getInstance().drawRay(position, result, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
|
||||||
#endif
|
#endif
|
||||||
|
@ -372,6 +374,23 @@ bool CompositorHelper::calculateRayUICollisionPoint(const glm::vec3& position, c
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CompositorHelper::calculateParabolaUICollisionPoint(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, glm::vec3& result, float& parabolicDistance) const {
|
||||||
|
glm::mat4 uiToWorld = getUiTransform();
|
||||||
|
glm::mat4 worldToUi = glm::inverse(uiToWorld);
|
||||||
|
glm::vec3 localOrigin = transformPoint(worldToUi, origin);
|
||||||
|
glm::vec3 localVelocity = glm::normalize(transformVectorFast(worldToUi, velocity));
|
||||||
|
glm::vec3 localAcceleration = glm::normalize(transformVectorFast(worldToUi, acceleration));
|
||||||
|
|
||||||
|
const float UI_RADIUS = 1.0f;
|
||||||
|
float intersectionDistance;
|
||||||
|
if (findParabolaSphereIntersection(localOrigin, localVelocity, localAcceleration, glm::vec3(0.0f), UI_RADIUS, intersectionDistance)) {
|
||||||
|
result = origin + velocity * intersectionDistance + 0.5f * acceleration * intersectionDistance * intersectionDistance;
|
||||||
|
parabolicDistance = intersectionDistance;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec2 CompositorHelper::sphericalToOverlay(const glm::vec2& sphericalPos) const {
|
glm::vec2 CompositorHelper::sphericalToOverlay(const glm::vec2& sphericalPos) const {
|
||||||
glm::vec2 result = sphericalPos;
|
glm::vec2 result = sphericalPos;
|
||||||
result.x *= -1.0f;
|
result.x *= -1.0f;
|
||||||
|
|
|
@ -52,6 +52,7 @@ public:
|
||||||
void setRenderingWidget(QWidget* widget) { _renderingWidget = widget; }
|
void setRenderingWidget(QWidget* widget) { _renderingWidget = widget; }
|
||||||
|
|
||||||
bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
|
bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
|
||||||
|
bool calculateParabolaUICollisionPoint(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, glm::vec3& result, float& parabolicDistance) const;
|
||||||
|
|
||||||
bool isHMD() const;
|
bool isHMD() const;
|
||||||
bool fakeEventActive() const { return _fakeMouseEvent; }
|
bool fakeEventActive() const { return _fakeMouseEvent; }
|
||||||
|
|
|
@ -139,15 +139,14 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
|
||||||
glm::vec3 forward = rotation * Vectors::FRONT;
|
glm::vec3 forward = rotation * Vectors::FRONT;
|
||||||
if (glm::dot(forward, direction) > 0.0f) {
|
if (glm::dot(forward, direction) > 0.0f) {
|
||||||
face = MAX_Z_FACE;
|
face = MAX_Z_FACE;
|
||||||
surfaceNormal = rotation * -Vectors::FRONT;
|
surfaceNormal = -forward;
|
||||||
} else {
|
} else {
|
||||||
face = MIN_Z_FACE;
|
face = MIN_Z_FACE;
|
||||||
surfaceNormal = rotation * Vectors::FRONT;
|
surfaceNormal = forward;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
bool TextEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
@ -174,9 +173,8 @@ bool TextEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, c
|
||||||
surfaceNormal = rotation * -Vectors::FRONT;
|
surfaceNormal = rotation * -Vectors::FRONT;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEntityItem::setText(const QString& value) {
|
void TextEntityItem::setText(const QString& value) {
|
||||||
|
|
|
@ -116,10 +116,10 @@ bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const g
|
||||||
glm::vec3 forward = rotation * Vectors::FRONT;
|
glm::vec3 forward = rotation * Vectors::FRONT;
|
||||||
if (glm::dot(forward, direction) > 0.0f) {
|
if (glm::dot(forward, direction) > 0.0f) {
|
||||||
face = MAX_Z_FACE;
|
face = MAX_Z_FACE;
|
||||||
surfaceNormal = rotation * -Vectors::FRONT;
|
surfaceNormal = -forward;
|
||||||
} else {
|
} else {
|
||||||
face = MIN_Z_FACE;
|
face = MIN_Z_FACE;
|
||||||
surfaceNormal = rotation * Vectors::FRONT;
|
surfaceNormal = forward;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -303,68 +303,123 @@ bool AABox::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& v
|
||||||
// Solve the intersection for each face of the cube. As we go, keep track of the smallest, positive, real distance
|
// 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
|
// that is within the bounds of the other two dimensions
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
// TODO: handle case where a is 0
|
if (fabsf(acceleration[i]) < EPSILON) {
|
||||||
a = 0.5f * acceleration[i];
|
// Handle the degenerate case where we only have a line in this axis
|
||||||
b = velocity[i];
|
if (origin[i] < _corner[i]) {
|
||||||
if (origin[i] < _corner[i]) {
|
{ // min
|
||||||
// If we're below _corner, we only need to check the min face
|
if (velocity[i] > 0.0f) {
|
||||||
{ // min
|
float possibleDistance = (_corner[i] - origin[i]) / velocity[i];
|
||||||
c = origin[i] - _corner[i];
|
bool hit = false;
|
||||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
if (hit) {
|
||||||
bool hit = false;
|
minFace = BoxFace(2 * i);
|
||||||
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
minNormal = glm::vec3(0.0f);
|
||||||
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
minNormal[i] = -1.0f;
|
||||||
if (hit) {
|
}
|
||||||
minFace = BoxFace(2 * i);
|
|
||||||
minNormal = glm::vec3(0.0f);
|
|
||||||
minNormal[i] = -1.0f;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (origin[i] > _corner[i] + _scale[i]) {
|
||||||
} else if (origin[i] > _corner[i] + _scale[i]) {
|
{ // max
|
||||||
// If we're above _corner + _scale, we only need to check the max face
|
if (velocity[i] < 0.0f) {
|
||||||
{ // max
|
float possibleDistance = (_corner[i] + _scale[i] - origin[i]) / velocity[i];
|
||||||
c = origin[i] - (_corner[i] + _scale[i]);
|
bool hit = false;
|
||||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
if (hit) {
|
||||||
bool hit = false;
|
minFace = BoxFace(2 * i + 1);
|
||||||
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
minNormal = glm::vec3(0.0f);
|
||||||
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
minNormal[i] = 1.0f;
|
||||||
if (hit) {
|
}
|
||||||
minFace = BoxFace(2 * i + 1);
|
}
|
||||||
minNormal = glm::vec3(0.0f);
|
}
|
||||||
minNormal[i] = 1.0f;
|
} else {
|
||||||
|
{ // min
|
||||||
|
if (velocity[i] < 0.0f) {
|
||||||
|
float possibleDistance = (_corner[i] - origin[i]) / velocity[i];
|
||||||
|
bool hit = false;
|
||||||
|
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i + 1);
|
||||||
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ // max
|
||||||
|
if (velocity[i] > 0.0f) {
|
||||||
|
float possibleDistance = (_corner[i] + _scale[i] - origin[i]) / velocity[i];
|
||||||
|
bool hit = false;
|
||||||
|
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i);
|
||||||
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = -1.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If we're inside on this axis, we could hit either face depending on our velocity and acceleration, so we need to check both
|
a = 0.5f * acceleration[i];
|
||||||
{ // min
|
b = velocity[i];
|
||||||
c = origin[i] - _corner[i];
|
if (origin[i] < _corner[i]) {
|
||||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
// If we're below _corner, we only need to check the min face
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
{ // min
|
||||||
bool hit = false;
|
c = origin[i] - _corner[i];
|
||||||
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||||
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
if (hit) {
|
bool hit = false;
|
||||||
minFace = BoxFace(2 * i);
|
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
minNormal = glm::vec3(0.0f);
|
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
minNormal[i] = -1.0f;
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i);
|
||||||
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = -1.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (origin[i] > _corner[i] + _scale[i]) {
|
||||||
{ // max
|
// If we're above _corner + _scale, we only need to check the max face
|
||||||
c = origin[i] - (_corner[i] + _scale[i]);
|
{ // max
|
||||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
c = origin[i] - (_corner[i] + _scale[i]);
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||||
bool hit = false;
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
bool hit = false;
|
||||||
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
if (hit) {
|
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
minFace = BoxFace(2 * i + 1);
|
if (hit) {
|
||||||
minNormal = glm::vec3(0.0f);
|
minFace = BoxFace(2 * i + 1);
|
||||||
minNormal[i] = 1.0f;
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we're inside on this axis, we could hit either face depending on our velocity and acceleration, so we need to check both
|
||||||
|
{ // min
|
||||||
|
c = origin[i] - _corner[i];
|
||||||
|
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||||
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
|
bool hit = false;
|
||||||
|
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i + 1);
|
||||||
|
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.x, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i);
|
||||||
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = -1.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,14 +454,29 @@ bool AABox::parabolaPlaneIntersectsBoundingSphere(const glm::vec3& origin, const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the normal of the plane, the cross product of two vectors on the plane
|
float velocityLength2 = glm::length2(velocity);
|
||||||
// Assumes: velocity and acceleration != 0 and normalize(velocity) != normalize(acceleration)
|
if (glm::length2(acceleration) < EPSILON) {
|
||||||
glm::vec3 normal = glm::normalize(glm::cross(velocity, acceleration));
|
if (velocityLength2 < EPSILON) {
|
||||||
|
// No intersection if velocity == acceleration == (0, 0, 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Handle the degenerate case where acceleration == (0, 0, 0)
|
||||||
|
return rayHitsBoundingSphere(origin, glm::normalize(velocity));
|
||||||
|
} else {
|
||||||
|
glm::vec3 vectorOnPlane = velocity;
|
||||||
|
if (glm::dot(glm::normalize(velocity), glm::normalize(acceleration)) > 1.0f - EPSILON) {
|
||||||
|
// Handle the degenerate case where velocity is parallel to acceleration
|
||||||
|
// We pick t = 1 and calculate a second point on the plane
|
||||||
|
vectorOnPlane = velocity + 0.5f * acceleration;
|
||||||
|
}
|
||||||
|
// Get the normal of the plane, the cross product of two vectors on the plane
|
||||||
|
glm::vec3 normal = glm::normalize(glm::cross(vectorOnPlane, acceleration));
|
||||||
|
|
||||||
// Project vector from plane to sphere center onto the normal
|
// Project vector from plane to sphere center onto the normal
|
||||||
float distance = glm::dot(localCenter, normal);
|
float distance = glm::dot(localCenter, normal);
|
||||||
if (distance * distance < radiusSquared) {
|
if (distance * distance < radiusSquared) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,7 +289,7 @@ void AACube::checkPossibleParabolicIntersection(float t, int i, float& minDistan
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AACube::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
bool AACube::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
|
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
|
||||||
float minDistance = FLT_MAX;
|
float minDistance = FLT_MAX;
|
||||||
BoxFace minFace;
|
BoxFace minFace;
|
||||||
glm::vec3 minNormal;
|
glm::vec3 minNormal;
|
||||||
|
@ -299,68 +299,123 @@ bool AACube::findParabolaIntersection(const glm::vec3& origin, const glm::vec3&
|
||||||
// Solve the intersection for each face of the cube. As we go, keep track of the smallest, positive, real distance
|
// 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
|
// that is within the bounds of the other two dimensions
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
// TODO: handle case where a is 0
|
if (fabsf(acceleration[i]) < EPSILON) {
|
||||||
a = 0.5f * acceleration[i];
|
// Handle the degenerate case where we only have a line in this axis
|
||||||
b = velocity[i];
|
if (origin[i] < _corner[i]) {
|
||||||
if (origin[i] < _corner[i]) {
|
{ // min
|
||||||
// If we're below _corner, we only need to check the min face
|
if (velocity[i] > 0.0f) {
|
||||||
{ // min
|
float possibleDistance = (_corner[i] - origin[i]) / velocity[i];
|
||||||
c = origin[i] - _corner[i];
|
bool hit = false;
|
||||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
if (hit) {
|
||||||
bool hit = false;
|
minFace = BoxFace(2 * i);
|
||||||
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
minNormal = glm::vec3(0.0f);
|
||||||
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
minNormal[i] = -1.0f;
|
||||||
if (hit) {
|
}
|
||||||
minFace = BoxFace(2 * i);
|
|
||||||
minNormal = glm::vec3(0.0f);
|
|
||||||
minNormal[i] = -1.0f;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (origin[i] > _corner[i] + _scale) {
|
||||||
} else if (origin[i] > _corner[i] + _scale) {
|
{ // max
|
||||||
// If we're above _corner + _scale, we only need to check the max face
|
if (velocity[i] < 0.0f) {
|
||||||
{ // max
|
float possibleDistance = (_corner[i] + _scale - origin[i]) / velocity[i];
|
||||||
c = origin[i] - (_corner[i] + _scale);
|
bool hit = false;
|
||||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
if (hit) {
|
||||||
bool hit = false;
|
minFace = BoxFace(2 * i + 1);
|
||||||
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
minNormal = glm::vec3(0.0f);
|
||||||
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
minNormal[i] = 1.0f;
|
||||||
if (hit) {
|
}
|
||||||
minFace = BoxFace(2 * i + 1);
|
}
|
||||||
minNormal = glm::vec3(0.0f);
|
}
|
||||||
minNormal[i] = 1.0f;
|
} else {
|
||||||
|
{ // min
|
||||||
|
if (velocity[i] < 0.0f) {
|
||||||
|
float possibleDistance = (_corner[i] - origin[i]) / velocity[i];
|
||||||
|
bool hit = false;
|
||||||
|
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i + 1);
|
||||||
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ // max
|
||||||
|
if (velocity[i] > 0.0f) {
|
||||||
|
float possibleDistance = (_corner[i] + _scale - origin[i]) / velocity[i];
|
||||||
|
bool hit = false;
|
||||||
|
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i);
|
||||||
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = -1.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If we're inside on this axis, we could hit either face depending on our velocity and acceleration, so we need to check both
|
a = 0.5f * acceleration[i];
|
||||||
{ // min
|
b = velocity[i];
|
||||||
c = origin[i] - _corner[i];
|
if (origin[i] < _corner[i]) {
|
||||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
// If we're below _corner, we only need to check the min face
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
{ // min
|
||||||
bool hit = false;
|
c = origin[i] - _corner[i];
|
||||||
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||||
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
if (hit) {
|
bool hit = false;
|
||||||
minFace = BoxFace(2 * i);
|
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
minNormal = glm::vec3(0.0f);
|
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
minNormal[i] = -1.0f;
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i);
|
||||||
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = -1.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (origin[i] > _corner[i] + _scale) {
|
||||||
{ // max
|
// If we're above _corner + _scale, we only need to check the max face
|
||||||
c = origin[i] - (_corner[i] + _scale);
|
{ // max
|
||||||
possibleDistances = { FLT_MAX, FLT_MAX };
|
c = origin[i] - (_corner[i] + _scale);
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||||
bool hit = false;
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
bool hit = false;
|
||||||
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
if (hit) {
|
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
minFace = BoxFace(2 * i + 1);
|
if (hit) {
|
||||||
minNormal = glm::vec3(0.0f);
|
minFace = BoxFace(2 * i + 1);
|
||||||
minNormal[i] = 1.0f;
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we're inside on this axis, we could hit either face depending on our velocity and acceleration, so we need to check both
|
||||||
|
{ // min
|
||||||
|
c = origin[i] - _corner[i];
|
||||||
|
possibleDistances = { FLT_MAX, FLT_MAX };
|
||||||
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
|
bool hit = false;
|
||||||
|
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i + 1);
|
||||||
|
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.x, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
|
||||||
|
if (hit) {
|
||||||
|
minFace = BoxFace(2 * i);
|
||||||
|
minNormal = glm::vec3(0.0f);
|
||||||
|
minNormal[i] = -1.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -734,13 +734,21 @@ bool findParabolaRectangleIntersection(const glm::vec3& origin, const glm::vec3&
|
||||||
glm::vec2 localCorner = -0.5f * dimensions;
|
glm::vec2 localCorner = -0.5f * dimensions;
|
||||||
|
|
||||||
float minDistance = FLT_MAX;
|
float minDistance = FLT_MAX;
|
||||||
float a = 0.5f * acceleration.z;
|
if (fabsf(acceleration.z) < EPSILON) {
|
||||||
float b = velocity.z;
|
// Handle the degenerate case where we only have a line in the z-axis
|
||||||
float c = origin.z;
|
if (fabsf(velocity.z) > EPSILON) {
|
||||||
glm::vec2 possibleDistances = { FLT_MAX, FLT_MAX };
|
float possibleDistance = -origin.z / velocity.z;
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
checkPossibleParabolicIntersectionWithZPlane(possibleDistance, minDistance, origin, velocity, acceleration, localCorner, dimensions);
|
||||||
checkPossibleParabolicIntersectionWithZPlane(possibleDistances.x, minDistance, origin, velocity, acceleration, localCorner, dimensions);
|
}
|
||||||
checkPossibleParabolicIntersectionWithZPlane(possibleDistances.y, minDistance, origin, velocity, acceleration, localCorner, dimensions);
|
} else {
|
||||||
|
float a = 0.5f * acceleration.z;
|
||||||
|
float b = velocity.z;
|
||||||
|
float c = origin.z;
|
||||||
|
glm::vec2 possibleDistances = { FLT_MAX, FLT_MAX };
|
||||||
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
|
checkPossibleParabolicIntersectionWithZPlane(possibleDistances.x, minDistance, origin, velocity, acceleration, localCorner, dimensions);
|
||||||
|
checkPossibleParabolicIntersectionWithZPlane(possibleDistances.y, minDistance, origin, velocity, acceleration, localCorner, dimensions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (minDistance < FLT_MAX) {
|
if (minDistance < FLT_MAX) {
|
||||||
parabolicDistance = minDistance;
|
parabolicDistance = minDistance;
|
||||||
|
@ -754,43 +762,72 @@ bool findParabolaSphereIntersection(const glm::vec3& origin, const glm::vec3& ve
|
||||||
glm::vec3 localCenter = center - origin;
|
glm::vec3 localCenter = center - origin;
|
||||||
float radiusSquared = radius * radius;
|
float radiusSquared = radius * radius;
|
||||||
|
|
||||||
// Get the normal of the plane, the cross product of two vectors on the plane
|
float velocityLength2 = glm::length2(velocity);
|
||||||
// Assumes: velocity and acceleration != 0 and normalize(velocity) != normalize(acceleration)
|
float accelerationLength = glm::length(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);
|
|
||||||
// Exit early if the sphere doesn't intersect the plane defined by the parabola
|
|
||||||
if (fabsf(distance) > radius) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 circleCenter = center - distance * normal;
|
|
||||||
float circleRadius = sqrtf(radiusSquared - distance * distance);
|
|
||||||
glm::vec3 q = glm::normalize(acceleration);
|
|
||||||
glm::vec3 p = glm::cross(normal, q);
|
|
||||||
|
|
||||||
float a1 = glm::length(acceleration) * 0.5f;
|
|
||||||
float b1 = glm::dot(velocity, q);
|
|
||||||
float c1 = glm::dot(origin - circleCenter, q);
|
|
||||||
float a2 = glm::dot(velocity, p);
|
|
||||||
float b2 = glm::dot(origin - circleCenter, p);
|
|
||||||
|
|
||||||
float a = a1 * a1;
|
|
||||||
float b = 2.0f * a1 * b1;
|
|
||||||
float c = 2.0f * a1 * c1 + b1 * b1 + a2 * a2;
|
|
||||||
float d = 2.0f * b1 * c1 + 2.0f * a2 * b2;
|
|
||||||
float e = c1 * c1 + b2 * b2 - circleRadius * circleRadius;
|
|
||||||
|
|
||||||
float minDistance = FLT_MAX;
|
float minDistance = FLT_MAX;
|
||||||
glm::vec4 possibleDistances(FLT_MAX);
|
|
||||||
if (computeRealQuarticRoots(a, b, c, d, e, possibleDistances)) {
|
if (accelerationLength < EPSILON) {
|
||||||
for (int i = 0; i < 4; i++) {
|
if (velocityLength2 < EPSILON) {
|
||||||
if (possibleDistances[i] < minDistance && possibleDistances[i] > 0.0f) {
|
// No intersection if velocity == acceleration == (0, 0, 0)
|
||||||
minDistance = possibleDistances[i];
|
return false;
|
||||||
|
}
|
||||||
|
// Handle the degenerate case where acceleration == (0, 0, 0)
|
||||||
|
glm::vec3 offset = origin - center;
|
||||||
|
float a = glm::dot(velocity, velocity);
|
||||||
|
float b = 2.0f * glm::dot(velocity, offset);
|
||||||
|
float c = glm::dot(offset, offset) - radius * radius;
|
||||||
|
glm::vec2 possibleDistances(FLT_MAX);
|
||||||
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
if (possibleDistances[i] < minDistance && possibleDistances[i] > 0.0f) {
|
||||||
|
minDistance = possibleDistances[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
glm::vec3 vectorOnPlane = velocity;
|
||||||
|
if (fabsf(glm::dot(glm::normalize(velocity), glm::normalize(acceleration))) > 1.0f - EPSILON) {
|
||||||
|
// Handle the degenerate case where velocity is parallel to acceleration
|
||||||
|
// We pick t = 1 and calculate a second point on the plane
|
||||||
|
vectorOnPlane = velocity + 0.5f * acceleration;
|
||||||
|
}
|
||||||
|
// Get the normal of the plane, the cross product of two vectors on the plane
|
||||||
|
glm::vec3 normal = glm::normalize(glm::cross(vectorOnPlane, acceleration));
|
||||||
|
|
||||||
|
// Project vector from plane to sphere center onto the normal
|
||||||
|
float distance = glm::dot(localCenter, normal);
|
||||||
|
// Exit early if the sphere doesn't intersect the plane defined by the parabola
|
||||||
|
if (fabsf(distance) > radius) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 circleCenter = center - distance * normal;
|
||||||
|
float circleRadius = sqrtf(radiusSquared - distance * distance);
|
||||||
|
glm::vec3 q = glm::normalize(acceleration);
|
||||||
|
glm::vec3 p = glm::cross(normal, q);
|
||||||
|
|
||||||
|
float a1 = accelerationLength * 0.5f;
|
||||||
|
float b1 = glm::dot(velocity, q);
|
||||||
|
float c1 = glm::dot(origin - circleCenter, q);
|
||||||
|
float a2 = glm::dot(velocity, p);
|
||||||
|
float b2 = glm::dot(origin - circleCenter, p);
|
||||||
|
|
||||||
|
float a = a1 * a1;
|
||||||
|
float b = 2.0f * a1 * b1;
|
||||||
|
float c = 2.0f * a1 * c1 + b1 * b1 + a2 * a2;
|
||||||
|
float d = 2.0f * b1 * c1 + 2.0f * a2 * b2;
|
||||||
|
float e = c1 * c1 + b2 * b2 - circleRadius * circleRadius;
|
||||||
|
|
||||||
|
glm::vec4 possibleDistances(FLT_MAX);
|
||||||
|
if (computeRealQuarticRoots(a, b, c, d, e, possibleDistances)) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (possibleDistances[i] < minDistance && possibleDistances[i] > 0.0f) {
|
||||||
|
minDistance = possibleDistances[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minDistance < FLT_MAX) {
|
if (minDistance < FLT_MAX) {
|
||||||
parabolicDistance = minDistance;
|
parabolicDistance = minDistance;
|
||||||
return true;
|
return true;
|
||||||
|
@ -837,15 +874,24 @@ bool findParabolaTriangleIntersection(const glm::vec3& origin, const glm::vec3&
|
||||||
glm::vec3 localAcceleration = inverseRot * acceleration;
|
glm::vec3 localAcceleration = inverseRot * acceleration;
|
||||||
|
|
||||||
float minDistance = FLT_MAX;
|
float minDistance = FLT_MAX;
|
||||||
float a = 0.5f * localAcceleration.z;
|
if (fabsf(localAcceleration.z) < EPSILON) {
|
||||||
float b = localVelocity.z;
|
if (fabsf(localVelocity.z) < EPSILON) {
|
||||||
float c = localOrigin.z;
|
return false;
|
||||||
glm::vec2 possibleDistances = { FLT_MAX, FLT_MAX };
|
}
|
||||||
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
float possibleDistance = -localOrigin.z / localVelocity.z;
|
||||||
checkPossibleParabolicIntersectionWithTriangle(possibleDistances.x, minDistance, origin, velocity, acceleration,
|
checkPossibleParabolicIntersectionWithTriangle(possibleDistance, minDistance, origin, velocity, acceleration,
|
||||||
localVelocity, localAcceleration, normal, v0, v1, v2, allowBackface);
|
|
||||||
checkPossibleParabolicIntersectionWithTriangle(possibleDistances.y, minDistance, origin, velocity, acceleration,
|
|
||||||
localVelocity, localAcceleration, normal, v0, v1, v2, allowBackface);
|
localVelocity, localAcceleration, normal, v0, v1, v2, allowBackface);
|
||||||
|
} else {
|
||||||
|
float a = 0.5f * localAcceleration.z;
|
||||||
|
float b = localVelocity.z;
|
||||||
|
float c = localOrigin.z;
|
||||||
|
glm::vec2 possibleDistances = { FLT_MAX, FLT_MAX };
|
||||||
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
|
checkPossibleParabolicIntersectionWithTriangle(possibleDistances.x, minDistance, origin, velocity, acceleration,
|
||||||
|
localVelocity, localAcceleration, normal, v0, v1, v2, allowBackface);
|
||||||
|
checkPossibleParabolicIntersectionWithTriangle(possibleDistances.y, minDistance, origin, velocity, acceleration,
|
||||||
|
localVelocity, localAcceleration, normal, v0, v1, v2, allowBackface);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (minDistance < FLT_MAX) {
|
if (minDistance < FLT_MAX) {
|
||||||
parabolicDistance = minDistance;
|
parabolicDistance = minDistance;
|
||||||
|
@ -854,6 +900,85 @@ bool findParabolaTriangleIntersection(const glm::vec3& origin, const glm::vec3&
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool findParabolaCapsuleIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
const glm::vec3& start, const glm::vec3& end, float radius, const glm::quat& rotation, float& parabolicDistance) {
|
||||||
|
if (start == end) {
|
||||||
|
return findParabolaSphereIntersection(origin, velocity, acceleration, start, radius, parabolicDistance); // handle degenerate case
|
||||||
|
}
|
||||||
|
if (glm::distance2(origin, start) < radius * radius) { // inside start sphere
|
||||||
|
float startDistance;
|
||||||
|
bool intersectsStart = findParabolaSphereIntersection(origin, velocity, acceleration, start, radius, startDistance);
|
||||||
|
if (glm::distance2(origin, end) < radius * radius) { // also inside end sphere
|
||||||
|
float endDistance;
|
||||||
|
bool intersectsEnd = findParabolaSphereIntersection(origin, velocity, acceleration, end, radius, endDistance);
|
||||||
|
if (endDistance < startDistance) {
|
||||||
|
parabolicDistance = endDistance;
|
||||||
|
return intersectsEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parabolicDistance = startDistance;
|
||||||
|
return intersectsStart;
|
||||||
|
} else if (glm::distance2(origin, end) < radius * radius) { // inside end sphere (and not start sphere)
|
||||||
|
return findParabolaSphereIntersection(origin, velocity, acceleration, end, radius, parabolicDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are either inside the middle of the capsule or outside it completely
|
||||||
|
// Either way, we need to check all three parts of the capsule and find the closest intersection
|
||||||
|
glm::vec3 results(FLT_MAX);
|
||||||
|
findParabolaSphereIntersection(origin, velocity, acceleration, start, radius, results[0]);
|
||||||
|
findParabolaSphereIntersection(origin, velocity, acceleration, end, radius, results[1]);
|
||||||
|
|
||||||
|
// We rotate the infinite cylinder to be aligned with the y-axis and then cap the values at the end
|
||||||
|
glm::quat inverseRot = glm::inverse(rotation);
|
||||||
|
glm::vec3 localOrigin = inverseRot * (origin - start);
|
||||||
|
glm::vec3 localVelocity = inverseRot * velocity;
|
||||||
|
glm::vec3 localAcceleration = inverseRot * acceleration;
|
||||||
|
float capsuleLength = glm::length(end - start);
|
||||||
|
|
||||||
|
const float MIN_ACCELERATION_PRODUCT = 0.00001f;
|
||||||
|
if (fabsf(localAcceleration.x * localAcceleration.z) < MIN_ACCELERATION_PRODUCT) {
|
||||||
|
// Handle the degenerate case where we only have a line in the XZ plane
|
||||||
|
float a = localVelocity.x * localVelocity.x + localVelocity.z * localVelocity.z;
|
||||||
|
float b = 2.0f * (localVelocity.x * localOrigin.x + localVelocity.z * localOrigin.z);
|
||||||
|
float c = localOrigin.x * localOrigin.x + localOrigin.z * localOrigin.z - radius * radius;
|
||||||
|
glm::vec2 possibleDistances = { FLT_MAX, FLT_MAX };
|
||||||
|
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
if (possibleDistances[i] < results[2] && possibleDistances[i] > 0.0f) {
|
||||||
|
float y = localOrigin.y + localVelocity.y * possibleDistances[i] + 0.5f * localAcceleration.y * possibleDistances[i] * possibleDistances[i];
|
||||||
|
if (y > 0.0f && y < capsuleLength) {
|
||||||
|
results[2] = possibleDistances[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
float a = 0.25f * (localAcceleration.x * localAcceleration.x + localAcceleration.z * localAcceleration.z);
|
||||||
|
float b = localVelocity.x * localAcceleration.x + localVelocity.z * localAcceleration.z;
|
||||||
|
float c = localOrigin.x * localAcceleration.x + localOrigin.z * localAcceleration.z + localVelocity.x * localVelocity.x + localVelocity.z * localVelocity.z;
|
||||||
|
float d = 2.0f * (localOrigin.x * localVelocity.x + localOrigin.z * localVelocity.z);
|
||||||
|
float e = localOrigin.x * localOrigin.x + localOrigin.z * localOrigin.z - radius * radius;
|
||||||
|
glm::vec4 possibleDistances(FLT_MAX);
|
||||||
|
if (computeRealQuarticRoots(a, b, c, d, e, possibleDistances)) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (possibleDistances[i] < results[2] && possibleDistances[i] > 0.0f) {
|
||||||
|
float y = localOrigin.y + localVelocity.y * possibleDistances[i] + 0.5f * localAcceleration.y * possibleDistances[i] * possibleDistances[i];
|
||||||
|
if (y > 0.0f && y < capsuleLength) {
|
||||||
|
results[2] = possibleDistances[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float minDistance = FLT_MAX;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
minDistance = glm::min(minDistance, results[i]);
|
||||||
|
}
|
||||||
|
parabolicDistance = minDistance;
|
||||||
|
return minDistance != FLT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
void swingTwistDecomposition(const glm::quat& rotation,
|
void swingTwistDecomposition(const glm::quat& rotation,
|
||||||
const glm::vec3& direction,
|
const glm::vec3& direction,
|
||||||
glm::quat& swing,
|
glm::quat& swing,
|
||||||
|
@ -1100,7 +1225,7 @@ bool computeRealQuadraticRoots(float a, float b, float c, glm::vec2& roots) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following functions provide an analytical solution to a quartic equation, adapted from the solution here: https://github.com/sasamil/Quartic
|
// The following functions provide an analytical solution to a quartic equation, adapted from the solution here: https://github.com/sasamil/Quartic
|
||||||
unsigned int solveP3(float *x, float a, float b, float c) {
|
unsigned int solveP3(float* x, float a, float b, float c) {
|
||||||
float a2 = a * a;
|
float a2 = a * a;
|
||||||
float q = (a2 - 3.0f * b) / 9.0f;
|
float q = (a2 - 3.0f * b) / 9.0f;
|
||||||
float r = (a * (2.0f * a2 - 9.0f * b) + 27.0f * c) / 54.0f;
|
float r = (a * (2.0f * a2 - 9.0f * b) + 27.0f * c) / 54.0f;
|
||||||
|
@ -1221,11 +1346,5 @@ bool solve_quartic(float a, float b, float c, float d, glm::vec4& roots) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool computeRealQuarticRoots(float a, float b, float c, float d, float e, glm::vec4& roots) {
|
bool computeRealQuarticRoots(float a, float b, float c, float d, float e, glm::vec4& roots) {
|
||||||
a = 1.0f;
|
return solve_quartic(b / a, c / a, d / a, e / a, roots);
|
||||||
b = b / a;
|
|
||||||
c = c / a;
|
|
||||||
d = d / a;
|
|
||||||
e = e / a;
|
|
||||||
|
|
||||||
return solve_quartic(b, c, d, e, roots);
|
|
||||||
}
|
}
|
|
@ -97,6 +97,9 @@ bool findParabolaSphereIntersection(const glm::vec3& origin, const glm::vec3& ve
|
||||||
bool findParabolaTriangleIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
bool findParabolaTriangleIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& parabolicDistance, bool allowBackface = false);
|
const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& parabolicDistance, bool allowBackface = false);
|
||||||
|
|
||||||
|
bool findParabolaCapsuleIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||||
|
const glm::vec3& start, const glm::vec3& end, float radius, const glm::quat& rotation, float& parabolicDistance);
|
||||||
|
|
||||||
/// \brief decomposes rotation into its components such that: rotation = swing * twist
|
/// \brief decomposes rotation into its components such that: rotation = swing * twist
|
||||||
/// \param rotation[in] rotation to decompose
|
/// \param rotation[in] rotation to decompose
|
||||||
/// \param direction[in] normalized axis about which the twist happens (typically original direction before rotation applied)
|
/// \param direction[in] normalized axis about which the twist happens (typically original direction before rotation applied)
|
||||||
|
|
Loading…
Reference in a new issue