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.
This commit is contained in:
Anthony J. Thibault 2016-03-26 12:09:36 -07:00
parent b4e70d9101
commit bf433487fa
8 changed files with 73 additions and 65 deletions

View file

@ -103,7 +103,6 @@ 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

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

View file

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

View file

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

View file

@ -146,20 +146,12 @@ void Model::enqueueLocationChange() {
render::PendingChanges pendingChanges;
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._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<MeshPartPayload>(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<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset, geometrySkinnedMeshBound);
_modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
}
shapeID++;

View file

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

View file

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

View file

@ -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<float>::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;