add ray picking to the model scripting interface

This commit is contained in:
ZappoMan 2014-05-23 17:09:37 -07:00
parent 7036233a47
commit efd0580cfb
12 changed files with 268 additions and 20 deletions

View file

@ -41,6 +41,22 @@ function mouseMoveEvent(event) {
print("voxelAt.x/y/z/s=" + voxelAt.x + ", " + voxelAt.y + ", " + voxelAt.z + ": " + voxelAt.s);
print("voxelAt.red/green/blue=" + voxelAt.red + ", " + voxelAt.green + ", " + voxelAt.blue);
}
intersection = Models.findRayIntersection(pickRay);
if (!intersection.accurate) {
print(">>> NOTE: intersection not accurate. will try calling Models.findRayIntersectionBlocking()");
intersection = Models.findRayIntersectionBlocking(pickRay);
print(">>> AFTER BLOCKING CALL intersection.accurate=" + intersection.accurate);
}
if (intersection.intersects) {
print("intersection modelID.id=" + intersection.modelID.id);
print("intersection modelProperties.modelURL=" + intersection.modelProperties.modelURL);
print("intersection face=" + intersection.face);
print("intersection distance=" + intersection.distance);
print("intersection intersection.x/y/z=" + intersection.intersection.x + ", "
+ intersection.intersection.y + ", " + intersection.intersection.z);
}
}
Controller.mouseMoveEvent.connect(mouseMoveEvent);

View file

@ -140,6 +140,37 @@ void ModelTreeElement::update(ModelTreeUpdateArgs& args) {
}
}
bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject) {
// only called if we do intersect our bounding cube, but find if we actually intersect with models...
QList<ModelItem>::iterator modelItr = _modelItems->begin();
QList<ModelItem>::const_iterator modelEnd = _modelItems->end();
bool somethingIntersected = false;
while(modelItr != modelEnd) {
ModelItem& model = (*modelItr);
AACube modelCube = model.getAACube();
float localDistance;
BoxFace localFace;
// if the ray doesn't intersect with our cube, we can stop searching!
if (modelCube.findRayIntersection(origin, direction, localDistance, localFace)) {
if (localDistance < distance) {
distance = localDistance;
face = localFace;
*intersectedObject = (void*)(&model);
somethingIntersected = true;
}
}
++modelItr;
}
return somethingIntersected;
}
bool ModelTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const {
QList<ModelItem>::iterator modelItr = _modelItems->begin();

View file

@ -100,6 +100,11 @@ public:
virtual bool isRendered() const { return getShouldRender(); }
virtual bool deleteApproved() const { return !hasModels(); }
virtual bool canRayIntersect() const { return hasModels(); }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject);
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const;

View file

@ -173,3 +173,115 @@ QVector<ModelItemID> ModelsScriptingInterface::findModels(const glm::vec3& cente
return result;
}
RayToModelIntersectionResult ModelsScriptingInterface::findRayIntersection(const PickRay& ray) {
return findRayIntersectionWorker(ray, Octree::TryLock);
}
RayToModelIntersectionResult ModelsScriptingInterface::findRayIntersectionBlocking(const PickRay& ray) {
return findRayIntersectionWorker(ray, Octree::Lock);
}
RayToModelIntersectionResult ModelsScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
Octree::lockType lockType) {
RayToModelIntersectionResult result;
if (_modelTree) {
OctreeElement* element;
ModelItem* intersectedModel;
result.intersects = _modelTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
(void**)&intersectedModel, lockType, &result.accurate);
if (result.intersects && intersectedModel) {
result.modelID = intersectedModel->getModelItemID();
result.modelProperties = intersectedModel->getProperties();
result.intersection = ray.origin + (ray.direction * result.distance);
}
}
return result;
}
RayToModelIntersectionResult::RayToModelIntersectionResult() :
intersects(false),
accurate(true), // assume it's accurate
modelID(),
modelProperties(),
distance(0),
face()
{
};
QScriptValue RayToModelIntersectionResultToScriptValue(QScriptEngine* engine, const RayToModelIntersectionResult& value) {
QScriptValue obj = engine->newObject();
obj.setProperty("intersects", value.intersects);
obj.setProperty("accurate", value.accurate);
QScriptValue modelItemValue = ModelItemIDtoScriptValue(engine, value.modelID);
obj.setProperty("modelID", modelItemValue);
QScriptValue modelPropertiesValue = ModelItemPropertiesToScriptValue(engine, value.modelProperties);
obj.setProperty("modelProperties", modelPropertiesValue);
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 RayToModelIntersectionResultFromScriptValue(const QScriptValue& object, RayToModelIntersectionResult& value) {
value.intersects = object.property("intersects").toVariant().toBool();
value.accurate = object.property("accurate").toVariant().toBool();
QScriptValue modelIDValue = object.property("modelID");
if (modelIDValue.isValid()) {
ModelItemIDfromScriptValue(modelIDValue, value.modelID);
}
QScriptValue modelPropertiesValue = object.property("modelProperties");
if (modelPropertiesValue.isValid()) {
ModelItemPropertiesFromScriptValue(modelPropertiesValue, value.modelProperties);
}
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);
}
}

View file

@ -15,10 +15,30 @@
#include <QtCore/QObject>
#include <CollisionInfo.h>
#include <Octree.h>
#include <OctreeScriptingInterface.h>
#include <RegisteredMetaTypes.h>
#include "ModelEditPacketSender.h"
class RayToModelIntersectionResult {
public:
RayToModelIntersectionResult();
bool intersects;
bool accurate;
ModelItemID modelID;
ModelItemProperties modelProperties;
float distance;
BoxFace face;
glm::vec3 intersection;
};
Q_DECLARE_METATYPE(RayToModelIntersectionResult)
QScriptValue RayToModelIntersectionResultToScriptValue(QScriptEngine* engine, const RayToModelIntersectionResult& results);
void RayToModelIntersectionResultFromScriptValue(const QScriptValue& object, RayToModelIntersectionResult& results);
/// handles scripting of Model commands from JS passed to assigned clients
class ModelsScriptingInterface : public OctreeScriptingInterface {
Q_OBJECT
@ -59,6 +79,16 @@ public slots:
/// this function will not find any models in script engine contexts which don't have access to models
QVector<ModelItemID> findModels(const glm::vec3& center, float radius) const;
/// If the scripting context has visible voxels, this will determine a ray intersection, the results
/// may be inaccurate if the engine is unable to access the visible voxels, in which case result.accurate
/// will be false.
Q_INVOKABLE RayToModelIntersectionResult findRayIntersection(const PickRay& ray);
/// If the scripting context has visible voxels, this will determine a ray intersection, and will block in
/// order to return an accurate result
Q_INVOKABLE RayToModelIntersectionResult findRayIntersectionBlocking(const PickRay& ray);
signals:
void modelCollisionWithVoxel(const ModelItemID& modelID, const VoxelDetail& voxel, const CollisionInfo& collision);
void modelCollisionWithModel(const ModelItemID& idA, const ModelItemID& idB, const CollisionInfo& collision);
@ -66,6 +96,9 @@ signals:
private:
void queueModelMessage(PacketType packetType, ModelItemID modelID, const ModelItemProperties& properties);
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode
RayToModelIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType);
uint32_t _nextCreatorTokenID;
ModelTree* _modelTree;
};

View file

@ -596,34 +596,26 @@ public:
OctreeElement*& element;
float& distance;
BoxFace& face;
void** intersectedObject;
bool found;
};
bool findRayIntersectionOp(OctreeElement* element, void* extraData) {
RayArgs* args = static_cast<RayArgs*>(extraData);
AACube box = element->getAACube();
float distance;
BoxFace face;
if (!box.findRayIntersection(args->origin, args->direction, distance, face)) {
return false;
}
if (!element->isLeaf()) {
return true; // recurse on children
}
distance *= TREE_SCALE;
if (element->hasContent() && (!args->found || distance < args->distance)) {
args->element = element;
args->distance = distance;
args->face = face;
bool keepSearching = true;
if (element->findRayIntersection(args->origin, args->direction, keepSearching,
args->element, args->distance, args->face, args->intersectedObject)) {
args->found = true;
}
return false;
return keepSearching;
}
bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElement*& element, float& distance, BoxFace& face,
OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject,
Octree::lockType lockType, bool* accurateResult) {
RayArgs args = { origin / (float)(TREE_SCALE), direction, element, distance, face, false};
RayArgs args = { origin / (float)(TREE_SCALE), direction, element, distance, face, intersectedObject, false};
distance = FLT_MAX;
bool gotLock = false;
if (lockType == Octree::Lock) {

View file

@ -268,6 +268,7 @@ public:
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElement*& node, float& distance, BoxFace& face,
void** intersectedObject = NULL,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject = NULL,

View file

@ -1302,6 +1302,55 @@ void OctreeElement::notifyUpdateHooks() {
}
}
bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject) {
keepSearching = true; // assume that we will continue searching after this.
// by default, we only allow intersections with leaves with content
if (!canRayIntersect()) {
return false; // we don't intersect with non-leaves, and we keep searching
}
AACube cube = getAACube();
float localDistance;
BoxFace localFace;
// if the ray doesn't intersect with our cube, we can stop searching!
if (!cube.findRayIntersection(origin, direction, localDistance, localFace)) {
keepSearching = false; // no point in continuing to search
return false; // we did not intersect
}
// we did hit this element, so calculate appropriate distances
localDistance *= TREE_SCALE;
if (localDistance < distance) {
if (findDetailedRayIntersection(origin, direction, keepSearching,
element, distance, face, intersectedObject)) {
distance = localDistance;
face = localFace;
return true;
}
}
return false;
}
bool OctreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject) {
// we did hit this element, so calculate appropriate distances
if (hasContent()) {
element = this;
if (intersectedObject) {
*intersectedObject = this;
}
return true; // we did intersect
}
return false; // we did not intersect
}
bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const {
return _cube.findSpherePenetration(center, radius, penetration);

View file

@ -102,6 +102,14 @@ public:
virtual bool deleteApproved() const { return true; }
virtual bool canRayIntersect() const { return isLeaf(); }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& node, float& distance, BoxFace& face,
void** intersectedObject = NULL);
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject);
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const;

View file

@ -130,7 +130,7 @@ RayToVoxelIntersectionResult LocalVoxels::findRayIntersectionWorker(const PickRa
RayToVoxelIntersectionResult result;
if (_tree) {
OctreeElement* element;
result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, NULL,
lockType, &result.accurate);
if (result.intersects) {
VoxelTreeElement* voxel = (VoxelTreeElement*)element;

View file

@ -224,6 +224,7 @@ void ScriptEngine::init() {
qScriptRegisterMetaType(&_engine, ModelItemPropertiesToScriptValue, ModelItemPropertiesFromScriptValue);
qScriptRegisterMetaType(&_engine, ModelItemIDtoScriptValue, ModelItemIDfromScriptValue);
qScriptRegisterMetaType(&_engine, RayToModelIntersectionResultToScriptValue, RayToModelIntersectionResultFromScriptValue);
qScriptRegisterSequenceMetaType<QVector<ModelItemID> >(&_engine);
qScriptRegisterSequenceMetaType<QVector<glm::vec2> >(&_engine);

View file

@ -135,7 +135,7 @@ RayToVoxelIntersectionResult VoxelsScriptingInterface::findRayIntersectionWorker
RayToVoxelIntersectionResult result;
if (_tree) {
OctreeElement* element;
result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, NULL,
lockType, &result.accurate);
if (result.intersects) {
VoxelTreeElement* voxel = (VoxelTreeElement*)element;