diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9c861006b6..de9b134b83 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2975,6 +2975,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(). @@ -3451,6 +3456,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 f20d72fcb6..31c865ad90 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/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 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; 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/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index df86ba2a8f..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, Boxes& bounds) 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 f9d9b0eeb4..a3e95c68c0 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 - // the returned box is the bounding box of ALL the evaluated part bounds. - Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const; + // evaluate the bounding boxes of the parts in the range [start, end] + // 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); } 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 854630abf0..d295b07caf 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -58,7 +58,6 @@ void MeshPartPayload::updateMeshPart(const std::shared_ptr& d auto vertexFormat = _drawMesh->getVertexFormat(); _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); _drawPart = _drawMesh->getPartBuffer().get(partIndex); - _localBound = _drawMesh->evalPartBound(partIndex); } } @@ -352,7 +351,23 @@ void ModelMeshPartPayload::initCache() { void ModelMeshPartPayload::notifyLocationChanged() { - _model->_needsUpdateClusterMatrices = true; + +} + +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices) { + ModelMeshPartPayload::updateTransform(transform, offsetTransform); + + if (clusterMatrices.size() > 0) { + _worldBound = AABox(); + for (auto& clusterMatrix : clusterMatrices) { + AABox clusterBound = _localBound; + clusterBound.transform(clusterMatrix); + _worldBound += clusterBound; + } + + // clusterMatrix has world rotation but not world translation. + _worldBound.translate(transform.getTranslation()); + } } ItemKey ModelMeshPartPayload::getKey() const { @@ -377,12 +392,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 { assert(_model->isLoaded()); const FBXGeometry& geometry = _model->getFBXGeometry(); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 9f73a46a93..41869ec7e3 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -74,15 +74,15 @@ namespace render { class ModelMeshPartPayload : public MeshPartPayload { public: ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); - + typedef render::Payload Payload; typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; + void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices); // 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; @@ -101,4 +101,11 @@ public: bool _isBlendShaped{ false }; }; +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 545d239549..8d59d5f736 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -130,25 +130,60 @@ void Model::setOffset(const glm::vec3& offset) { } void Model::enqueueLocationChange() { - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - Transform transform; - transform.setTranslation(_translation); - transform.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 offset; - offset.setScale(_scale); - offset.postTranslate(_offset); + // do nothing, if the model has already been destroyed. + auto self = weakSelf.lock(); + if (!self) { + return; + } - render::PendingChanges pendingChanges; - foreach (auto itemID, _renderItems.keys()) { - pendingChanges.updateItem(itemID, [transform, offset](MeshPartPayload& data) { - data.updateTransform(transform, offset); - data.notifyLocationChanged(); - }); - } + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - scene->enqueuePendingChanges(pendingChanges); + Transform modelTransform; + modelTransform.setScale(self->_scale); + modelTransform.setTranslation(self->_translation); + modelTransform.setRotation(self->_rotation); + + Transform modelMeshOffset; + if (self->isLoaded()) { + // includes model offset and unitScale. + modelMeshOffset = Transform(self->_rig->getGeometryToRigTransform()); + } else { + modelMeshOffset.postTranslate(self->_offset); + } + + // 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); + + render::PendingChanges pendingChanges; + foreach (auto itemID, self->_modelMeshRenderItems.keys()) { + pendingChanges.updateItem(itemID, [modelTransform, modelMeshOffset](ModelMeshPartPayload& data) { + + // 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()); + + // update the model transform and bounding box for this render item. + const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); + data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, state.clusterMatrices); + }); + } + + 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() { @@ -497,8 +532,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 +552,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 +590,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 +610,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 +620,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; } @@ -1175,10 +1241,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); @@ -1204,9 +1274,9 @@ void Model::segregateMeshGroups() { _collisionHullMaterial->setMetallic(0.02f); _collisionHullMaterial->setRoughness(0.5f); } - _renderItemsSet << std::make_shared(networkMesh, partIndex, _collisionHullMaterial, transform, offset); + _collisionRenderItemsSet << std::make_shared(networkMesh, partIndex, _collisionHullMaterial, transform, offset); } else { - _renderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); + _modelMeshRenderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); } shapeID++; @@ -1229,13 +1299,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 785b902b3a..e4a8fa3b36 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) { @@ -375,8 +376,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 }; diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index 0b453e6f36..a3afa220c9 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 AABox::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 (isInvalid()) { + _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); } @@ -484,17 +493,31 @@ 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); } +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; auto maximum = _corner + _scale; @@ -544,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 ec06c60121..30bde2d717 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,10 +92,19 @@ 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); - 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; diff --git a/tests/shared/src/AABoxTests.cpp b/tests/shared/src/AABoxTests.cpp index fd709a488c..b9ab95bb09 100644 --- a/tests/shared/src/AABoxTests.cpp +++ b/tests/shared/src/AABoxTests.cpp @@ -151,3 +151,21 @@ void AABoxTests::testTouchesSphere() { } } +void AABoxTests::testScale() { + 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 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); +} 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