From b4e70d9101df4ded16c7c95ee2067c0ee4ba373c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 25 Mar 2016 21:16:53 -0700 Subject: [PATCH 01/10] WIP: checkpoint * bug fix in AABox::operator+= * added AABox::emiggen * Avatar now has a default bound for it's skinned mesh. * WIP: AABox tests; NEED MORE * Model: split collision and model mesh render items. Because ModelMeshRenderItems need special handling to update bounds for animated joints. * Model: dynamically update the bound for rigidly bound animated meshes * Rig: added access to geometryToRigTransform * RenderableModelEntityItem: try to update bounds for skinned mesh to be the entity dimentions (this doesn't seem to be working) * Geometry.cpp: removed unused bounds parameter in evalPartBounds * ModelMeshPartPayload: bounds updating * non-animated: use existing _localBound * rigid bound mesh: use _localBound transformed by clusterMatrix joint transform * fully skinned mesh: use _skinnedMeshBound provided by the application. --- interface/src/avatar/Avatar.cpp | 3 + libraries/animation/src/Rig.h | 2 + .../src/RenderableModelEntityItem.cpp | 4 + libraries/model/src/model/Geometry.cpp | 2 +- libraries/model/src/model/Geometry.h | 4 +- .../render-utils/src/MeshPartPayload.cpp | 64 ++++++--- libraries/render-utils/src/MeshPartPayload.h | 20 ++- libraries/render-utils/src/Model.cpp | 125 ++++++++++++++---- libraries/render-utils/src/Model.h | 15 ++- libraries/shared/src/AABox.cpp | 31 ++++- libraries/shared/src/AABox.h | 5 +- tests/shared/src/AABoxTests.cpp | 12 ++ tests/shared/src/AABoxTests.h | 1 + 13 files changed, 229 insertions(+), 59 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f722210a8e..24e786caa2 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -56,6 +56,8 @@ const float DISPLAYNAME_ALPHA = 1.0f; const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); +const AABox DEFAULT_AVATAR_SKINNED_MESH_BOUND(glm::vec3(-1.0, -1.0, -1.0), glm::vec3(2.0f)); + namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { return ItemKey::Builder::opaqueShape(); @@ -101,6 +103,7 @@ Avatar::Avatar(RigPointer rig) : _headData = static_cast(new Head(this)); _skeletonModel = std::make_shared(this, nullptr, rig); + _skeletonModel->setSkinnedMeshBound(DEFAULT_AVATAR_SKINNED_MESH_BOUND); } Avatar::~Avatar() { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 1f9a02d8ab..3a27b9304b 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -221,6 +221,8 @@ public: void setEnableInverseKinematics(bool enable); + const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; } + protected: bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } void updateAnimationStateHandlers(); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 312e1fcea5..e0d6c90758 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -54,6 +54,8 @@ void RenderableModelEntityItem::setModelURL(const QString& url) { // Here we reset those guards. This doesn't cause the entity values to change -- it just allows the model to match once it comes in. _model->setScaleToFit(false, getDimensions()); _model->setSnapModelToRegistrationPoint(false, getRegistrationPoint()); + AABox skinnedMeshBound(getPosition() - getDimensions() * getRegistrationPoint(), getDimensions()); + _model->setSkinnedMeshBound(skinnedMeshBound); } ModelEntityItem::setModelURL(url); @@ -76,6 +78,8 @@ void RenderableModelEntityItem::loader() { if (_model) { _model->setURL(getParsedModelURL()); _model->setCollisionModelURL(QUrl(getCompoundShapeURL())); + AABox skinnedMeshBound(getPosition() - getDimensions() * getRegistrationPoint(), getDimensions()); + _model->setSkinnedMeshBound(skinnedMeshBound); } } diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index df86ba2a8f..1c4072b6f3 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -111,7 +111,7 @@ Box Mesh::evalPartBound(int partNum) const { return box; } -Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const { +Box Mesh::evalPartBounds(int partStart, int partEnd) const { Box totalBound; auto part = _partBuffer.cbegin() + partStart; auto partItEnd = _partBuffer.cbegin() + partEnd; diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index f9d9b0eeb4..b8873dba17 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -108,9 +108,9 @@ public: // evaluate the bounding box of A part Box evalPartBound(int partNum) const; - // evaluate the bounding boxes of the parts in the range [start, end[ and fill the bounds parameter + // evaluate the bounding boxes of the parts in the range [start, end] // the returned box is the bounding box of ALL the evaluated part bounds. - Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const; + Box evalPartBounds(int partStart, int partEnd) const; static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast(topo); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 363def05a1..482d630cdb 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -52,17 +52,6 @@ MeshPartPayload::MeshPartPayload(model::MeshPointer mesh, int partIndex, model:: updateTransform(transform, offsetTransform); } -void MeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) { - _drawMesh = drawMesh; - if (_drawMesh) { - auto vertexFormat = _drawMesh->getVertexFormat(); - _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); - _drawPart = _drawMesh->getPartBuffer().get(partIndex); - - _localBound = _drawMesh->evalPartBound(partIndex); - } -} - void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) { _transform = transform; _offsetTransform = offsetTransform; @@ -71,6 +60,16 @@ void MeshPartPayload::updateTransform(const Transform& transform, const Transfor _worldBound.transform(_drawTransform); } +void MeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) { + _drawMesh = drawMesh; + if (_drawMesh) { + auto vertexFormat = _drawMesh->getVertexFormat(); + _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); + _drawPart = _drawMesh->getPartBuffer().get(partIndex); + _localBound = _drawMesh->evalPartBound(partIndex); + } +} + void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) { _drawMaterial = drawMaterial; } @@ -316,10 +315,13 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren } } -ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : +ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, + const Transform& offsetTransform, const AABox& skinnedMeshBound) : _model(model), _meshIndex(_meshIndex), - _shapeID(shapeIndex) { + _shapeID(shapeIndex), + _skinnedMeshBound(skinnedMeshBound) { + auto& modelMesh = _model->_geometry->getMeshes().at(_meshIndex)->_mesh; updateMeshPart(modelMesh, partIndex); @@ -351,6 +353,36 @@ void ModelMeshPartPayload::notifyLocationChanged() { _model->_needsUpdateClusterMatrices = true; } +void ModelMeshPartPayload::updateTransformForRigidlyBoundMesh(const Transform& transform, const Transform& clusterTransform, const Transform& offsetTransform) { + ModelMeshPartPayload::updateTransform(transform, offsetTransform); + + // clusterMatrix has world rotation but not world translation. + Transform worldTranslation, geomToWorld; + worldTranslation.setTranslation(transform.getTranslation()); + Transform::mult(geomToWorld, worldTranslation, clusterTransform); + + // transform the localBound into world space + _worldBound = _localBound; + _worldBound.transform(geomToWorld); +} + +void ModelMeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) { + _drawMesh = drawMesh; + if (_drawMesh) { + auto vertexFormat = _drawMesh->getVertexFormat(); + _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); + _drawPart = _drawMesh->getPartBuffer().get(partIndex); + + // this is a skinned mesh.. + if (vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX)) { + // use the specified skinned bounding box. + _localBound = _skinnedMeshBound; + } else { + _localBound = _drawMesh->evalPartBound(partIndex); + } + } +} + ItemKey ModelMeshPartPayload::getKey() const { ItemKey::Builder builder; builder.withTypeShape(); @@ -373,12 +405,6 @@ ItemKey ModelMeshPartPayload::getKey() const { return builder.build(); } -Item::Bound ModelMeshPartPayload::getBound() const { - // NOTE: we can't cache this bounds because we need to handle the case of a moving - // entity or mesh part. - return _model->getPartBounds(_meshIndex, _partIndex, _transform.getTranslation(), _transform.getRotation()); -} - ShapeKey ModelMeshPartPayload::getShapeKey() const { const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); const std::vector>& networkMeshes = _model->_geometry->getMeshes(); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index feccf7ee64..0aadf9baf8 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -64,6 +64,7 @@ public: bool _hasColorAttrib = false; }; + namespace render { template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload); template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload); @@ -73,16 +74,18 @@ namespace render { class ModelMeshPartPayload : public MeshPartPayload { public: - ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); - + ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, + const Transform& offsetTransform, const AABox& skinnedMeshBound); + typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - void notifyLocationChanged() override; + virtual void updateMeshPart(model::MeshPointer drawMesh, int partIndex) override; + virtual void notifyLocationChanged() override; + void updateTransformForRigidlyBoundMesh(const Transform& transform, const Transform& jointTransform, const Transform& offsetTransform); // Render Item interface render::ItemKey getKey() const override; - render::Item::Bound getBound() const override; render::ShapeKey getShapeKey() const override; // shape interface void render(RenderArgs* args) const override; @@ -99,6 +102,15 @@ public: bool _isSkinned{ false }; bool _isBlendShaped{ false }; + + AABox _skinnedMeshBound; }; +namespace render { + template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload); + template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload); + template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload); + template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args); +} + #endif // hifi_MeshPartPayload_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 0a483914df..e47f8caf65 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -102,6 +102,7 @@ void Model::setRotation(const glm::quat& rotation) { void Model::setScale(const glm::vec3& scale) { setScaleInternal(scale); + // if anyone sets scale manually, then we are no longer scaled to fit _scaleToFit = false; _scaledToFit = false; @@ -130,22 +131,46 @@ void Model::setOffset(const glm::vec3& offset) { void Model::enqueueLocationChange() { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - Transform transform; - transform.setTranslation(_translation); - transform.setRotation(_rotation); + Transform modelTransform; + modelTransform.setScale(_scale); + modelTransform.setTranslation(_translation); + modelTransform.setRotation(_rotation); Transform offset; - offset.setScale(_scale); - offset.postTranslate(_offset); + if (_geometry && _geometry->isLoaded()) { + offset = Transform(_rig->getGeometryToRigTransform()); + } else { + offset.postTranslate(_offset); + } render::PendingChanges pendingChanges; - foreach (auto itemID, _renderItems.keys()) { - pendingChanges.updateItem(itemID, [transform, offset](MeshPartPayload& data) { - data.updateTransform(transform, offset); + foreach (auto itemID, _modelMeshRenderItems.keys()) { + pendingChanges.updateItem(itemID, [modelTransform, offset](ModelMeshPartPayload& data) { + //data._model->updateClusterMatrices(data._transform.getTranslation(), data._transform.getRotation()); + const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); + if (state.clusterBuffer) { + data.updateTransform(modelTransform, offset); + } else { + // HACK: check for bugs... + AnimPose clusterMat(state.clusterMatrices[0]); + + Transform xform; + xform.setScale(clusterMat.scale); + xform.setRotation(clusterMat.rot); + xform.setTranslation(clusterMat.trans); + data.updateTransformForRigidlyBoundMesh(modelTransform, xform, offset); + } data.notifyLocationChanged(); }); } + foreach (auto itemID, _collisionRenderItems.keys()) { + pendingChanges.updateItem(itemID, [modelTransform, offset](MeshPartPayload& data) { + data.updateTransform(modelTransform, offset); + data.notifyLocationChanged(); + }); + } + scene->enqueuePendingChanges(pendingChanges); } @@ -497,8 +522,11 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen _isVisible = newValue; render::PendingChanges pendingChanges; - foreach (auto item, _renderItems.keys()) { - pendingChanges.resetItem(item, _renderItems[item]); + foreach (auto item, _modelMeshRenderItems.keys()) { + pendingChanges.resetItem(item, _modelMeshRenderItems[item]); + } + foreach (auto item, _collisionRenderItems.keys()) { + pendingChanges.resetItem(item, _modelMeshRenderItems[item]); } scene->enqueuePendingChanges(pendingChanges); } @@ -514,14 +542,25 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan bool somethingAdded = false; - foreach (auto renderItem, _renderItemsSet) { + foreach (auto renderItem, _modelMeshRenderItemsSet) { + auto item = scene->allocateID(); + auto renderPayload = std::make_shared(renderItem); + pendingChanges.resetItem(item, renderPayload); + pendingChanges.updateItem(item, [](ModelMeshPartPayload& data) { + data.notifyLocationChanged(); + }); + _modelMeshRenderItems.insert(item, renderPayload); + somethingAdded = true; + } + + foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); pendingChanges.resetItem(item, renderPayload); pendingChanges.updateItem(item, [](MeshPartPayload& data) { data.notifyLocationChanged(); }); - _renderItems.insert(item, renderPayload); + _collisionRenderItems.insert(item, renderPayload); somethingAdded = true; } @@ -541,7 +580,19 @@ bool Model::addToScene(std::shared_ptr scene, bool somethingAdded = false; - foreach (auto renderItem, _renderItemsSet) { + foreach (auto renderItem, _modelMeshRenderItemsSet) { + auto item = scene->allocateID(); + auto renderPayload = std::make_shared(renderItem); + renderPayload->addStatusGetters(statusGetters); + pendingChanges.resetItem(item, renderPayload); + pendingChanges.updateItem(item, [](ModelMeshPartPayload& data) { + data.notifyLocationChanged(); + }); + _modelMeshRenderItems.insert(item, renderPayload); + somethingAdded = true; + } + + foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); renderPayload->addStatusGetters(statusGetters); @@ -549,7 +600,7 @@ bool Model::addToScene(std::shared_ptr scene, pendingChanges.updateItem(item, [](MeshPartPayload& data) { data.notifyLocationChanged(); }); - _renderItems.insert(item, renderPayload); + _collisionRenderItems.insert(item, renderPayload); somethingAdded = true; } @@ -559,11 +610,16 @@ bool Model::addToScene(std::shared_ptr scene, } void Model::removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges) { - foreach (auto item, _renderItems.keys()) { + foreach (auto item, _modelMeshRenderItems.keys()) { pendingChanges.removeItem(item); } - _renderItems.clear(); - _renderItemsSet.clear(); + _modelMeshRenderItems.clear(); + _modelMeshRenderItemsSet.clear(); + foreach (auto item, _collisionRenderItems.keys()) { + pendingChanges.removeItem(item); + } + _collisionRenderItems.clear(); + _collisionRenderItemsSet.clear(); _meshGroupsKnown = false; _readyWhenAdded = false; } @@ -1191,10 +1247,14 @@ void Model::segregateMeshGroups() { return; } - Q_ASSERT(_renderItems.isEmpty()); // We should not have any existing renderItems if we enter this section of code - Q_ASSERT(_renderItemsSet.isEmpty()); // We should not have any existing renderItemsSet if we enter this section of code + // We should not have any existing renderItems if we enter this section of code + Q_ASSERT(_modelMeshRenderItems.isEmpty()); + Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); + Q_ASSERT(_collisionRenderItems.isEmpty()); + Q_ASSERT(_collisionRenderItemsSet.isEmpty()); - _renderItemsSet.clear(); + _modelMeshRenderItemsSet.clear(); + _collisionRenderItemsSet.clear(); Transform transform; transform.setTranslation(_translation); @@ -1220,9 +1280,14 @@ void Model::segregateMeshGroups() { _collisionHullMaterial->setMetallic(0.02f); _collisionHullMaterial->setRoughness(0.5f); } - _renderItemsSet << std::make_shared(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset); + _collisionRenderItemsSet << std::make_shared(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset); } else { - _renderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); + AABox geometrySkinnedMeshBound = _skinnedMeshBound; + + // transform bound from model into geometry space. + geometrySkinnedMeshBound.transform(Transform(glm::inverse(_rig->getGeometryToRigTransform()))); + + _modelMeshRenderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset, geometrySkinnedMeshBound); } shapeID++; @@ -1245,13 +1310,21 @@ bool Model::initWhenReady(render::ScenePointer scene) { offset.setScale(_scale); offset.postTranslate(_offset); - foreach (auto renderItem, _renderItemsSet) { + foreach (auto renderItem, _modelMeshRenderItemsSet) { + auto item = scene->allocateID(); + auto renderPayload = std::make_shared(renderItem); + _modelMeshRenderItems.insert(item, renderPayload); + pendingChanges.resetItem(item, renderPayload); + pendingChanges.updateItem(item, [transform, offset](MeshPartPayload& data) { + data.notifyLocationChanged(); + }); + } + foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); - _renderItems.insert(item, renderPayload); + _collisionRenderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); - pendingChanges.updateItem(item, [transform,offset](MeshPartPayload& data) { - data.updateTransform(transform, offset); + pendingChanges.updateItem(item, [transform, offset](MeshPartPayload& data) { data.notifyLocationChanged(); }); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 4e51dc4f33..019795f116 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -44,6 +44,7 @@ namespace render { typedef unsigned int ItemID; } class MeshPartPayload; +class ModelMeshPartPayload; class ModelRenderLocations; inline uint qHash(const std::shared_ptr& a, uint seed) { @@ -222,6 +223,9 @@ public: const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } + // bounding box used for mesh that is influnced by multiple animated bones. + void setSkinnedMeshBound(const AABox& skinnedMeshBound) { _skinnedMeshBound = skinnedMeshBound; } + protected: void setPupilDilation(float dilation) { _pupilDilation = dilation; } @@ -371,8 +375,12 @@ protected: bool _renderCollisionHull; - QSet> _renderItemsSet; - QMap _renderItems; + QSet> _collisionRenderItemsSet; + QMap _collisionRenderItems; + + QSet> _modelMeshRenderItemsSet; + QMap _modelMeshRenderItems; + bool _readyWhenAdded { false }; bool _needsReload { true }; bool _needsUpdateClusterMatrices { true }; @@ -382,6 +390,9 @@ protected: friend class ModelMeshPartPayload; RigPointer _rig; + + // 2 meter^3 box + AABox _skinnedMeshBound { glm::vec3(-1.0, -1.0, -1.0), glm::vec3(2.0f) }; }; Q_DECLARE_METATYPE(ModelPointer) diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index 0b453e6f36..786ef40257 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -17,6 +17,8 @@ #include "GeometryUtil.h" #include "NumericalConstants.h" +const glm::vec3 INFINITY_VECTOR(std::numeric_limits::infinity()); + AABox::AABox(const AACube& other) : _corner(other.getCorner()), _scale(other.getScale(), other.getScale(), other.getScale()) { } @@ -34,7 +36,7 @@ AABox::AABox(const glm::vec3& corner, const glm::vec3& dimensions) : _corner(corner), _scale(dimensions) { }; -AABox::AABox() : _corner(std::numeric_limits::infinity()), _scale(0.0f) { +AABox::AABox() : _corner(INFINITY_VECTOR), _scale(0.0f) { }; glm::vec3 AABox::calcCenter() const { @@ -475,8 +477,15 @@ AABox AABox::clamp(float min, float max) const { } AABox& AABox::operator += (const glm::vec3& point) { - _corner = glm::min(_corner, point); - _scale = glm::max(_scale, point - _corner); + + if (_corner == INFINITY_VECTOR) { + _corner = glm::min(_corner, point); + } else { + glm::vec3 maximum(_corner + _scale); + _corner = glm::min(_corner, point); + maximum = glm::max(maximum, point); + _scale = maximum - _corner; + } return (*this); } @@ -489,11 +498,25 @@ AABox& AABox::operator += (const AABox& box) { return (*this); } -void AABox::scale(const glm::vec3& scale) { +void AABox::embiggen(float scale) { + _corner += scale * (-0.5f * _scale); + _scale *= scale; +} + +void AABox::embiggen(const glm::vec3& scale) { + _corner += scale * (-0.5f * _scale); + _scale *= scale; +} + +void AABox::scale(float scale) { _corner *= scale; _scale *= scale; } +void AABox::scale(const glm::vec3& scale) { + _corner *= scale; + _scale *= scale; +} void AABox::rotate(const glm::quat& rotation) { auto minimum = _corner; diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index ec06c60121..000d4be734 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -58,7 +58,6 @@ public: const glm::vec3& getMinimumPoint() const { return _corner; } glm::vec3 getMaximumPoint() const { return calcTopFarLeft(); } - bool contains(const glm::vec3& point) const; bool contains(const AABox& otherBox) const; bool touches(const AABox& otherBox) const; @@ -93,6 +92,10 @@ public: void scale(float scale); void scale(const glm::vec3& scale); + /// make the AABox bigger (scale about it's center) + void embiggen(float scale); + void embiggen(const glm::vec3& scale); + // Transform the extents with transform void transform(const Transform& transform); diff --git a/tests/shared/src/AABoxTests.cpp b/tests/shared/src/AABoxTests.cpp index fd709a488c..41f05616c4 100644 --- a/tests/shared/src/AABoxTests.cpp +++ b/tests/shared/src/AABoxTests.cpp @@ -151,3 +151,15 @@ void AABoxTests::testTouchesSphere() { } } +void AABoxTests::testScale() { + AABox box(glm::vec3(2.0f), glm::vec3(1.0f)); + QCOMPARE(box.contains(glm::vec3(0.0f)), false); + box.scale(glm::vec3(10.0f)); + QCOMPARE(box.contains(glm::vec3(0.0f)), false); + QCOMPARE(box.contains(glm::vec3(2.0f * 10.0f)), true); + + AABox box(glm::vec3(2.0f), glm::vec3(1.0f)); + QCOMPARE(box.contains(glm::vec3(0.0f)), false); + box.embiggen(glm::vec3(10.0f)); + QCOMPARE(box.contains(glm::vec3(0.0f)), false); +} diff --git a/tests/shared/src/AABoxTests.h b/tests/shared/src/AABoxTests.h index fe7afede57..c777f8e94f 100644 --- a/tests/shared/src/AABoxTests.h +++ b/tests/shared/src/AABoxTests.h @@ -23,6 +23,7 @@ private slots: void testCtorsAndSetters(); void testContainsPoint(); void testTouchesSphere(); + void testScale(); }; #endif // hifi_AABoxTests_h From bf433487fadac3b94187ac54ff9432d8ef689a49 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 26 Mar 2016 12:09:36 -0700 Subject: [PATCH 02/10] Dynamic bound update for skinned mesh * Use all cluster matrices to compute bound for skinned mesh. This is far less expensive then doing per-vertex work, but it's not free, for avatars especially. * Remove skinnedMeshBound, compute it instead. * Compute clusterMatrices in render update, because we need them to update bounds. --- interface/src/avatar/Avatar.cpp | 1 - .../src/RenderableModelEntityItem.cpp | 4 -- .../render-utils/src/MeshPartPayload.cpp | 38 +++++--------- libraries/render-utils/src/MeshPartPayload.h | 7 +-- libraries/render-utils/src/Model.cpp | 25 +++------- libraries/render-utils/src/Model.h | 6 --- libraries/shared/src/AABox.cpp | 50 +++++++++++++++++-- libraries/shared/src/AABox.h | 7 ++- 8 files changed, 73 insertions(+), 65 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 24e786caa2..07227d2f68 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -103,7 +103,6 @@ Avatar::Avatar(RigPointer rig) : _headData = static_cast(new Head(this)); _skeletonModel = std::make_shared(this, nullptr, rig); - _skeletonModel->setSkinnedMeshBound(DEFAULT_AVATAR_SKINNED_MESH_BOUND); } Avatar::~Avatar() { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e0d6c90758..312e1fcea5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -54,8 +54,6 @@ void RenderableModelEntityItem::setModelURL(const QString& url) { // Here we reset those guards. This doesn't cause the entity values to change -- it just allows the model to match once it comes in. _model->setScaleToFit(false, getDimensions()); _model->setSnapModelToRegistrationPoint(false, getRegistrationPoint()); - AABox skinnedMeshBound(getPosition() - getDimensions() * getRegistrationPoint(), getDimensions()); - _model->setSkinnedMeshBound(skinnedMeshBound); } ModelEntityItem::setModelURL(url); @@ -78,8 +76,6 @@ void RenderableModelEntityItem::loader() { if (_model) { _model->setURL(getParsedModelURL()); _model->setCollisionModelURL(QUrl(getCompoundShapeURL())); - AABox skinnedMeshBound(getPosition() - getDimensions() * getRegistrationPoint(), getDimensions()); - _model->setSkinnedMeshBound(skinnedMeshBound); } } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 482d630cdb..bfa6c48857 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -316,11 +316,10 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren } ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, - const Transform& offsetTransform, const AABox& skinnedMeshBound) : + const Transform& offsetTransform) : _model(model), _meshIndex(_meshIndex), - _shapeID(shapeIndex), - _skinnedMeshBound(skinnedMeshBound) { + _shapeID(shapeIndex) { auto& modelMesh = _model->_geometry->getMeshes().at(_meshIndex)->_mesh; updateMeshPart(modelMesh, partIndex); @@ -353,33 +352,20 @@ void ModelMeshPartPayload::notifyLocationChanged() { _model->_needsUpdateClusterMatrices = true; } -void ModelMeshPartPayload::updateTransformForRigidlyBoundMesh(const Transform& transform, const Transform& clusterTransform, const Transform& offsetTransform) { +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const glm::mat4* clusterMatrices, size_t numClusterMatrices) { ModelMeshPartPayload::updateTransform(transform, offsetTransform); - // clusterMatrix has world rotation but not world translation. - Transform worldTranslation, geomToWorld; - worldTranslation.setTranslation(transform.getTranslation()); - Transform::mult(geomToWorld, worldTranslation, clusterTransform); + if (numClusterMatrices > 0) { - // transform the localBound into world space - _worldBound = _localBound; - _worldBound.transform(geomToWorld); -} - -void ModelMeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) { - _drawMesh = drawMesh; - if (_drawMesh) { - auto vertexFormat = _drawMesh->getVertexFormat(); - _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); - _drawPart = _drawMesh->getPartBuffer().get(partIndex); - - // this is a skinned mesh.. - if (vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX)) { - // use the specified skinned bounding box. - _localBound = _skinnedMeshBound; - } else { - _localBound = _drawMesh->evalPartBound(partIndex); + _worldBound = AABox(); + for (size_t i = 0; i < numClusterMatrices; i++) { + AABox clusterBound = _localBound; + clusterBound.transform(clusterMatrices[i]); + _worldBound += clusterBound; } + + // clusterMatrix has world rotation but not world translation. + _worldBound.translate(transform.getTranslation()); } } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 0aadf9baf8..7b059afadd 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -75,14 +75,13 @@ namespace render { class ModelMeshPartPayload : public MeshPartPayload { public: ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, - const Transform& offsetTransform, const AABox& skinnedMeshBound); + const Transform& offsetTransform); typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - virtual void updateMeshPart(model::MeshPointer drawMesh, int partIndex) override; virtual void notifyLocationChanged() override; - void updateTransformForRigidlyBoundMesh(const Transform& transform, const Transform& jointTransform, const Transform& offsetTransform); + void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const glm::mat4* clusterMatrices, size_t numClusterMatrices); // Render Item interface render::ItemKey getKey() const override; @@ -102,8 +101,6 @@ public: bool _isSkinned{ false }; bool _isBlendShaped{ false }; - - AABox _skinnedMeshBound; }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e47f8caf65..b463a80403 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -146,20 +146,12 @@ void Model::enqueueLocationChange() { render::PendingChanges pendingChanges; foreach (auto itemID, _modelMeshRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, offset](ModelMeshPartPayload& data) { - //data._model->updateClusterMatrices(data._transform.getTranslation(), data._transform.getRotation()); - const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - if (state.clusterBuffer) { - data.updateTransform(modelTransform, offset); - } else { - // HACK: check for bugs... - AnimPose clusterMat(state.clusterMatrices[0]); - Transform xform; - xform.setScale(clusterMat.scale); - xform.setRotation(clusterMat.rot); - xform.setTranslation(clusterMat.trans); - data.updateTransformForRigidlyBoundMesh(modelTransform, xform, offset); - } + data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation()); + const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); + size_t numClusterMatrices = data._model->getGeometry()->getFBXGeometry().meshes.at(data._meshIndex).clusters.size(); + + data.updateTransformForSkinnedMesh(modelTransform, offset, &state.clusterMatrices[0], numClusterMatrices); data.notifyLocationChanged(); }); } @@ -1282,12 +1274,7 @@ void Model::segregateMeshGroups() { } _collisionRenderItemsSet << std::make_shared(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset); } else { - AABox geometrySkinnedMeshBound = _skinnedMeshBound; - - // transform bound from model into geometry space. - geometrySkinnedMeshBound.transform(Transform(glm::inverse(_rig->getGeometryToRigTransform()))); - - _modelMeshRenderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset, geometrySkinnedMeshBound); + _modelMeshRenderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); } shapeID++; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 019795f116..d9eb20cb32 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -223,9 +223,6 @@ public: const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } - // bounding box used for mesh that is influnced by multiple animated bones. - void setSkinnedMeshBound(const AABox& skinnedMeshBound) { _skinnedMeshBound = skinnedMeshBound; } - protected: void setPupilDilation(float dilation) { _pupilDilation = dilation; } @@ -390,9 +387,6 @@ protected: friend class ModelMeshPartPayload; RigPointer _rig; - - // 2 meter^3 box - AABox _skinnedMeshBound { glm::vec3(-1.0, -1.0, -1.0), glm::vec3(2.0f) }; }; Q_DECLARE_METATYPE(ModelPointer) diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index 786ef40257..a3afa220c9 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -17,7 +17,7 @@ #include "GeometryUtil.h" #include "NumericalConstants.h" -const glm::vec3 INFINITY_VECTOR(std::numeric_limits::infinity()); +const glm::vec3 AABox::INFINITY_VECTOR(std::numeric_limits::infinity()); AABox::AABox(const AACube& other) : _corner(other.getCorner()), _scale(other.getScale(), other.getScale(), other.getScale()) { @@ -478,7 +478,7 @@ AABox AABox::clamp(float min, float max) const { AABox& AABox::operator += (const glm::vec3& point) { - if (_corner == INFINITY_VECTOR) { + if (isInvalid()) { _corner = glm::min(_corner, point); } else { glm::vec3 maximum(_corner + _scale); @@ -493,7 +493,7 @@ AABox& AABox::operator += (const glm::vec3& point) { AABox& AABox::operator += (const AABox& box) { if (!box.isInvalid()) { (*this) += box._corner; - _scale = glm::max(_scale, box.calcTopFarLeft() - _corner); + (*this) += box.calcTopFarLeft(); } return (*this); } @@ -567,3 +567,47 @@ void AABox::transform(const Transform& transform) { rotate(transform.getRotation()); translate(transform.getTranslation()); } + +void AABox::transform(const glm::mat4& matrix) { + auto minimum = _corner; + auto maximum = _corner + _scale; + + glm::vec3 bottomLeftNear(minimum.x, minimum.y, minimum.z); + glm::vec3 bottomRightNear(maximum.x, minimum.y, minimum.z); + glm::vec3 bottomLeftFar(minimum.x, minimum.y, maximum.z); + glm::vec3 bottomRightFar(maximum.x, minimum.y, maximum.z); + glm::vec3 topLeftNear(minimum.x, maximum.y, minimum.z); + glm::vec3 topRightNear(maximum.x, maximum.y, minimum.z); + glm::vec3 topLeftFar(minimum.x, maximum.y, maximum.z); + glm::vec3 topRightFar(maximum.x, maximum.y, maximum.z); + + glm::vec3 bottomLeftNearTransformed = transformPoint(matrix, bottomLeftNear); + glm::vec3 bottomRightNearTransformed = transformPoint(matrix, bottomRightNear); + glm::vec3 bottomLeftFarTransformed = transformPoint(matrix, bottomLeftFar); + glm::vec3 bottomRightFarTransformed = transformPoint(matrix, bottomRightFar); + glm::vec3 topLeftNearTransformed = transformPoint(matrix, topLeftNear); + glm::vec3 topRightNearTransformed = transformPoint(matrix, topRightNear); + glm::vec3 topLeftFarTransformed = transformPoint(matrix, topLeftFar); + glm::vec3 topRightFarTransformed = transformPoint(matrix, topRightFar); + + minimum = glm::min(bottomLeftNearTransformed, + glm::min(bottomRightNearTransformed, + glm::min(bottomLeftFarTransformed, + glm::min(bottomRightFarTransformed, + glm::min(topLeftNearTransformed, + glm::min(topRightNearTransformed, + glm::min(topLeftFarTransformed, + topRightFarTransformed))))))); + + maximum = glm::max(bottomLeftNearTransformed, + glm::max(bottomRightNearTransformed, + glm::max(bottomLeftFarTransformed, + glm::max(bottomRightFarTransformed, + glm::max(topLeftNearTransformed, + glm::max(topRightNearTransformed, + glm::max(topLeftFarTransformed, + topRightFarTransformed))))))); + + _corner = minimum; + _scale = maximum - minimum; +} diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index 000d4be734..30bde2d717 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -99,7 +99,12 @@ public: // Transform the extents with transform void transform(const Transform& transform); - bool isInvalid() const { return _corner == glm::vec3(std::numeric_limits::infinity()); } + // Transform the extents with matrix + void transform(const glm::mat4& matrix); + + static const glm::vec3 INFINITY_VECTOR; + + bool isInvalid() const { return _corner == INFINITY_VECTOR; } private: glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const; From 49a4d104a69a316f9ed2cbc5954e61e4c2612f8d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 26 Mar 2016 13:27:05 -0700 Subject: [PATCH 03/10] Minimize diff with master --- interface/src/avatar/Avatar.cpp | 2 -- .../render-utils/src/MeshPartPayload.cpp | 20 +++++++++---------- libraries/render-utils/src/MeshPartPayload.h | 6 ++---- libraries/render-utils/src/Model.cpp | 1 - 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 07227d2f68..f722210a8e 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -56,8 +56,6 @@ const float DISPLAYNAME_ALPHA = 1.0f; const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); -const AABox DEFAULT_AVATAR_SKINNED_MESH_BOUND(glm::vec3(-1.0, -1.0, -1.0), glm::vec3(2.0f)); - namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { return ItemKey::Builder::opaqueShape(); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index bfa6c48857..8d738e44d8 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -52,14 +52,6 @@ MeshPartPayload::MeshPartPayload(model::MeshPointer mesh, int partIndex, model:: updateTransform(transform, offsetTransform); } -void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) { - _transform = transform; - _offsetTransform = offsetTransform; - Transform::mult(_drawTransform, _transform, _offsetTransform); - _worldBound = _localBound; - _worldBound.transform(_drawTransform); -} - void MeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) { _drawMesh = drawMesh; if (_drawMesh) { @@ -70,6 +62,14 @@ void MeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) } } +void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) { + _transform = transform; + _offsetTransform = offsetTransform; + Transform::mult(_drawTransform, _transform, _offsetTransform); + _worldBound = _localBound; + _worldBound.transform(_drawTransform); +} + void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) { _drawMaterial = drawMaterial; } @@ -315,12 +315,10 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren } } -ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, - const Transform& offsetTransform) : +ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : _model(model), _meshIndex(_meshIndex), _shapeID(shapeIndex) { - auto& modelMesh = _model->_geometry->getMeshes().at(_meshIndex)->_mesh; updateMeshPart(modelMesh, partIndex); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 7b059afadd..546c0938fa 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -64,7 +64,6 @@ public: bool _hasColorAttrib = false; }; - namespace render { template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload); template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload); @@ -74,13 +73,12 @@ namespace render { class ModelMeshPartPayload : public MeshPartPayload { public: - ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, - const Transform& offsetTransform); + ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - virtual void notifyLocationChanged() override; + void notifyLocationChanged() override; void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const glm::mat4* clusterMatrices, size_t numClusterMatrices); // Render Item interface diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b463a80403..38b7aa2e89 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -102,7 +102,6 @@ void Model::setRotation(const glm::quat& rotation) { void Model::setScale(const glm::vec3& scale) { setScaleInternal(scale); - // if anyone sets scale manually, then we are no longer scaled to fit _scaleToFit = false; _scaledToFit = false; From c671fe796694dc216a88dc71e222ff2534021444 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 26 Mar 2016 13:36:56 -0700 Subject: [PATCH 04/10] Updated AABox tests --- tests/shared/src/AABoxTests.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/shared/src/AABoxTests.cpp b/tests/shared/src/AABoxTests.cpp index 41f05616c4..b9ab95bb09 100644 --- a/tests/shared/src/AABoxTests.cpp +++ b/tests/shared/src/AABoxTests.cpp @@ -152,14 +152,20 @@ void AABoxTests::testTouchesSphere() { } void AABoxTests::testScale() { - AABox box(glm::vec3(2.0f), glm::vec3(1.0f)); - QCOMPARE(box.contains(glm::vec3(0.0f)), false); - box.scale(glm::vec3(10.0f)); - QCOMPARE(box.contains(glm::vec3(0.0f)), false); - QCOMPARE(box.contains(glm::vec3(2.0f * 10.0f)), true); + AABox box1(glm::vec3(2.0f), glm::vec3(1.0f)); + QCOMPARE(box1.contains(glm::vec3(0.0f)), false); + box1.scale(glm::vec3(10.0f)); + QCOMPARE(box1.contains(glm::vec3(0.0f)), false); + QCOMPARE(box1.contains(glm::vec3(2.0f * 10.0f)), true); - AABox box(glm::vec3(2.0f), glm::vec3(1.0f)); - QCOMPARE(box.contains(glm::vec3(0.0f)), false); - box.embiggen(glm::vec3(10.0f)); - QCOMPARE(box.contains(glm::vec3(0.0f)), false); + AABox box2(glm::vec3(2.0f), glm::vec3(1.0f)); + QCOMPARE(box2.contains(glm::vec3(0.0f)), false); + box2.embiggen(glm::vec3(10.0f)); + QCOMPARE(box2.contains(glm::vec3(0.0f)), true); + + AABox box3; + box3 += glm::vec3(0.0f, 0.0f, 0.0f); + box3 += glm::vec3(1.0f, 1.0f, 1.0f); + box3 += glm::vec3(-1.0f, -1.0f, -1.0f); + QCOMPARE(box3.contains(glm::vec3(0.5f, 0.5f, 0.5f)), true); } From f5a86666a14a93667be7f21ed64c982240c341d5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 28 Mar 2016 09:56:15 -0700 Subject: [PATCH 05/10] Model: fix for collision mesh rendering --- libraries/render-utils/src/Model.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 38b7aa2e89..55aa000ef4 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -135,29 +135,33 @@ void Model::enqueueLocationChange() { modelTransform.setTranslation(_translation); modelTransform.setRotation(_rotation); - Transform offset; + Transform modelMeshOffset; if (_geometry && _geometry->isLoaded()) { - offset = Transform(_rig->getGeometryToRigTransform()); + modelMeshOffset = Transform(_rig->getGeometryToRigTransform()); } else { - offset.postTranslate(_offset); + modelMeshOffset.postTranslate(_offset); } + Transform collisionMeshOffset; + collisionMeshOffset.postTranslate(_offset); + + render::PendingChanges pendingChanges; foreach (auto itemID, _modelMeshRenderItems.keys()) { - pendingChanges.updateItem(itemID, [modelTransform, offset](ModelMeshPartPayload& data) { + pendingChanges.updateItem(itemID, [modelTransform, modelMeshOffset](ModelMeshPartPayload& data) { data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation()); const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); size_t numClusterMatrices = data._model->getGeometry()->getFBXGeometry().meshes.at(data._meshIndex).clusters.size(); - data.updateTransformForSkinnedMesh(modelTransform, offset, &state.clusterMatrices[0], numClusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, &state.clusterMatrices[0], numClusterMatrices); data.notifyLocationChanged(); }); } foreach (auto itemID, _collisionRenderItems.keys()) { - pendingChanges.updateItem(itemID, [modelTransform, offset](MeshPartPayload& data) { - data.updateTransform(modelTransform, offset); + pendingChanges.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { + data.updateTransform(modelTransform, collisionMeshOffset); data.notifyLocationChanged(); }); } From e77cf544830713c26027275b8890deb2124b28fc Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 28 Mar 2016 13:29:28 -0700 Subject: [PATCH 06/10] Geometry.cpp: rename method --- libraries/model/src/model/Geometry.cpp | 2 +- libraries/model/src/model/Geometry.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 1c4072b6f3..2bb6cfa436 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -111,7 +111,7 @@ Box Mesh::evalPartBound(int partNum) const { return box; } -Box Mesh::evalPartBounds(int partStart, int partEnd) const { +Box Mesh::evalPartsBound(int partStart, int partEnd) const { Box totalBound; auto part = _partBuffer.cbegin() + partStart; auto partItEnd = _partBuffer.cbegin() + partEnd; diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index b8873dba17..a3e95c68c0 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -109,8 +109,8 @@ public: // evaluate the bounding box of A part Box evalPartBound(int partNum) const; // evaluate the bounding boxes of the parts in the range [start, end] - // the returned box is the bounding box of ALL the evaluated part bounds. - Box evalPartBounds(int partStart, int partEnd) const; + // the returned box is the bounding box of ALL the evaluated parts bound. + Box evalPartsBound(int partStart, int partEnd) const; static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast(topo); } From 115fd607a0912d8c48e2a82e60271607378c3be3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 28 Mar 2016 19:47:30 -0700 Subject: [PATCH 07/10] Address performance issues introduced with this PR. * Prevent clusterMatrices from being invalidated and re-computed in each updateItem lambda. We do this by not setting _model->_needsUpdateClusterMatrices = true; * Prevent redundant work if Model::enqueueLocationChange is called multiple times per frame. We do this by introducing a preRenderLambdas map in the Application class. Instead of adding work directly to the scene PendingChanges queue Model::enqueueLocationChange adds a lambda to the Application preRenderLambdas map. The Application ensures that only one lambda will be invoked for each model per frame. --- interface/src/Application.cpp | 15 ++++ interface/src/Application.h | 5 ++ .../src/AbstractViewStateInterface.h | 2 + .../render-utils/src/MeshPartPayload.cpp | 2 +- libraries/render-utils/src/Model.cpp | 76 +++++++++++-------- 5 files changed, 69 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 381816e81f..84794f7c31 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2985,6 +2985,11 @@ void Application::updateLOD() { } } +void Application::pushPreRenderLambda(void* key, std::function func) { + std::unique_lock guard(_preRenderLambdasLock); + _preRenderLambdas[key] = func; +} + // Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone. // (Maybe this code should be moved there?) // The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition(). @@ -3461,6 +3466,16 @@ void Application::update(float deltaTime) { QMetaObject::invokeMethod(DependencyManager::get().data(), "sendDownstreamAudioStatsPacket", Qt::QueuedConnection); } } + + { + PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0); + + std::unique_lock guard(_preRenderLambdasLock); + for (auto& iter : _preRenderLambdas) { + iter.second(); + } + _preRenderLambdas.clear(); + } } diff --git a/interface/src/Application.h b/interface/src/Application.h index d21e647bc7..9124755011 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -211,6 +211,8 @@ public: render::EnginePointer getRenderEngine() override { return _renderEngine; } gpu::ContextPointer getGPUContext() const { return _gpuContext; } + virtual void pushPreRenderLambda(void* key, std::function func) override; + const QRect& getMirrorViewRect() const { return _mirrorViewRect; } void updateMyAvatarLookAtPosition(); @@ -510,6 +512,9 @@ private: bool _cursorNeedsChanging { false }; QThread* _deadlockWatchdogThread; + + std::map> _preRenderLambdas; + std::mutex _preRenderLambdasLock; }; #endif // hifi_Application_h diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h index 815cb45423..7c7c263562 100644 --- a/libraries/render-utils/src/AbstractViewStateInterface.h +++ b/libraries/render-utils/src/AbstractViewStateInterface.h @@ -46,6 +46,8 @@ public: virtual render::ScenePointer getMain3DScene() = 0; virtual render::EnginePointer getRenderEngine() = 0; + virtual void pushPreRenderLambda(void* key, std::function func) = 0; + // FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface static AbstractViewStateInterface* instance(); static void setInstance(AbstractViewStateInterface* instance); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 8d738e44d8..41352f1532 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -347,7 +347,7 @@ void ModelMeshPartPayload::initCache() { void ModelMeshPartPayload::notifyLocationChanged() { - _model->_needsUpdateClusterMatrices = true; + } void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const glm::mat4* clusterMatrices, size_t numClusterMatrices) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 55aa000ef4..87534ffac1 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -128,45 +128,61 @@ void Model::setOffset(const glm::vec3& offset) { } void Model::enqueueLocationChange() { - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - Transform modelTransform; - modelTransform.setScale(_scale); - modelTransform.setTranslation(_translation); - modelTransform.setRotation(_rotation); + // queue up this work for later processing, at the end of update and just before rendering. + // the application will ensure only the last lambda is actually invoked. + void* key = (void*)this; + std::weak_ptr weakSelf = shared_from_this(); + AbstractViewStateInterface::instance()->pushPreRenderLambda(key, [weakSelf]() { - Transform modelMeshOffset; - if (_geometry && _geometry->isLoaded()) { - modelMeshOffset = Transform(_rig->getGeometryToRigTransform()); - } else { - modelMeshOffset.postTranslate(_offset); - } + // do nothing, if the model has already been destroyed. + auto self = weakSelf.lock(); + if (!self) { + return; + } - Transform collisionMeshOffset; - collisionMeshOffset.postTranslate(_offset); + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + Transform modelTransform; + modelTransform.setScale(self->_scale); + modelTransform.setTranslation(self->_translation); + modelTransform.setRotation(self->_rotation); - render::PendingChanges pendingChanges; - foreach (auto itemID, _modelMeshRenderItems.keys()) { - pendingChanges.updateItem(itemID, [modelTransform, modelMeshOffset](ModelMeshPartPayload& data) { + Transform modelMeshOffset; + if (self->_geometry && self->_geometry->isLoaded()) { + // includes model offset and unitScale. + modelMeshOffset = Transform(self->_rig->getGeometryToRigTransform()); + } else { + modelMeshOffset.postTranslate(self->_offset); + } - data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation()); - const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - size_t numClusterMatrices = data._model->getGeometry()->getFBXGeometry().meshes.at(data._meshIndex).clusters.size(); + // only apply offset only, collision mesh does not share the same unit scale as the FBX file's mesh. + Transform collisionMeshOffset; + collisionMeshOffset.postTranslate(self->_offset); - data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, &state.clusterMatrices[0], numClusterMatrices); - data.notifyLocationChanged(); - }); - } + render::PendingChanges pendingChanges; + foreach (auto itemID, self->_modelMeshRenderItems.keys()) { + pendingChanges.updateItem(itemID, [modelTransform, modelMeshOffset](ModelMeshPartPayload& data) { - foreach (auto itemID, _collisionRenderItems.keys()) { - pendingChanges.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { - data.updateTransform(modelTransform, collisionMeshOffset); - data.notifyLocationChanged(); - }); - } + // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. + data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation()); - scene->enqueuePendingChanges(pendingChanges); + // update the model transform and bounding box for this render item. + const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); + size_t numClusterMatrices = data._model->getGeometry()->getFBXGeometry().meshes.at(data._meshIndex).clusters.size(); + data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, &state.clusterMatrices[0], numClusterMatrices); + }); + } + + foreach (auto itemID, self->_collisionRenderItems.keys()) { + pendingChanges.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { + // update the model transform for this render item. + data.updateTransform(modelTransform, collisionMeshOffset); + }); + } + + scene->enqueuePendingChanges(pendingChanges); + }); } void Model::initJointTransforms() { From 24ca5b3d60a7fe0919d88e74578fe71f71ede623 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 28 Mar 2016 20:29:02 -0700 Subject: [PATCH 08/10] Update after merge changes to NetworkGeometry. Also cleaned up API for ModelMeshPartPayload::updateTransformForSkinnedMesh() to pass a QVector const ref, instead of a raw pointer and a size. --- libraries/render-utils/src/MeshPartPayload.cpp | 9 ++++----- libraries/render-utils/src/MeshPartPayload.h | 2 +- libraries/render-utils/src/Model.cpp | 5 ++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 8a498e99e3..d295b07caf 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -354,15 +354,14 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const glm::mat4* clusterMatrices, size_t numClusterMatrices) { +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices) { ModelMeshPartPayload::updateTransform(transform, offsetTransform); - if (numClusterMatrices > 0) { - + if (clusterMatrices.size() > 0) { _worldBound = AABox(); - for (size_t i = 0; i < numClusterMatrices; i++) { + for (auto& clusterMatrix : clusterMatrices) { AABox clusterBound = _localBound; - clusterBound.transform(clusterMatrices[i]); + clusterBound.transform(clusterMatrix); _worldBound += clusterBound; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 08c606271c..41869ec7e3 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -79,7 +79,7 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const glm::mat4* clusterMatrices, size_t numClusterMatrices); + void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices); // Render Item interface render::ItemKey getKey() const override; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a29c090bfc..8d59d5f736 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -151,7 +151,7 @@ void Model::enqueueLocationChange() { modelTransform.setRotation(self->_rotation); Transform modelMeshOffset; - if (self->_geometry && self->_geometry->isLoaded()) { + if (self->isLoaded()) { // includes model offset and unitScale. modelMeshOffset = Transform(self->_rig->getGeometryToRigTransform()); } else { @@ -171,8 +171,7 @@ void Model::enqueueLocationChange() { // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - size_t numClusterMatrices = data._model->getGeometry()->getFBXGeometry().meshes.at(data._meshIndex).clusters.size(); - data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, &state.clusterMatrices[0], numClusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, state.clusterMatrices); }); } From 80dfed77d75fa3fe2b13ce34d9e310ee1b38eebf Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 29 Mar 2016 10:01:10 -0700 Subject: [PATCH 09/10] Fix for flickering eyeballs Calling glm::axis() on an identity quaternion does not result in a normalized vector. This vector was used within Rig::updateEyeJoint() to limit the rotation of the eye balls, to prevent the eyes from rolling back into the avatar's head. If the avatar was looking straight ahead, this could result in bad quaternions in the eye ball joint matrices, which in turn would cause the eye ball mesh or any mesh influenced by the eyeball joints not to render. --- libraries/animation/src/Rig.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index a2b664d064..6a8f190808 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1056,7 +1056,9 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm // limit rotation const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; - deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat)); + if (fabsf(glm::angle(deltaQuat)) > MAX_ANGLE) { + deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat)); + } // directly set absolutePose rotation _internalPoseSet._absolutePoses[index].rot = deltaQuat * headQuat; From a2c520a74a7b0ad982c95e8dcb1f0c948d8306e4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 29 Mar 2016 10:47:26 -0700 Subject: [PATCH 10/10] ModelOverlay: use a shared_ptr not a model member This is necessary for shared_ptr and weak_ptr to work for Models contained within a ModelOverlay. --- interface/src/ui/overlays/ModelOverlay.cpp | 44 +++++++++++----------- interface/src/ui/overlays/ModelOverlay.h | 4 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 010698800b..adf08934f0 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -18,22 +18,22 @@ QString const ModelOverlay::TYPE = "model"; ModelOverlay::ModelOverlay() - : _model(std::make_shared()), + : _model(std::make_shared(std::make_shared())), _modelTextures(QVariantMap()), _updateModel(false) { - _model.init(); + _model->init(); _isLoaded = false; } ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(std::make_shared()), + _model(std::make_shared(std::make_shared())), _modelTextures(QVariantMap()), _url(modelOverlay->_url), _updateModel(false) { - _model.init(); + _model->init(); if (_url.isValid()) { _updateModel = true; _isLoaded = false; @@ -44,27 +44,27 @@ void ModelOverlay::update(float deltatime) { if (_updateModel) { _updateModel = false; - _model.setSnapModelToCenter(true); - _model.setScale(getDimensions()); - _model.setRotation(getRotation()); - _model.setTranslation(getPosition()); - _model.setURL(_url); - _model.simulate(deltatime, true); + _model->setSnapModelToCenter(true); + _model->setScale(getDimensions()); + _model->setRotation(getRotation()); + _model->setTranslation(getPosition()); + _model->setURL(_url); + _model->simulate(deltatime, true); } else { - _model.simulate(deltatime); + _model->simulate(deltatime); } - _isLoaded = _model.isActive(); + _isLoaded = _model->isActive(); } bool ModelOverlay::addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) { Volume3DOverlay::addToScene(overlay, scene, pendingChanges); - _model.addToScene(scene, pendingChanges); + _model->addToScene(scene, pendingChanges); return true; } void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) { Volume3DOverlay::removeFromScene(overlay, scene, pendingChanges); - _model.removeFromScene(scene, pendingChanges); + _model->removeFromScene(scene, pendingChanges); } void ModelOverlay::render(RenderArgs* args) { @@ -73,9 +73,9 @@ void ModelOverlay::render(RenderArgs* args) { // fix them up in the scene render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; - if (_model.needsFixupInScene()) { - _model.removeFromScene(scene, pendingChanges); - _model.addToScene(scene, pendingChanges); + if (_model->needsFixupInScene()) { + _model->removeFromScene(scene, pendingChanges); + _model->addToScene(scene, pendingChanges); } scene->enqueuePendingChanges(pendingChanges); @@ -100,7 +100,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { if (newScale.x <= 0 || newScale.y <= 0 || newScale.z <= 0) { setDimensions(scale); } else { - _model.setScaleToFit(true, getDimensions()); + _model->setScaleToFit(true, getDimensions()); _updateModel = true; } } @@ -120,7 +120,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { QUrl newTextureURL = textureMap[key].toUrl(); qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL; - QMetaObject::invokeMethod(&_model, "setTextureWithNameToURL", Qt::AutoConnection, + QMetaObject::invokeMethod(_model.get(), "setTextureWithNameToURL", Qt::AutoConnection, Q_ARG(const QString&, key), Q_ARG(const QUrl&, newTextureURL)); @@ -134,7 +134,7 @@ QVariant ModelOverlay::getProperty(const QString& property) { return _url.toString(); } if (property == "dimensions" || property == "scale" || property == "size") { - return vec3toVariant(_model.getScaleToFitDimensions()); + return vec3toVariant(_model->getScaleToFitDimensions()); } if (property == "textures") { if (_modelTextures.size() > 0) { @@ -155,13 +155,13 @@ bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& float& distance, BoxFace& face, glm::vec3& surfaceNormal) { QString subMeshNameTemp; - return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, subMeshNameTemp); + return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, subMeshNameTemp); } bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo) { - return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo); + return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo); } ModelOverlay* ModelOverlay::createClone() const { diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 0b67f7ed37..36ff75cb6a 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -41,11 +41,11 @@ public: private: - Model _model; + ModelPointer _model; QVariantMap _modelTextures; QUrl _url; bool _updateModel; }; -#endif // hifi_ModelOverlay_h \ No newline at end of file +#endif // hifi_ModelOverlay_h