mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 18:55:01 +02:00
add ray picking to the model scripting interface
This commit is contained in:
parent
7036233a47
commit
efd0580cfb
12 changed files with 268 additions and 20 deletions
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue