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.
This commit is contained in:
Anthony J. Thibault 2016-03-25 21:16:53 -07:00
parent bb42d84bbd
commit b4e70d9101
13 changed files with 229 additions and 59 deletions

View file

@ -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<HeadData*>(new Head(this));
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr, rig);
_skeletonModel->setSkinnedMeshBound(DEFAULT_AVATAR_SKINNED_MESH_BOUND);
}
Avatar::~Avatar() {

View file

@ -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();

View file

@ -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);
}
}

View file

@ -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<Part>() + partStart;
auto partItEnd = _partBuffer.cbegin<Part>() + partEnd;

View file

@ -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<gpu::Primitive>(topo); }

View file

@ -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<model::Mesh::Part>(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<model::Mesh::Part>(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<model::Mesh::Part>(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<std::unique_ptr<NetworkMesh>>& networkMeshes = _model->_geometry->getMeshes();

View file

@ -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<ModelMeshPartPayload> 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

View file

@ -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<MeshPartPayload>(itemID, [transform, offset](MeshPartPayload& data) {
data.updateTransform(transform, offset);
foreach (auto itemID, _modelMeshRenderItems.keys()) {
pendingChanges.updateItem<ModelMeshPartPayload>(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<MeshPartPayload>(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<render::Scene> 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<render::Scene> scene, render::PendingChan
bool somethingAdded = false;
foreach (auto renderItem, _renderItemsSet) {
foreach (auto renderItem, _modelMeshRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
data.notifyLocationChanged();
});
_modelMeshRenderItems.insert(item, renderPayload);
somethingAdded = true;
}
foreach (auto renderItem, _collisionRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<MeshPartPayload>(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<render::Scene> scene,
bool somethingAdded = false;
foreach (auto renderItem, _renderItemsSet) {
foreach (auto renderItem, _modelMeshRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
data.notifyLocationChanged();
});
_modelMeshRenderItems.insert(item, renderPayload);
somethingAdded = true;
}
foreach (auto renderItem, _collisionRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
renderPayload->addStatusGetters(statusGetters);
@ -549,7 +600,7 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
pendingChanges.updateItem<MeshPartPayload>(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<render::Scene> scene,
}
void Model::removeFromScene(std::shared_ptr<render::Scene> 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<MeshPartPayload>(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset);
_collisionRenderItemsSet << std::make_shared<MeshPartPayload>(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset);
} else {
_renderItemsSet << std::make_shared<ModelMeshPartPayload>(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<ModelMeshPartPayload>(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<ModelMeshPartPayload::Payload>(renderItem);
_modelMeshRenderItems.insert(item, renderPayload);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<ModelMeshPartPayload>(item, [transform, offset](MeshPartPayload& data) {
data.notifyLocationChanged();
});
}
foreach (auto renderItem, _collisionRenderItemsSet) {
auto item = scene->allocateID();
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
_renderItems.insert(item, renderPayload);
_collisionRenderItems.insert(item, renderPayload);
pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<MeshPartPayload>(item, [transform,offset](MeshPartPayload& data) {
data.updateTransform(transform, offset);
pendingChanges.updateItem<MeshPartPayload>(item, [transform, offset](MeshPartPayload& data) {
data.notifyLocationChanged();
});
}

View file

@ -44,6 +44,7 @@ namespace render {
typedef unsigned int ItemID;
}
class MeshPartPayload;
class ModelMeshPartPayload;
class ModelRenderLocations;
inline uint qHash(const std::shared_ptr<MeshPartPayload>& 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<std::shared_ptr<MeshPartPayload>> _renderItemsSet;
QMap<render::ItemID, render::PayloadPointer> _renderItems;
QSet<std::shared_ptr<MeshPartPayload>> _collisionRenderItemsSet;
QMap<render::ItemID, render::PayloadPointer> _collisionRenderItems;
QSet<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItemsSet;
QMap<render::ItemID, render::PayloadPointer> _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)

View file

@ -17,6 +17,8 @@
#include "GeometryUtil.h"
#include "NumericalConstants.h"
const glm::vec3 INFINITY_VECTOR(std::numeric_limits<float>::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<float>::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;

View file

@ -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);

View file

@ -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);
}

View file

@ -23,6 +23,7 @@ private slots:
void testCtorsAndSetters();
void testContainsPoint();
void testTouchesSphere();
void testScale();
};
#endif // hifi_AABoxTests_h