add ray picking to 3D overlays

This commit is contained in:
ZappoMan 2014-10-02 20:44:49 -07:00
parent ab58a4e5b8
commit 175e5d5f79
12 changed files with 228 additions and 16 deletions

View file

@ -14,6 +14,7 @@
SelectionDisplay = (function () {
var that = {};
var overlayNames = new Array();
var lastAvatarPosition = MyAvatar.position;
var lastAvatarOrientation = MyAvatar.orientation;
@ -217,6 +218,48 @@ SelectionDisplay = (function () {
isFacingAvatar: false
});
overlayNames[highlightBox] = "highlightBox";
overlayNames[selectionBox] = "selectionBox";
overlayNames[baseOfEntityProjectionOverlay] = "baseOfEntityProjectionOverlay";
overlayNames[grabberMoveUp] = "grabberMoveUp";
overlayNames[grabberLBN] = "grabberLBN";
overlayNames[grabberLBF] = "grabberLBF";
overlayNames[grabberRBN] = "grabberRBN";
overlayNames[grabberRBF] = "grabberRBF";
overlayNames[grabberLTN] = "grabberLTN";
overlayNames[grabberLTF] = "grabberLTF";
overlayNames[grabberRTN] = "grabberRTN";
overlayNames[grabberRTF] = "grabberRTF";
overlayNames[grabberTOP] = "grabberTOP";
overlayNames[grabberBOTTOM] = "grabberBOTTOM";
overlayNames[grabberLEFT] = "grabberLEFT";
overlayNames[grabberRIGHT] = "grabberRIGHT";
overlayNames[grabberNEAR] = "grabberNEAR";
overlayNames[grabberFAR] = "grabberFAR";
overlayNames[grabberEdgeTR] = "grabberEdgeTR";
overlayNames[grabberEdgeTL] = "grabberEdgeTL";
overlayNames[grabberEdgeTF] = "grabberEdgeTF";
overlayNames[grabberEdgeTN] = "grabberEdgeTN";
overlayNames[grabberEdgeBR] = "grabberEdgeBR";
overlayNames[grabberEdgeBL] = "grabberEdgeBL";
overlayNames[grabberEdgeBF] = "grabberEdgeBF";
overlayNames[grabberEdgeBN] = "grabberEdgeBN";
overlayNames[grabberEdgeNR] = "grabberEdgeNR";
overlayNames[grabberEdgeNL] = "grabberEdgeNL";
overlayNames[grabberEdgeFR] = "grabberEdgeFR";
overlayNames[grabberEdgeFL] = "grabberEdgeFL";
overlayNames[yawHandle] = "yawHandle";
overlayNames[pitchHandle] = "pitchHandle";
overlayNames[rollHandle] = "rollHandle";
overlayNames[rotateOverlayInner] = "rotateOverlayInner";
overlayNames[rotateOverlayOuter] = "rotateOverlayOuter";
overlayNames[rotateOverlayCurrent] = "rotateOverlayCurrent";
that.cleanup = function () {
Overlays.deleteOverlay(highlightBox);
Overlays.deleteOverlay(selectionBox);
@ -579,22 +622,26 @@ print("select()...... entityID:" + entityID.id);
that.checkMove = function() {
if (currentSelection.isKnownID &&
(!Vec3.equal(MyAvatar.position, lastAvatarPosition) || !Quat.equal(MyAvatar.orientation, lastAvatarOrientation))){
print("checkMove calling .... select()");
//print("Vec3.equal(MyAvatar.position, lastAvatarPosition):" + Vec3.equal(MyAvatar.position, lastAvatarPosition);
//Vec3.print("MyAvatar.position:", MyAvatar.position);
//Vec3.print("lastAvatarPosition:", lastAvatarPosition);
//print("Quat.equal(MyAvatar.orientation, lastAvatarOrientation):" + Quat.equal(MyAvatar.orientation, lastAvatarOrientation));
//Quat.print("MyAvatar.orientation:", MyAvatar.orientation);
//Quat.print("lastAvatarOrientation:", lastAvatarOrientation);
that.select(currentSelection);
}
};
that.mousePressEvent = function(event) {
print("SelectionDisplay.mousePressEvent() x:" + event.x + " y:" + event.y);
var pickRay = Camera.computePickRay(event.x, event.y);
var result = Overlays.findRayIntersection(pickRay);
if (result.intersects) {
print("something intersects... ");
print(" result.overlayID:" + result.overlayID + "[" + overlayNames[result.overlayID] + "]");
print(" result.intersects:" + result.intersects);
print(" result.overlayID:" + result.overlayID);
print(" result.distance:" + result.distance);
print(" result.face:" + result.face);
Vec3.print(" result.intersection:", result.intersection);
}
};
that.mouseMoveEvent = function(event) {

View file

@ -225,6 +225,9 @@ var toolBar = (function () {
if (activeButton === toolBar.clicked(clickedOverlay)) {
isActive = !isActive;
if (!isActive) {
selectionDisplay.unselectAll();
}
return true;
}

View file

@ -3874,6 +3874,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool)));
scriptEngine->registerGlobalObject("Overlays", &_overlays);
qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, RayToOverlayIntersectionResultFromScriptValue);
QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", WindowScriptingInterface::getInstance());
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,

View file

@ -25,8 +25,8 @@ Base3DOverlay::Base3DOverlay() :
_position(DEFAULT_POSITION),
_lineWidth(DEFAULT_LINE_WIDTH),
_isSolid(DEFAULT_IS_SOLID),
_isDashedLine(DEFAULT_IS_DASHED_LINE),
_rotation()
_rotation(),
_isDashedLine(DEFAULT_IS_DASHED_LINE)
{
}
@ -114,6 +114,12 @@ void Base3DOverlay::setProperties(const QScriptValue& properties) {
}
}
bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face) const {
return false;
}
void Base3DOverlay::drawDashedLine(const glm::vec3& start, const glm::vec3& end) {
glBegin(GL_LINES);
@ -145,3 +151,5 @@ void Base3DOverlay::drawDashedLine(const glm::vec3& start, const glm::vec3& end)
}

View file

@ -13,7 +13,8 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
//#include <glm/gtx/extented_min_max.hpp>
#include <BoxBase.h>
#include "Overlay.h"
@ -42,6 +43,8 @@ public:
virtual void setProperties(const QScriptValue& properties);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
protected:
void drawDashedLine(const glm::vec3& start, const glm::vec3& end);

View file

@ -80,6 +80,7 @@ void BillboardOverlay::render() {
glBegin(GL_QUADS); {
glTexCoord2f((float)_fromImage.x() / (float)_size.width(),
(float)_fromImage.y() / (float)_size.height());
glVertex2f(-x, -y);
glTexCoord2f(((float)_fromImage.x() + (float)_fromImage.width()) / (float)_size.width(),
(float)_fromImage.y() / (float)_size.height());
@ -161,3 +162,20 @@ void BillboardOverlay::replyFinished() {
_billboard = reply->readAll();
_isLoaded = true;
}
bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face) const {
if (_billboardTexture) {
float maxSize = glm::max(_fromImage.width(), _fromImage.height());
float x = _fromImage.width() / (2.0f * maxSize);
float y = -_fromImage.height() / (2.0f * maxSize);
float maxDimension = glm::max(x,y);
float scaledDimension = maxDimension * _scale;
glm::vec3 corner = getCenter() - glm::vec3(scaledDimension, scaledDimension, scaledDimension) ;
AACube myCube(corner, scaledDimension * 2.0f);
return myCube.findRayIntersection(origin, direction, distance, face);
}
return false;
}

View file

@ -26,6 +26,8 @@ public:
virtual void render();
virtual void setProperties(const QScriptValue& properties);
void setClipFromSource(const QRect& bounds) { _fromImage = bounds; }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
private slots:
void replyFinished();

View file

@ -74,7 +74,6 @@ public:
void setColorPulse(float value) { _colorPulse = value; }
void setAlphaPulse(float value) { _alphaPulse = value; }
virtual void setProperties(const QScriptValue& properties);
protected:

View file

@ -8,6 +8,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <limits>
#include <Application.h>
#include "BillboardOverlay.h"
@ -255,6 +256,104 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
return 0; // not found
}
RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray) {
float bestDistance = std::numeric_limits<float>::max();
RayToOverlayIntersectionResult result;
QMapIterator<unsigned int, Overlay*> i(_overlays3D);
i.toBack();
while (i.hasPrevious()) {
i.previous();
unsigned int thisID = i.key();
Base3DOverlay* thisOverlay = static_cast<Base3DOverlay*>(i.value());
if (thisOverlay->getVisible() && thisOverlay->isLoaded()) {
float thisDistance;
BoxFace thisFace;
if (thisOverlay->findRayIntersection(ray.origin, ray.direction, thisDistance, thisFace)) {
if (thisDistance < bestDistance) {
bestDistance = thisDistance;
result.intersects = true;
result.distance = thisDistance;
result.face = thisFace;
result.overlayID = thisID;
result.intersection = ray.origin + (ray.direction * thisDistance);
}
}
}
}
return result;
}
RayToOverlayIntersectionResult::RayToOverlayIntersectionResult() :
intersects(false),
overlayID(-1),
distance(0),
face(),
intersection()
{
}
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
QScriptValue obj = engine->newObject();
obj.setProperty("intersects", value.intersects);
obj.setProperty("overlayID", value.overlayID);
obj.setProperty("distance", value.distance);
QString faceName = "";
// handle BoxFace
switch (value.face) {
case MIN_X_FACE:
faceName = "MIN_X_FACE";
break;
case MAX_X_FACE:
faceName = "MAX_X_FACE";
break;
case MIN_Y_FACE:
faceName = "MIN_Y_FACE";
break;
case MAX_Y_FACE:
faceName = "MAX_Y_FACE";
break;
case MIN_Z_FACE:
faceName = "MIN_Z_FACE";
break;
case MAX_Z_FACE:
faceName = "MAX_Z_FACE";
break;
case UNKNOWN_FACE:
faceName = "UNKNOWN_FACE";
break;
}
obj.setProperty("face", faceName);
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
obj.setProperty("intersection", intersection);
return obj;
}
void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value) {
value.intersects = object.property("intersects").toVariant().toBool();
value.overlayID = object.property("overlayID").toVariant().toInt();
value.distance = object.property("distance").toVariant().toFloat();
QString faceName = object.property("face").toVariant().toString();
if (faceName == "MIN_X_FACE") {
value.face = MIN_X_FACE;
} else if (faceName == "MAX_X_FACE") {
value.face = MAX_X_FACE;
} else if (faceName == "MIN_Y_FACE") {
value.face = MIN_Y_FACE;
} else if (faceName == "MAX_Y_FACE") {
value.face = MAX_Y_FACE;
} else if (faceName == "MIN_Z_FACE") {
value.face = MIN_Z_FACE;
} else {
value.face = MAX_Z_FACE;
};
QScriptValue intersection = object.property("intersection");
if (intersection.isValid()) {
vec3FromScriptValue(intersection, value.intersection);
}
}
bool Overlays::isLoaded(unsigned int id) {
QReadLocker lock(&_lock);
Overlay* overlay = _overlays2D.value(id);

View file

@ -15,6 +15,21 @@
#include "Overlay.h"
class RayToOverlayIntersectionResult {
public:
RayToOverlayIntersectionResult();
bool intersects;
int overlayID;
float distance;
BoxFace face;
glm::vec3 intersection;
};
Q_DECLARE_METATYPE(RayToOverlayIntersectionResult);
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value);
void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value);
class Overlays : public QObject {
Q_OBJECT
public:
@ -36,8 +51,11 @@ public slots:
/// deletes a particle
void deleteOverlay(unsigned int id);
/// returns the top most overlay at the screen point, or 0 if not overlay at that point
/// returns the top most 2D overlay at the screen point, or 0 if not overlay at that point
unsigned int getOverlayAtPoint(const glm::vec2& point);
/// returns details about the closest 3D Overlay hit by the pick ray
RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray);
/// returns whether the overlay's assets are loaded or not
bool isLoaded(unsigned int id);
@ -52,5 +70,6 @@ private:
QReadWriteLock _deleteLock;
};
#endif // hifi_Overlays_h

View file

@ -12,6 +12,7 @@
#include "InterfaceConfig.h"
#include <QGLWidget>
#include <AABox.h>
#include <SharedUtil.h>
#include <StreamUtils.h>
@ -80,3 +81,12 @@ void Volume3DOverlay::setProperties(const QScriptValue& properties) {
}
}
}
bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face) const {
// TODO: this is not exactly accurate because it doesn't properly handle rotation... but it's close enough for our
// current use cases. We do need to improve it to be more accurate
AABox myBox(getCorner(), _dimensions);
return myBox.findRayIntersection(origin, direction, distance, face);
}

View file

@ -30,6 +30,7 @@ public:
// getters
const glm::vec3& getCenter() const { return _position; } // TODO: consider adding registration point!!
glm::vec3 getCorner() const { return _position - (_dimensions * 0.5f); } // TODO: consider adding registration point!!
const glm::vec3& getDimensions() const { return _dimensions; }
// setters
@ -38,6 +39,8 @@ public:
virtual void setProperties(const QScriptValue& properties);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
protected:
glm::vec3 _dimensions;
};