mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 20:08:56 +02:00
Merge pull request #13845 from SamGondelman/perf
Move blendshape packing to worker thread
This commit is contained in:
commit
467fe56103
12 changed files with 162 additions and 165 deletions
|
@ -451,6 +451,9 @@ Menu::Menu() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ComputeBlendshapes, 0, true,
|
||||||
|
DependencyManager::get<ModelBlender>().data(), SLOT(setComputeBlendshapes(bool)));
|
||||||
|
|
||||||
// Developer > Assets >>>
|
// Developer > Assets >>>
|
||||||
// Menu item is not currently needed but code should be kept in case it proves useful again at some stage.
|
// Menu item is not currently needed but code should be kept in case it proves useful again at some stage.
|
||||||
//#define WANT_ASSET_MIGRATION
|
//#define WANT_ASSET_MIGRATION
|
||||||
|
|
|
@ -221,6 +221,7 @@ namespace MenuOption {
|
||||||
const QString NotificationSounds = "play_notification_sounds";
|
const QString NotificationSounds = "play_notification_sounds";
|
||||||
const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot";
|
const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot";
|
||||||
const QString NotificationSoundsTablet = "play_notification_sounds_tablet";
|
const QString NotificationSoundsTablet = "play_notification_sounds_tablet";
|
||||||
|
const QString ComputeBlendshapes = "Compute Blendshapes";
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // hifi_Menu_h
|
#endif // hifi_Menu_h
|
||||||
|
|
|
@ -359,21 +359,21 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||||
QReadLocker locker(&_hashLock);
|
QReadLocker locker(&_hashLock);
|
||||||
QVector<AvatarSharedPointer>::iterator avatarItr = _avatarsToFade.begin();
|
QVector<AvatarSharedPointer>::iterator avatarItr = _avatarsToFade.begin();
|
||||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||||
|
render::Transaction transaction;
|
||||||
while (avatarItr != _avatarsToFade.end()) {
|
while (avatarItr != _avatarsToFade.end()) {
|
||||||
auto avatar = std::static_pointer_cast<Avatar>(*avatarItr);
|
auto avatar = std::static_pointer_cast<Avatar>(*avatarItr);
|
||||||
avatar->updateFadingStatus(scene);
|
avatar->updateFadingStatus(scene);
|
||||||
if (!avatar->isFading()) {
|
if (!avatar->isFading()) {
|
||||||
// fading to zero is such a rare event we push a unique transaction for each
|
// fading to zero is such a rare event we push a unique transaction for each
|
||||||
if (avatar->isInScene()) {
|
if (avatar->isInScene()) {
|
||||||
render::Transaction transaction;
|
|
||||||
avatar->removeFromScene(*avatarItr, scene, transaction);
|
avatar->removeFromScene(*avatarItr, scene, transaction);
|
||||||
scene->enqueueTransaction(transaction);
|
|
||||||
}
|
}
|
||||||
avatarItr = _avatarsToFade.erase(avatarItr);
|
avatarItr = _avatarsToFade.erase(avatarItr);
|
||||||
} else {
|
} else {
|
||||||
++avatarItr;
|
++avatarItr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scene->enqueueTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
||||||
|
|
|
@ -28,6 +28,20 @@
|
||||||
#include <graphics/Geometry.h>
|
#include <graphics/Geometry.h>
|
||||||
#include <graphics/Material.h>
|
#include <graphics/Material.h>
|
||||||
|
|
||||||
|
#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().
|
// See comment in FBXReader::parseFBX().
|
||||||
static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23;
|
static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23;
|
||||||
static const QByteArray FBX_BINARY_PROLOG("Kaydara FBX Binary ");
|
static const QByteArray FBX_BINARY_PROLOG("Kaydara FBX Binary ");
|
||||||
|
@ -226,6 +240,7 @@ 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;
|
||||||
|
|
|
@ -34,20 +34,6 @@
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
class FBXNode;
|
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.
|
/// Reads FBX geometry from the supplied model and mapping data.
|
||||||
/// \exception QString if an error occurs in parsing
|
/// \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);
|
FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f);
|
||||||
|
|
|
@ -20,9 +20,6 @@
|
||||||
#include <AABox.h>
|
#include <AABox.h>
|
||||||
#include <Extents.h>
|
#include <Extents.h>
|
||||||
|
|
||||||
#include <glm/gtc/packing.hpp>
|
|
||||||
#include <glm/detail/type_vec.hpp>
|
|
||||||
|
|
||||||
namespace glm {
|
namespace glm {
|
||||||
using hvec2 = glm::tvec2<glm::detail::hdata>;
|
using hvec2 = glm::tvec2<glm::detail::hdata>;
|
||||||
using hvec4 = glm::tvec4<glm::detail::hdata>;
|
using hvec4 = glm::tvec4<glm::detail::hdata>;
|
||||||
|
@ -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 <typename T>
|
template <typename T>
|
||||||
glm::uint32 forEachGlmVec(const gpu::BufferView& view, std::function<bool(glm::uint32 index, const T& value)> func) {
|
glm::uint32 forEachGlmVec(const gpu::BufferView& view, std::function<bool(glm::uint32 index, const T& value)> func) {
|
||||||
QVector<glm::uint32> result;
|
QVector<glm::uint32> result;
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/packing.hpp>
|
||||||
|
#include <glm/detail/type_vec.hpp>
|
||||||
|
|
||||||
#include "GpuHelpers.h"
|
#include "GpuHelpers.h"
|
||||||
|
|
||||||
|
@ -44,7 +46,31 @@ namespace buffer_helpers {
|
||||||
gpu::BufferView clone(const gpu::BufferView& input);
|
gpu::BufferView clone(const gpu::BufferView& input);
|
||||||
gpu::BufferView resized(const gpu::BufferView& input, glm::uint32 numElements);
|
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 {
|
namespace mesh {
|
||||||
glm::uint32 forEachVertex(const graphics::MeshPointer& mesh, std::function<bool(glm::uint32 index, const QVariantMap& attributes)> func);
|
glm::uint32 forEachVertex(const graphics::MeshPointer& mesh, std::function<bool(glm::uint32 index, const QVariantMap& attributes)> func);
|
||||||
|
|
|
@ -76,6 +76,7 @@ void CauterizedModel::createRenderItemSet() {
|
||||||
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
||||||
int shapeID = 0;
|
int shapeID = 0;
|
||||||
uint32_t numMeshes = (uint32_t)meshes.size();
|
uint32_t numMeshes = (uint32_t)meshes.size();
|
||||||
|
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
||||||
for (uint32_t i = 0; i < numMeshes; i++) {
|
for (uint32_t i = 0; i < numMeshes; i++) {
|
||||||
const auto& mesh = meshes.at(i);
|
const auto& mesh = meshes.at(i);
|
||||||
if (!mesh) {
|
if (!mesh) {
|
||||||
|
@ -85,6 +86,10 @@ 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() && !_blendedVertexBuffers[i]) {
|
||||||
|
_blendedVertexBuffers[i] = std::make_shared<gpu::Buffer>();
|
||||||
|
_blendedVertexBuffers[i]->resize(fbxGeometry.meshes[i].vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType)));
|
||||||
|
}
|
||||||
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);
|
||||||
|
@ -170,9 +175,10 @@ 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
|
||||||
if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||||
|
if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||||
DependencyManager::get<ModelBlender>()->noteRequiresBlend(getThisPointer());
|
modelBlender->noteRequiresBlend(getThisPointer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,7 +208,9 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
|
||||||
|
|
||||||
bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning();
|
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);
|
auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex);
|
||||||
const Model::MeshState& state = model->getMeshState(_meshIndex);
|
const Model::MeshState& state = model->getMeshState(_meshIndex);
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,9 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>();
|
int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>();
|
||||||
int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType<Geometry::WeakPointer >();
|
int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType<Geometry::WeakPointer>();
|
||||||
int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
|
int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3>>();
|
||||||
|
int normalTypeVecTypeId = qRegisterMetaType<QVector<NormalType>>("QVector<NormalType>");
|
||||||
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
||||||
#define HTTP_INVALID_COM "http://invalid.com"
|
#define HTTP_INVALID_COM "http://invalid.com"
|
||||||
|
|
||||||
|
@ -301,41 +302,52 @@ 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);
|
||||||
|
|
||||||
// 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<gpu::Buffer>();
|
|
||||||
if (!mesh.blendshapes.isEmpty()) {
|
if (!mesh.blendshapes.isEmpty()) {
|
||||||
std::vector<NormalType> normalsAndTangents;
|
if (!_blendedVertexBuffers[i]) {
|
||||||
normalsAndTangents.reserve(mesh.normals.size() + mesh.tangents.size());
|
_blendedVertexBuffers[i] = std::make_shared<gpu::Buffer>();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
const auto& buffer = _blendedVertexBuffers[i];
|
||||||
|
QVector<NormalType> normalsAndTangents;
|
||||||
|
normalsAndTangents.resize(2 * mesh.normals.size());
|
||||||
|
|
||||||
buffer->resize(mesh.vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType)));
|
// Interleave normals and tangents
|
||||||
buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) mesh.vertices.constData());
|
// Parallel version for performance
|
||||||
buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3),
|
tbb::parallel_for(tbb::blocked_range<int>(0, mesh.normals.size()), [&](const tbb::blocked_range<int>& range) {
|
||||||
mesh.normals.size() * 2 * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data());
|
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;
|
needFullUpdate = true;
|
||||||
emit rigReady();
|
emit rigReady();
|
||||||
|
@ -1245,20 +1257,21 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe
|
||||||
|
|
||||||
void Blender::run() {
|
void Blender::run() {
|
||||||
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() } });
|
||||||
QVector<glm::vec3> vertices, normals, tangents;
|
QVector<glm::vec3> vertices;
|
||||||
|
QVector<NormalType> normalsAndTangents;
|
||||||
if (_model) {
|
if (_model) {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
int normalsAndTangentsOffset = 0;
|
||||||
foreach (const FBXMesh& mesh, _meshes) {
|
foreach (const FBXMesh& mesh, _meshes) {
|
||||||
if (mesh.blendshapes.isEmpty()) {
|
if (mesh.blendshapes.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
vertices += mesh.vertices;
|
vertices += mesh.vertices;
|
||||||
normals += mesh.normals;
|
normalsAndTangents += mesh.normalsAndTangents;
|
||||||
tangents += mesh.tangents;
|
|
||||||
glm::vec3* meshVertices = vertices.data() + offset;
|
glm::vec3* meshVertices = vertices.data() + offset;
|
||||||
glm::vec3* meshNormals = normals.data() + offset;
|
NormalType* meshNormalsAndTangents = normalsAndTangents.data() + normalsAndTangentsOffset;
|
||||||
glm::vec3* meshTangents = tangents.data() + offset;
|
|
||||||
offset += mesh.vertices.size();
|
offset += mesh.vertices.size();
|
||||||
|
normalsAndTangentsOffset += mesh.normalsAndTangents.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);
|
||||||
|
@ -1268,22 +1281,39 @@ void Blender::run() {
|
||||||
}
|
}
|
||||||
float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE;
|
float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE;
|
||||||
const FBXBlendshape& blendshape = mesh.blendshapes.at(i);
|
const FBXBlendshape& blendshape = mesh.blendshapes.at(i);
|
||||||
for (int j = 0; j < blendshape.indices.size(); j++) {
|
tbb::parallel_for(tbb::blocked_range<int>(0, blendshape.indices.size()), [&](const tbb::blocked_range<int>& range) {
|
||||||
int index = blendshape.indices.at(j);
|
for (auto j = range.begin(); j < range.end(); j++) {
|
||||||
meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient;
|
int index = blendshape.indices.at(j);
|
||||||
meshNormals[index] += blendshape.normals.at(j) * normalCoefficient;
|
meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient;
|
||||||
if (blendshape.tangents.size() > j) {
|
|
||||||
meshTangents[index] += blendshape.tangents.at(j) * normalCoefficient;
|
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<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(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices),
|
Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices),
|
||||||
Q_ARG(const QVector<glm::vec3>&, normals), Q_ARG(const QVector<glm::vec3>&, tangents));
|
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) {
|
||||||
|
@ -1443,9 +1473,10 @@ 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
|
||||||
if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||||
|
if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||||
DependencyManager::get<ModelBlender>()->noteRequiresBlend(getThisPointer());
|
modelBlender->noteRequiresBlend(getThisPointer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1462,83 +1493,31 @@ bool Model::maybeStartBlender() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry,
|
void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry,
|
||||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, const QVector<glm::vec3>& tangents) {
|
const QVector<glm::vec3>& vertices, const QVector<NormalType>& normalsAndTangents) {
|
||||||
auto geometryRef = geometry.lock();
|
auto geometryRef = geometry.lock();
|
||||||
if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) {
|
if (!geometryRef || _renderGeometry != geometryRef || blendNumber < _appliedBlendNumber) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_appliedBlendNumber = blendNumber;
|
_appliedBlendNumber = blendNumber;
|
||||||
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
std::vector<NormalType> normalsAndTangents;
|
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()) {
|
if (mesh.blendshapes.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::BufferPointer& buffer = _blendedVertexBuffers[i];
|
|
||||||
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 offset = index * sizeof(glm::vec3);
|
const auto& buffer = _blendedVertexBuffers[i];
|
||||||
|
assert(buffer);
|
||||||
normalsAndTangents.clear();
|
buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + mesh.normalsAndTangents.size() * sizeof(NormalType));
|
||||||
normalsAndTangents.resize(normals.size()+tangents.size());
|
buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3));
|
||||||
// assert(normalsAndTangents.size() == 2 * vertexCount);
|
buffer->setSubData(verticesSize, 2 * mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType));
|
||||||
|
|
||||||
// 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<size_t>(index, index+vertexCount), [&](const tbb::blocked_range<size_t>& 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());
|
|
||||||
|
|
||||||
index += vertexCount;
|
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
|
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
||||||
int shapeID = 0;
|
int shapeID = 0;
|
||||||
uint32_t numMeshes = (uint32_t)meshes.size();
|
uint32_t numMeshes = (uint32_t)meshes.size();
|
||||||
|
auto fbxGeometry = getFBXGeometry();
|
||||||
for (uint32_t i = 0; i < numMeshes; i++) {
|
for (uint32_t i = 0; i < numMeshes; i++) {
|
||||||
const auto& mesh = meshes.at(i);
|
const auto& mesh = meshes.at(i);
|
||||||
if (!mesh) {
|
if (!mesh) {
|
||||||
|
@ -1613,6 +1593,9 @@ 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() && !_blendedVertexBuffers[i]) {
|
||||||
|
_blendedVertexBuffers[i] = std::make_shared<gpu::Buffer>();
|
||||||
|
}
|
||||||
_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() : "");
|
||||||
|
@ -1725,11 +1708,10 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry,
|
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry,
|
||||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals,
|
const QVector<glm::vec3>& vertices, const QVector<NormalType>& normalsAndTangents) {
|
||||||
const QVector<glm::vec3>& tangents) {
|
|
||||||
if (model) {
|
if (model) {
|
||||||
model->setBlendedVertices(blendNumber, geometry, vertices, normals, tangents);
|
model->setBlendedVertices(blendNumber, geometry, vertices, normalsAndTangents);
|
||||||
}
|
}
|
||||||
_pendingBlenders--;
|
_pendingBlenders--;
|
||||||
{
|
{
|
||||||
|
@ -1745,4 +1727,3 @@ void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ public:
|
||||||
|
|
||||||
/// 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 Geometry::WeakPointer& geometry,
|
||||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, const QVector<glm::vec3>& tangents);
|
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; }
|
||||||
|
@ -413,7 +413,7 @@ protected:
|
||||||
|
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
|
|
||||||
gpu::Buffers _blendedVertexBuffers;
|
std::unordered_map<int, gpu::BufferPointer> _blendedVertexBuffers;
|
||||||
|
|
||||||
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
|
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
|
||||||
|
|
||||||
|
@ -503,9 +503,12 @@ public:
|
||||||
/// Adds the specified model to the list requiring vertex blends.
|
/// Adds the specified model to the list requiring vertex blends.
|
||||||
void noteRequiresBlend(ModelPointer model);
|
void noteRequiresBlend(ModelPointer model);
|
||||||
|
|
||||||
|
bool shouldComputeBlendshapes() { return _computeBlendshapes; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry,
|
void setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry,
|
||||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, const QVector<glm::vec3>& tangents);
|
const QVector<glm::vec3>& vertices, const QVector<NormalType>& normalsAndTangents);
|
||||||
|
void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Mutex = std::mutex;
|
using Mutex = std::mutex;
|
||||||
|
@ -517,6 +520,8 @@ private:
|
||||||
std::set<ModelWeakPointer, std::owner_less<ModelWeakPointer>> _modelsRequiringBlends;
|
std::set<ModelWeakPointer, std::owner_less<ModelWeakPointer>> _modelsRequiringBlends;
|
||||||
int _pendingBlenders;
|
int _pendingBlenders;
|
||||||
Mutex _mutex;
|
Mutex _mutex;
|
||||||
|
|
||||||
|
bool _computeBlendshapes { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,9 @@ 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
|
||||||
if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||||
|
if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||||
DependencyManager::get<ModelBlender>()->noteRequiresBlend(getThisPointer());
|
modelBlender->noteRequiresBlend(getThisPointer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue