Merge pull request #13976 from SamGondelman/crash

Blendshape fixes
This commit is contained in:
Sam Gateau 2018-09-12 22:19:44 -07:00 committed by GitHub
commit 6285487214
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 51 deletions

View file

@ -240,7 +240,6 @@ public:
QVector<glm::vec3> vertices; QVector<glm::vec3> vertices;
QVector<glm::vec3> normals; QVector<glm::vec3> normals;
QVector<glm::vec3> tangents; QVector<glm::vec3> tangents;
mutable QVector<NormalType> normalsAndTangents; // Populated later if needed for blendshapes
QVector<glm::vec3> colors; QVector<glm::vec3> colors;
QVector<glm::vec2> texCoords; QVector<glm::vec2> texCoords;
QVector<glm::vec2> texCoords1; QVector<glm::vec2> texCoords1;

View file

@ -86,7 +86,7 @@ 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++) {
if (!fbxGeometry.meshes[i].blendshapes.empty()) { if (!fbxGeometry.meshes[i].blendshapes.empty() && _blendedVertexBuffers.find(i) == _blendedVertexBuffers.end()) {
initializeBlendshapes(fbxGeometry.meshes[i], i); initializeBlendshapes(fbxGeometry.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);
@ -97,6 +97,7 @@ void CauterizedModel::createRenderItemSet() {
shapeID++; shapeID++;
} }
} }
_blendedVertexBuffersInitialized = true;
} else { } else {
Model::createRenderItemSet(); Model::createRenderItemSet();
} }

View file

@ -302,15 +302,22 @@ bool Model::updateGeometry() {
assert(_meshStates.empty()); assert(_meshStates.empty());
const FBXGeometry& fbxGeometry = getFBXGeometry(); const FBXGeometry& fbxGeometry = getFBXGeometry();
int i = 0;
foreach (const FBXMesh& mesh, fbxGeometry.meshes) { foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
MeshState state; MeshState state;
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);
if (!mesh.blendshapes.empty() && _blendedVertexBuffers.find(i) == _blendedVertexBuffers.end()) {
initializeBlendshapes(mesh, i);
}
i++;
} }
_blendedVertexBuffersInitialized = true;
needFullUpdate = true; needFullUpdate = true;
emit rigReady(); emit rigReady();
} }
return needFullUpdate; return needFullUpdate;
} }
@ -1027,6 +1034,10 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti
_modelMeshMaterialNames.clear(); _modelMeshMaterialNames.clear();
_modelMeshRenderItemShapes.clear(); _modelMeshRenderItemShapes.clear();
_blendedVertexBuffers.clear();
_normalsAndTangents.clear();
_blendedVertexBuffersInitialized = false;
_addedToScene = false; _addedToScene = false;
_renderInfoVertexCount = 0; _renderInfoVertexCount = 0;
@ -1273,8 +1284,7 @@ QStringList Model::getJointNames() const {
class Blender : public QRunnable { class Blender : public QRunnable {
public: public:
Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, const QVector<float>& blendshapeCoefficients);
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients);
virtual void run() override; virtual void run() override;
@ -1283,37 +1293,37 @@ private:
ModelPointer _model; ModelPointer _model;
int _blendNumber; int _blendNumber;
Geometry::WeakPointer _geometry; Geometry::WeakPointer _geometry;
QVector<FBXMesh> _meshes;
QVector<float> _blendshapeCoefficients; QVector<float> _blendshapeCoefficients;
}; };
Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, const QVector<float>& blendshapeCoefficients) :
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients) :
_model(model), _model(model),
_blendNumber(blendNumber), _blendNumber(blendNumber),
_geometry(geometry), _geometry(geometry),
_meshes(meshes),
_blendshapeCoefficients(blendshapeCoefficients) { _blendshapeCoefficients(blendshapeCoefficients) {
} }
void Blender::run() { void Blender::run() {
DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } });
QVector<glm::vec3> vertices; QVector<glm::vec3> vertices;
QVector<NormalType> normalsAndTangents; QVector<NormalType> normalsAndTangents;
if (_model) { if (_model && _model->isLoaded()) {
DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } });
int offset = 0; int offset = 0;
int normalsAndTangentsOffset = 0; int normalsAndTangentsOffset = 0;
foreach (const FBXMesh& mesh, _meshes) { auto meshes = _model->getFBXGeometry().meshes;
if (mesh.blendshapes.isEmpty()) { int meshIndex = 0;
foreach (const FBXMesh& mesh, meshes) {
auto modelMeshNormalsAndTangents = _model->_normalsAndTangents.find(meshIndex++);
if (mesh.blendshapes.isEmpty() || modelMeshNormalsAndTangents == _model->_normalsAndTangents.end()) {
continue; continue;
} }
vertices += mesh.vertices; vertices += mesh.vertices;
normalsAndTangents += mesh.normalsAndTangents; normalsAndTangents += modelMeshNormalsAndTangents->second;
glm::vec3* meshVertices = vertices.data() + offset; glm::vec3* meshVertices = vertices.data() + offset;
NormalType* meshNormalsAndTangents = normalsAndTangents.data() + normalsAndTangentsOffset; NormalType* meshNormalsAndTangents = normalsAndTangents.data() + normalsAndTangentsOffset;
offset += mesh.vertices.size(); offset += mesh.vertices.size();
normalsAndTangentsOffset += mesh.normalsAndTangents.size(); normalsAndTangentsOffset += modelMeshNormalsAndTangents->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(), mesh.blendshapes.size()); i < n; i++) {
float vertexCoefficient = _blendshapeCoefficients.at(i); float vertexCoefficient = _blendshapeCoefficients.at(i);
@ -1353,9 +1363,8 @@ void Blender::run() {
} }
// 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(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector<glm::vec3>, vertices),
Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(QVector<NormalType>, normalsAndTangents));
Q_ARG(const QVector<NormalType>&, normalsAndTangents));
} }
void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) {
@ -1526,18 +1535,15 @@ bool Model::maybeStartBlender() {
if (isLoaded()) { if (isLoaded()) {
const FBXGeometry& fbxGeometry = getFBXGeometry(); const FBXGeometry& fbxGeometry = getFBXGeometry();
if (fbxGeometry.hasBlendedMeshes()) { if (fbxGeometry.hasBlendedMeshes()) {
QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _renderGeometry, QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _renderGeometry, _blendshapeCoefficients));
fbxGeometry.meshes, _blendshapeCoefficients));
return true; return true;
} }
} }
return false; return false;
} }
void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, void Model::setBlendedVertices(int blendNumber, const QVector<glm::vec3>& vertices, const QVector<NormalType>& normalsAndTangents) {
const QVector<glm::vec3>& vertices, const QVector<NormalType>& normalsAndTangents) { if (!isLoaded() || blendNumber < _appliedBlendNumber || !_blendedVertexBuffersInitialized) {
auto geometryRef = geometry.lock();
if (!geometryRef || _renderGeometry != geometryRef || blendNumber < _appliedBlendNumber) {
return; return;
} }
_appliedBlendNumber = blendNumber; _appliedBlendNumber = blendNumber;
@ -1546,26 +1552,28 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
int normalAndTangentIndex = 0; int normalAndTangentIndex = 0;
for (int i = 0; i < fbxGeometry.meshes.size(); i++) { for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i); const FBXMesh& mesh = fbxGeometry.meshes.at(i);
if (mesh.blendshapes.isEmpty()) { auto meshNormalsAndTangents = _normalsAndTangents.find(i);
if (mesh.blendshapes.isEmpty() || meshNormalsAndTangents == _normalsAndTangents.end()) {
continue; continue;
} }
const auto vertexCount = mesh.vertices.size(); const auto vertexCount = mesh.vertices.size();
const auto verticesSize = vertexCount * sizeof(glm::vec3); const auto verticesSize = vertexCount * sizeof(glm::vec3);
const auto& buffer = _blendedVertexBuffers[i]; const auto& buffer = _blendedVertexBuffers.find(i);
assert(buffer && _blendedVertexBuffersInitialized); assert(buffer != _blendedVertexBuffers.end());
buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + mesh.normalsAndTangents.size() * sizeof(NormalType)); buffer->second->resize(mesh.vertices.size() * sizeof(glm::vec3) + meshNormalsAndTangents->second.size() * sizeof(NormalType));
buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3)); buffer->second->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3));
buffer->setSubData(verticesSize, mesh.normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType)); buffer->second->setSubData(verticesSize, meshNormalsAndTangents->second.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType));
index += vertexCount; index += vertexCount;
normalAndTangentIndex += mesh.normalsAndTangents.size(); normalAndTangentIndex += meshNormalsAndTangents->second.size();
} }
} }
void Model::deleteGeometry() { void Model::deleteGeometry() {
_deleteGeometryCounter++; _deleteGeometryCounter++;
_blendedVertexBuffers.clear(); _blendedVertexBuffers.clear();
_normalsAndTangents.clear();
_blendedVertexBuffersInitialized = false; _blendedVertexBuffersInitialized = false;
_meshStates.clear(); _meshStates.clear();
_rig.destroyAnimGraph(); _rig.destroyAnimGraph();
@ -1599,6 +1607,7 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const {
} }
void Model::initializeBlendshapes(const FBXMesh& mesh, int index) { void Model::initializeBlendshapes(const FBXMesh& mesh, int index) {
_blendedVertexBuffers[index] = std::make_shared<gpu::Buffer>();
QVector<NormalType> normalsAndTangents; QVector<NormalType> normalsAndTangents;
normalsAndTangents.resize(2 * mesh.normals.size()); normalsAndTangents.resize(2 * mesh.normals.size());
@ -1627,12 +1636,10 @@ void Model::initializeBlendshapes(const FBXMesh& mesh, int index) {
} }
}); });
const auto verticesSize = mesh.vertices.size() * sizeof(glm::vec3); const auto verticesSize = mesh.vertices.size() * sizeof(glm::vec3);
_blendedVertexBuffers[index] = std::make_shared<gpu::Buffer>();
_blendedVertexBuffers[index]->resize(mesh.vertices.size() * sizeof(glm::vec3) + normalsAndTangents.size() * sizeof(NormalType)); _blendedVertexBuffers[index]->resize(mesh.vertices.size() * sizeof(glm::vec3) + normalsAndTangents.size() * sizeof(NormalType));
_blendedVertexBuffers[index]->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData()); _blendedVertexBuffers[index]->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData());
_blendedVertexBuffers[index]->setSubData(verticesSize, normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); _blendedVertexBuffers[index]->setSubData(verticesSize, normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data());
mesh.normalsAndTangents = normalsAndTangents; _normalsAndTangents[index] = normalsAndTangents;
_blendedVertexBuffersInitialized = true;
} }
void Model::createRenderItemSet() { void Model::createRenderItemSet() {
@ -1673,7 +1680,7 @@ 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++) {
if (!fbxGeometry.meshes[i].blendshapes.empty()) { if (!fbxGeometry.meshes[i].blendshapes.empty() && _blendedVertexBuffers.find(i) == _blendedVertexBuffers.end()) {
initializeBlendshapes(fbxGeometry.meshes[i], i); initializeBlendshapes(fbxGeometry.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);
@ -1683,6 +1690,7 @@ void Model::createRenderItemSet() {
shapeID++; shapeID++;
} }
} }
_blendedVertexBuffersInitialized = true;
} }
bool Model::isRenderable() const { bool Model::isRenderable() const {
@ -1775,35 +1783,38 @@ ModelBlender::~ModelBlender() {
} }
void ModelBlender::noteRequiresBlend(ModelPointer model) { void ModelBlender::noteRequiresBlend(ModelPointer model) {
Lock lock(_mutex);
if (_pendingBlenders < QThread::idealThreadCount()) { if (_pendingBlenders < QThread::idealThreadCount()) {
if (model->maybeStartBlender()) { if (model->maybeStartBlender()) {
_pendingBlenders++; _pendingBlenders++;
return;
} }
return;
} }
{ _modelsRequiringBlends.insert(model);
Lock lock(_mutex);
_modelsRequiringBlends.insert(model);
}
} }
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, QVector<glm::vec3> vertices, QVector<NormalType> normalsAndTangents) {
const QVector<glm::vec3>& vertices, const QVector<NormalType>& normalsAndTangents) {
if (model) { if (model) {
model->setBlendedVertices(blendNumber, geometry, vertices, normalsAndTangents); model->setBlendedVertices(blendNumber, vertices, normalsAndTangents);
} }
_pendingBlenders--;
{ {
Lock lock(_mutex); Lock lock(_mutex);
for (auto i = _modelsRequiringBlends.begin(); i != _modelsRequiringBlends.end();) { _pendingBlenders--;
_modelsRequiringBlends.erase(model);
std::set<ModelWeakPointer, std::owner_less<ModelWeakPointer>> modelsToErase;
for (auto i = _modelsRequiringBlends.begin(); i != _modelsRequiringBlends.end(); i++) {
auto weakPtr = *i; auto weakPtr = *i;
_modelsRequiringBlends.erase(i++); // remove front of the set
ModelPointer nextModel = weakPtr.lock(); ModelPointer nextModel = weakPtr.lock();
if (nextModel && nextModel->maybeStartBlender()) { if (nextModel && nextModel->maybeStartBlender()) {
_pendingBlenders++; _pendingBlenders++;
return; break;
} else {
modelsToErase.insert(weakPtr);
} }
} }
for (auto& weakPtr : modelsToErase) {
_modelsRequiringBlends.erase(weakPtr);
}
} }
} }

View file

@ -144,8 +144,7 @@ public:
bool maybeStartBlender(); bool maybeStartBlender();
/// Sets blended vertices computed in a separate thread. /// Sets blended vertices computed in a separate thread.
void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, void setBlendedVertices(int blendNumber, const QVector<glm::vec3>& vertices, const QVector<NormalType>& normalsAndTangents);
const QVector<glm::vec3>& vertices, const QVector<NormalType>& normalsAndTangents);
bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); } bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); }
bool isAddedToScene() const { return _addedToScene; } bool isAddedToScene() const { return _addedToScene; }
@ -345,7 +344,7 @@ 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);
bool areBlendedVertexBuffersInitialized(int index) { return _blendedVertexBuffersInitialized; } std::unordered_map<int, QVector<NormalType>> _normalsAndTangents;
public slots: public slots:
void loadURLFinished(bool success); void loadURLFinished(bool success);
@ -521,8 +520,7 @@ public:
bool shouldComputeBlendshapes() { return _computeBlendshapes; } bool shouldComputeBlendshapes() { return _computeBlendshapes; }
public slots: public slots:
void setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, void setBlendedVertices(ModelPointer model, int blendNumber, QVector<glm::vec3> vertices, QVector<NormalType> normalsAndTangents);
const QVector<glm::vec3>& vertices, const QVector<NormalType>& normalsAndTangents);
void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; } void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; }
private: private: