From 8ec14568fe72bbf3468004f38a9b2614484a10a1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 27 May 2014 13:39:32 -0700 Subject: [PATCH] support ray picking against the AABB for the rotated model extents --- interface/src/models/ModelTreeRenderer.cpp | 55 ++++++++-------------- interface/src/models/ModelTreeRenderer.h | 5 +- libraries/fbx/src/FBXReader.cpp | 37 +++++++++++++++ libraries/fbx/src/FBXReader.h | 5 +- libraries/models/src/ModelTree.h | 11 +++++ libraries/models/src/ModelTreeElement.cpp | 37 ++++++++++++++- 6 files changed, 111 insertions(+), 39 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 6d7c61dca1..8ffd61043a 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -11,6 +11,8 @@ #include +#include + #include "InterfaceConfig.h" #include "Menu.h" #include "ModelTreeRenderer.h" @@ -34,8 +36,13 @@ ModelTreeRenderer::~ModelTreeRenderer() { void ModelTreeRenderer::init() { OctreeRenderer::init(); + static_cast(_tree)->setFBXService(this); } +void ModelTreeRenderer::setTree(Octree* newTree) { + OctreeRenderer::setTree(newTree); + static_cast(_tree)->setFBXService(this); +} void ModelTreeRenderer::update() { if (_tree) { @@ -48,6 +55,16 @@ void ModelTreeRenderer::render(RenderMode renderMode) { OctreeRenderer::render(renderMode); } +const FBXGeometry* ModelTreeRenderer::getGeometryForModel(const ModelItem& modelItem) { + const FBXGeometry* result = NULL; + Model* model = getModel(modelItem); + if (model) { + result = &model->getGeometry()->getFBXGeometry(); + + } + return result; +} + Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { Model* model = NULL; @@ -73,42 +90,6 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { return model; } -void calculateRotatedExtents(Extents& extents, const glm::quat& rotation) { - glm::vec3 bottomLeftNear(extents.minimum.x, extents.minimum.y, extents.minimum.z); - glm::vec3 bottomRightNear(extents.maximum.x, extents.minimum.y, extents.minimum.z); - glm::vec3 bottomLeftFar(extents.minimum.x, extents.minimum.y, extents.maximum.z); - glm::vec3 bottomRightFar(extents.maximum.x, extents.minimum.y, extents.maximum.z); - glm::vec3 topLeftNear(extents.minimum.x, extents.maximum.y, extents.minimum.z); - glm::vec3 topRightNear(extents.maximum.x, extents.maximum.y, extents.minimum.z); - glm::vec3 topLeftFar(extents.minimum.x, extents.maximum.y, extents.maximum.z); - glm::vec3 topRightFar(extents.maximum.x, extents.maximum.y, extents.maximum.z); - - glm::vec3 bottomLeftNearRotated = rotation * bottomLeftNear; - glm::vec3 bottomRightNearRotated = rotation * bottomRightNear; - glm::vec3 bottomLeftFarRotated = rotation * bottomLeftFar; - glm::vec3 bottomRightFarRotated = rotation * bottomRightFar; - glm::vec3 topLeftNearRotated = rotation * topLeftNear; - glm::vec3 topRightNearRotated = rotation * topRightNear; - glm::vec3 topLeftFarRotated = rotation * topLeftFar; - glm::vec3 topRightFarRotated = rotation * topRightFar; - - extents.minimum = glm::min(bottomLeftNearRotated, - glm::min(bottomRightNearRotated, - glm::min(bottomLeftFarRotated, - glm::min(bottomRightFarRotated, - glm::min(topLeftNearRotated, - glm::min(topRightNearRotated, - glm::min(topLeftFarRotated,topRightFarRotated))))))); - - extents.maximum = glm::max(bottomLeftNearRotated, - glm::max(bottomRightNearRotated, - glm::max(bottomLeftFarRotated, - glm::max(bottomRightFarRotated, - glm::max(topLeftNearRotated, - glm::max(topRightNearRotated, - glm::max(topLeftFarRotated,topRightFarRotated))))))); -} - void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) { args->_elementsTouched++; // actually render it here... @@ -242,6 +223,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) model->render(alpha, modelRenderMode); if (!isShadowMode && displayModelBounds) { + glm::vec3 unRotatedMinimum = model->getUnscaledMeshExtents().minimum; glm::vec3 unRotatedMaximum = model->getUnscaledMeshExtents().maximum; glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum; @@ -279,6 +261,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) glutWireCube(1.0); glPopMatrix(); + } glPopMatrix(); diff --git a/interface/src/models/ModelTreeRenderer.h b/interface/src/models/ModelTreeRenderer.h index e0b8d7d0a2..2d418b1a25 100644 --- a/interface/src/models/ModelTreeRenderer.h +++ b/interface/src/models/ModelTreeRenderer.h @@ -26,7 +26,7 @@ #include "renderer/Model.h" // Generic client side Octree renderer class. -class ModelTreeRenderer : public OctreeRenderer { +class ModelTreeRenderer : public OctreeRenderer, public ModelItemFBXService { public: ModelTreeRenderer(); virtual ~ModelTreeRenderer(); @@ -38,6 +38,7 @@ public: virtual void renderElement(OctreeElement* element, RenderArgs* args); virtual float getSizeScale() const; virtual int getBoundaryLevelAdjust() const; + virtual void setTree(Octree* newTree); void update(); @@ -48,6 +49,8 @@ public: virtual void init(); virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE); + virtual const FBXGeometry* getGeometryForModel(const ModelItem& modelItem); + protected: Model* getModel(const ModelItem& modelItem); QMap _knownModelsItemModels; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 713870dafb..60412cf0ce 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2052,3 +2052,40 @@ FBXGeometry readSVO(const QByteArray& model) { return geometry; } + +void calculateRotatedExtents(Extents& extents, const glm::quat& rotation) { + glm::vec3 bottomLeftNear(extents.minimum.x, extents.minimum.y, extents.minimum.z); + glm::vec3 bottomRightNear(extents.maximum.x, extents.minimum.y, extents.minimum.z); + glm::vec3 bottomLeftFar(extents.minimum.x, extents.minimum.y, extents.maximum.z); + glm::vec3 bottomRightFar(extents.maximum.x, extents.minimum.y, extents.maximum.z); + glm::vec3 topLeftNear(extents.minimum.x, extents.maximum.y, extents.minimum.z); + glm::vec3 topRightNear(extents.maximum.x, extents.maximum.y, extents.minimum.z); + glm::vec3 topLeftFar(extents.minimum.x, extents.maximum.y, extents.maximum.z); + glm::vec3 topRightFar(extents.maximum.x, extents.maximum.y, extents.maximum.z); + + glm::vec3 bottomLeftNearRotated = rotation * bottomLeftNear; + glm::vec3 bottomRightNearRotated = rotation * bottomRightNear; + glm::vec3 bottomLeftFarRotated = rotation * bottomLeftFar; + glm::vec3 bottomRightFarRotated = rotation * bottomRightFar; + glm::vec3 topLeftNearRotated = rotation * topLeftNear; + glm::vec3 topRightNearRotated = rotation * topRightNear; + glm::vec3 topLeftFarRotated = rotation * topLeftFar; + glm::vec3 topRightFarRotated = rotation * topRightFar; + + extents.minimum = glm::min(bottomLeftNearRotated, + glm::min(bottomRightNearRotated, + glm::min(bottomLeftFarRotated, + glm::min(bottomRightFarRotated, + glm::min(topLeftNearRotated, + glm::min(topRightNearRotated, + glm::min(topLeftFarRotated,topRightFarRotated))))))); + + extents.maximum = glm::max(bottomLeftNearRotated, + glm::max(bottomRightNearRotated, + glm::max(bottomLeftFarRotated, + glm::max(bottomRightFarRotated, + glm::max(topLeftNearRotated, + glm::max(topRightNearRotated, + glm::max(topLeftFarRotated,topRightFarRotated))))))); +} + diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 8073ab36a9..4c93f3dc5e 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -51,7 +51,8 @@ public: bool containsPoint(const glm::vec3& point) const; /// \return whether or not the extents are empty - bool isEmpty() { return minimum == maximum; } + bool isEmpty() const { return minimum == maximum; } + bool isValid() const { return !((minimum == glm::vec3(FLT_MAX)) && (maximum == glm::vec3(-FLT_MAX))); } glm::vec3 minimum; glm::vec3 maximum; @@ -238,4 +239,6 @@ FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping); /// Reads SVO geometry from the supplied model data. FBXGeometry readSVO(const QByteArray& model); +void calculateRotatedExtents(Extents& extents, const glm::quat& rotation); + #endif // hifi_FBXReader_h diff --git a/libraries/models/src/ModelTree.h b/libraries/models/src/ModelTree.h index 596ca9074d..c450f04e2d 100644 --- a/libraries/models/src/ModelTree.h +++ b/libraries/models/src/ModelTree.h @@ -20,6 +20,11 @@ public: virtual void modelCreated(const ModelItem& newModel, const SharedNodePointer& senderNode) = 0; }; +class ModelItemFBXService { +public: + virtual const FBXGeometry* getGeometryForModel(const ModelItem& modelItem) = 0; +}; + class ModelTree : public Octree { Q_OBJECT public: @@ -74,6 +79,11 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode); void handleAddModelResponse(const QByteArray& packet); + + void setFBXService(ModelItemFBXService* service) { _fbxService = service; } + const FBXGeometry* getGeometryForModel(const ModelItem& modelItem) { + return _fbxService ? _fbxService->getGeometryForModel(modelItem) : NULL; + } private: @@ -96,6 +106,7 @@ private: QReadWriteLock _recentlyDeletedModelsLock; QMultiMap _recentlyDeletedModelItemIDs; + ModelItemFBXService* _fbxService; }; #endif // hifi_ModelTree_h diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index dcb3253ef4..dad592f838 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include "ModelTree.h" @@ -158,7 +159,41 @@ bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, cons // if the ray doesn't intersect with our cube, we can stop searching! if (modelCube.findRayIntersection(origin, direction, localDistance, localFace)) { - if (localDistance < distance) { + const FBXGeometry* fbxGeometry = _myTree->getGeometryForModel(model); + if (fbxGeometry && fbxGeometry->meshExtents.isValid()) { + Extents extents = fbxGeometry->meshExtents; + + // NOTE: these extents are model space, so we need to scale and center them accordingly + // size is our "target size in world space" + // we need to set our model scale so that the extents of the mesh, fit in a cube that size... + float maxDimension = glm::distance(extents.maximum, extents.minimum); + float scale = model.getSize() / maxDimension; + + glm::vec3 halfDimensions = (extents.maximum - extents.minimum) * 0.5f; + glm::vec3 offset = -extents.minimum - halfDimensions; + + extents.minimum += offset; + extents.maximum += offset; + + extents.minimum *= scale; + extents.maximum *= scale; + + calculateRotatedExtents(extents, model.getModelRotation()); + + extents.minimum += model.getPosition(); + extents.maximum += model.getPosition(); + + AABox rotatedExtentsBox(extents.minimum, (extents.maximum - extents.minimum)); + + if (rotatedExtentsBox.findRayIntersection(origin, direction, localDistance, localFace)) { + if (localDistance < distance) { + distance = localDistance; + face = localFace; + *intersectedObject = (void*)(&model); + somethingIntersected = true; + } + } + } else if (localDistance < distance) { distance = localDistance; face = localFace; *intersectedObject = (void*)(&model);