mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 12:57:18 +02:00
Merge pull request #14184 from SamGondelman/blendshape
Move blendshape buffers to render thread
This commit is contained in:
commit
9c841db9fb
11 changed files with 180 additions and 85 deletions
|
@ -703,6 +703,19 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
|
||||||
return displayNameRenderer;
|
return displayNameRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Avatar::metaBlendshapeOperator(int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets, const QVector<int>& blendedMeshSizes,
|
||||||
|
const render::ItemIDs& subItemIDs) {
|
||||||
|
render::Transaction transaction;
|
||||||
|
transaction.updateItem<AvatarData>(_renderItemID, [blendshapeNumber, blendshapeOffsets, blendedMeshSizes,
|
||||||
|
subItemIDs](AvatarData& avatar) {
|
||||||
|
auto avatarPtr = dynamic_cast<Avatar*>(&avatar);
|
||||||
|
if (avatarPtr) {
|
||||||
|
avatarPtr->setBlendedVertices(blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||||
auto avatarPayload = new render::Payload<AvatarData>(self);
|
auto avatarPayload = new render::Payload<AvatarData>(self);
|
||||||
auto avatarPayloadPointer = std::shared_ptr<render::Payload<AvatarData>>(avatarPayload);
|
auto avatarPayloadPointer = std::shared_ptr<render::Payload<AvatarData>>(avatarPayload);
|
||||||
|
@ -712,7 +725,8 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc
|
||||||
// INitialize the _render bound as we are creating the avatar render item
|
// INitialize the _render bound as we are creating the avatar render item
|
||||||
_renderBound = getBounds();
|
_renderBound = getBounds();
|
||||||
transaction.resetItem(_renderItemID, avatarPayloadPointer);
|
transaction.resetItem(_renderItemID, avatarPayloadPointer);
|
||||||
_skeletonModel->addToScene(scene, transaction);
|
using namespace std::placeholders;
|
||||||
|
_skeletonModel->addToScene(scene, transaction, std::bind(&Avatar::metaBlendshapeOperator, this, _1, _2, _3, _4));
|
||||||
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
||||||
_skeletonModel->setGroupCulled(true);
|
_skeletonModel->setGroupCulled(true);
|
||||||
_skeletonModel->setCanCastShadow(true);
|
_skeletonModel->setCanCastShadow(true);
|
||||||
|
@ -791,7 +805,7 @@ void Avatar::updateRenderItem(render::Transaction& transaction) {
|
||||||
avatarPtr->_renderBound = renderBound;
|
avatarPtr->_renderBound = renderBound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -935,7 +949,8 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) {
|
if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) {
|
||||||
_skeletonModel->removeFromScene(scene, transaction);
|
_skeletonModel->removeFromScene(scene, transaction);
|
||||||
_skeletonModel->addToScene(scene, transaction);
|
using namespace std::placeholders;
|
||||||
|
_skeletonModel->addToScene(scene, transaction, std::bind(&Avatar::metaBlendshapeOperator, this, _1, _2, _3, _4));
|
||||||
|
|
||||||
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
||||||
_skeletonModel->setGroupCulled(true);
|
_skeletonModel->setGroupCulled(true);
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#include "Rig.h"
|
#include "Rig.h"
|
||||||
#include <ThreadSafeValueCache.h>
|
#include <ThreadSafeValueCache.h>
|
||||||
|
|
||||||
|
#include "MetaModelPayload.h"
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar);
|
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar);
|
||||||
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar);
|
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar);
|
||||||
|
@ -108,7 +110,7 @@ private:
|
||||||
float _scale { 1.0f };
|
float _scale { 1.0f };
|
||||||
};
|
};
|
||||||
|
|
||||||
class Avatar : public AvatarData, public scriptable::ModelProvider {
|
class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaModelPayload {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
// This property has JSDoc in MyAvatar.h.
|
// This property has JSDoc in MyAvatar.h.
|
||||||
|
@ -620,6 +622,9 @@ protected:
|
||||||
static const float ATTACHMENT_LOADING_PRIORITY;
|
static const float ATTACHMENT_LOADING_PRIORITY;
|
||||||
|
|
||||||
LoadingStatus _loadingStatus { LoadingStatus::NoModel };
|
LoadingStatus _loadingStatus { LoadingStatus::NoModel };
|
||||||
|
|
||||||
|
void metaBlendshapeOperator(int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets, const QVector<int>& blendedMeshSizes,
|
||||||
|
const render::ItemIDs& subItemIDs);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Avatar_h
|
#endif // hifi_Avatar_h
|
||||||
|
|
|
@ -24,8 +24,6 @@
|
||||||
|
|
||||||
#include "RenderableEntityItem.h"
|
#include "RenderableEntityItem.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Model;
|
class Model;
|
||||||
class EntityTreeRenderer;
|
class EntityTreeRenderer;
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ void CauterizedModel::createRenderItemSet() {
|
||||||
shapeID++;
|
shapeID++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_blendshapeBuffersInitialized = true;
|
_blendshapeOffsetsInitialized = true;
|
||||||
} else {
|
} else {
|
||||||
Model::createRenderItemSet();
|
Model::createRenderItemSet();
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,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 (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||||
modelBlender->noteRequiresBlend(getThisPointer());
|
modelBlender->noteRequiresBlend(getThisPointer());
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,6 +209,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
|
||||||
bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning();
|
bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning();
|
||||||
|
|
||||||
auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex);
|
auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex);
|
||||||
|
_meshNumVertices = (int)modelMesh->getNumVertices();
|
||||||
const Model::MeshState& state = model->getMeshState(_meshIndex);
|
const Model::MeshState& state = model->getMeshState(_meshIndex);
|
||||||
|
|
||||||
updateMeshPart(modelMesh, partIndex);
|
updateMeshPart(modelMesh, partIndex);
|
||||||
|
@ -238,19 +239,12 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
|
||||||
|
|
||||||
initCache(model);
|
initCache(model);
|
||||||
|
|
||||||
if (_isBlendShaped) {
|
|
||||||
auto buffer = model->_blendshapeBuffers.find(meshIndex);
|
|
||||||
if (buffer != model->_blendshapeBuffers.end()) {
|
|
||||||
_blendshapeBuffer = buffer->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
// On mac AMD, we specifically need to have a _blendshapeBuffer bound when using a deformed mesh pipeline
|
// On mac AMD, we specifically need to have a _meshBlendshapeBuffer bound when using a deformed mesh pipeline
|
||||||
// it cannot be null otherwise we crash in the drawcall using a deformed pipeline with a skinned only (not blendshaped) mesh
|
// it cannot be null otherwise we crash in the drawcall using a deformed pipeline with a skinned only (not blendshaped) mesh
|
||||||
if ((_isBlendShaped || _isSkinned) && !_blendshapeBuffer) {
|
if ((_isBlendShaped || _isSkinned)) {
|
||||||
glm::vec4 data;
|
glm::vec4 data;
|
||||||
_blendshapeBuffer = std::make_shared<gpu::Buffer>(sizeof(glm::vec4), reinterpret_cast<const gpu::Byte*>(&data));
|
_meshBlendshapeBuffer = std::make_shared<gpu::Buffer>(sizeof(glm::vec4), reinterpret_cast<const gpu::Byte*>(&data));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -292,8 +286,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clu
|
||||||
if (!_clusterBuffer) {
|
if (!_clusterBuffer) {
|
||||||
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterMatrices.size() * sizeof(glm::mat4),
|
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterMatrices.size() * sizeof(glm::mat4),
|
||||||
(const gpu::Byte*) clusterMatrices.data());
|
(const gpu::Byte*) clusterMatrices.data());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
_clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4),
|
_clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4),
|
||||||
(const gpu::Byte*) clusterMatrices.data());
|
(const gpu::Byte*) clusterMatrices.data());
|
||||||
}
|
}
|
||||||
|
@ -313,8 +306,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector<Model::Transfor
|
||||||
if (!_clusterBuffer) {
|
if (!_clusterBuffer) {
|
||||||
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
|
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
|
||||||
(const gpu::Byte*) clusterDualQuaternions.data());
|
(const gpu::Byte*) clusterDualQuaternions.data());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
_clusterBuffer->setSubData(0, clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
|
_clusterBuffer->setSubData(0, clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
|
||||||
(const gpu::Byte*) clusterDualQuaternions.data());
|
(const gpu::Byte*) clusterDualQuaternions.data());
|
||||||
}
|
}
|
||||||
|
@ -403,8 +395,8 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
|
||||||
void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
|
void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
|
||||||
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
|
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
|
||||||
batch.setInputFormat((_drawMesh->getVertexFormat()));
|
batch.setInputFormat((_drawMesh->getVertexFormat()));
|
||||||
if (_blendshapeBuffer) {
|
if (_meshBlendshapeBuffer) {
|
||||||
batch.setResourceBuffer(0, _blendshapeBuffer);
|
batch.setResourceBuffer(0, _meshBlendshapeBuffer);
|
||||||
}
|
}
|
||||||
batch.setInputStream(0, _drawMesh->getVertexStream());
|
batch.setInputStream(0, _drawMesh->getVertexStream());
|
||||||
}
|
}
|
||||||
|
@ -431,7 +423,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
|
||||||
bindMesh(batch);
|
bindMesh(batch);
|
||||||
|
|
||||||
// IF deformed pass the mesh key
|
// IF deformed pass the mesh key
|
||||||
auto drawcallInfo = (uint16_t) (((_isBlendShaped && args->_enableBlendshape) << 0) | ((_isSkinned && args->_enableSkinning) << 1));
|
auto drawcallInfo = (uint16_t) (((_isBlendShaped && _meshBlendshapeBuffer && args->_enableBlendshape) << 0) | ((_isSkinned && args->_enableSkinning) << 1));
|
||||||
if (drawcallInfo) {
|
if (drawcallInfo) {
|
||||||
batch.setDrawcallUniform(drawcallInfo);
|
batch.setDrawcallUniform(drawcallInfo);
|
||||||
}
|
}
|
||||||
|
@ -483,3 +475,12 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<Model::Tr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelMeshPartPayload::setBlendshapeBuffer(const std::unordered_map<int, gpu::BufferPointer>& blendshapeBuffers, const QVector<int>& blendedMeshSizes) {
|
||||||
|
if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) {
|
||||||
|
auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex);
|
||||||
|
if (blendshapeBuffer != blendshapeBuffers.end()) {
|
||||||
|
_meshBlendshapeBuffer = blendshapeBuffer->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -134,10 +134,13 @@ public:
|
||||||
bool _isBlendShaped { false };
|
bool _isBlendShaped { false };
|
||||||
bool _hasTangents { false };
|
bool _hasTangents { false };
|
||||||
|
|
||||||
|
void setBlendshapeBuffer(const std::unordered_map<int, gpu::BufferPointer>& blendshapeBuffers, const QVector<int>& blendedMeshSizes);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initCache(const ModelPointer& model);
|
void initCache(const ModelPointer& model);
|
||||||
|
|
||||||
gpu::BufferPointer _blendshapeBuffer;
|
gpu::BufferPointer _meshBlendshapeBuffer;
|
||||||
|
int _meshNumVertices;
|
||||||
render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() };
|
render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
55
libraries/render-utils/src/MetaModelPayload.cpp
Normal file
55
libraries/render-utils/src/MetaModelPayload.cpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
//
|
||||||
|
// MetaModelPayload.cpp
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman on 10/9/18.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "MetaModelPayload.h"
|
||||||
|
|
||||||
|
#include "AbstractViewStateInterface.h"
|
||||||
|
#include "MeshPartPayload.h"
|
||||||
|
|
||||||
|
void MetaModelPayload::setBlendedVertices(int blendNumber, const QVector<BlendshapeOffset>& blendshapeOffsets, const QVector<int>& blendedMeshSizes, const render::ItemIDs& subRenderItems) {
|
||||||
|
PROFILE_RANGE(render, __FUNCTION__);
|
||||||
|
if (blendNumber < _appliedBlendNumber) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_appliedBlendNumber = blendNumber;
|
||||||
|
|
||||||
|
// We have fewer meshes than before. Invalidate everything
|
||||||
|
if (blendedMeshSizes.length() < (int)_blendshapeBuffers.size()) {
|
||||||
|
_blendshapeBuffers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (int i = 0; i < blendedMeshSizes.size(); i++) {
|
||||||
|
int numVertices = blendedMeshSizes.at(i);
|
||||||
|
|
||||||
|
// This mesh isn't blendshaped
|
||||||
|
if (numVertices == 0) {
|
||||||
|
_blendshapeBuffers.erase(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& buffer = _blendshapeBuffers.find(i);
|
||||||
|
const auto blendShapeBufferSize = numVertices * sizeof(BlendshapeOffset);
|
||||||
|
if (buffer == _blendshapeBuffers.end()) {
|
||||||
|
_blendshapeBuffers[i] = std::make_shared<gpu::Buffer>(blendShapeBufferSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset), blendShapeBufferSize);
|
||||||
|
} else {
|
||||||
|
buffer->second->setData(blendShapeBufferSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
index += numVertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
render::Transaction transaction;
|
||||||
|
for (auto& id : subRenderItems) {
|
||||||
|
transaction.updateItem<ModelMeshPartPayload>(id, [this, blendedMeshSizes](ModelMeshPartPayload& data) {
|
||||||
|
data.setBlendshapeBuffer(_blendshapeBuffers, blendedMeshSizes);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||||
|
}
|
30
libraries/render-utils/src/MetaModelPayload.h
Normal file
30
libraries/render-utils/src/MetaModelPayload.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// MetaModelPayload.h
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman on 10/9/18.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_MetaModelPayload_h
|
||||||
|
#define hifi_MetaModelPayload_h
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "Model.h"
|
||||||
|
|
||||||
|
#include "gpu/Buffer.h"
|
||||||
|
|
||||||
|
class MetaModelPayload {
|
||||||
|
public:
|
||||||
|
void setBlendedVertices(int blendNumber, const QVector<BlendshapeOffset>& blendshapeOffsets, const QVector<int>& blendedMeshSizes, const render::ItemIDs& subRenderItems);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<int, gpu::BufferPointer> _blendshapeBuffers;
|
||||||
|
int _appliedBlendNumber { 0 };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -62,8 +62,6 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) :
|
||||||
_snapModelToRegistrationPoint(false),
|
_snapModelToRegistrationPoint(false),
|
||||||
_snappedToRegistrationPoint(false),
|
_snappedToRegistrationPoint(false),
|
||||||
_url(HTTP_INVALID_COM),
|
_url(HTTP_INVALID_COM),
|
||||||
_blendNumber(0),
|
|
||||||
_appliedBlendNumber(0),
|
|
||||||
_isWireframe(false),
|
_isWireframe(false),
|
||||||
_renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build())
|
_renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build())
|
||||||
{
|
{
|
||||||
|
@ -311,7 +309,7 @@ bool Model::updateGeometry() {
|
||||||
initializeBlendshapes(mesh, i);
|
initializeBlendshapes(mesh, i);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
_blendshapeBuffersInitialized = true;
|
_blendshapeOffsetsInitialized = true;
|
||||||
needFullUpdate = true;
|
needFullUpdate = true;
|
||||||
emit rigReady();
|
emit rigReady();
|
||||||
}
|
}
|
||||||
|
@ -979,7 +977,8 @@ const render::ItemKey Model::getRenderItemKeyGlobalFlags() const {
|
||||||
|
|
||||||
bool Model::addToScene(const render::ScenePointer& scene,
|
bool Model::addToScene(const render::ScenePointer& scene,
|
||||||
render::Transaction& transaction,
|
render::Transaction& transaction,
|
||||||
render::Item::Status::Getters& statusGetters) {
|
render::Item::Status::Getters& statusGetters,
|
||||||
|
BlendShapeOperator modelBlendshapeOperator) {
|
||||||
if (!_addedToScene && isLoaded()) {
|
if (!_addedToScene && isLoaded()) {
|
||||||
updateClusterMatrices();
|
updateClusterMatrices();
|
||||||
if (_modelMeshRenderItems.empty()) {
|
if (_modelMeshRenderItems.empty()) {
|
||||||
|
@ -987,10 +986,11 @@ bool Model::addToScene(const render::ScenePointer& scene,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_modelBlendshapeOperator = modelBlendshapeOperator;
|
||||||
|
|
||||||
bool somethingAdded = false;
|
bool somethingAdded = false;
|
||||||
|
|
||||||
if (_modelMeshRenderItemsMap.empty()) {
|
if (_modelMeshRenderItemsMap.empty()) {
|
||||||
|
|
||||||
bool hasTransparent = false;
|
bool hasTransparent = false;
|
||||||
size_t verticesCount = 0;
|
size_t verticesCount = 0;
|
||||||
foreach(auto renderItem, _modelMeshRenderItems) {
|
foreach(auto renderItem, _modelMeshRenderItems) {
|
||||||
|
@ -1032,9 +1032,8 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti
|
||||||
_modelMeshMaterialNames.clear();
|
_modelMeshMaterialNames.clear();
|
||||||
_modelMeshRenderItemShapes.clear();
|
_modelMeshRenderItemShapes.clear();
|
||||||
|
|
||||||
_blendshapeBuffers.clear();
|
|
||||||
_blendshapeOffsets.clear();
|
_blendshapeOffsets.clear();
|
||||||
_blendshapeBuffersInitialized = false;
|
_blendshapeOffsetsInitialized = false;
|
||||||
|
|
||||||
_addedToScene = false;
|
_addedToScene = false;
|
||||||
|
|
||||||
|
@ -1437,7 +1436,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 (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||||
modelBlender->noteRequiresBlend(getThisPointer());
|
modelBlender->noteRequiresBlend(getThisPointer());
|
||||||
}
|
}
|
||||||
|
@ -1445,9 +1444,8 @@ void Model::updateClusterMatrices() {
|
||||||
|
|
||||||
void Model::deleteGeometry() {
|
void Model::deleteGeometry() {
|
||||||
_deleteGeometryCounter++;
|
_deleteGeometryCounter++;
|
||||||
_blendshapeBuffers.clear();
|
|
||||||
_blendshapeOffsets.clear();
|
_blendshapeOffsets.clear();
|
||||||
_blendshapeBuffersInitialized = false;
|
_blendshapeOffsetsInitialized = false;
|
||||||
_meshStates.clear();
|
_meshStates.clear();
|
||||||
_rig.destroyAnimGraph();
|
_rig.destroyAnimGraph();
|
||||||
_blendedBlendshapeCoefficients.clear();
|
_blendedBlendshapeCoefficients.clear();
|
||||||
|
@ -1525,7 +1523,7 @@ void Model::createRenderItemSet() {
|
||||||
shapeID++;
|
shapeID++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_blendshapeBuffersInitialized = true;
|
_blendshapeOffsetsInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::isRenderable() const {
|
bool Model::isRenderable() const {
|
||||||
|
@ -1654,6 +1652,7 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe
|
||||||
|
|
||||||
void Blender::run() {
|
void Blender::run() {
|
||||||
QVector<BlendshapeOffset> blendshapeOffsets;
|
QVector<BlendshapeOffset> blendshapeOffsets;
|
||||||
|
QVector<int> blendedMeshSizes;
|
||||||
if (_model && _model->isLoaded()) {
|
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 offset = 0;
|
int offset = 0;
|
||||||
|
@ -1662,12 +1661,22 @@ void Blender::run() {
|
||||||
foreach(const FBXMesh& mesh, meshes) {
|
foreach(const FBXMesh& mesh, meshes) {
|
||||||
auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++);
|
auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++);
|
||||||
if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) {
|
if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) {
|
||||||
|
// Not blendshaped or not initialized
|
||||||
|
blendedMeshSizes.push_back(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesh.vertices.size() != modelMeshBlendshapeOffsets->second.size()) {
|
||||||
|
// Mesh sizes don't match. Something has gone wrong
|
||||||
|
blendedMeshSizes.push_back(0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
blendshapeOffsets += modelMeshBlendshapeOffsets->second;
|
blendshapeOffsets += modelMeshBlendshapeOffsets->second;
|
||||||
BlendshapeOffset* meshBlendshapeOffsets = blendshapeOffsets.data() + offset;
|
BlendshapeOffset* meshBlendshapeOffsets = blendshapeOffsets.data() + offset;
|
||||||
offset += modelMeshBlendshapeOffsets->second.size();
|
int numVertices = modelMeshBlendshapeOffsets->second.size();
|
||||||
|
blendedMeshSizes.push_back(numVertices);
|
||||||
|
offset += numVertices;
|
||||||
std::vector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets(modelMeshBlendshapeOffsets->second.size());
|
std::vector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets(modelMeshBlendshapeOffsets->second.size());
|
||||||
|
|
||||||
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
||||||
|
@ -1711,7 +1720,7 @@ 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(QVector<BlendshapeOffset>, blendshapeOffsets));
|
Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector<BlendshapeOffset>, blendshapeOffsets), Q_ARG(QVector<int>, blendedMeshSizes));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::maybeStartBlender() {
|
bool Model::maybeStartBlender() {
|
||||||
|
@ -1722,43 +1731,18 @@ bool Model::maybeStartBlender() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::setBlendedVertices(int blendNumber, const QVector<BlendshapeOffset>& blendshapeOffsets) {
|
|
||||||
PROFILE_RANGE(render, __FUNCTION__);
|
|
||||||
if (!isLoaded() || blendNumber < _appliedBlendNumber || !_blendshapeBuffersInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_appliedBlendNumber = blendNumber;
|
|
||||||
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
|
|
||||||
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
|
|
||||||
auto meshBlendshapeOffsets = _blendshapeOffsets.find(i);
|
|
||||||
const auto& buffer = _blendshapeBuffers.find(i);
|
|
||||||
if (mesh.blendshapes.isEmpty() || meshBlendshapeOffsets == _blendshapeOffsets.end() || buffer == _blendshapeBuffers.end()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto blendshapeOffsetSize = meshBlendshapeOffsets->second.size() * sizeof(BlendshapeOffset);
|
|
||||||
buffer->second->setSubData(0, blendshapeOffsetSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset));
|
|
||||||
|
|
||||||
index += meshBlendshapeOffsets->second.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Model::initializeBlendshapes(const FBXMesh& mesh, int index) {
|
void Model::initializeBlendshapes(const FBXMesh& mesh, int index) {
|
||||||
if (mesh.blendshapes.empty()) {
|
if (mesh.blendshapes.empty()) {
|
||||||
// mesh doesn't have blendshape, did we allocate one though ?
|
// mesh doesn't have blendshape, did we allocate one though ?
|
||||||
if (_blendshapeBuffers.find(index) != _blendshapeBuffers.end()) {
|
if (_blendshapeOffsets.find(index) != _blendshapeOffsets.end()) {
|
||||||
qWarning() << "Mesh does not have Blendshape yet a blendshapeOffset buffer is allocated ?";
|
qWarning() << "Mesh does not have Blendshape yet the blendshapeOffsets are allocated ?";
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Mesh has blendshape, let s allocate the local buffer if not done yet
|
// Mesh has blendshape, let s allocate the local buffer if not done yet
|
||||||
if (_blendshapeBuffers.find(index) == _blendshapeBuffers.end()) {
|
if (_blendshapeOffsets.find(index) == _blendshapeOffsets.end()) {
|
||||||
QVector<BlendshapeOffset> blendshapeOffset;
|
QVector<BlendshapeOffset> blendshapeOffset;
|
||||||
blendshapeOffset.fill(BlendshapeOffset(), mesh.vertices.size());
|
blendshapeOffset.fill(BlendshapeOffset(), mesh.vertices.size());
|
||||||
const auto blendshapeOffsetsSize = blendshapeOffset.size() * sizeof(BlendshapeOffset);
|
|
||||||
_blendshapeBuffers[index] = std::make_shared<gpu::Buffer>(blendshapeOffsetsSize, (const gpu::Byte*) blendshapeOffset.constData(), blendshapeOffsetsSize);
|
|
||||||
_blendshapeOffsets[index] = blendshapeOffset;
|
_blendshapeOffsets[index] = blendshapeOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1791,10 +1775,14 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, QVector<BlendshapeOffset> blendshapeOffsets) {
|
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, QVector<BlendshapeOffset> blendshapeOffsets, QVector<int> blendedMeshSizes) {
|
||||||
if (model) {
|
if (model) {
|
||||||
model->setBlendedVertices(blendNumber, blendshapeOffsets);
|
auto blendshapeOperator = model->getModelBlendshapeOperator();
|
||||||
|
if (blendshapeOperator) {
|
||||||
|
blendshapeOperator(blendNumber, blendshapeOffsets, blendedMeshSizes, model->fetchRenderItemIDs());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Lock lock(_mutex);
|
Lock lock(_mutex);
|
||||||
_pendingBlenders--;
|
_pendingBlenders--;
|
||||||
|
|
|
@ -86,6 +86,7 @@ struct BlendshapeOffsetUnpacked {
|
||||||
};
|
};
|
||||||
|
|
||||||
using BlendshapeOffset = BlendshapeOffsetPacked;
|
using BlendshapeOffset = BlendshapeOffsetPacked;
|
||||||
|
using BlendShapeOperator = std::function<void(int, const QVector<BlendshapeOffset>&, const QVector<int>&, const render::ItemIDs&)>;
|
||||||
|
|
||||||
/// A generic 3D model displaying geometry loaded from a URL.
|
/// A generic 3D model displaying geometry loaded from a URL.
|
||||||
class Model : public QObject, public std::enable_shared_from_this<Model>, public scriptable::ModelProvider {
|
class Model : public QObject, public std::enable_shared_from_this<Model>, public scriptable::ModelProvider {
|
||||||
|
@ -141,7 +142,14 @@ public:
|
||||||
}
|
}
|
||||||
bool addToScene(const render::ScenePointer& scene,
|
bool addToScene(const render::ScenePointer& scene,
|
||||||
render::Transaction& transaction,
|
render::Transaction& transaction,
|
||||||
render::Item::Status::Getters& statusGetters);
|
BlendShapeOperator modelBlendshapeOperator) {
|
||||||
|
auto getters = render::Item::Status::Getters(0);
|
||||||
|
return addToScene(scene, transaction, getters, modelBlendshapeOperator);
|
||||||
|
}
|
||||||
|
bool addToScene(const render::ScenePointer& scene,
|
||||||
|
render::Transaction& transaction,
|
||||||
|
render::Item::Status::Getters& statusGetters,
|
||||||
|
BlendShapeOperator modelBlendshapeOperator = nullptr);
|
||||||
void removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction);
|
void removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction);
|
||||||
bool isRenderable() const;
|
bool isRenderable() const;
|
||||||
|
|
||||||
|
@ -155,9 +163,6 @@ public:
|
||||||
|
|
||||||
bool maybeStartBlender();
|
bool maybeStartBlender();
|
||||||
|
|
||||||
/// Sets blended vertices computed in a separate thread.
|
|
||||||
void setBlendedVertices(int blendNumber, const QVector<BlendshapeOffset>& blendshapeOffsets);
|
|
||||||
|
|
||||||
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; }
|
||||||
|
|
||||||
|
@ -339,6 +344,7 @@ public:
|
||||||
|
|
||||||
uint32_t getGeometryCounter() const { return _deleteGeometryCounter; }
|
uint32_t getGeometryCounter() const { return _deleteGeometryCounter; }
|
||||||
const QMap<render::ItemID, render::PayloadPointer>& getRenderItems() const { return _modelMeshRenderItemsMap; }
|
const QMap<render::ItemID, render::PayloadPointer>& getRenderItems() const { return _modelMeshRenderItemsMap; }
|
||||||
|
BlendShapeOperator getModelBlendshapeOperator() const { return _modelBlendshapeOperator; }
|
||||||
|
|
||||||
void renderDebugMeshBoxes(gpu::Batch& batch);
|
void renderDebugMeshBoxes(gpu::Batch& batch);
|
||||||
|
|
||||||
|
@ -432,18 +438,13 @@ protected:
|
||||||
|
|
||||||
virtual void deleteGeometry();
|
virtual void deleteGeometry();
|
||||||
|
|
||||||
QVector<float> _blendshapeCoefficients;
|
|
||||||
|
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
|
|
||||||
std::unordered_map<int, gpu::BufferPointer> _blendshapeBuffers;
|
BlendShapeOperator _modelBlendshapeOperator { nullptr };
|
||||||
bool _blendshapeBuffersInitialized{ false };
|
QVector<float> _blendshapeCoefficients;
|
||||||
|
|
||||||
QVector<QVector<QSharedPointer<Texture>>> _dilatedTextures;
|
|
||||||
|
|
||||||
QVector<float> _blendedBlendshapeCoefficients;
|
QVector<float> _blendedBlendshapeCoefficients;
|
||||||
int _blendNumber;
|
int _blendNumber { 0 };
|
||||||
int _appliedBlendNumber;
|
bool _blendshapeOffsetsInitialized { false };
|
||||||
|
|
||||||
mutable QMutex _mutex{ QMutex::Recursive };
|
mutable QMutex _mutex{ QMutex::Recursive };
|
||||||
|
|
||||||
|
@ -460,7 +461,6 @@ protected:
|
||||||
// debug rendering support
|
// debug rendering support
|
||||||
int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;
|
int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;
|
||||||
|
|
||||||
|
|
||||||
static AbstractViewStateInterface* _viewState;
|
static AbstractViewStateInterface* _viewState;
|
||||||
|
|
||||||
QVector<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItems;
|
QVector<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItems;
|
||||||
|
@ -533,7 +533,7 @@ public:
|
||||||
bool shouldComputeBlendshapes() { return _computeBlendshapes; }
|
bool shouldComputeBlendshapes() { return _computeBlendshapes; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setBlendedVertices(ModelPointer model, int blendNumber, QVector<BlendshapeOffset> blendshapeOffsets);
|
void setBlendedVertices(ModelPointer model, int blendNumber, QVector<BlendshapeOffset> blendshapeOffsets, QVector<int> blendedMeshSizes);
|
||||||
void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; }
|
void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -78,7 +78,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 (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||||
modelBlender->noteRequiresBlend(getThisPointer());
|
modelBlender->noteRequiresBlend(getThisPointer());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue