mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 09:08:37 +02: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));
|
_headData = static_cast<HeadData*>(new Head(this));
|
||||||
|
|
||||||
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr, rig);
|
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr, rig);
|
||||||
_skeletonModel->setSkinnedMeshBound(DEFAULT_AVATAR_SKINNED_MESH_BOUND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar::~Avatar() {
|
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.
|
// 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->setScaleToFit(false, getDimensions());
|
||||||
_model->setSnapModelToRegistrationPoint(false, getRegistrationPoint());
|
_model->setSnapModelToRegistrationPoint(false, getRegistrationPoint());
|
||||||
AABox skinnedMeshBound(getPosition() - getDimensions() * getRegistrationPoint(), getDimensions());
|
|
||||||
_model->setSkinnedMeshBound(skinnedMeshBound);
|
|
||||||
}
|
}
|
||||||
ModelEntityItem::setModelURL(url);
|
ModelEntityItem::setModelURL(url);
|
||||||
|
|
||||||
|
@ -78,8 +76,6 @@ void RenderableModelEntityItem::loader() {
|
||||||
if (_model) {
|
if (_model) {
|
||||||
_model->setURL(getParsedModelURL());
|
_model->setURL(getParsedModelURL());
|
||||||
_model->setCollisionModelURL(QUrl(getCompoundShapeURL()));
|
_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,
|
ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform,
|
||||||
const Transform& offsetTransform, const AABox& skinnedMeshBound) :
|
const Transform& offsetTransform) :
|
||||||
_model(model),
|
_model(model),
|
||||||
_meshIndex(_meshIndex),
|
_meshIndex(_meshIndex),
|
||||||
_shapeID(shapeIndex),
|
_shapeID(shapeIndex) {
|
||||||
_skinnedMeshBound(skinnedMeshBound) {
|
|
||||||
|
|
||||||
auto& modelMesh = _model->_geometry->getMeshes().at(_meshIndex)->_mesh;
|
auto& modelMesh = _model->_geometry->getMeshes().at(_meshIndex)->_mesh;
|
||||||
updateMeshPart(modelMesh, partIndex);
|
updateMeshPart(modelMesh, partIndex);
|
||||||
|
@ -353,33 +352,20 @@ void ModelMeshPartPayload::notifyLocationChanged() {
|
||||||
_model->_needsUpdateClusterMatrices = true;
|
_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);
|
ModelMeshPartPayload::updateTransform(transform, offsetTransform);
|
||||||
|
|
||||||
// clusterMatrix has world rotation but not world translation.
|
if (numClusterMatrices > 0) {
|
||||||
Transform worldTranslation, geomToWorld;
|
|
||||||
worldTranslation.setTranslation(transform.getTranslation());
|
|
||||||
Transform::mult(geomToWorld, worldTranslation, clusterTransform);
|
|
||||||
|
|
||||||
// transform the localBound into world space
|
_worldBound = AABox();
|
||||||
_worldBound = _localBound;
|
for (size_t i = 0; i < numClusterMatrices; i++) {
|
||||||
_worldBound.transform(geomToWorld);
|
AABox clusterBound = _localBound;
|
||||||
}
|
clusterBound.transform(clusterMatrices[i]);
|
||||||
|
_worldBound += clusterBound;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clusterMatrix has world rotation but not world translation.
|
||||||
|
_worldBound.translate(transform.getTranslation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,14 +75,13 @@ namespace render {
|
||||||
class ModelMeshPartPayload : public MeshPartPayload {
|
class ModelMeshPartPayload : public MeshPartPayload {
|
||||||
public:
|
public:
|
||||||
ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform,
|
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 render::Payload<ModelMeshPartPayload> Payload;
|
||||||
typedef Payload::DataPointer Pointer;
|
typedef Payload::DataPointer Pointer;
|
||||||
|
|
||||||
virtual void updateMeshPart(model::MeshPointer drawMesh, int partIndex) override;
|
|
||||||
virtual void notifyLocationChanged() 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 Item interface
|
||||||
render::ItemKey getKey() const override;
|
render::ItemKey getKey() const override;
|
||||||
|
@ -102,8 +101,6 @@ public:
|
||||||
|
|
||||||
bool _isSkinned{ false };
|
bool _isSkinned{ false };
|
||||||
bool _isBlendShaped{ false };
|
bool _isBlendShaped{ false };
|
||||||
|
|
||||||
AABox _skinnedMeshBound;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
|
|
|
@ -146,20 +146,12 @@ void Model::enqueueLocationChange() {
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
foreach (auto itemID, _modelMeshRenderItems.keys()) {
|
foreach (auto itemID, _modelMeshRenderItems.keys()) {
|
||||||
pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, offset](ModelMeshPartPayload& data) {
|
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;
|
data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation());
|
||||||
xform.setScale(clusterMat.scale);
|
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex);
|
||||||
xform.setRotation(clusterMat.rot);
|
size_t numClusterMatrices = data._model->getGeometry()->getFBXGeometry().meshes.at(data._meshIndex).clusters.size();
|
||||||
xform.setTranslation(clusterMat.trans);
|
|
||||||
data.updateTransformForRigidlyBoundMesh(modelTransform, xform, offset);
|
data.updateTransformForSkinnedMesh(modelTransform, offset, &state.clusterMatrices[0], numClusterMatrices);
|
||||||
}
|
|
||||||
data.notifyLocationChanged();
|
data.notifyLocationChanged();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1282,12 +1274,7 @@ void Model::segregateMeshGroups() {
|
||||||
}
|
}
|
||||||
_collisionRenderItemsSet << std::make_shared<MeshPartPayload>(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset);
|
_collisionRenderItemsSet << std::make_shared<MeshPartPayload>(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset);
|
||||||
} else {
|
} else {
|
||||||
AABox geometrySkinnedMeshBound = _skinnedMeshBound;
|
_modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
||||||
|
|
||||||
// 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++;
|
shapeID++;
|
||||||
|
|
|
@ -223,9 +223,6 @@ public:
|
||||||
|
|
||||||
const glm::vec3& getRegistrationPoint() const { return _registrationPoint; }
|
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:
|
protected:
|
||||||
|
|
||||||
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
|
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
|
||||||
|
@ -390,9 +387,6 @@ protected:
|
||||||
|
|
||||||
friend class ModelMeshPartPayload;
|
friend class ModelMeshPartPayload;
|
||||||
RigPointer _rig;
|
RigPointer _rig;
|
||||||
|
|
||||||
// 2 meter^3 box
|
|
||||||
AABox _skinnedMeshBound { glm::vec3(-1.0, -1.0, -1.0), glm::vec3(2.0f) };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ModelPointer)
|
Q_DECLARE_METATYPE(ModelPointer)
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "GeometryUtil.h"
|
#include "GeometryUtil.h"
|
||||||
#include "NumericalConstants.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) :
|
AABox::AABox(const AACube& other) :
|
||||||
_corner(other.getCorner()), _scale(other.getScale(), other.getScale(), other.getScale()) {
|
_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) {
|
AABox& AABox::operator += (const glm::vec3& point) {
|
||||||
|
|
||||||
if (_corner == INFINITY_VECTOR) {
|
if (isInvalid()) {
|
||||||
_corner = glm::min(_corner, point);
|
_corner = glm::min(_corner, point);
|
||||||
} else {
|
} else {
|
||||||
glm::vec3 maximum(_corner + _scale);
|
glm::vec3 maximum(_corner + _scale);
|
||||||
|
@ -493,7 +493,7 @@ AABox& AABox::operator += (const glm::vec3& point) {
|
||||||
AABox& AABox::operator += (const AABox& box) {
|
AABox& AABox::operator += (const AABox& box) {
|
||||||
if (!box.isInvalid()) {
|
if (!box.isInvalid()) {
|
||||||
(*this) += box._corner;
|
(*this) += box._corner;
|
||||||
_scale = glm::max(_scale, box.calcTopFarLeft() - _corner);
|
(*this) += box.calcTopFarLeft();
|
||||||
}
|
}
|
||||||
return (*this);
|
return (*this);
|
||||||
}
|
}
|
||||||
|
@ -567,3 +567,47 @@ void AABox::transform(const Transform& transform) {
|
||||||
rotate(transform.getRotation());
|
rotate(transform.getRotation());
|
||||||
translate(transform.getTranslation());
|
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
|
// Transform the extents with transform
|
||||||
void transform(const Transform& 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:
|
private:
|
||||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||||
|
|
Loading…
Reference in a new issue