diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index fe7083ea30..a7d7dcf8b3 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -451,6 +451,9 @@ Menu::Menu() { }); } + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ComputeBlendshapes, 0, true, + DependencyManager::get().data(), SLOT(setComputeBlendshapes(bool))); + // Developer > Assets >>> // Menu item is not currently needed but code should be kept in case it proves useful again at some stage. //#define WANT_ASSET_MIGRATION diff --git a/interface/src/Menu.h b/interface/src/Menu.h index c489a3f0b0..c55bffdcb6 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -221,6 +221,7 @@ namespace MenuOption { const QString NotificationSounds = "play_notification_sounds"; const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot"; const QString NotificationSoundsTablet = "play_notification_sounds_tablet"; + const QString ComputeBlendshapes = "Compute Blendshapes"; } #endif // hifi_Menu_h diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 3112b6e8bd..9f45482e84 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -359,21 +359,21 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { QReadLocker locker(&_hashLock); QVector::iterator avatarItr = _avatarsToFade.begin(); const render::ScenePointer& scene = qApp->getMain3DScene(); + render::Transaction transaction; while (avatarItr != _avatarsToFade.end()) { auto avatar = std::static_pointer_cast(*avatarItr); avatar->updateFadingStatus(scene); if (!avatar->isFading()) { // fading to zero is such a rare event we push a unique transaction for each if (avatar->isInScene()) { - render::Transaction transaction; avatar->removeFromScene(*avatarItr, scene, transaction); - scene->enqueueTransaction(transaction); } avatarItr = _avatarsToFade.erase(avatarItr); } else { ++avatarItr; } } + scene->enqueueTransaction(transaction); } AvatarSharedPointer AvatarManager::newSharedAvatar() { diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fc94236c96..7bd87a4554 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -28,6 +28,20 @@ #include #include +#if defined(Q_OS_ANDROID) +#define FBX_PACK_NORMALS 0 +#else +#define FBX_PACK_NORMALS 1 +#endif + +#if FBX_PACK_NORMALS +using NormalType = glm::uint32; +#define FBX_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2 +#else +using NormalType = glm::vec3; +#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ +#endif + // See comment in FBXReader::parseFBX(). static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; static const QByteArray FBX_BINARY_PROLOG("Kaydara FBX Binary "); @@ -226,6 +240,7 @@ public: QVector vertices; QVector normals; QVector tangents; + mutable QVector normalsAndTangents; // Populated later if needed for blendshapes QVector colors; QVector texCoords; QVector texCoords1; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index ad002b237c..c391ea6647 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -34,20 +34,6 @@ class QIODevice; class FBXNode; -#if defined(Q_OS_ANDROID) -#define FBX_PACK_NORMALS 0 -#else -#define FBX_PACK_NORMALS 1 -#endif - -#if FBX_PACK_NORMALS -using NormalType = glm::uint32; -#define FBX_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2 -#else -using NormalType = glm::vec3; -#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ -#endif - /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index 5c7e6ff892..2fd0d90da3 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -20,9 +20,6 @@ #include #include -#include -#include - namespace glm { using hvec2 = glm::tvec2; using hvec4 = glm::tvec4; @@ -62,32 +59,6 @@ namespace { } } -void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { - auto absNormal = glm::abs(normal); - auto absTangent = glm::abs(tangent); - normal /= glm::max(1e-6f, glm::max(glm::max(absNormal.x, absNormal.y), absNormal.z)); - tangent /= glm::max(1e-6f, glm::max(glm::max(absTangent.x, absTangent.y), absTangent.z)); - normal = glm::clamp(normal, -1.0f, 1.0f); - tangent = glm::clamp(tangent, -1.0f, 1.0f); - normal *= 511.0f; - tangent *= 511.0f; - normal = glm::round(normal); - tangent = glm::round(tangent); - - glm::detail::i10i10i10i2 normalStruct; - glm::detail::i10i10i10i2 tangentStruct; - normalStruct.data.x = int(normal.x); - normalStruct.data.y = int(normal.y); - normalStruct.data.z = int(normal.z); - normalStruct.data.w = 0; - tangentStruct.data.x = int(tangent.x); - tangentStruct.data.y = int(tangent.y); - tangentStruct.data.z = int(tangent.z); - tangentStruct.data.w = 0; - packedNormal = normalStruct.pack; - packedTangent = tangentStruct.pack; -} - template glm::uint32 forEachGlmVec(const gpu::BufferView& view, std::function func) { QVector result; diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.h b/libraries/graphics/src/graphics/BufferViewHelpers.h index f877341d50..a9707c3128 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.h +++ b/libraries/graphics/src/graphics/BufferViewHelpers.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "GpuHelpers.h" @@ -44,7 +46,31 @@ namespace buffer_helpers { gpu::BufferView clone(const gpu::BufferView& input); gpu::BufferView resized(const gpu::BufferView& input, glm::uint32 numElements); - void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent); + inline void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { + auto absNormal = glm::abs(normal); + auto absTangent = glm::abs(tangent); + normal /= glm::max(1e-6f, glm::max(glm::max(absNormal.x, absNormal.y), absNormal.z)); + tangent /= glm::max(1e-6f, glm::max(glm::max(absTangent.x, absTangent.y), absTangent.z)); + normal = glm::clamp(normal, -1.0f, 1.0f); + tangent = glm::clamp(tangent, -1.0f, 1.0f); + normal *= 511.0f; + tangent *= 511.0f; + normal = glm::round(normal); + tangent = glm::round(tangent); + + glm::detail::i10i10i10i2 normalStruct; + glm::detail::i10i10i10i2 tangentStruct; + normalStruct.data.x = int(normal.x); + normalStruct.data.y = int(normal.y); + normalStruct.data.z = int(normal.z); + normalStruct.data.w = 0; + tangentStruct.data.x = int(tangent.x); + tangentStruct.data.y = int(tangent.y); + tangentStruct.data.z = int(tangent.z); + tangentStruct.data.w = 0; + packedNormal = normalStruct.pack; + packedTangent = tangentStruct.pack; + } namespace mesh { glm::uint32 forEachVertex(const graphics::MeshPointer& mesh, std::function func); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 80a9c5ccae..3966d04b6d 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -76,6 +76,7 @@ void CauterizedModel::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); + const FBXGeometry& fbxGeometry = getFBXGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -85,6 +86,10 @@ void CauterizedModel::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { + if (!fbxGeometry.meshes[i].blendshapes.empty() && !_blendedVertexBuffers[i]) { + _blendedVertexBuffers[i] = std::make_shared(); + _blendedVertexBuffers[i]->resize(fbxGeometry.meshes[i].vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); + } auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); auto material = getGeometry()->getShapeMaterial(shapeID); @@ -170,9 +175,10 @@ void CauterizedModel::updateClusterMatrices() { } // post the blender if we're not currently waiting for one to finish - if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + auto modelBlender = DependencyManager::get(); + if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; - DependencyManager::get()->noteRequiresBlend(getThisPointer()); + modelBlender->noteRequiresBlend(getThisPointer()); } } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index ca6e736a65..94a5026fb4 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -208,7 +208,9 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning(); - _blendedVertexBuffer = model->_blendedVertexBuffers[_meshIndex]; + if (!model->getFBXGeometry().meshes[meshIndex].blendshapes.isEmpty()) { + _blendedVertexBuffer = model->_blendedVertexBuffers[meshIndex]; + } auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); const Model::MeshState& state = model->getMeshState(_meshIndex); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8f4eb57f8e..71250c6483 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -42,8 +42,9 @@ using namespace std; int nakedModelPointerTypeId = qRegisterMetaType(); -int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType(); -int vec3VectorTypeId = qRegisterMetaType >(); +int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType(); +int vec3VectorTypeId = qRegisterMetaType>(); +int normalTypeVecTypeId = qRegisterMetaType>("QVector"); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; #define HTTP_INVALID_COM "http://invalid.com" @@ -301,41 +302,52 @@ bool Model::updateGeometry() { assert(_meshStates.empty()); const FBXGeometry& fbxGeometry = getFBXGeometry(); + int i = 0; foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterDualQuaternions.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.push_back(state); - // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index - // later in ModelMeshPayload, however the vast majority of meshes will not have them. - // TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes? - auto buffer = std::make_shared(); if (!mesh.blendshapes.isEmpty()) { - std::vector normalsAndTangents; - normalsAndTangents.reserve(mesh.normals.size() + mesh.tangents.size()); - - for (auto normalIt = mesh.normals.begin(), tangentIt = mesh.tangents.begin(); - normalIt != mesh.normals.end(); - ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS - glm::uint32 finalNormal; - glm::uint32 finalTangent; - buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); -#else - const auto finalNormal = *normalIt; - const auto finalTangent = *tangentIt; -#endif - normalsAndTangents.push_back(finalNormal); - normalsAndTangents.push_back(finalTangent); + if (!_blendedVertexBuffers[i]) { + _blendedVertexBuffers[i] = std::make_shared(); } + const auto& buffer = _blendedVertexBuffers[i]; + QVector normalsAndTangents; + normalsAndTangents.resize(2 * mesh.normals.size()); - buffer->resize(mesh.vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); - buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) mesh.vertices.constData()); - buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * 2 * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); + // Interleave normals and tangents + // Parallel version for performance + tbb::parallel_for(tbb::blocked_range(0, mesh.normals.size()), [&](const tbb::blocked_range& range) { + auto normalsRange = std::make_pair(mesh.normals.begin() + range.begin(), mesh.normals.begin() + range.end()); + auto tangentsRange = std::make_pair(mesh.tangents.begin() + range.begin(), mesh.tangents.begin() + range.end()); + auto normalsAndTangentsIt = normalsAndTangents.begin() + 2 * range.begin(); + + for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; + normalIt != normalsRange.second; + ++normalIt, ++tangentIt) { +#if FBX_PACK_NORMALS + glm::uint32 finalNormal; + glm::uint32 finalTangent; + buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); +#else + const auto& finalNormal = *normalIt; + const auto& finalTangent = *tangentIt; +#endif + *normalsAndTangentsIt = finalNormal; + ++normalsAndTangentsIt; + *normalsAndTangentsIt = finalTangent; + ++normalsAndTangentsIt; + } + }); + const auto verticesSize = mesh.vertices.size() * sizeof(glm::vec3); + buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + normalsAndTangents.size() * sizeof(NormalType)); + buffer->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData()); + buffer->setSubData(verticesSize, 2 * mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); + mesh.normalsAndTangents = normalsAndTangents; } - _blendedVertexBuffers.push_back(buffer); + i++; } needFullUpdate = true; emit rigReady(); @@ -1245,20 +1257,21 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe void Blender::run() { DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); - QVector vertices, normals, tangents; + QVector vertices; + QVector normalsAndTangents; if (_model) { int offset = 0; + int normalsAndTangentsOffset = 0; foreach (const FBXMesh& mesh, _meshes) { if (mesh.blendshapes.isEmpty()) { continue; } vertices += mesh.vertices; - normals += mesh.normals; - tangents += mesh.tangents; + normalsAndTangents += mesh.normalsAndTangents; glm::vec3* meshVertices = vertices.data() + offset; - glm::vec3* meshNormals = normals.data() + offset; - glm::vec3* meshTangents = tangents.data() + offset; + NormalType* meshNormalsAndTangents = normalsAndTangents.data() + normalsAndTangentsOffset; offset += mesh.vertices.size(); + normalsAndTangentsOffset += mesh.normalsAndTangents.size(); const float NORMAL_COEFFICIENT_SCALE = 0.01f; for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) { float vertexCoefficient = _blendshapeCoefficients.at(i); @@ -1268,22 +1281,39 @@ void Blender::run() { } float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; const FBXBlendshape& blendshape = mesh.blendshapes.at(i); - for (int j = 0; j < blendshape.indices.size(); j++) { - int index = blendshape.indices.at(j); - meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; - meshNormals[index] += blendshape.normals.at(j) * normalCoefficient; - if (blendshape.tangents.size() > j) { - meshTangents[index] += blendshape.tangents.at(j) * normalCoefficient; + tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) { + for (auto j = range.begin(); j < range.end(); j++) { + int index = blendshape.indices.at(j); + meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; + + glm::vec3 normal = mesh.normals.at(index) + blendshape.normals.at(j) * normalCoefficient; + glm::vec3 tangent; + if (index < mesh.tangents.size()) { + tangent = mesh.tangents.at(index); + if ((int)j < blendshape.tangents.size()) { + tangent += blendshape.tangents.at(j) * normalCoefficient; + } + } +#if FBX_PACK_NORMALS + glm::uint32 finalNormal; + glm::uint32 finalTangent; + buffer_helpers::packNormalAndTangent(normal, tangent, finalNormal, finalTangent); +#else + const auto& finalNormal = normal; + const auto& finalTangent = tangent; +#endif + meshNormalsAndTangents[2 * index] = finalNormal; + meshNormalsAndTangents[2 * index + 1] = finalTangent; } - } + }); } } } - // post the result to the geometry cache, 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().data(), "setBlendedVertices", - Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), - Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector&, vertices), - Q_ARG(const QVector&, normals), Q_ARG(const QVector&, tangents)); + Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), + Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector&, vertices), + Q_ARG(const QVector&, normalsAndTangents)); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { @@ -1443,9 +1473,10 @@ void Model::updateClusterMatrices() { } // post the blender if we're not currently waiting for one to finish - if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + auto modelBlender = DependencyManager::get(); + if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; - DependencyManager::get()->noteRequiresBlend(getThisPointer()); + modelBlender->noteRequiresBlend(getThisPointer()); } } @@ -1462,83 +1493,31 @@ bool Model::maybeStartBlender() { } void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, const QVector& tangents) { + const QVector& vertices, const QVector& normalsAndTangents) { auto geometryRef = geometry.lock(); - if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { + if (!geometryRef || _renderGeometry != geometryRef || blendNumber < _appliedBlendNumber) { return; } _appliedBlendNumber = blendNumber; const FBXGeometry& fbxGeometry = getFBXGeometry(); int index = 0; - std::vector normalsAndTangents; + int normalAndTangentIndex = 0; for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.blendshapes.isEmpty()) { continue; } - gpu::BufferPointer& buffer = _blendedVertexBuffers[i]; const auto vertexCount = mesh.vertices.size(); const auto verticesSize = vertexCount * sizeof(glm::vec3); - const auto offset = index * sizeof(glm::vec3); - - normalsAndTangents.clear(); - normalsAndTangents.resize(normals.size()+tangents.size()); -// assert(normalsAndTangents.size() == 2 * vertexCount); - - // Interleave normals and tangents -#if 0 - // Sequential version for debugging - auto normalsRange = std::make_pair(normals.begin() + index, normals.begin() + index + vertexCount); - auto tangentsRange = std::make_pair(tangents.begin() + index, tangents.begin() + index + vertexCount); - auto normalsAndTangentsIt = normalsAndTangents.begin(); - - for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; - normalIt != normalsRange.second; - ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS - glm::uint32 finalNormal; - glm::uint32 finalTangent; - buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); -#else - const auto finalNormal = *normalIt; - const auto finalTangent = *tangentIt; -#endif - *normalsAndTangentsIt = finalNormal; - ++normalsAndTangentsIt; - *normalsAndTangentsIt = finalTangent; - ++normalsAndTangentsIt; - } -#else - // Parallel version for performance - tbb::parallel_for(tbb::blocked_range(index, index+vertexCount), [&](const tbb::blocked_range& range) { - auto normalsRange = std::make_pair(normals.begin() + range.begin(), normals.begin() + range.end()); - auto tangentsRange = std::make_pair(tangents.begin() + range.begin(), tangents.begin() + range.end()); - auto normalsAndTangentsIt = normalsAndTangents.begin() + (range.begin()-index)*2; - - for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; - normalIt != normalsRange.second; - ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS - glm::uint32 finalNormal; - glm::uint32 finalTangent; - buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); -#else - const auto finalNormal = *normalIt; - const auto finalTangent = *tangentIt; -#endif - *normalsAndTangentsIt = finalNormal; - ++normalsAndTangentsIt; - *normalsAndTangentsIt = finalTangent; - ++normalsAndTangentsIt; - } - }); -#endif - - buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + offset); - buffer->setSubData(verticesSize, 2 * vertexCount * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); + const auto& buffer = _blendedVertexBuffers[i]; + assert(buffer); + buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + mesh.normalsAndTangents.size() * sizeof(NormalType)); + buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3)); + buffer->setSubData(verticesSize, 2 * mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType)); index += vertexCount; + normalAndTangentIndex += 2 * mesh.normals.size(); } } @@ -1604,6 +1583,7 @@ void Model::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); + auto fbxGeometry = getFBXGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -1613,6 +1593,9 @@ void Model::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { + if (fbxGeometry.meshes[i].blendshapes.empty() && !_blendedVertexBuffers[i]) { + _blendedVertexBuffers[i] = std::make_shared(); + } _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); auto material = getGeometry()->getShapeMaterial(shapeID); _modelMeshMaterialNames.push_back(material ? material->getName() : ""); @@ -1725,11 +1708,10 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { } } -void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, - const QVector& tangents) { +void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, + const QVector& vertices, const QVector& normalsAndTangents) { if (model) { - model->setBlendedVertices(blendNumber, geometry, vertices, normals, tangents); + model->setBlendedVertices(blendNumber, geometry, vertices, normalsAndTangents); } _pendingBlenders--; { @@ -1745,4 +1727,3 @@ void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const } } } - diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 627e5fddab..677be61261 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -135,7 +135,7 @@ public: /// Sets blended vertices computed in a separate thread. void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, const QVector& tangents); + const QVector& vertices, const QVector& normalsAndTangents); bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); } bool isAddedToScene() const { return _addedToScene; } @@ -413,7 +413,7 @@ protected: QUrl _url; - gpu::Buffers _blendedVertexBuffers; + std::unordered_map _blendedVertexBuffers; QVector > > _dilatedTextures; @@ -503,9 +503,12 @@ public: /// Adds the specified model to the list requiring vertex blends. void noteRequiresBlend(ModelPointer model); + bool shouldComputeBlendshapes() { return _computeBlendshapes; } + public slots: void setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, const QVector& tangents); + const QVector& vertices, const QVector& normalsAndTangents); + void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; } private: using Mutex = std::mutex; @@ -517,6 +520,8 @@ private: std::set> _modelsRequiringBlends; int _pendingBlenders; Mutex _mutex; + + bool _computeBlendshapes { true }; }; diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 079e6f75ef..f9b90f38ba 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -77,8 +77,9 @@ void SoftAttachmentModel::updateClusterMatrices() { } // post the blender if we're not currently waiting for one to finish - if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + auto modelBlender = DependencyManager::get(); + if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; - DependencyManager::get()->noteRequiresBlend(getThisPointer()); + modelBlender->noteRequiresBlend(getThisPointer()); } }