mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
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:
parent
b4e70d9101
commit
bf433487fa
8 changed files with 73 additions and 65 deletions
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue