mirror of
https://github.com/overte-org/overte.git
synced 2025-04-16 13:56:24 +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->setPulseMax(1.0);
|
||||
_keyboardFocusHighlight->setColorPulse(1.0);
|
||||
_keyboardFocusHighlight->setIgnoreRayIntersection(true);
|
||||
_keyboardFocusHighlight->setIgnorePickIntersection(true);
|
||||
_keyboardFocusHighlight->setDrawInFront(false);
|
||||
_keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);
|
||||
}
|
||||
|
|
|
@ -573,6 +573,77 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
|||
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
|
||||
float AvatarManager::getAvatarSortCoefficient(const QString& name) {
|
||||
if (name == "size") {
|
||||
|
|
|
@ -141,6 +141,10 @@ public:
|
|||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
const QVector<EntityItemID>& avatarsToDiscard);
|
||||
|
||||
Q_INVOKABLE ParabolaToAvatarIntersectionResult findParabolaIntersectionVector(const PickParabola& pick,
|
||||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
const QVector<EntityItemID>& avatarsToDiscard);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.getAvatarSortCoefficient
|
||||
* @param {string} name
|
||||
|
|
|
@ -26,29 +26,29 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick)
|
|||
}
|
||||
|
||||
PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) {
|
||||
/*ParabolaToOverlayIntersectionResult overlayRes =
|
||||
ParabolaToOverlayIntersectionResult overlayRes =
|
||||
qApp->getOverlays().findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(),
|
||||
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
|
||||
if (overlayRes.intersects) {
|
||||
return std::make_shared<ParabolaPickResult>(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo);
|
||||
} else {*/
|
||||
} else {
|
||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
PickResultPointer ParabolaPick::getAvatarIntersection(const PickParabola& pick) {
|
||||
/*ParabolaToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findParabolaIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
||||
ParabolaToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findParabolaIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
||||
if (avatarRes.intersects) {
|
||||
return std::make_shared<ParabolaPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.parabolicDistance, avatarRes.intersection, pick, glm::vec3(NAN), avatarRes.extraInfo);
|
||||
} else {*/
|
||||
} else {
|
||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
PickResultPointer ParabolaPick::getHUDIntersection(const PickParabola& pick) {
|
||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||
//glm::vec3 hudRes = DependencyManager::get<HMDScriptingInterface>()->calculateParabolaUICollisionPoint(pick);
|
||||
//return std::make_shared<ParabolaPickResult>(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick);
|
||||
float parabolicDistance;
|
||||
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), parabolicDistance, hudRes, pick);
|
||||
}
|
||||
|
||||
glm::vec3 ParabolaPick::getAcceleration() const {
|
||||
|
|
|
@ -58,11 +58,11 @@ public:
|
|||
}
|
||||
|
||||
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 {
|
||||
auto newParabolaRes = std::static_pointer_cast<ParabolaPickResult>(newRes);
|
||||
if (newParabolaRes->distance < distance) {
|
||||
if (newParabolaRes->parabolicDistance < parabolicDistance) {
|
||||
return std::make_shared<ParabolaPickResult>(*newParabolaRes);
|
||||
} else {
|
||||
return std::make_shared<ParabolaPickResult>(*this);
|
||||
|
|
|
@ -35,6 +35,12 @@ glm::vec3 HMDScriptingInterface::calculateRayUICollisionPoint(const glm::vec3& p
|
|||
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 {
|
||||
return qApp->getApplicationCompositor().overlayFromSphereSurface(position);
|
||||
}
|
||||
|
|
|
@ -100,6 +100,8 @@ public:
|
|||
*/
|
||||
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
|
||||
* 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.
|
||||
|
|
|
@ -23,7 +23,7 @@ Base3DOverlay::Base3DOverlay() :
|
|||
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
||||
_isSolid(DEFAULT_IS_SOLID),
|
||||
_isDashedLine(DEFAULT_IS_DASHED_LINE),
|
||||
_ignoreRayIntersection(false),
|
||||
_ignorePickIntersection(false),
|
||||
_drawInFront(false),
|
||||
_drawHUDLayer(false)
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
|||
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
||||
_isSolid(base3DOverlay->_isSolid),
|
||||
_isDashedLine(base3DOverlay->_isDashedLine),
|
||||
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
|
||||
_ignorePickIntersection(base3DOverlay->_ignorePickIntersection),
|
||||
_drawInFront(base3DOverlay->_drawInFront),
|
||||
_drawHUDLayer(base3DOverlay->_drawHUDLayer),
|
||||
_isGrabbable(base3DOverlay->_isGrabbable),
|
||||
|
@ -183,8 +183,10 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
if (properties["dashed"].isValid()) {
|
||||
setIsDashedLine(properties["dashed"].toBool());
|
||||
}
|
||||
if (properties["ignoreRayIntersection"].isValid()) {
|
||||
setIgnoreRayIntersection(properties["ignoreRayIntersection"].toBool());
|
||||
if (properties["ignorePickIntersection"].isValid()) {
|
||||
setIgnorePickIntersection(properties["ignorePickIntersection"].toBool());
|
||||
} else if (properties["ignoreRayIntersection"].isValid()) {
|
||||
setIgnorePickIntersection(properties["ignoreRayIntersection"].toBool());
|
||||
}
|
||||
|
||||
if (properties["parentID"].isValid()) {
|
||||
|
@ -224,8 +226,7 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
* 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:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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") {
|
||||
return _isDashedLine;
|
||||
}
|
||||
if (property == "ignoreRayIntersection") {
|
||||
return _ignoreRayIntersection;
|
||||
if (property == "ignorePickIntersection" || property == "ignoreRayIntersection") {
|
||||
return _ignorePickIntersection;
|
||||
}
|
||||
if (property == "drawInFront") {
|
||||
return _drawInFront;
|
||||
|
@ -282,11 +283,6 @@ QVariant Base3DOverlay::getProperty(const QString& 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) {
|
||||
SpatiallyNestable::locationChanged(tellPhysics);
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
bool getIsSolid() const { return _isSolid; }
|
||||
bool getIsDashedLine() const { return _isDashedLine; }
|
||||
bool getIsSolidLine() const { return !_isDashedLine; }
|
||||
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
|
||||
bool getIgnorePickIntersection() const { return _ignorePickIntersection; }
|
||||
bool getDrawInFront() const { return _drawInFront; }
|
||||
bool getDrawHUDLayer() const { return _drawHUDLayer; }
|
||||
bool getIsGrabbable() const { return _isGrabbable; }
|
||||
|
@ -53,7 +53,7 @@ public:
|
|||
|
||||
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
|
||||
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 setDrawHUDLayer(bool value) { _drawHUDLayer = value; }
|
||||
void setIsGrabbable(bool value) { _isGrabbable = value; }
|
||||
|
@ -69,13 +69,21 @@ public:
|
|||
virtual 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);
|
||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) { return false; }
|
||||
|
||||
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking = false) {
|
||||
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;
|
||||
|
||||
protected:
|
||||
|
@ -91,7 +99,7 @@ protected:
|
|||
|
||||
bool _isSolid;
|
||||
bool _isDashedLine;
|
||||
bool _ignoreRayIntersection;
|
||||
bool _ignorePickIntersection;
|
||||
bool _drawInFront;
|
||||
bool _drawHUDLayer;
|
||||
bool _isGrabbable { false };
|
||||
|
|
|
@ -397,8 +397,7 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
* 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:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||
|
||||
// Scale the dimensions by the diameter
|
||||
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 localHitPosition = glm::inverse(getWorldOrientation()) * (hitPosition - getWorldPosition());
|
||||
localHitPosition.x /= getDimensions().x;
|
||||
localHitPosition.y /= getDimensions().y;
|
||||
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 {
|
||||
|
|
|
@ -54,8 +54,10 @@ public:
|
|||
void setMajorTickMarksColor(const xColor& value) { _majorTickMarksColor = 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;
|
||||
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;
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
|
|||
_contextOverlay->setPulseMin(CONTEXT_OVERLAY_UNHOVERED_PULSEMIN);
|
||||
_contextOverlay->setPulseMax(CONTEXT_OVERLAY_UNHOVERED_PULSEMAX);
|
||||
_contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE);
|
||||
_contextOverlay->setIgnoreRayIntersection(false);
|
||||
_contextOverlay->setIgnorePickIntersection(false);
|
||||
_contextOverlay->setDrawInFront(true);
|
||||
_contextOverlay->setURL(PathUtils::resourcesUrl() + "images/inspect-icon.png");
|
||||
_contextOverlay->setIsFacingAvatar(true);
|
||||
|
|
|
@ -160,8 +160,7 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
* 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:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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;
|
||||
|
||||
// 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:
|
||||
Transform evalRenderTransform() override;
|
||||
|
|
|
@ -216,8 +216,7 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
* 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:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||
if (_texture && _texture->isLoaded()) {
|
||||
// Make sure position and rotation is updated.
|
||||
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.
|
||||
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 maxSize = glm::max(width, height);
|
||||
glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize);
|
||||
glm::quat rotation = transform.getRotation();
|
||||
|
||||
// FIXME - face and surfaceNormal not being set
|
||||
return findRayRectangleIntersection(origin, direction,
|
||||
transform.getRotation(),
|
||||
transform.getTranslation(),
|
||||
dimensions, distance);
|
||||
if (findRayRectangleIntersection(origin, direction, rotation, transform.getTranslation(), dimensions, 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 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;
|
||||
|
|
|
@ -42,8 +42,10 @@ public:
|
|||
QVariant getProperty(const QString& property) override;
|
||||
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;
|
||||
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;
|
||||
|
||||
|
|
|
@ -288,8 +288,7 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
* 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:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||
|
||||
QVariantMap extraInfo;
|
||||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking);
|
||||
}
|
||||
|
||||
bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool 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 {
|
||||
return new ModelOverlay(this);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,11 @@ public:
|
|||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override;
|
||||
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;
|
||||
|
||||
|
|
|
@ -546,7 +546,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay
|
|||
continue;
|
||||
}
|
||||
|
||||
if (thisOverlay && thisOverlay->getVisible() && !thisOverlay->getIgnoreRayIntersection() && thisOverlay->isLoaded()) {
|
||||
if (thisOverlay && thisOverlay->getVisible() && !thisOverlay->getIgnorePickIntersection() && thisOverlay->isLoaded()) {
|
||||
float thisDistance;
|
||||
BoxFace thisFace;
|
||||
glm::vec3 thisSurfaceNormal;
|
||||
|
@ -573,6 +573,54 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay
|
|||
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) {
|
||||
auto obj = engine->newObject();
|
||||
obj.setProperty("intersects", value.intersects);
|
||||
|
@ -1046,7 +1094,8 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
i.next();
|
||||
OverlayID thisID = i.key();
|
||||
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
|
||||
glm::vec3 dimensions = overlay->getDimensions();
|
||||
glm::vec3 low = dimensions * -0.5f;
|
||||
|
|
|
@ -119,6 +119,11 @@ public:
|
|||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
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 mouseDoublePressEvent(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,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking) {
|
||||
// FIXME - face and surfaceNormal not being returned
|
||||
return findRayRectangleIntersection(origin, direction, getWorldOrientation(), getWorldPosition(), getDimensions(), distance);
|
||||
glm::vec2 xyDimensions = getDimensions();
|
||||
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() {
|
||||
|
|
|
@ -32,6 +32,8 @@ public:
|
|||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
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:
|
||||
glm::vec2 _dimensions;
|
||||
|
|
|
@ -140,8 +140,7 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
|
|||
* 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:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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>.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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);
|
||||
}
|
||||
|
||||
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 transform = getTransform();
|
||||
#ifndef USE_SN_SCALE
|
||||
|
|
|
@ -32,6 +32,8 @@ public:
|
|||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
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:
|
||||
// Centered local bounding box
|
||||
|
|
|
@ -539,8 +539,7 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
* 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:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
|
||||
* @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.
|
||||
* @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 {
|
||||
return new Web3DOverlay(this);
|
||||
}
|
||||
|
|
|
@ -52,9 +52,6 @@ public:
|
|||
void setProperties(const QVariantMap& properties) 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;
|
||||
|
||||
enum InputMode {
|
||||
|
|
|
@ -1563,6 +1563,7 @@ void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
}
|
||||
|
||||
void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
|
||||
// FIXME: this doesn't take into account Avatar rotation
|
||||
ShapeInfo shapeInfo;
|
||||
computeShapeInfo(shapeInfo);
|
||||
glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <CursorManager.h>
|
||||
#include <gl/GLWidget.h>
|
||||
|
||||
#include "GeometryUtil.h"
|
||||
|
||||
// Used to animate the magnification windows
|
||||
|
||||
//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));
|
||||
|
||||
const float UI_RADIUS = 1.0f;
|
||||
float instersectionDistance;
|
||||
if (raySphereIntersect(localDirection, localPosition, UI_RADIUS, &instersectionDistance)) {
|
||||
result = transformPoint(uiToWorld, localPosition + localDirection * instersectionDistance);
|
||||
float intersectionDistance;
|
||||
if (raySphereIntersect(localDirection, localPosition, UI_RADIUS, &intersectionDistance)) {
|
||||
result = transformPoint(uiToWorld, localPosition + localDirection * intersectionDistance);
|
||||
#ifdef WANT_DEBUG
|
||||
DebugDraw::getInstance().drawRay(position, result, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
|
||||
#endif
|
||||
|
@ -372,6 +374,23 @@ bool CompositorHelper::calculateRayUICollisionPoint(const glm::vec3& position, c
|
|||
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 result = sphericalPos;
|
||||
result.x *= -1.0f;
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
void setRenderingWidget(QWidget* widget) { _renderingWidget = widget; }
|
||||
|
||||
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 fakeEventActive() const { return _fakeMouseEvent; }
|
||||
|
|
|
@ -139,15 +139,14 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
|
|||
glm::vec3 forward = rotation * Vectors::FRONT;
|
||||
if (glm::dot(forward, direction) > 0.0f) {
|
||||
face = MAX_Z_FACE;
|
||||
surfaceNormal = rotation * -Vectors::FRONT;
|
||||
surfaceNormal = -forward;
|
||||
} else {
|
||||
face = MIN_Z_FACE;
|
||||
surfaceNormal = rotation * Vectors::FRONT;
|
||||
surfaceNormal = forward;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
if (glm::dot(forward, direction) > 0.0f) {
|
||||
face = MAX_Z_FACE;
|
||||
surfaceNormal = rotation * -Vectors::FRONT;
|
||||
surfaceNormal = -forward;
|
||||
} else {
|
||||
face = MIN_Z_FACE;
|
||||
surfaceNormal = rotation * Vectors::FRONT;
|
||||
surfaceNormal = forward;
|
||||
}
|
||||
return true;
|
||||
} 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
|
||||
// that is within the bounds of the other two dimensions
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// TODO: handle case where a is 0
|
||||
a = 0.5f * acceleration[i];
|
||||
b = velocity[i];
|
||||
if (origin[i] < _corner[i]) {
|
||||
// If we're below _corner, we only need to check the min face
|
||||
{ // 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);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = -1.0f;
|
||||
if (fabsf(acceleration[i]) < EPSILON) {
|
||||
// Handle the degenerate case where we only have a line in this axis
|
||||
if (origin[i] < _corner[i]) {
|
||||
{ // 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);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = -1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (origin[i] > _corner[i] + _scale[i]) {
|
||||
// If we're above _corner + _scale, we only need to check the max face
|
||||
{ // 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 + 1);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = 1.0f;
|
||||
} else if (origin[i] > _corner[i] + _scale[i]) {
|
||||
{ // 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 + 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 {
|
||||
// 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);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = -1.0f;
|
||||
a = 0.5f * acceleration[i];
|
||||
b = velocity[i];
|
||||
if (origin[i] < _corner[i]) {
|
||||
// If we're below _corner, we only need to check the min face
|
||||
{ // 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);
|
||||
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 + 1);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = 1.0f;
|
||||
} else if (origin[i] > _corner[i] + _scale[i]) {
|
||||
// If we're above _corner + _scale, we only need to check the max face
|
||||
{ // 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 + 1);
|
||||
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;
|
||||
}
|
||||
|
||||
// Get the normal of the plane, the cross product of two vectors on the plane
|
||||
// Assumes: velocity and acceleration != 0 and normalize(velocity) != normalize(acceleration)
|
||||
glm::vec3 normal = glm::normalize(glm::cross(velocity, acceleration));
|
||||
float velocityLength2 = glm::length2(velocity);
|
||||
if (glm::length2(acceleration) < EPSILON) {
|
||||
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
|
||||
float distance = glm::dot(localCenter, normal);
|
||||
if (distance * distance < radiusSquared) {
|
||||
return true;
|
||||
// Project vector from plane to sphere center onto the normal
|
||||
float distance = glm::dot(localCenter, normal);
|
||||
if (distance * distance < radiusSquared) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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,
|
||||
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
|
||||
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
|
||||
float minDistance = FLT_MAX;
|
||||
BoxFace minFace;
|
||||
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
|
||||
// that is within the bounds of the other two dimensions
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// TODO: handle case where a is 0
|
||||
a = 0.5f * acceleration[i];
|
||||
b = velocity[i];
|
||||
if (origin[i] < _corner[i]) {
|
||||
// If we're below _corner, we only need to check the min face
|
||||
{ // 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);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = -1.0f;
|
||||
if (fabsf(acceleration[i]) < EPSILON) {
|
||||
// Handle the degenerate case where we only have a line in this axis
|
||||
if (origin[i] < _corner[i]) {
|
||||
{ // 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);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = -1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (origin[i] > _corner[i] + _scale) {
|
||||
// If we're above _corner + _scale, we only need to check the max face
|
||||
{ // 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 + 1);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = 1.0f;
|
||||
} else if (origin[i] > _corner[i] + _scale) {
|
||||
{ // 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 + 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 {
|
||||
// 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);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = -1.0f;
|
||||
a = 0.5f * acceleration[i];
|
||||
b = velocity[i];
|
||||
if (origin[i] < _corner[i]) {
|
||||
// If we're below _corner, we only need to check the min face
|
||||
{ // 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);
|
||||
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 + 1);
|
||||
minNormal = glm::vec3(0.0f);
|
||||
minNormal[i] = 1.0f;
|
||||
} else if (origin[i] > _corner[i] + _scale) {
|
||||
// If we're above _corner + _scale, we only need to check the max face
|
||||
{ // 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 + 1);
|
||||
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;
|
||||
|
||||
float minDistance = FLT_MAX;
|
||||
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 (fabsf(acceleration.z) < EPSILON) {
|
||||
// Handle the degenerate case where we only have a line in the z-axis
|
||||
if (fabsf(velocity.z) > EPSILON) {
|
||||
float possibleDistance = -origin.z / velocity.z;
|
||||
checkPossibleParabolicIntersectionWithZPlane(possibleDistance, 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) {
|
||||
parabolicDistance = minDistance;
|
||||
|
@ -754,43 +762,72 @@ bool findParabolaSphereIntersection(const glm::vec3& origin, const glm::vec3& ve
|
|||
glm::vec3 localCenter = center - origin;
|
||||
float radiusSquared = radius * radius;
|
||||
|
||||
// Get the normal of the plane, the cross product of two vectors on the plane
|
||||
// Assumes: velocity and acceleration != 0 and normalize(velocity) != normalize(acceleration)
|
||||
glm::vec3 normal = glm::normalize(glm::cross(velocity, acceleration));
|
||||
|
||||
// Project vector from plane to sphere center onto the normal
|
||||
float distance = glm::dot(localCenter, normal);
|
||||
// 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 velocityLength2 = glm::length2(velocity);
|
||||
float accelerationLength = glm::length(acceleration);
|
||||
float minDistance = FLT_MAX;
|
||||
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 (accelerationLength < EPSILON) {
|
||||
if (velocityLength2 < EPSILON) {
|
||||
// No intersection if velocity == acceleration == (0, 0, 0)
|
||||
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) {
|
||||
parabolicDistance = minDistance;
|
||||
return true;
|
||||
|
@ -837,15 +874,24 @@ bool findParabolaTriangleIntersection(const glm::vec3& origin, const glm::vec3&
|
|||
glm::vec3 localAcceleration = inverseRot * acceleration;
|
||||
|
||||
float minDistance = FLT_MAX;
|
||||
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,
|
||||
if (fabsf(localAcceleration.z) < EPSILON) {
|
||||
if (fabsf(localVelocity.z) < EPSILON) {
|
||||
return false;
|
||||
}
|
||||
float possibleDistance = -localOrigin.z / localVelocity.z;
|
||||
checkPossibleParabolicIntersectionWithTriangle(possibleDistance, minDistance, origin, velocity, acceleration,
|
||||
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) {
|
||||
parabolicDistance = minDistance;
|
||||
|
@ -854,6 +900,85 @@ bool findParabolaTriangleIntersection(const glm::vec3& origin, const glm::vec3&
|
|||
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,
|
||||
const glm::vec3& direction,
|
||||
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
|
||||
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 q = (a2 - 3.0f * b) / 9.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) {
|
||||
a = 1.0f;
|
||||
b = b / a;
|
||||
c = c / a;
|
||||
d = d / a;
|
||||
e = e / a;
|
||||
|
||||
return solve_quartic(b, c, d, e, roots);
|
||||
return solve_quartic(b / a, c / a, d / a, e / a, 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,
|
||||
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
|
||||
/// \param rotation[in] rotation to decompose
|
||||
/// \param direction[in] normalized axis about which the twist happens (typically original direction before rotation applied)
|
||||
|
|
Loading…
Reference in a new issue