From 3dd8fd838b7a81b2b6ff0b0c892006dde802c176 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jan 2017 10:34:41 -0800 Subject: [PATCH 01/15] move cauterization out of Model class --- .../src/avatar/CauterizedMeshPartPayload.cpp | 72 ++++++ .../src/avatar/CauterizedMeshPartPayload.h | 29 +++ interface/src/avatar/CauterizedModel.cpp | 236 ++++++++++++++++++ interface/src/avatar/CauterizedModel.h | 49 ++++ interface/src/avatar/SkeletonModel.cpp | 8 +- interface/src/avatar/SkeletonModel.h | 13 +- interface/src/avatar/SoftAttachmentModel.cpp | 3 +- interface/src/avatar/SoftAttachmentModel.h | 9 +- .../render-utils/src/MeshPartPayload.cpp | 41 +-- libraries/render-utils/src/MeshPartPayload.h | 8 +- libraries/render-utils/src/Model.cpp | 37 +-- libraries/render-utils/src/Model.h | 40 ++- 12 files changed, 430 insertions(+), 115 deletions(-) create mode 100644 interface/src/avatar/CauterizedMeshPartPayload.cpp create mode 100644 interface/src/avatar/CauterizedMeshPartPayload.h create mode 100644 interface/src/avatar/CauterizedModel.cpp create mode 100644 interface/src/avatar/CauterizedModel.h diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp new file mode 100644 index 0000000000..e073b2cedf --- /dev/null +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -0,0 +1,72 @@ +// +// MeshPartPayload.cpp +// interface/src/renderer +// +// Created by Sam Gateau on 10/3/15. +// Copyright 2015 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 "CauterizedMeshPartPayload.h" + +#include + +#include "SkeletonModel.h" +//#include "EntityItem.h" + +using namespace render; + +CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) + : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} + +void CauterizedMeshPartPayload::updateTransformForSkinnedCauterizedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices) { + _transform = transform; + _cauterizedTransform = transform; + + if (clusterMatrices.size() > 0) { + _worldBound = AABox(); + for (auto& clusterMatrix : clusterMatrices) { + AABox clusterBound = _localBound; + clusterBound.transform(clusterMatrix); + _worldBound += clusterBound; + } + + _worldBound.transform(transform); + if (clusterMatrices.size() == 1) { + _transform = _transform.worldTransform(Transform(clusterMatrices[0])); + if (cauterizedClusterMatrices.size() != 0) { + _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); + } else { + _cauterizedTransform = _transform; + } + } + } +} + +void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { + // Still relying on the raw data from the model + const Model::MeshState& state = _model->getMeshState(_meshIndex); + SkeletonModel* skeleton = static_cast(_model); + bool canCauterize = (renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE); + + if (state.clusterBuffer) { + if (canCauterize && skeleton->getCauterizeBones()) { + const Model::MeshState& cState = skeleton->getCauterizeMeshState(_meshIndex); + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, cState.clusterBuffer); + } else { + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); + } + batch.setModelTransform(_transform); + } else { + if (canCauterize && skeleton->getCauterizeBones()) { + batch.setModelTransform(_cauterizedTransform); + } else { + batch.setModelTransform(_transform); + } + } +} + diff --git a/interface/src/avatar/CauterizedMeshPartPayload.h b/interface/src/avatar/CauterizedMeshPartPayload.h new file mode 100644 index 0000000000..f4319ead6f --- /dev/null +++ b/interface/src/avatar/CauterizedMeshPartPayload.h @@ -0,0 +1,29 @@ +// +// CauterizedModelMeshPartPayload.h +// interface/src/avatar +// +// Created by AndrewMeadows 2017.01.17 +// Copyright 2017 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_CauterizedMeshPartPayload_h +#define hifi_CauterizedMeshPartPayload_h + +#include + +class CauterizedMeshPartPayload : public ModelMeshPartPayload { +public: + CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); + void updateTransformForSkinnedCauterizedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices); + + void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; +private: + Transform _cauterizedTransform; +}; + +#endif // hifi_CauterizedMeshPartPayload_h diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp new file mode 100644 index 0000000000..d58d392bb0 --- /dev/null +++ b/interface/src/avatar/CauterizedModel.cpp @@ -0,0 +1,236 @@ +// +// CauterizedModel.cpp +// interface/src/avatar +// +// Created by Andrew Meadows 2017.01.17 +// Copyright 2017 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 +//#include + +#include "CauterizedModel.h" + +#include +#include +#include + +#include "CauterizedMeshPartPayload.h" + + +CauterizedModel::CauterizedModel(RigPointer rig, QObject* parent) : Model(rig, parent) { +} + +CauterizedModel::~CauterizedModel() { +} + +void CauterizedModel::deleteGeometry() { + Model::deleteGeometry(); + _cauterizeMeshStates.clear(); +} + +// Called within Model::simulate call, below. +void CauterizedModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + Model::updateRig(deltaTime, parentTransform); + _needsUpdateClusterMatrices = true; +} + +void CauterizedModel::createVisibleRenderItemSet() { + // Temporary HACK: use base class method for now + Model::createVisibleRenderItemSet(); +} + +void CauterizedModel::createCollisionRenderItemSet() { + // Temporary HACK: use base class method for now + Model::createCollisionRenderItemSet(); +} + +bool CauterizedModel::updateGeometry() { + bool returnValue = Model::updateGeometry(); + if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { + const FBXGeometry& fbxGeometry = getFBXGeometry(); + foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + Model::MeshState state; + state.clusterMatrices.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } + } + return returnValue; +} + +void CauterizedModel::updateClusterMatrices() { + PerformanceTimer perfTimer("CauterizedModel::updateClusterMatrices"); + + if (!_needsUpdateClusterMatrices || !isLoaded()) { + return; + } + _needsUpdateClusterMatrices = false; + const FBXGeometry& geometry = getFBXGeometry(); + + for (int i = 0; i < _meshStates.size(); i++) { + Model::MeshState& state = _meshStates[i]; + const FBXMesh& mesh = geometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); +#if GLM_ARCH & GLM_ARCH_SSE2 + glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix; + glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); + state.clusterMatrices[j] = out; +#else + state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; +#endif + } + + // Once computed the cluster matrices, update the buffer(s) + if (mesh.clusters.size() > 1) { + if (!state.clusterBuffer) { + state.clusterBuffer = std::make_shared(state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } else { + state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } + } + } + + // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. + if (!_cauterizeBoneSet.empty()) { + static const glm::mat4 zeroScale( + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale; + + for (int i = 0; i < _cauterizeMeshStates.size(); i++) { + Model::MeshState& state = _cauterizeMeshStates[i]; + const FBXMesh& mesh = geometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointMatrix = cauterizeMatrix; + } +#if GLM_ARCH & GLM_ARCH_SSE2 + glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix; + glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); + state.clusterMatrices[j] = out; +#else + state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; +#endif + } + + if (!_cauterizeBoneSet.empty() && (state.clusterMatrices.size() > 1)) { + if (!state.clusterBuffer) { + state.clusterBuffer = + std::make_shared(state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } else { + state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); + } + } + } + } + + // post the blender if we're not currently waiting for one to finish + if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + _blendedBlendshapeCoefficients = _blendshapeCoefficients; + DependencyManager::get()->noteRequiresBlend(getThisPointer()); + } +} + +void CauterizedModel::updateRenderItems() { + // Temporary HACK: use base class method for now + Model::updateRenderItems(); +} + +#ifdef FOO +// TODO: finish implementing this +void CauterizedModel::updateRenderItems() { + if (!_addedToScene) { + return; + } + + glm::vec3 scale = getScale(); + if (_collisionGeometry) { + // _collisionGeometry is already scaled + scale = glm::vec3(1.0f); + } + _needsUpdateClusterMatrices = true; + _renderItemsNeedUpdate = false; + + // queue up this work for later processing, at the end of update and just before rendering. + // the application will ensure only the last lambda is actually invoked. + void* key = (void*)this; + std::weak_ptr weakSelf = shared_from_this(); + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { + + // do nothing, if the model has already been destroyed. + auto self = weakSelf.lock(); + if (!self) { + return; + } + + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + + Transform modelTransform; + modelTransform.setTranslation(self->getTranslation()); + modelTransform.setRotation(self->getRotation()); + + Transform scaledModelTransform(modelTransform); + scaledModelTransform.setScale(scale); + + uint32_t deleteGeometryCounter = self->getGeometryCounter(); + + // TODO: handle two cases: + // (a) our payloads are of type ModelMeshPartPayload + // (b) our payloads are of type ModelMeshPartPayload + render::PendingChanges pendingChanges; + QList keys = self->getRenderItems().keys(); + foreach (auto itemID, keys) { + pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { + if (data._model && data._model->isLoaded()) { + if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { + data.startFade(); + } + // Ensure the model geometry was not reset between frames + if (deleteGeometryCounter == data._model->getGeometryCounter()) { + // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. + data._model->updateClusterMatrices(); + + // update the model transform and bounding box for this render item. + const Model::MeshState& state = data._model->getMeshState(data._meshIndex); + CauterizedModel* cModel = static_cast(data._model); + const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); + data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); + } + } + }); + } + + /* + // collision mesh does not share the same unit scale as the FBX file's mesh: only apply offset + Transform collisionMeshOffset; + collisionMeshOffset.setIdentity(); + foreach (auto itemID, self->_collisionRenderItems.keys()) { + pendingChanges.updateItem(itemID, [scaledModelTransform, collisionMeshOffset](MeshPartPayload& data) { + // update the model transform for this render item. + data.updateTransform(scaledModelTransform, collisionMeshOffset); + }); + } + */ + + scene->enqueuePendingChanges(pendingChanges); + }); +} +#endif // FOO + +const Model::MeshState& CauterizedModel::getCauterizeMeshState(int index) const { + assert(index < _meshStates.size()); + return _cauterizeMeshStates.at(index); +} diff --git a/interface/src/avatar/CauterizedModel.h b/interface/src/avatar/CauterizedModel.h new file mode 100644 index 0000000000..12a9723dd4 --- /dev/null +++ b/interface/src/avatar/CauterizedModel.h @@ -0,0 +1,49 @@ +// +// CauterizeableModel.h +// interface/src/avatar +// +// Created by Andrew Meadows 2016.01.17 +// Copyright 2017 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_CauterizedModel_h +#define hifi_CauterizedModel_h + + +#include + +class CauterizedModel : public Model { + Q_OBJECT + +public: + CauterizedModel(RigPointer rig, QObject* parent); + virtual ~CauterizedModel(); + + void deleteGeometry() override; + virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; + + void setCauterizeBones(bool flag) { _cauterizeBones = flag; } + bool getCauterizeBones() const { return _cauterizeBones; } + + const std::unordered_set& getCauterizeBoneSet() const { return _cauterizeBoneSet; } + void setCauterizeBoneSet(const std::unordered_set& boneSet) { _cauterizeBoneSet = boneSet; } + + void createVisibleRenderItemSet() override; + void createCollisionRenderItemSet() override; + + bool updateGeometry() override; + virtual void updateClusterMatrices() override; + void updateRenderItems() override; + + const Model::MeshState& getCauterizeMeshState(int index) const; + +protected: + std::unordered_set _cauterizeBoneSet; + QVector _cauterizeMeshStates; + bool _cauterizeBones { false }; +}; + +#endif // hifi_CauterizedModel_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 889f0ef36b..54f6682191 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -24,7 +24,7 @@ #include "AnimDebugDraw.h" SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : - Model(rig, parent), + CauterizedModel(rig, parent), _owningAvatar(owningAvatar), _boundingCapsuleLocalOffset(0.0f), _boundingCapsuleRadius(0.0f), @@ -166,7 +166,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); // evaluate AnimGraph animation and update jointStates. - Model::updateRig(deltaTime, parentTransform); + CauterizedModel::updateRig(deltaTime, parentTransform); Rig::EyeParameters eyeParams; eyeParams.worldHeadOrientation = headParams.worldHeadOrientation; @@ -178,10 +178,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; _rig->updateFromEyeParameters(eyeParams); - } else { - - Model::updateRig(deltaTime, parentTransform); + CauterizedModel::updateRig(deltaTime, parentTransform); // This is a little more work than we really want. // diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 8e61e6f3ca..7a6081a010 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -12,8 +12,7 @@ #ifndef hifi_SkeletonModel_h #define hifi_SkeletonModel_h - -#include +#include "CauterizedModel.h" class Avatar; class MuscleConstraint; @@ -23,7 +22,7 @@ using SkeletonModelPointer = std::shared_ptr; using SkeletonModelWeakPointer = std::weak_ptr; /// A skeleton loaded from a model. -class SkeletonModel : public Model { +class SkeletonModel : public CauterizedModel { Q_OBJECT public: @@ -31,10 +30,10 @@ public: SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); ~SkeletonModel(); - virtual void initJointStates() override; + void initJointStates() override; - virtual void simulate(float deltaTime, bool fullUpdate = true) override; - virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; + void simulate(float deltaTime, bool fullUpdate = true) override; + void updateRig(float deltaTime, glm::mat4 parentTransform) override; void updateAttitude(); /// Returns the index of the left hand joint, or -1 if not found. @@ -105,7 +104,7 @@ public: float getHeadClipDistance() const { return _headClipDistance; } - virtual void onInvalidate() override; + void onInvalidate() override; signals: diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/interface/src/avatar/SoftAttachmentModel.cpp index 6351495598..530801007e 100644 --- a/interface/src/avatar/SoftAttachmentModel.cpp +++ b/interface/src/avatar/SoftAttachmentModel.cpp @@ -13,7 +13,7 @@ #include "InterfaceLogging.h" SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) : - Model(rig, parent), + CauterizedModel(rig, parent), _rigOverride(rigOverride) { assert(_rig); assert(_rigOverride); @@ -38,6 +38,7 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const { // virtual // use the _rigOverride matrices instead of the Model::_rig void SoftAttachmentModel::updateClusterMatrices() { + // adebug TODO: this needs work? if (!_needsUpdateClusterMatrices) { return; } diff --git a/interface/src/avatar/SoftAttachmentModel.h b/interface/src/avatar/SoftAttachmentModel.h index cdf957514c..fea679839a 100644 --- a/interface/src/avatar/SoftAttachmentModel.h +++ b/interface/src/avatar/SoftAttachmentModel.h @@ -12,7 +12,7 @@ #ifndef hifi_SoftAttachmentModel_h #define hifi_SoftAttachmentModel_h -#include +#include "CauterizedModel.h" // A model that allows the creator to specify a secondary rig instance. // When the cluster matrices are created for rendering, the @@ -22,16 +22,15 @@ // This is used by Avatar instances to wear clothing that follows the same // animated pose as the SkeletonModel. -class SoftAttachmentModel : public Model { +class SoftAttachmentModel : public CauterizedModel { Q_OBJECT public: - SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride); ~SoftAttachmentModel(); - virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; - virtual void updateClusterMatrices() override; + void updateRig(float deltaTime, glm::mat4 parentTransform) override; + void updateClusterMatrices() override; protected: int getJointIndexOverride(int i) const; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index e3b2527e67..52eb006b9f 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -251,7 +251,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } } -void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, bool canCauterize) const { +void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { batch.setModelTransform(_drawTransform); } @@ -265,7 +265,7 @@ void MeshPartPayload::render(RenderArgs* args) const { assert(locations); // Bind the model transform and the skinCLusterMatrices if needed - bindTransform(batch, locations); + bindTransform(batch, locations, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed bindMesh(batch); @@ -359,11 +359,8 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices) { +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { _transform = transform; - _cauterizedTransform = transform; if (clusterMatrices.size() > 0) { _worldBound = AABox(); @@ -372,16 +369,7 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf clusterBound.transform(clusterMatrix); _worldBound += clusterBound; } - _worldBound.transform(transform); - if (clusterMatrices.size() == 1) { - _transform = _transform.worldTransform(Transform(clusterMatrices[0])); - if (cauterizedClusterMatrices.size() != 0) { - _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); - } else { - _cauterizedTransform = _transform; - } - } } } @@ -525,24 +513,13 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { } } -void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, bool canCauterize) const { +void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - const Model::MeshState& state = _model->_meshStates.at(_meshIndex); - + const Model::MeshState& state = _model->getMeshState(_meshIndex); if (state.clusterBuffer) { - if (canCauterize && _model->getCauterizeBones()) { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.cauterizedClusterBuffer); - } else { - batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); - } - batch.setModelTransform(_transform); - } else { - if (canCauterize && _model->getCauterizeBones()) { - batch.setModelTransform(_cauterizedTransform); - } else { - batch.setModelTransform(_transform); - } + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); } + batch.setModelTransform(_transform); } void ModelMeshPartPayload::startFade() { @@ -596,9 +573,8 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { assert(locations); // Bind the model transform and the skinCLusterMatrices if needed - bool canCauterize = args->_renderMode != RenderArgs::SHADOW_RENDER_MODE; _model->updateClusterMatrices(); - bindTransform(batch, locations, canCauterize); + bindTransform(batch, locations, args->_renderMode); //Bind the index buffer and vertex buffer and Blend shapes if needed bindMesh(batch); @@ -621,4 +597,3 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } } - diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index b7a8cf63f0..53160db91e 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -48,7 +48,7 @@ public: void drawCall(gpu::Batch& batch) const; virtual void bindMesh(gpu::Batch& batch) const; virtual void bindMaterial(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations) const; - virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize = true) const; + virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const; // Payload resource cached values std::shared_ptr _drawMesh; @@ -86,8 +86,7 @@ public: void notifyLocationChanged() override; void updateTransformForSkinnedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices); + const QVector& clusterMatrices); // Entity fade in void startFade(); @@ -102,13 +101,12 @@ public: // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch) const override; - void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize = true) const override; + void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; void initCache(); Model* _model; - Transform _cauterizedTransform; int _meshIndex; int _shapeID; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 14391b9f16..ef613e8c14 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -91,7 +91,6 @@ Model::Model(RigPointer rig, QObject* parent) : _scaledToFit(false), _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), - _cauterizeBones(false), _url(HTTP_INVALID_COM), _isVisible(true), _blendNumber(0), @@ -238,7 +237,7 @@ void Model::updateRenderItems() { // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices, state.cauterizedClusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); } } }); @@ -294,8 +293,6 @@ bool Model::updateGeometry() { foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); - state.cauterizedClusterMatrices.resize(mesh.clusters.size()); - _meshStates.append(state); // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index @@ -1159,13 +1156,6 @@ void Model::updateClusterMatrices() { } _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); - static const glm::mat4 zeroScale( - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); - auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale; - for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -1179,20 +1169,6 @@ void Model::updateClusterMatrices() { #else state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; #endif - - // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. - if (!_cauterizeBoneSet.empty()) { - if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { - jointMatrix = cauterizeMatrix; - } -#if GLM_ARCH & GLM_ARCH_SSE2 - glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix; - glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); - state.cauterizedClusterMatrices[j] = out; -#else - state.cauterizedClusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; -#endif - } } // Once computed the cluster matrices, update the buffer(s) @@ -1204,17 +1180,6 @@ void Model::updateClusterMatrices() { state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.clusterMatrices.constData()); } - - if (!_cauterizeBoneSet.empty() && (state.cauterizedClusterMatrices.size() > 1)) { - if (!state.cauterizedClusterBuffer) { - state.cauterizedClusterBuffer = - std::make_shared(state.cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.cauterizedClusterMatrices.constData()); - } else { - state.cauterizedClusterBuffer->setSubData(0, state.cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.cauterizedClusterMatrices.constData()); - } - } } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index a11d6d511e..246d67180e 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -101,7 +101,7 @@ public: bool isLayeredInFront() const { return _isLayeredInFront; } - void updateRenderItems(); + virtual void updateRenderItems(); void setRenderItemsNeedUpdate() { _renderItemsNeedUpdate = true; } bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; } AABox getRenderableMeshBound() const; @@ -215,12 +215,6 @@ public: bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit glm::vec3 getScaleToFitDimensions() const; /// the dimensions model is scaled to, including inferred y/z - void setCauterizeBones(bool flag) { _cauterizeBones = flag; } - bool getCauterizeBones() const { return _cauterizeBones; } - - const std::unordered_set& getCauterizeBoneSet() const { return _cauterizeBoneSet; } - void setCauterizeBoneSet(const std::unordered_set& boneSet) { _cauterizeBoneSet = boneSet; } - int getBlendshapeCoefficientsNum() const { return _blendshapeCoefficients.size(); } float getBlendshapeCoefficient(int index) const { return ((index < 0) && (index >= _blendshapeCoefficients.size())) ? 0.0f : _blendshapeCoefficients.at(index); @@ -231,7 +225,7 @@ public: const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } // returns 'true' if needs fullUpdate after geometry change - bool updateGeometry(); + virtual bool updateGeometry(); void setCollisionMesh(model::MeshPointer mesh); void setLoadingPriority(float priority) { _loadingPriority = priority; } @@ -242,6 +236,18 @@ public: int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } + class MeshState { + public: + QVector clusterMatrices; + gpu::BufferPointer clusterBuffer; + + }; + + const MeshState& getMeshState(int index) { return _meshStates.at(index); } + + uint32_t getGeometryCounter() const { return _deleteGeometryCounter; } + const QMap& getRenderItems() const { return _modelMeshRenderItems; } + public slots: void loadURLFinished(bool success); @@ -298,18 +304,7 @@ protected: bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to - class MeshState { - public: - QVector clusterMatrices; - QVector cauterizedClusterMatrices; - gpu::BufferPointer clusterBuffer; - gpu::BufferPointer cauterizedClusterBuffer; - - }; - QVector _meshStates; - std::unordered_set _cauterizeBoneSet; - bool _cauterizeBones; virtual void initJointStates(); @@ -342,7 +337,7 @@ protected: protected: - void deleteGeometry(); + virtual void deleteGeometry(); void initJointTransforms(); QVector _blendshapeCoefficients; @@ -371,12 +366,11 @@ protected: void recalculateMeshBoxes(bool pickAgainstTriangles = false); void createRenderItemSet(); - void createVisibleRenderItemSet(); - void createCollisionRenderItemSet(); + virtual void createVisibleRenderItemSet(); + virtual void createCollisionRenderItemSet(); bool _isWireframe; - // debug rendering support void renderDebugMeshBoxes(gpu::Batch& batch); int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; From 189da81bb1ac00eb2164a9887b41f10d1a71021b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jan 2017 10:44:54 -0800 Subject: [PATCH 02/15] fix for crash on unloaded model --- interface/src/avatar/CauterizedModel.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index d58d392bb0..5c06412d9c 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -49,16 +49,18 @@ void CauterizedModel::createCollisionRenderItemSet() { } bool CauterizedModel::updateGeometry() { - bool returnValue = Model::updateGeometry(); - if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { - const FBXGeometry& fbxGeometry = getFBXGeometry(); - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { - Model::MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); - _cauterizeMeshStates.append(state); + bool needsFullUpdate = Model::updateGeometry(); + if (needsFullUpdate) { + if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { + const FBXGeometry& fbxGeometry = getFBXGeometry(); + foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + Model::MeshState state; + state.clusterMatrices.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } } } - return returnValue; + return needsFullUpdate; } void CauterizedModel::updateClusterMatrices() { From 1a3fa849859a9112028df0e9f5c2a6cb8f52b552 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jan 2017 14:54:48 -0800 Subject: [PATCH 03/15] add newline at EOF --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ab4ae02a8e..4adad0d938 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1363,4 +1363,4 @@ void Avatar::ensureInScene(AvatarSharedPointer self) { if (!_inScene) { addToScene(self); } -} \ No newline at end of file +} From bfbef91f531613c2c3cad9ad2b8136e83ec6a29f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jan 2017 18:35:48 -0800 Subject: [PATCH 04/15] re-enable cauterization of meshes for MyAvatar --- interface/src/avatar/Avatar.cpp | 1 + .../src/avatar/CauterizedMeshPartPayload.cpp | 9 +- interface/src/avatar/CauterizedModel.cpp | 219 ++++++++++-------- interface/src/avatar/CauterizedModel.h | 16 +- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/SoftAttachmentModel.cpp | 1 - .../render-utils/src/MeshPartPayload.cpp | 3 + 7 files changed, 141 insertions(+), 112 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4adad0d938..ee5e397592 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1047,6 +1047,7 @@ void Avatar::setModelURLFinished(bool success) { static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride) { if (isSoft) { // cast to std::shared_ptr + // TODO: re-enable cauterization for the SoftAttachmentModel when this is MyAvatar return std::dynamic_pointer_cast(std::make_shared(std::make_shared(), nullptr, rigOverride)); } else { return std::make_shared(std::make_shared()); diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp index e073b2cedf..a7db9608fe 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -44,6 +44,9 @@ void CauterizedMeshPartPayload::updateTransformForSkinnedCauterizedMesh(const Tr _cauterizedTransform = _transform; } } + } else { + _worldBound = _localBound; + _worldBound.transform(_drawTransform); } } @@ -51,10 +54,10 @@ void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::S // Still relying on the raw data from the model const Model::MeshState& state = _model->getMeshState(_meshIndex); SkeletonModel* skeleton = static_cast(_model); - bool canCauterize = (renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE); + bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); if (state.clusterBuffer) { - if (canCauterize && skeleton->getCauterizeBones()) { + if (useCauterizedMesh) { const Model::MeshState& cState = skeleton->getCauterizeMeshState(_meshIndex); batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, cState.clusterBuffer); } else { @@ -62,7 +65,7 @@ void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::S } batch.setModelTransform(_transform); } else { - if (canCauterize && skeleton->getCauterizeBones()) { + if (useCauterizedMesh) { batch.setModelTransform(_cauterizedTransform); } else { batch.setModelTransform(_transform); diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index 5c06412d9c..30566e357d 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -9,9 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -//#include -//#include - #include "CauterizedModel.h" #include @@ -21,7 +18,8 @@ #include "CauterizedMeshPartPayload.h" -CauterizedModel::CauterizedModel(RigPointer rig, QObject* parent) : Model(rig, parent) { +CauterizedModel::CauterizedModel(RigPointer rig, QObject* parent) : + Model(rig, parent) { } CauterizedModel::~CauterizedModel() { @@ -32,15 +30,64 @@ void CauterizedModel::deleteGeometry() { _cauterizeMeshStates.clear(); } -// Called within Model::simulate call, below. -void CauterizedModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - Model::updateRig(deltaTime, parentTransform); - _needsUpdateClusterMatrices = true; +bool CauterizedModel::updateGeometry() { + bool needsFullUpdate = Model::updateGeometry(); + if (_isCauterized && needsFullUpdate) { + assert(_cauterizeMeshStates.empty()); + const FBXGeometry& fbxGeometry = getFBXGeometry(); + foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + Model::MeshState state; + state.clusterMatrices.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } + } + return needsFullUpdate; } void CauterizedModel::createVisibleRenderItemSet() { - // Temporary HACK: use base class method for now - Model::createVisibleRenderItemSet(); + if (_isCauterized) { + assert(isLoaded()); + const auto& meshes = _renderGeometry->getMeshes(); + + // all of our mesh vectors must match in size + if ((int)meshes.size() != _meshStates.size()) { + qCDebug(renderlogging) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; + return; + } + + // We should not have any existing renderItems if we enter this section of code + Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); + + _modelMeshRenderItemsSet.clear(); + + Transform transform; + transform.setTranslation(_translation); + transform.setRotation(_rotation); + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + + // 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(); + for (uint32_t i = 0; i < numMeshes; i++) { + const auto& mesh = meshes.at(i); + if (!mesh) { + continue; + } + + // Create the render payloads + int numParts = (int)mesh->getNumParts(); + for (int partIndex = 0; partIndex < numParts; partIndex++) { + auto ptr = std::make_shared(this, i, partIndex, shapeID, transform, offset); + _modelMeshRenderItemsSet << std::static_pointer_cast(ptr); + shapeID++; + } + } + } else { + Model::createVisibleRenderItemSet(); + } } void CauterizedModel::createCollisionRenderItemSet() { @@ -48,19 +95,10 @@ void CauterizedModel::createCollisionRenderItemSet() { Model::createCollisionRenderItemSet(); } -bool CauterizedModel::updateGeometry() { - bool needsFullUpdate = Model::updateGeometry(); - if (needsFullUpdate) { - if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { - const FBXGeometry& fbxGeometry = getFBXGeometry(); - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { - Model::MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); - _cauterizeMeshStates.append(state); - } - } - } - return needsFullUpdate; +// Called within Model::simulate call, below. +void CauterizedModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + Model::updateRig(deltaTime, parentTransform); + _needsUpdateClusterMatrices = true; } void CauterizedModel::updateClusterMatrices() { @@ -147,90 +185,71 @@ void CauterizedModel::updateClusterMatrices() { } void CauterizedModel::updateRenderItems() { - // Temporary HACK: use base class method for now - Model::updateRenderItems(); -} - -#ifdef FOO -// TODO: finish implementing this -void CauterizedModel::updateRenderItems() { - if (!_addedToScene) { - return; - } - - glm::vec3 scale = getScale(); - if (_collisionGeometry) { - // _collisionGeometry is already scaled - scale = glm::vec3(1.0f); - } - _needsUpdateClusterMatrices = true; - _renderItemsNeedUpdate = false; - - // queue up this work for later processing, at the end of update and just before rendering. - // the application will ensure only the last lambda is actually invoked. - void* key = (void*)this; - std::weak_ptr weakSelf = shared_from_this(); - AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { - - // do nothing, if the model has already been destroyed. - auto self = weakSelf.lock(); - if (!self) { + if (_isCauterized) { + if (!_addedToScene) { return; } - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - - Transform modelTransform; - modelTransform.setTranslation(self->getTranslation()); - modelTransform.setRotation(self->getRotation()); - - Transform scaledModelTransform(modelTransform); - scaledModelTransform.setScale(scale); - - uint32_t deleteGeometryCounter = self->getGeometryCounter(); - - // TODO: handle two cases: - // (a) our payloads are of type ModelMeshPartPayload - // (b) our payloads are of type ModelMeshPartPayload - render::PendingChanges pendingChanges; - QList keys = self->getRenderItems().keys(); - foreach (auto itemID, keys) { - pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { - if (data._model && data._model->isLoaded()) { - if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { - data.startFade(); - } - // Ensure the model geometry was not reset between frames - if (deleteGeometryCounter == data._model->getGeometryCounter()) { - // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. - data._model->updateClusterMatrices(); - - // update the model transform and bounding box for this render item. - const Model::MeshState& state = data._model->getMeshState(data._meshIndex); - CauterizedModel* cModel = static_cast(data._model); - const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); - data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); - } - } - }); + glm::vec3 scale = getScale(); + if (_collisionGeometry) { + // _collisionGeometry is already scaled + scale = glm::vec3(1.0f); } + _needsUpdateClusterMatrices = true; + _renderItemsNeedUpdate = false; - /* - // collision mesh does not share the same unit scale as the FBX file's mesh: only apply offset - Transform collisionMeshOffset; - collisionMeshOffset.setIdentity(); - foreach (auto itemID, self->_collisionRenderItems.keys()) { - pendingChanges.updateItem(itemID, [scaledModelTransform, collisionMeshOffset](MeshPartPayload& data) { - // update the model transform for this render item. - data.updateTransform(scaledModelTransform, collisionMeshOffset); - }); - } - */ + // queue up this work for later processing, at the end of update and just before rendering. + // the application will ensure only the last lambda is actually invoked. + void* key = (void*)this; + std::weak_ptr weakSelf = shared_from_this(); + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { + // do nothing, if the model has already been destroyed. + auto self = weakSelf.lock(); + if (!self) { + return; + } - scene->enqueuePendingChanges(pendingChanges); - }); + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + + Transform modelTransform; + modelTransform.setTranslation(self->getTranslation()); + modelTransform.setRotation(self->getRotation()); + + Transform scaledModelTransform(modelTransform); + scaledModelTransform.setScale(scale); + + uint32_t deleteGeometryCounter = self->getGeometryCounter(); + + render::PendingChanges pendingChanges; + QList keys = self->getRenderItems().keys(); + foreach (auto itemID, keys) { + pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { + if (data._model && data._model->isLoaded()) { + if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { + data.startFade(); + } + // Ensure the model geometry was not reset between frames + if (deleteGeometryCounter == data._model->getGeometryCounter()) { + // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. + data._model->updateClusterMatrices(); + + // update the model transform and bounding box for this render item. + const Model::MeshState& state = data._model->getMeshState(data._meshIndex); + CauterizedModel* cModel = static_cast(data._model); + assert(data._meshIndex < cModel->_cauterizeMeshStates.size()); + const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); + data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); + } + } + }); + } + + scene->enqueuePendingChanges(pendingChanges); + }); + } else { + Model::updateRenderItems(); + } } -#endif // FOO const Model::MeshState& CauterizedModel::getCauterizeMeshState(int index) const { assert(index < _meshStates.size()); diff --git a/interface/src/avatar/CauterizedModel.h b/interface/src/avatar/CauterizedModel.h index 12a9723dd4..01e0b13650 100644 --- a/interface/src/avatar/CauterizedModel.h +++ b/interface/src/avatar/CauterizedModel.h @@ -22,19 +22,22 @@ public: CauterizedModel(RigPointer rig, QObject* parent); virtual ~CauterizedModel(); - void deleteGeometry() override; - virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; + void flagAsCauterized() { _isCauterized = true; } + bool getIsCauterized() const { return _isCauterized; } - void setCauterizeBones(bool flag) { _cauterizeBones = flag; } - bool getCauterizeBones() const { return _cauterizeBones; } + void setEnableCauterization(bool flag) { _enableCauterization = flag; } + bool getEnableCauterization() const { return _enableCauterization; } const std::unordered_set& getCauterizeBoneSet() const { return _cauterizeBoneSet; } void setCauterizeBoneSet(const std::unordered_set& boneSet) { _cauterizeBoneSet = boneSet; } + void deleteGeometry() override; + bool updateGeometry() override; + void createVisibleRenderItemSet() override; void createCollisionRenderItemSet() override; - bool updateGeometry() override; + virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; virtual void updateClusterMatrices() override; void updateRenderItems() override; @@ -43,7 +46,8 @@ public: protected: std::unordered_set _cauterizeBoneSet; QVector _cauterizeMeshStates; - bool _cauterizeBones { false }; + bool _isCauterized { false }; + bool _enableCauterization { false }; }; #endif // hifi_CauterizedModel_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 488752b309..dd95c5963d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -116,12 +116,12 @@ MyAvatar::MyAvatar(RigPointer rig) : _hmdAtRestDetector(glm::vec3(0), glm::quat()) { using namespace recording; + _skeletonModel->flagAsCauterized(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } - // Necessary to select the correct slot using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool); @@ -1592,7 +1592,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { // toggle using the cauterizedBones depending on where the camera is and the rendering pass type. const bool shouldDrawHead = shouldRenderHead(renderArgs); if (shouldDrawHead != _prevShouldDrawHead) { - _skeletonModel->setCauterizeBones(!shouldDrawHead); + _skeletonModel->setEnableCauterization(!shouldDrawHead); } _prevShouldDrawHead = shouldDrawHead; } diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/interface/src/avatar/SoftAttachmentModel.cpp index 530801007e..da7ca0b87d 100644 --- a/interface/src/avatar/SoftAttachmentModel.cpp +++ b/interface/src/avatar/SoftAttachmentModel.cpp @@ -38,7 +38,6 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const { // virtual // use the _rigOverride matrices instead of the Model::_rig void SoftAttachmentModel::updateClusterMatrices() { - // adebug TODO: this needs work? if (!_needsUpdateClusterMatrices) { return; } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 52eb006b9f..57498abff9 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -370,6 +370,9 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf _worldBound += clusterBound; } _worldBound.transform(transform); + } else { + _worldBound = _localBound; + _worldBound.transform(_drawTransform); } } From 5a2e33775825ff3e142a7abec9572dc1a404fe6d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 09:22:18 -0800 Subject: [PATCH 05/15] fix comments --- interface/src/avatar/CauterizedMeshPartPayload.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/interface/src/avatar/CauterizedMeshPartPayload.cpp index a7db9608fe..c8ec90dcee 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/interface/src/avatar/CauterizedMeshPartPayload.cpp @@ -1,9 +1,9 @@ // -// MeshPartPayload.cpp +// CauterizedMeshPartPayload.cpp // interface/src/renderer // -// Created by Sam Gateau on 10/3/15. -// Copyright 2015 High Fidelity, Inc. +// Created by Andrew Meadows 2017.01.17 +// Copyright 2017 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 @@ -14,7 +14,6 @@ #include #include "SkeletonModel.h" -//#include "EntityItem.h" using namespace render; From 9631864ae261d60910c0a5be3ccc7c6c1dc603d3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 09:46:29 -0800 Subject: [PATCH 06/15] make soft attachments cauterized for MyAvatar --- interface/src/avatar/Avatar.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ee5e397592..6061812d2a 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1044,11 +1044,14 @@ void Avatar::setModelURLFinished(bool success) { // create new model, can return an instance of a SoftAttachmentModel rather then Model -static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride) { +static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride, bool isCauterized) { if (isSoft) { // cast to std::shared_ptr - // TODO: re-enable cauterization for the SoftAttachmentModel when this is MyAvatar - return std::dynamic_pointer_cast(std::make_shared(std::make_shared(), nullptr, rigOverride)); + std::shared_ptr softModel = std::make_shared(std::make_shared(), nullptr, rigOverride); + if (isCauterized) { + softModel->flagAsCauterized(); + } + return std::dynamic_pointer_cast(softModel); } else { return std::make_shared(std::make_shared()); } @@ -1074,12 +1077,12 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { for (int i = 0; i < attachmentData.size(); i++) { if (i == (int)_attachmentModels.size()) { // if number of attachments has been increased, we need to allocate a new model - _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig())); + _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar())); } else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { // if the attachment has changed type, we need to re-allocate a new one. _attachmentsToRemove.push_back(_attachmentModels[i]); - _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig()); + _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()); } _attachmentModels[i]->setURL(attachmentData[i].modelURL); } From 1d6f47e3d82b7a20a9b8c4c37b482e55d962044d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 10:46:10 -0800 Subject: [PATCH 07/15] repack to reduce MeshPartPayload footprint 8 bytes --- libraries/render-utils/src/MeshPartPayload.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 53160db91e..a2c932b589 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -51,18 +51,17 @@ public: virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const; // Payload resource cached values - std::shared_ptr _drawMesh; - int _partIndex = 0; - model::Mesh::Part _drawPart; - - std::shared_ptr _drawMaterial; - - model::Box _localBound; Transform _drawTransform; Transform _transform; - mutable model::Box _worldBound; + int _partIndex = 0; + bool _hasColorAttrib { false }; - bool _hasColorAttrib = false; + model::Box _localBound; + mutable model::Box _worldBound; + std::shared_ptr _drawMesh; + + std::shared_ptr _drawMaterial; + model::Mesh::Part _drawPart; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } size_t getMaterialTextureSize() { return _drawMaterial ? _drawMaterial->getTextureSize() : 0; } From d3af420516601584b9a3d02d59dcb948b0684b1c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 13:07:50 -0800 Subject: [PATCH 08/15] fix broken model transforms --- libraries/render-utils/src/MeshPartPayload.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 57498abff9..10162ee437 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -370,6 +370,9 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf _worldBound += clusterBound; } _worldBound.transform(transform); + if (clusterMatrices.size() == 1) { + _transform = _transform.worldTransform(Transform(clusterMatrices[0])); + } } else { _worldBound = _localBound; _worldBound.transform(_drawTransform); From b7c1bfaf5803a4599e4efdd39732a165485e8563 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 14:01:40 -0800 Subject: [PATCH 09/15] another attempt to fix model transform --- libraries/render-utils/src/MeshPartPayload.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 10162ee437..57346ceb53 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -369,13 +369,13 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf clusterBound.transform(clusterMatrix); _worldBound += clusterBound; } - _worldBound.transform(transform); + _worldBound.transform(_transform); if (clusterMatrices.size() == 1) { _transform = _transform.worldTransform(Transform(clusterMatrices[0])); } } else { _worldBound = _localBound; - _worldBound.transform(_drawTransform); + _worldBound.transform(_transform); } } From 10cdea9cd8356b82d051b43f4d1f3179399ea899 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 14:17:50 -0800 Subject: [PATCH 10/15] cleanup model fade logic --- interface/src/avatar/CauterizedModel.cpp | 3 - .../render-utils/src/MeshPartPayload.cpp | 80 +++++++++---------- libraries/render-utils/src/MeshPartPayload.h | 15 ++-- libraries/render-utils/src/Model.cpp | 4 - libraries/shared/src/Interpolate.cpp | 9 ++- libraries/shared/src/Interpolate.h | 4 + 6 files changed, 58 insertions(+), 57 deletions(-) diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index 30566e357d..02107e9d24 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -225,9 +225,6 @@ void CauterizedModel::updateRenderItems() { foreach (auto itemID, keys) { pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { if (data._model && data._model->isLoaded()) { - if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { - data.startFade(); - } // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->getGeometryCounter()) { // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 57346ceb53..fa180a654a 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -402,7 +402,7 @@ ItemKey ModelMeshPartPayload::getKey() const { } } - if (!_hasFinishedFade) { + if (_fadeState != FADE_COMPLETE) { builder.withTransparent(); } @@ -472,7 +472,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } ShapeKey::Builder builder; - if (isTranslucent || !_hasFinishedFade) { + if (isTranslucent || _fadeState != FADE_COMPLETE) { builder.withTranslucent(); } if (hasTangents) { @@ -513,9 +513,10 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } - float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - if (!_hasColorAttrib || fadeRatio < 1.0f) { - batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); + if (_fadeState != FADE_COMPLETE) { + batch._glColor4f(1.0f, 1.0f, 1.0f, computeFadeAlpha()); + } else if (!_hasColorAttrib) { + batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); } } @@ -528,17 +529,23 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: batch.setModelTransform(_transform); } -void ModelMeshPartPayload::startFade() { - bool shouldFade = EntityItem::getEntitiesShouldFadeFunction()(); - if (shouldFade) { - _fadeStartTime = usecTimestampNow(); - _hasStartedFade = true; - _hasFinishedFade = false; - } else { - _isFading = true; - _hasStartedFade = true; - _hasFinishedFade = true; +float ModelMeshPartPayload::computeFadeAlpha() const { + if (_fadeState == FADE_WAITING_TO_START) { + return 0.0f; } + float fadeAlpha = 1.0f; + const float INV_FADE_PERIOD = 1.0f / (float)(1 * USECS_PER_SECOND); + float fraction = (float)(usecTimestampNow() - _fadeStartTime) * INV_FADE_PERIOD; + if (fraction < 1.0f) { + fadeAlpha = Interpolate::simpleNonLinearBlend(fraction); + } + if (fadeAlpha >= 1.0f) { + _fadeState = FADE_COMPLETE; + // when fade-in completes we flag model for one last "render item update" + _model->setRenderItemsNeedUpdate(); + return 1.0f; + } + return Interpolate::simpleNonLinearBlend(fadeAlpha); } void ModelMeshPartPayload::render(RenderArgs* args) const { @@ -548,33 +555,28 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { return; // bail asap } - // If we didn't start the fade in, check if we are ready to now.... - if (!_hasStartedFade && _model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { - const_cast(*this).startFade(); + if (_fadeState == FADE_WAITING_TO_START) { + if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + if (EntityItem::getEntitiesShouldFadeFunction()()) { + _fadeStartTime = usecTimestampNow(); + _fadeState = FADE_IN_PROGRESS; + } else { + _fadeState = FADE_COMPLETE; + } + _model->setRenderItemsNeedUpdate(); + } else { + return; + } } - // If we still didn't start the fade in, bail - if (!_hasStartedFade) { + if (!args) { return; } - - // When an individual mesh parts like this finishes its fade, we will mark the Model as - // having render items that need updating - bool nextIsFading = _isFading ? isStillFading() : false; - bool startFading = !_isFading && !_hasFinishedFade && _hasStartedFade; - bool endFading = _isFading && !nextIsFading; - if (startFading || endFading) { - _isFading = startFading; - _hasFinishedFade = endFading; - _model->setRenderItemsNeedUpdate(); - } - - gpu::Batch& batch = *(args->_batch); - if (!getShapeKey().isValid()) { return; } + gpu::Batch& batch = *(args->_batch); auto locations = args->_pipeline->locations; assert(locations); @@ -588,9 +590,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // apply material properties bindMaterial(batch, locations); - if (args) { - args->_details._materialSwitches++; - } + args->_details._materialSwitches++; // Draw! { @@ -598,8 +598,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { drawCall(batch); } - if (args) { - const int INDICES_PER_TRIANGLE = 3; - args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; - } + const int INDICES_PER_TRIANGLE = 3; + args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index a2c932b589..7d0aeab2bd 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -21,6 +21,10 @@ #include +const uint8_t FADE_WAITING_TO_START = 0; +const uint8_t FADE_IN_PROGRESS = 1; +const uint8_t FADE_COMPLETE = 2; + class Model; class MeshPartPayload { @@ -87,10 +91,7 @@ public: void updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices); - // Entity fade in - void startFade(); - bool hasStartedFade() { return _hasStartedFade; } - bool isStillFading() const { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + float computeFadeAlpha() const; // Render Item interface render::ItemKey getKey() const override; @@ -113,10 +114,8 @@ public: bool _isBlendShaped{ false }; private: - quint64 _fadeStartTime { 0 }; - bool _hasStartedFade { false }; - mutable bool _hasFinishedFade { false }; - mutable bool _isFading { false }; + mutable quint64 _fadeStartTime { 0 }; + mutable uint8_t _fadeState { FADE_WAITING_TO_START }; }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ef613e8c14..b79e69a9b7 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -227,9 +227,6 @@ void Model::updateRenderItems() { foreach (auto itemID, self->_modelMeshRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](ModelMeshPartPayload& data) { if (data._model && data._model->isLoaded()) { - if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { - data.startFade(); - } // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->_deleteGeometryCounter) { // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. @@ -1450,7 +1447,6 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { { Lock lock(_mutex); - _modelsRequiringBlends.insert(model); } } diff --git a/libraries/shared/src/Interpolate.cpp b/libraries/shared/src/Interpolate.cpp index b20c7b8fac..ba93a21a8e 100644 --- a/libraries/shared/src/Interpolate.cpp +++ b/libraries/shared/src/Interpolate.cpp @@ -61,6 +61,13 @@ float Interpolate::interpolate3Points(float y1, float y2, float y3, float u) { } } +float Interpolate::simpleNonLinearBlend(float fraction) { + // uses arctan() to map a linear distribution in domain [0,1] to a non-linear blend (slow out, slow in) in range [0,1] + const float WIDTH = 20.0f; + const float INV_ARCTAN_WIDTH = 0.339875327433f; // 1 / (2 * atan(WIDTH/2)) + return 0.5f + atanf(WIDTH * (fraction - 0.5f)) * INV_ARCTAN_WIDTH; +} + float Interpolate::calculateFadeRatio(quint64 start) { const float FADE_TIME = 1.0f; float t = 2.0f * std::min(((float)(usecTimestampNow() - start)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); @@ -69,4 +76,4 @@ float Interpolate::calculateFadeRatio(quint64 start) { // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly const float EASING_SCALE = 1.001f; return std::min(EASING_SCALE * fadeRatio, 1.0f); -} \ No newline at end of file +} diff --git a/libraries/shared/src/Interpolate.h b/libraries/shared/src/Interpolate.h index a9fa5baad2..79ebd2f7fc 100644 --- a/libraries/shared/src/Interpolate.h +++ b/libraries/shared/src/Interpolate.h @@ -25,6 +25,10 @@ public: // pass through all three y values. Return value lies wholly within the range of y values passed in. static float interpolate3Points(float y1, float y2, float y3, float u); + // returns smooth in and out blend between 0 and 1 + // DANGER: assumes fraction is properly inside range [0, 1] + static float simpleNonLinearBlend(float fraction); + static float calculateFadeRatio(quint64 start); }; From eba96c45d7d1a8d5319814dad0f18069118463b8 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 8 Jan 2017 18:30:40 -0500 Subject: [PATCH 11/15] fix warnings for signed comp in AudioMixerSlavePool --- assignment-client/src/audio/AudioMixerSlavePool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerSlavePool.cpp b/assignment-client/src/audio/AudioMixerSlavePool.cpp index 6446092448..1b884fa089 100644 --- a/assignment-client/src/audio/AudioMixerSlavePool.cpp +++ b/assignment-client/src/audio/AudioMixerSlavePool.cpp @@ -131,7 +131,7 @@ void AudioMixerSlavePool::setNumThreads(int numThreads) { } void AudioMixerSlavePool::resize(int numThreads) { - assert(_numThreads == _slaves.size()); + assert(_numThreads == (int)_slaves.size()); #ifdef AUDIO_SINGLE_THREADED qDebug("%s: running single threaded", __FUNCTION__, numThreads); @@ -182,6 +182,6 @@ void AudioMixerSlavePool::resize(int numThreads) { } _numThreads = _numStarted = _numFinished = numThreads; - assert(_numThreads == _slaves.size()); + assert(_numThreads == (int)_slaves.size()); #endif } From c83cd4a94a238ef418e474f7b57a197bf0f58da1 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 19 Jan 2017 16:03:46 -0800 Subject: [PATCH 12/15] Complex checkpoint --- interface/resources/qml/hifi/Pal.qml | 34 ++++++++++++++++++++------ scripts/system/libraries/entityList.js | 2 +- scripts/system/pal.js | 12 ++++++--- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 989b560e4e..374f9618bb 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -416,6 +416,20 @@ Rectangle { } } } + Timer { + property var selected + property int userIndex + id: selectionTimer + onTriggered: { + if (selected) { + table.selection.clear(); // for now, no multi-select + table.selection.select(userIndex); + table.positionViewAtRow(userIndex, ListView.Visible); + } else { + table.selection.deselect(userIndex); + } + } + } function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml var data = optionalData || userModelData, length = data.length; @@ -453,19 +467,25 @@ Rectangle { case 'select': var sessionIds = message.params[0]; var selected = message.params[1]; + var alreadyRefreshed = message.params[2]; var userIndex = findSessionIndex(sessionIds[0]); if (sessionIds.length > 1) { letterbox("", "", 'Only one user can be selected at a time.'); } else if (userIndex < 0) { - letterbox("", "", 'The last editor is not among this list of users.'); - } else { - if (selected) { - table.selection.clear(); // for now, no multi-select - table.selection.select(userIndex); - table.positionViewAtRow(userIndex, ListView.Visible); + if (alreadyRefreshed === true) { + letterbox('', '', 'The last editor of this object is either you or not among this list of users.'); } else { - table.selection.deselect(userIndex); + pal.sendToScript({method: 'refresh', params: message.params}); } + } else { + if (alreadyRefreshed === true) { + selectionTimer.interval = 250; + } else { + selectionTimer.interval = 0; + } + selectionTimer.selected = selected; + selectionTimer.userIndex = userIndex; + selectionTimer.start(); } break; // Received an "updateUsername()" request from the JS diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 085d4f5e27..6dc2486ffb 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -134,7 +134,7 @@ EntityListTool = function(opts) { Window.alert('There were no recent users of the ' + selectionManager.selections.length + ' selected objects.'); } else { // No need to subscribe if we're just sending. - Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true]}), 'local'); + Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true, false]}), 'local'); } } else if (data.type == "delete") { deleteSelectedEntities(); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 3b34eed268..f148ad5fdb 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -233,7 +233,7 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like break; case 'refresh': removeOverlays(); - populateUserList(); + populateUserList(message.params); UserActivityLogger.palAction("refresh", ""); break; case 'updateGain': @@ -271,7 +271,7 @@ function addAvatarNode(id) { color: color(selected, false, 0.0), ignoreRayIntersection: false}, selected, true); } -function populateUserList() { +function populateUserList(selectData) { var data = []; AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { // sorting the identifiers is just an aid for debugging var avatar = AvatarList.getAvatar(id); @@ -295,7 +295,11 @@ function populateUserList() { data.push(avatarPalDatum); print('PAL data:', JSON.stringify(avatarPalDatum)); }); - pal.sendToQml({method: 'users', params: data}); + pal.sendToQml({ method: 'users', params: data }); + if (selectData) { + selectData[2] = true; + pal.sendToQml({ method: 'select', params: selectData }); + } } // The function that handles the reply from the server @@ -388,7 +392,7 @@ function removeOverlays() { function handleClick(pickRay) { ExtendedOverlay.applyPickRay(pickRay, function (overlay) { // Don't select directly. Tell qml, who will give us back a list of ids. - var message = {method: 'select', params: [[overlay.key], !overlay.selected]}; + var message = {method: 'select', params: [[overlay.key], !overlay.selected, false]}; pal.sendToQml(message); return true; }); From 9c9206acc0001de5ed0148a0da977e244fd2bcef Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 19 Jan 2017 17:37:09 -0800 Subject: [PATCH 13/15] Prevent logspam --- interface/resources/qml/hifi/Pal.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 374f9618bb..7b747f8fb2 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -218,10 +218,10 @@ Rectangle { id: nameCard // Properties displayName: styleData.value - userName: model && model.userName - audioLevel: model && model.audioLevel + userName: model ? model.userName : "" + audioLevel: model ? model.audioLevel : 0.0 visible: !isCheckBox && !isButton - uuid: model && model.sessionId + uuid: model ? model.sessionId : "" selected: styleData.selected isAdmin: model && model.admin // Size @@ -241,9 +241,9 @@ Rectangle { id: actionCheckBox visible: isCheckBox anchors.centerIn: parent - checked: model[styleData.role] + checked: model ? model[styleData.role] : false // If this is a "Personal Mute" checkbox, disable the checkbox if the "Ignore" checkbox is checked. - enabled: !(styleData.role === "personalMute" && model["ignore"]) + enabled: !(styleData.role === "personalMute" && (model ? model["ignore"] : true)) boxSize: 24 onClicked: { var newValue = !model[styleData.role] From 8f3c4794c6c5a5497b210ff1b74eb76353509a8b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 20 Jan 2017 10:21:18 -0800 Subject: [PATCH 14/15] Comments --- interface/resources/qml/hifi/Pal.qml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 7b747f8fb2..51ef6e17e3 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -416,9 +416,11 @@ Rectangle { } } } + // Timer used when selecting table rows that aren't yet present in the model + // (i.e. when selecting avatars using edit.js or sphere overlays) Timer { - property var selected - property int userIndex + property bool selected // Selected or deselected? + property int userIndex // The userIndex of the avatar we want to select id: selectionTimer onTriggered: { if (selected) { @@ -472,15 +474,20 @@ Rectangle { if (sessionIds.length > 1) { letterbox("", "", 'Only one user can be selected at a time.'); } else if (userIndex < 0) { + // If we've already refreshed the PAL and the avatar still isn't present in the model... if (alreadyRefreshed === true) { letterbox('', '', 'The last editor of this object is either you or not among this list of users.'); } else { pal.sendToScript({method: 'refresh', params: message.params}); } } else { + // If we've already refreshed the PAL and found the avatar in the model if (alreadyRefreshed === true) { + // Wait a little bit before trying to actually select the avatar in the table selectionTimer.interval = 250; } else { + // If we've found the avatar in the model and didn't need to refresh, + // select the avatar in the table immediately selectionTimer.interval = 0; } selectionTimer.selected = selected; From 11a57efafb2798078f8227445557f6384c077bbd Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 20 Jan 2017 13:44:29 -0800 Subject: [PATCH 15/15] Fix it! --- interface/resources/qml/hifi/Pal.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 51ef6e17e3..50f1808f94 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -426,7 +426,7 @@ Rectangle { if (selected) { table.selection.clear(); // for now, no multi-select table.selection.select(userIndex); - table.positionViewAtRow(userIndex, ListView.Visible); + table.positionViewAtRow(userIndex, ListView.Beginning); } else { table.selection.deselect(userIndex); }