mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 17:03:58 +02:00
add ray picking to 3D overlays
This commit is contained in:
parent
ab58a4e5b8
commit
175e5d5f79
12 changed files with 228 additions and 16 deletions
|
@ -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) {
|
||||
|
|
|
@ -225,6 +225,9 @@ var toolBar = (function () {
|
|||
|
||||
if (activeButton === toolBar.clicked(clickedOverlay)) {
|
||||
isActive = !isActive;
|
||||
if (!isActive) {
|
||||
selectionDisplay.unselectAll();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -74,7 +74,6 @@ public:
|
|||
void setColorPulse(float value) { _colorPulse = value; }
|
||||
void setAlphaPulse(float value) { _alphaPulse = value; }
|
||||
|
||||
|
||||
virtual void setProperties(const QScriptValue& properties);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue