mirror of
https://github.com/overte-org/overte.git
synced 2025-06-25 23:49:43 +02:00
Fix blender::run crash
Pass a shared_ptr of the HFMModel to the Blender. This will prevent the HFMModel from being destroyed on the main thread if an Avatar changes their skeletonModelURL. Also the _blendshapeOffset hash in Model has been eliminated, it was not necessary and was also a source of data races. The body of Blender::run has been updated slightly to reduce the number of allocation necessary for temporary QVectors.
This commit is contained in:
parent
4e8d3470d6
commit
98fe059d97
6 changed files with 70 additions and 99 deletions
|
@ -291,6 +291,7 @@ public:
|
||||||
class Model {
|
class Model {
|
||||||
public:
|
public:
|
||||||
using Pointer = std::shared_ptr<Model>;
|
using Pointer = std::shared_ptr<Model>;
|
||||||
|
using ConstPointer = std::shared_ptr<const Model>;
|
||||||
|
|
||||||
QString originalURL;
|
QString originalURL;
|
||||||
QString author;
|
QString author;
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
bool isHFMModelLoaded() const { return (bool)_hfmModel; }
|
bool isHFMModelLoaded() const { return (bool)_hfmModel; }
|
||||||
|
|
||||||
const HFMModel& getHFMModel() const { return *_hfmModel; }
|
const HFMModel& getHFMModel() const { return *_hfmModel; }
|
||||||
|
const HFMModel::ConstPointer& getConstHFMModelPointer() const { return _hfmModel; }
|
||||||
const MaterialMapping& getMaterialMapping() const { return _materialMapping; }
|
const MaterialMapping& getMaterialMapping() const { return _materialMapping; }
|
||||||
const GeometryMeshes& getMeshes() const { return *_meshes; }
|
const GeometryMeshes& getMeshes() const { return *_meshes; }
|
||||||
const std::shared_ptr<NetworkMaterial> getShapeMaterial(int shapeID) const;
|
const std::shared_ptr<NetworkMaterial> getShapeMaterial(int shapeID) const;
|
||||||
|
@ -59,7 +60,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Shared across all geometries, constant throughout lifetime
|
// Shared across all geometries, constant throughout lifetime
|
||||||
std::shared_ptr<const HFMModel> _hfmModel;
|
HFMModel::ConstPointer _hfmModel;
|
||||||
MaterialMapping _materialMapping;
|
MaterialMapping _materialMapping;
|
||||||
std::shared_ptr<const GeometryMeshes> _meshes;
|
std::shared_ptr<const GeometryMeshes> _meshes;
|
||||||
std::shared_ptr<const GeometryMeshParts> _meshParts;
|
std::shared_ptr<const GeometryMeshParts> _meshParts;
|
||||||
|
|
|
@ -86,8 +86,6 @@ void CauterizedModel::createRenderItemSet() {
|
||||||
// Create the render payloads
|
// Create the render payloads
|
||||||
int numParts = (int)mesh->getNumParts();
|
int numParts = (int)mesh->getNumParts();
|
||||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||||
initializeBlendshapes(hfmModel.meshes[i], i);
|
|
||||||
|
|
||||||
auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
|
auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
|
||||||
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
|
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
|
||||||
auto material = getGeometry()->getShapeMaterial(shapeID);
|
auto material = getGeometry()->getShapeMaterial(shapeID);
|
||||||
|
@ -96,7 +94,6 @@ void CauterizedModel::createRenderItemSet() {
|
||||||
shapeID++;
|
shapeID++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_blendshapeOffsetsInitialized = true;
|
|
||||||
} else {
|
} else {
|
||||||
Model::createRenderItemSet();
|
Model::createRenderItemSet();
|
||||||
}
|
}
|
||||||
|
@ -181,7 +178,7 @@ void CauterizedModel::updateClusterMatrices() {
|
||||||
|
|
||||||
// post the blender if we're not currently waiting for one to finish
|
// post the blender if we're not currently waiting for one to finish
|
||||||
auto modelBlender = DependencyManager::get<ModelBlender>();
|
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||||
if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
if (modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||||
modelBlender->noteRequiresBlend(getThisPointer());
|
modelBlender->noteRequiresBlend(getThisPointer());
|
||||||
}
|
}
|
||||||
|
|
|
@ -314,10 +314,8 @@ bool Model::updateGeometry() {
|
||||||
state.clusterDualQuaternions.resize(mesh.clusters.size());
|
state.clusterDualQuaternions.resize(mesh.clusters.size());
|
||||||
state.clusterMatrices.resize(mesh.clusters.size());
|
state.clusterMatrices.resize(mesh.clusters.size());
|
||||||
_meshStates.push_back(state);
|
_meshStates.push_back(state);
|
||||||
initializeBlendshapes(mesh, i);
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
_blendshapeOffsetsInitialized = true;
|
|
||||||
needFullUpdate = true;
|
needFullUpdate = true;
|
||||||
emit rigReady();
|
emit rigReady();
|
||||||
}
|
}
|
||||||
|
@ -1022,9 +1020,6 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti
|
||||||
_modelMeshRenderItemShapes.clear();
|
_modelMeshRenderItemShapes.clear();
|
||||||
_priorityMap.clear();
|
_priorityMap.clear();
|
||||||
|
|
||||||
_blendshapeOffsets.clear();
|
|
||||||
_blendshapeOffsetsInitialized = false;
|
|
||||||
|
|
||||||
_addedToScene = false;
|
_addedToScene = false;
|
||||||
|
|
||||||
_renderInfoVertexCount = 0;
|
_renderInfoVertexCount = 0;
|
||||||
|
@ -1415,7 +1410,7 @@ void Model::updateClusterMatrices() {
|
||||||
|
|
||||||
// post the blender if we're not currently waiting for one to finish
|
// post the blender if we're not currently waiting for one to finish
|
||||||
auto modelBlender = DependencyManager::get<ModelBlender>();
|
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||||
if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
if (modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||||
modelBlender->noteRequiresBlend(getThisPointer());
|
modelBlender->noteRequiresBlend(getThisPointer());
|
||||||
}
|
}
|
||||||
|
@ -1423,8 +1418,6 @@ void Model::updateClusterMatrices() {
|
||||||
|
|
||||||
void Model::deleteGeometry() {
|
void Model::deleteGeometry() {
|
||||||
_deleteGeometryCounter++;
|
_deleteGeometryCounter++;
|
||||||
_blendshapeOffsets.clear();
|
|
||||||
_blendshapeOffsetsInitialized = false;
|
|
||||||
_meshStates.clear();
|
_meshStates.clear();
|
||||||
_rig.destroyAnimGraph();
|
_rig.destroyAnimGraph();
|
||||||
_blendedBlendshapeCoefficients.clear();
|
_blendedBlendshapeCoefficients.clear();
|
||||||
|
@ -1494,7 +1487,6 @@ void Model::createRenderItemSet() {
|
||||||
// Create the render payloads
|
// Create the render payloads
|
||||||
int numParts = (int)mesh->getNumParts();
|
int numParts = (int)mesh->getNumParts();
|
||||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||||
initializeBlendshapes(hfmModel.meshes[i], i);
|
|
||||||
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
|
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
|
||||||
auto material = getGeometry()->getShapeMaterial(shapeID);
|
auto material = getGeometry()->getShapeMaterial(shapeID);
|
||||||
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
_modelMeshMaterialNames.push_back(material ? material->getName() : "");
|
||||||
|
@ -1502,7 +1494,6 @@ void Model::createRenderItemSet() {
|
||||||
shapeID++;
|
shapeID++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_blendshapeOffsetsInitialized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::isRenderable() const {
|
bool Model::isRenderable() const {
|
||||||
|
@ -1722,56 +1713,55 @@ void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& pac
|
||||||
class Blender : public QRunnable {
|
class Blender : public QRunnable {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, const QVector<float>& blendshapeCoefficients);
|
Blender(ModelPointer model, HFMModel::ConstPointer hfmModel, int blendNumber, const QVector<float>& blendshapeCoefficients);
|
||||||
|
|
||||||
virtual void run() override;
|
virtual void run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ModelPointer _model;
|
ModelPointer _model;
|
||||||
|
HFMModel::ConstPointer _hfmModel;
|
||||||
int _blendNumber;
|
int _blendNumber;
|
||||||
Geometry::WeakPointer _geometry;
|
|
||||||
QVector<float> _blendshapeCoefficients;
|
QVector<float> _blendshapeCoefficients;
|
||||||
};
|
};
|
||||||
|
|
||||||
Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, const QVector<float>& blendshapeCoefficients) :
|
Blender::Blender(ModelPointer model, HFMModel::ConstPointer hfmModel, int blendNumber, const QVector<float>& blendshapeCoefficients) :
|
||||||
_model(model),
|
_model(model),
|
||||||
|
_hfmModel(hfmModel),
|
||||||
_blendNumber(blendNumber),
|
_blendNumber(blendNumber),
|
||||||
_geometry(geometry),
|
|
||||||
_blendshapeCoefficients(blendshapeCoefficients) {
|
_blendshapeCoefficients(blendshapeCoefficients) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blender::run() {
|
void Blender::run() {
|
||||||
QVector<BlendshapeOffset> blendshapeOffsets;
|
|
||||||
QVector<int> blendedMeshSizes;
|
|
||||||
if (_model && _model->isLoaded()) {
|
|
||||||
DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } });
|
DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } });
|
||||||
|
int numBlendshapeOffsets = 0; // number of offsets required for all meshes.
|
||||||
|
int numMeshes = 0; // number of meshes in this model.
|
||||||
|
int maxVertsInMesh = 0; // number of vertices in the largest mesh.
|
||||||
|
for (auto meshIter = _hfmModel->meshes.cbegin(); meshIter != _hfmModel->meshes.cend(); ++meshIter) {
|
||||||
|
numMeshes++;
|
||||||
|
int numVertsInMesh = meshIter->vertices.size();
|
||||||
|
numBlendshapeOffsets += numVertsInMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all elements are default constructed to zero offsets.
|
||||||
|
QVector<BlendshapeOffset> packedBlendshapeOffsets(numBlendshapeOffsets);
|
||||||
|
QVector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets(numBlendshapeOffsets);
|
||||||
|
|
||||||
|
// allocate the required size
|
||||||
|
QVector<int> blendedMeshSizes;
|
||||||
|
blendedMeshSizes.reserve(numMeshes);
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
const auto& meshes = _model->getHFMModel().meshes;
|
for (auto meshIter = _hfmModel->meshes.cbegin(); meshIter != _hfmModel->meshes.cend(); ++meshIter) {
|
||||||
int meshIndex = 0;
|
if (meshIter->blendshapes.isEmpty()) {
|
||||||
for(const HFMMesh& mesh : meshes) {
|
|
||||||
auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++);
|
|
||||||
if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) {
|
|
||||||
// Not blendshaped or not initialized
|
|
||||||
blendedMeshSizes.push_back(0);
|
blendedMeshSizes.push_back(0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
int numVertsInMesh = meshIter->vertices.size();
|
||||||
|
blendedMeshSizes.push_back(numVertsInMesh);
|
||||||
|
|
||||||
if (mesh.vertices.size() != modelMeshBlendshapeOffsets->second.size()) {
|
// for each blendshape in this mesh, accumulate the offsets into unpackedBlendshapeOffsets.
|
||||||
// Mesh sizes don't match. Something has gone wrong
|
|
||||||
blendedMeshSizes.push_back(0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
blendshapeOffsets += modelMeshBlendshapeOffsets->second;
|
|
||||||
BlendshapeOffset* meshBlendshapeOffsets = blendshapeOffsets.data() + offset;
|
|
||||||
int numVertices = modelMeshBlendshapeOffsets->second.size();
|
|
||||||
blendedMeshSizes.push_back(numVertices);
|
|
||||||
offset += numVertices;
|
|
||||||
std::vector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets(modelMeshBlendshapeOffsets->second.size());
|
|
||||||
|
|
||||||
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
||||||
for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) {
|
for (int i = 0, n = qMin(_blendshapeCoefficients.size(), meshIter->blendshapes.size()); i < n; i++) {
|
||||||
float vertexCoefficient = _blendshapeCoefficients.at(i);
|
float vertexCoefficient = _blendshapeCoefficients.at(i);
|
||||||
const float EPSILON = 0.0001f;
|
const float EPSILON = 0.0001f;
|
||||||
if (vertexCoefficient < EPSILON) {
|
if (vertexCoefficient < EPSILON) {
|
||||||
|
@ -1779,62 +1769,49 @@ void Blender::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE;
|
float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE;
|
||||||
const HFMBlendshape& blendshape = mesh.blendshapes.at(i);
|
const HFMBlendshape& blendshape = meshIter->blendshapes.at(i);
|
||||||
for (int j = 0; j < blendshape.indices.size(); ++j) {
|
for (int j = 0; j < blendshape.indices.size(); ++j) {
|
||||||
int index = blendshape.indices.at(j);
|
int index = blendshape.indices.at(j);
|
||||||
|
|
||||||
auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[index];
|
auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[offset + index];
|
||||||
currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient;
|
currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient;
|
||||||
|
|
||||||
currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient;
|
currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient;
|
||||||
if (j < blendshape.tangents.size()) {
|
if (j < blendshape.tangents.size()) {
|
||||||
currentBlendshapeOffset.tangentOffset += blendshape.tangents.at(j) * normalCoefficient;
|
currentBlendshapeOffset.tangentOffset += blendshape.tangents.at(j) * normalCoefficient;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
offset += numVertsInMesh;
|
||||||
|
}
|
||||||
|
|
||||||
// Blendshape offsets are generrated, now let's pack it on its way to gpu
|
// convert unpackedBlendshapeOffsets into packedBlendshapeOffsets for the gpu.
|
||||||
// FIXME it feels like we could be more effectively using SIMD here
|
// FIXME it feels like we could be more effectively using SIMD here
|
||||||
{
|
{
|
||||||
auto unpacked = unpackedBlendshapeOffsets.data();
|
auto unpacked = unpackedBlendshapeOffsets.data();
|
||||||
auto packed = meshBlendshapeOffsets;
|
auto packed = packedBlendshapeOffsets.data();
|
||||||
for (int j = 0; j < (int)unpackedBlendshapeOffsets.size(); ++j) {
|
for (int i = 0; i < unpackedBlendshapeOffsets.size(); ++i) {
|
||||||
packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked));
|
packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked));
|
||||||
++unpacked;
|
++unpacked;
|
||||||
++packed;
|
++packed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
// post the result to the ModelBlender, which will dispatch to the model if still alive
|
// post the result to the ModelBlender, which will dispatch to the model if still alive
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<ModelBlender>().data(), "setBlendedVertices",
|
QMetaObject::invokeMethod(DependencyManager::get<ModelBlender>().data(), "setBlendedVertices",
|
||||||
Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector<BlendshapeOffset>, blendshapeOffsets), Q_ARG(QVector<int>, blendedMeshSizes));
|
Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber),
|
||||||
|
Q_ARG(QVector<BlendshapeOffset>, packedBlendshapeOffsets),
|
||||||
|
Q_ARG(QVector<int>, blendedMeshSizes));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::maybeStartBlender() {
|
bool Model::maybeStartBlender() {
|
||||||
if (isLoaded()) {
|
if (isLoaded()) {
|
||||||
QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _renderGeometry, _blendshapeCoefficients));
|
QThreadPool::globalInstance()->start(new Blender(getThisPointer(), getGeometry()->getConstHFMModelPointer(),
|
||||||
|
++_blendNumber, _blendshapeCoefficients));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::initializeBlendshapes(const HFMMesh& mesh, int index) {
|
|
||||||
if (mesh.blendshapes.empty()) {
|
|
||||||
// mesh doesn't have blendshape, did we allocate one though ?
|
|
||||||
if (_blendshapeOffsets.find(index) != _blendshapeOffsets.end()) {
|
|
||||||
qWarning() << "Mesh does not have Blendshape yet the blendshapeOffsets are allocated ?";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Mesh has blendshape, let s allocate the local buffer if not done yet
|
|
||||||
if (_blendshapeOffsets.find(index) == _blendshapeOffsets.end()) {
|
|
||||||
QVector<BlendshapeOffset> blendshapeOffset;
|
|
||||||
blendshapeOffset.fill(BlendshapeOffset(), mesh.vertices.size());
|
|
||||||
_blendshapeOffsets[index] = blendshapeOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelBlender::ModelBlender() :
|
ModelBlender::ModelBlender() :
|
||||||
_pendingBlenders(0) {
|
_pendingBlenders(0) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -363,8 +363,6 @@ public:
|
||||||
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName);
|
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName);
|
||||||
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName);
|
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName);
|
||||||
|
|
||||||
std::unordered_map<int, QVector<BlendshapeOffset>> _blendshapeOffsets;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void loadURLFinished(bool success);
|
void loadURLFinished(bool success);
|
||||||
|
|
||||||
|
@ -446,7 +444,6 @@ protected:
|
||||||
QVector<float> _blendshapeCoefficients;
|
QVector<float> _blendshapeCoefficients;
|
||||||
QVector<float> _blendedBlendshapeCoefficients;
|
QVector<float> _blendedBlendshapeCoefficients;
|
||||||
int _blendNumber { 0 };
|
int _blendNumber { 0 };
|
||||||
bool _blendshapeOffsetsInitialized { false };
|
|
||||||
|
|
||||||
mutable QMutex _mutex{ QMutex::Recursive };
|
mutable QMutex _mutex{ QMutex::Recursive };
|
||||||
|
|
||||||
|
@ -509,8 +506,6 @@ protected:
|
||||||
|
|
||||||
bool shouldInvalidatePayloadShapeKey(int meshIndex);
|
bool shouldInvalidatePayloadShapeKey(int meshIndex);
|
||||||
|
|
||||||
void initializeBlendshapes(const HFMMesh& mesh, int index);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float _loadingPriority { 0.0f };
|
float _loadingPriority { 0.0f };
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ void SoftAttachmentModel::updateClusterMatrices() {
|
||||||
|
|
||||||
// post the blender if we're not currently waiting for one to finish
|
// post the blender if we're not currently waiting for one to finish
|
||||||
auto modelBlender = DependencyManager::get<ModelBlender>();
|
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||||
if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
if (modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||||
modelBlender->noteRequiresBlend(getThisPointer());
|
modelBlender->noteRequiresBlend(getThisPointer());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue