From bf433487fadac3b94187ac54ff9432d8ef689a49 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 26 Mar 2016 12:09:36 -0700 Subject: [PATCH] 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;