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