Showing the changes to libraries/render-utils/src/Model.cpp (1 of 2941 files). Show all
Unified · Split
| @@ -39,16 +39,18 @@ · 38 unmodified lines | ||
| 39 | 39 | #include "RenderUtilsLogging.h" |
| 40 | 40 | #include <Trace.h> |
| 41 | 41 | |
| 42 | +#include <BlendshapeConstants.h> | |
| 43 | + | |
| 42 | 44 | using namespace std; |
| 43 | 45 | |
| 44 | 46 | int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>(); |
| 45 | −int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType<Geometry::WeakPointer>(); | |
| 47 | +int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType<NetworkModel::WeakPointer>(); | |
| 46 | 48 | int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3>>(); |
| 47 | 49 | int normalTypeVecTypeId = qRegisterMetaType<QVector<NormalType>>("QVector<NormalType>"); |
| 48 | 50 | float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; |
| 49 | 51 | #define HTTP_INVALID_COM "http://invalid.com" |
| 50 | 52 | |
| 51 | −Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : | |
| 53 | +Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride, uint64_t created) : | |
| 52 | 54 | QObject(parent), |
| 53 | 55 | _renderGeometry(), |
| 54 | 56 | _renderWatcher(_renderGeometry), |
| @@ -62,7 +64,8 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : · 7 unmodified lines | ||
| 62 | 64 | _snapModelToRegistrationPoint(false), |
| 63 | 65 | _snappedToRegistrationPoint(false), |
| 64 | 66 | _url(HTTP_INVALID_COM), |
| 65 | − _renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build()) | |
| 67 | + _renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build()), | |
| 68 | + _created(created) | |
| 66 | 69 | { |
| 67 | 70 | // we may have been created in the network thread, but we live in the main thread |
| 68 | 71 | if (_viewState) { |
| @@ -71,7 +74,7 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : · 2 unmodified lines | ||
| 71 | 74 | |
| 72 | 75 | setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); |
| 73 | 76 | |
| 74 | − connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished); | |
| 77 | + connect(&_renderWatcher, &ModelResourceWatcher::finished, this, &Model::loadURLFinished); | |
| 75 | 78 | } |
| 76 | 79 | |
| 77 | 80 | Model::~Model() { |
| @@ -151,7 +154,7 @@ void Model::setOffset(const glm::vec3& offset) { · 73 unmodified lines | ||
| 151 | 154 | } |
| 152 | 155 | |
| 153 | 156 | void Model::calculateTextureInfo() { |
| 154 | − if (!_hasCalculatedTextureInfo && isLoaded() && getGeometry()->areTexturesLoaded() && !_modelMeshRenderItems.isEmpty()) { | |
| 157 | + if (!_hasCalculatedTextureInfo && isLoaded() && getNetworkModel()->areTexturesLoaded() && !_modelMeshRenderItems.isEmpty()) { | |
| 155 | 158 | size_t textureSize = 0; |
| 156 | 159 | int textureCount = 0; |
| 157 | 160 | bool allTexturesLoaded = true; |
| @@ -178,15 +181,15 @@ int Model::getRenderInfoTextureCount() { · 20 unmodified lines | ||
| 178 | 181 | } |
| 179 | 182 | |
| 180 | 183 | bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) { |
| 181 | − if (!getGeometry()) { | |
| 184 | + if (!getNetworkModel()) { | |
| 182 | 185 | return true; |
| 183 | 186 | } |
| 184 | 187 | |
| 185 | 188 | const HFMModel& hfmModel = getHFMModel(); |
| 186 | − const auto& networkMeshes = getGeometry()->getMeshes(); | |
| 189 | + const auto& networkMeshes = getNetworkModel()->getMeshes(); | |
| 187 | 190 | // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown |
| 188 | 191 | // to false to rebuild out mesh groups. |
| 189 | − if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)hfmModel.meshes.size() || meshIndex >= (int)_meshStates.size()) { | |
| 192 | + if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)hfmModel.meshes.size()) { | |
| 190 | 193 | _needsFixupInScene = true; // trigger remove/add cycle |
| 191 | 194 | invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid |
| 192 | 195 | return true; |
| @@ -228,44 +231,45 @@ void Model::updateRenderItems() { · 35 unmodified lines | ||
| 228 | 231 | |
| 229 | 232 | render::Transaction transaction; |
| 230 | 233 | for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { |
| 231 | − | |
| 232 | 234 | auto itemID = self->_modelMeshRenderItemIDs[i]; |
| 233 | − auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; | |
| 234 | 235 | |
| 235 | − const auto& meshState = self->getMeshState(meshIndex); | |
| 236 | + const auto& shapeState = self->getShapeState(i); | |
| 236 | 237 | |
| 237 | − bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); | |
| 238 | − bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); | |
| 238 | + auto skinDeformerIndex = shapeState._skinDeformerIndex; | |
| 239 | 239 | |
| 240 | − transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, meshState, useDualQuaternionSkinning, | |
| 241 | − invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) { | |
| 242 | − if (useDualQuaternionSkinning) { | |
| 243 | − data.updateClusterBuffer(meshState.clusterDualQuaternions); | |
| 244 | − } else { | |
| 245 | − data.updateClusterBuffer(meshState.clusterMatrices); | |
| 246 | − } | |
| 240 | + bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(shapeState._meshIndex); | |
| 247 | 241 | |
| 248 | − Transform renderTransform = modelTransform; | |
| 242 | + if (skinDeformerIndex != hfm::UNDEFINED_KEY) { | |
| 243 | + const auto& meshState = self->getMeshState(skinDeformerIndex); | |
| 244 | + bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); | |
| 249 | 245 | |
| 250 | − if (useDualQuaternionSkinning) { | |
| 251 | − if (meshState.clusterDualQuaternions.size() == 1 || meshState.clusterDualQuaternions.size() == 2) { | |
| 252 | − const auto& dq = meshState.clusterDualQuaternions[0]; | |
| 253 | − Transform transform(dq.getRotation(), | |
| 254 | − dq.getScale(), | |
| 255 | − dq.getTranslation()); | |
| 256 | − renderTransform = modelTransform.worldTransform(Transform(transform)); | |
| 246 | + transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, shapeState, meshState, useDualQuaternionSkinning, | |
| 247 | + invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) { | |
| 248 | + if (useDualQuaternionSkinning) { | |
| 249 | + data.updateClusterBuffer(meshState.clusterDualQuaternions); | |
| 250 | + } else { | |
| 251 | + data.updateClusterBuffer(meshState.clusterMatrices); | |
| 257 | 252 | } |
| 258 | − } else { | |
| 259 | − if (meshState.clusterMatrices.size() == 1 || meshState.clusterMatrices.size() == 2) { | |
| 260 | − renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0])); | |
| 261 | − } | |
| 262 | − } | |
| 263 | − data.updateTransformForSkinnedMesh(renderTransform, modelTransform); | |
| 264 | 253 | |
| 265 | − data.setCauterized(cauterized); | |
| 266 | − data.updateKey(renderItemKeyGlobalFlags); | |
| 267 | − data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); | |
| 268 | − }); | |
| 254 | + Transform renderTransform = modelTransform; | |
| 255 | + data.updateTransform(renderTransform); | |
| 256 | + data.updateTransformAndBound(modelTransform.worldTransform(shapeState._rootFromJointTransform)); | |
| 257 | + | |
| 258 | + data.setCauterized(cauterized); | |
| 259 | + data.updateKey(renderItemKeyGlobalFlags); | |
| 260 | + data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); | |
| 261 | + }); | |
| 262 | + } else { | |
| 263 | + transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, shapeState, invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) { | |
| 264 | + | |
| 265 | + Transform renderTransform = modelTransform; | |
| 266 | + renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform); | |
| 267 | + data.updateTransform(renderTransform); | |
| 268 | + | |
| 269 | + data.updateKey(renderItemKeyGlobalFlags); | |
| 270 | + data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, false); | |
| 271 | + }); | |
| 272 | + } | |
| 269 | 273 | } |
| 270 | 274 | |
| 271 | 275 | AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); |
| @@ -291,6 +295,15 @@ void Model::reset() { · 19 unmodified lines | ||
| 291 | 295 | } |
| 292 | 296 | } |
| 293 | 297 | |
| 298 | +void Model::updateShapeStatesFromRig() { | |
| 299 | + for (auto& shape : _shapeStates) { | |
| 300 | + uint32_t jointId = shape._jointIndex; | |
| 301 | + if (jointId < (uint32_t) _rig.getJointStateCount()) { | |
| 302 | + shape._rootFromJointTransform = _rig.getJointTransform(jointId); | |
| 303 | + } | |
| 304 | + } | |
| 305 | +} | |
| 306 | + | |
| 294 | 307 | bool Model::updateGeometry() { |
| 295 | 308 | bool needFullUpdate = false; |
| 296 | 309 | |
| @@ -306,16 +319,27 @@ bool Model::updateGeometry() { · 9 unmodified lines | ||
| 306 | 319 | assert(_meshStates.empty()); |
| 307 | 320 | |
| 308 | 321 | const HFMModel& hfmModel = getHFMModel(); |
| 309 | − int i = 0; | |
| 310 | − foreach (const HFMMesh& mesh, hfmModel.meshes) { | |
| 322 | + | |
| 323 | + const auto& shapes = hfmModel.shapes; | |
| 324 | + _shapeStates.resize(shapes.size()); | |
| 325 | + for (uint32_t s = 0; s < (uint32_t) shapes.size(); ++s) { | |
| 326 | + auto& shapeState = _shapeStates[s]; | |
| 327 | + shapeState._jointIndex = shapes[s].joint; | |
| 328 | + shapeState._meshIndex = shapes[s].mesh; | |
| 329 | + shapeState._meshPartIndex = shapes[s].meshPart; | |
| 330 | + shapeState._skinDeformerIndex = shapes[s].skinDeformer; | |
| 331 | + } | |
| 332 | + updateShapeStatesFromRig(); | |
| 333 | + | |
| 334 | + const auto& hfmSkinDeformers = hfmModel.skinDeformers; | |
| 335 | + for (uint32_t i = 0; i < (uint32_t) hfmSkinDeformers.size(); i++) { | |
| 336 | + const auto& dynT = hfmSkinDeformers[i]; | |
| 311 | 337 | MeshState state; |
| 312 | − state.clusterDualQuaternions.resize(mesh.clusters.size()); | |
| 313 | − state.clusterMatrices.resize(mesh.clusters.size()); | |
| 338 | + state.clusterDualQuaternions.resize(dynT.clusters.size()); | |
| 339 | + state.clusterMatrices.resize(dynT.clusters.size()); | |
| 314 | 340 | _meshStates.push_back(state); |
| 315 | − initializeBlendshapes(mesh, i); | |
| 316 | − i++; | |
| 317 | 341 | } |
| 318 | − _blendshapeOffsetsInitialized = true; | |
| 342 | + | |
| 319 | 343 | needFullUpdate = true; |
| 320 | 344 | emit rigReady(); |
| 321 | 345 | } |
| @@ -445,7 +469,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g · 123 unmodified lines | ||
| 445 | 469 | } |
| 446 | 470 | |
| 447 | 471 | /**jsdoc |
| 448 | − * Information about a submesh intersection point. | |
| 472 | + * A submesh intersection point. | |
| 449 | 473 | * @typedef {object} SubmeshIntersection |
| 450 | 474 | * @property {Vec3} worldIntersectionPoint - The intersection point in world coordinates. |
| 451 | 475 | * @property {Vec3} meshIntersectionPoint - The intersection point in model coordinates. |
| @@ -643,8 +667,8 @@ glm::mat4 Model::getWorldToHFMMatrix() const { · 191 unmodified lines | ||
| 643 | 667 | // TODO: deprecate and remove |
| 644 | 668 | MeshProxyList Model::getMeshes() const { |
| 645 | 669 | MeshProxyList result; |
| 646 | − const Geometry::Pointer& renderGeometry = getGeometry(); | |
| 647 | − const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); | |
| 670 | + const NetworkModel::Pointer& renderGeometry = getNetworkModel(); | |
| 671 | + const NetworkModel::GeometryMeshes& meshes = renderGeometry->getMeshes(); | |
| 648 | 672 | |
| 649 | 673 | if (!isLoaded()) { |
| 650 | 674 | return result; |
| @@ -713,9 +737,9 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe · 62 unmodified lines | ||
| 713 | 737 | render::Transaction transaction; |
| 714 | 738 | for (int i = 0; i < (int) _modelMeshRenderItemIDs.size(); i++) { |
| 715 | 739 | auto itemID = _modelMeshRenderItemIDs[i]; |
| 716 | − auto shape = _modelMeshRenderItemShapes[i]; | |
| 740 | + auto& shape = _shapeStates[i]; | |
| 717 | 741 | // TODO: check to see if .partIndex matches too |
| 718 | − if (shape.meshIndex == meshIndex) { | |
| 742 | + if (shape._meshIndex == (uint32_t) meshIndex) { | |
| 719 | 743 | transaction.updateItem<ModelMeshPartPayload>(itemID, [=](ModelMeshPartPayload& data) { |
| 720 | 744 | data.updateMeshPart(mesh, partIndex); |
| 721 | 745 | }); |
| @@ -734,7 +758,7 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe · 12 unmodified lines | ||
| 734 | 758 | for (int partID = 0; partID < numParts; partID++) { |
| 735 | 759 | HFMMeshPart part; |
| 736 | 760 | part.triangleIndices = buffer_helpers::bufferToVector<int>(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); |
| 737 | − mesh.parts << part; | |
| 761 | + mesh.parts.push_back(part); | |
| 738 | 762 | } |
| 739 | 763 | { |
| 740 | 764 | foreach (const glm::vec3& vertex, mesh.vertices) { |
| @@ -745,7 +769,7 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe · 4 unmodified lines | ||
| 745 | 769 | mesh.meshExtents.maximum = glm::max(mesh.meshExtents.maximum, transformedVertex); |
| 746 | 770 | } |
| 747 | 771 | } |
| 748 | − hfmModel.meshes << mesh; | |
| 772 | + hfmModel.meshes.push_back(mesh); | |
| 749 | 773 | } |
| 750 | 774 | calculateTriangleSets(hfmModel); |
| 751 | 775 | } |
| @@ -762,9 +786,9 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { · 10 unmodified lines | ||
| 762 | 786 | } |
| 763 | 787 | |
| 764 | 788 | const HFMModel& hfmModel = getHFMModel(); |
| 765 | − int numberOfMeshes = hfmModel.meshes.size(); | |
| 789 | + uint32_t numberOfMeshes = (uint32_t)hfmModel.meshes.size(); | |
| 766 | 790 | int shapeID = 0; |
| 767 | − for (int i = 0; i < numberOfMeshes; i++) { | |
| 791 | + for (uint32_t i = 0; i < numberOfMeshes; i++) { | |
| 768 | 792 | const HFMMesh& hfmMesh = hfmModel.meshes.at(i); |
| 769 | 793 | if (auto mesh = hfmMesh._mesh) { |
| 770 | 794 | result.append(mesh); |
| @@ -772,13 +796,16 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { · 1 unmodified line | ||
| 772 | 796 | int numParts = (int)mesh->getNumParts(); |
| 773 | 797 | for (int partIndex = 0; partIndex < numParts; partIndex++) { |
| 774 | 798 | auto& materialName = _modelMeshMaterialNames[shapeID]; |
| 775 | − result.appendMaterial(graphics::MaterialLayer(getGeometry()->getShapeMaterial(shapeID), 0), shapeID, materialName); | |
| 776 | − | |
| 777 | − auto mappedMaterialIter = _materialMapping.find(shapeID); | |
| 778 | − if (mappedMaterialIter != _materialMapping.end()) { | |
| 779 | − auto mappedMaterials = mappedMaterialIter->second; | |
| 780 | − for (auto& mappedMaterial : mappedMaterials) { | |
| 781 | − result.appendMaterial(mappedMaterial, shapeID, materialName); | |
| 799 | + result.appendMaterial(graphics::MaterialLayer(getNetworkModel()->getShapeMaterial(shapeID), 0), shapeID, materialName); | |
| 800 | + | |
| 801 | + { | |
| 802 | + std::unique_lock<std::mutex> lock(_materialMappingMutex); | |
| 803 | + auto mappedMaterialIter = _materialMapping.find(shapeID); | |
| 804 | + if (mappedMaterialIter != _materialMapping.end()) { | |
| 805 | + auto mappedMaterials = mappedMaterialIter->second; | |
| 806 | + for (auto& mappedMaterial : mappedMaterials) { | |
| 807 | + result.appendMaterial(mappedMaterial, shapeID, materialName); | |
| 808 | + } | |
| 782 | 809 | } |
| 783 | 810 | } |
| 784 | 811 | shapeID++; |
| @@ -792,78 +819,70 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { · 7 unmodified lines | ||
| 792 | 819 | void Model::calculateTriangleSets(const HFMModel& hfmModel) { |
| 793 | 820 | PROFILE_RANGE(render, __FUNCTION__); |
| 794 | 821 | |
| 795 | − int numberOfMeshes = hfmModel.meshes.size(); | |
| 822 | + uint32_t meshInstanceCount = 0; | |
| 823 | + uint32_t lastMeshForInstanceCount = hfm::UNDEFINED_KEY; | |
| 824 | + for (const auto& shape : hfmModel.shapes) { | |
| 825 | + if (shape.mesh != lastMeshForInstanceCount) { | |
| 826 | + ++meshInstanceCount; | |
| 827 | + } | |
| 828 | + lastMeshForInstanceCount = shape.mesh; | |
| 829 | + } | |
| 796 | 830 | |
| 797 | 831 | _triangleSetsValid = true; |
| 798 | 832 | _modelSpaceMeshTriangleSets.clear(); |
| 799 | − _modelSpaceMeshTriangleSets.resize(numberOfMeshes); | |
| 800 | − | |
| 801 | − for (int i = 0; i < numberOfMeshes; i++) { | |
| 802 | − const HFMMesh& mesh = hfmModel.meshes.at(i); | |
| 803 | − | |
| 804 | − const int numberOfParts = mesh.parts.size(); | |
| 805 | − auto& meshTriangleSets = _modelSpaceMeshTriangleSets[i]; | |
| 806 | − meshTriangleSets.resize(numberOfParts); | |
| 807 | − | |
| 808 | − for (int j = 0; j < numberOfParts; j++) { | |
| 809 | − const HFMMeshPart& part = mesh.parts.at(j); | |
| 810 | − | |
| 811 | − auto& partTriangleSet = meshTriangleSets[j]; | |
| 812 | − | |
| 813 | − const int INDICES_PER_TRIANGLE = 3; | |
| 814 | − const int INDICES_PER_QUAD = 4; | |
| 815 | − const int TRIANGLES_PER_QUAD = 2; | |
| 816 | − | |
| 817 | − // tell our triangleSet how many triangles to expect. | |
| 818 | − int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD; | |
| 819 | − int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE; | |
| 820 | − int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris; | |
| 821 | − partTriangleSet.reserve(totalTriangles); | |
| 822 | − | |
| 823 | − auto meshTransform = hfmModel.offset * mesh.modelTransform; | |
| 824 | − | |
| 825 | − if (part.quadIndices.size() > 0) { | |
| 826 | − int vIndex = 0; | |
| 827 | − for (int q = 0; q < numberOfQuads; q++) { | |
| 828 | − int i0 = part.quadIndices[vIndex++]; | |
| 829 | − int i1 = part.quadIndices[vIndex++]; | |
| 830 | − int i2 = part.quadIndices[vIndex++]; | |
| 831 | − int i3 = part.quadIndices[vIndex++]; | |
| 832 | − | |
| 833 | − // track the model space version... these points will be transformed by the FST's offset, | |
| 834 | − // which includes the scaling, rotation, and translation specified by the FST/FBX, | |
| 835 | − // this can't change at runtime, so we can safely store these in our TriangleSet | |
| 836 | − glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f)); | |
| 837 | − glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f)); | |
| 838 | − glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f)); | |
| 839 | − glm::vec3 v3 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i3], 1.0f)); | |
| 840 | − | |
| 841 | − Triangle tri1 = { v0, v1, v3 }; | |
| 842 | − Triangle tri2 = { v1, v2, v3 }; | |
| 843 | − partTriangleSet.insert(tri1); | |
| 844 | − partTriangleSet.insert(tri2); | |
| 845 | − } | |
| 846 | − } | |
| 833 | + _modelSpaceMeshTriangleSets.reserve(meshInstanceCount); | |
| 834 | + | |
| 835 | + uint32_t lastMeshForTriangleBuilding = hfm::UNDEFINED_KEY; | |
| 836 | + glm::mat4 lastTransformForTriangleBuilding { 0 }; | |
| 837 | + std::vector<glm::vec3> transformedPoints; | |
| 838 | + for (const auto& shape : hfmModel.shapes) { | |
| 839 | + const uint32_t meshIndex = shape.mesh; | |
| 840 | + const hfm::Mesh& mesh = hfmModel.meshes.at(meshIndex); | |
| 841 | + const auto& triangleListMesh = mesh.triangleListMesh; | |
| 842 | + const glm::vec2 part = triangleListMesh.parts[shape.meshPart]; | |
| 843 | + glm::mat4 worldFromMeshTransform; | |
| 844 | + if (shape.joint != hfm::UNDEFINED_KEY) { | |
| 845 | + // globalTransform includes hfmModel.offset, | |
| 846 | + // which includes the scaling, rotation, and translation specified by the FST, | |
| 847 | + // and the scaling from the unit conversion in FBX. | |
| 848 | + // This can't change at runtime, so we can safely store these in our TriangleSet. | |
| 849 | + worldFromMeshTransform = hfmModel.joints[shape.joint].globalTransform; | |
| 850 | + } | |
| 851 | + | |
| 852 | + if (meshIndex != lastMeshForTriangleBuilding || worldFromMeshTransform != lastTransformForTriangleBuilding) { | |
| 853 | + lastMeshForTriangleBuilding = meshIndex; | |
| 854 | + lastTransformForTriangleBuilding = worldFromMeshTransform; | |
| 855 | + _modelSpaceMeshTriangleSets.emplace_back(); | |
| 856 | + _modelSpaceMeshTriangleSets.back().reserve(mesh.parts.size()); | |
| 847 | 857 | |
| 848 | − if (part.triangleIndices.size() > 0) { | |
| 849 | − int vIndex = 0; | |
| 850 | − for (int t = 0; t < numberOfTris; t++) { | |
| 851 | − int i0 = part.triangleIndices[vIndex++]; | |
| 852 | − int i1 = part.triangleIndices[vIndex++]; | |
| 853 | − int i2 = part.triangleIndices[vIndex++]; | |
| 854 | − | |
| 855 | − // track the model space version... these points will be transformed by the FST's offset, | |
| 856 | − // which includes the scaling, rotation, and translation specified by the FST/FBX, | |
| 857 | − // this can't change at runtime, so we can safely store these in our TriangleSet | |
| 858 | − glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f)); | |
| 859 | − glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f)); | |
| 860 | − glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f)); | |
| 861 | − | |
| 862 | − Triangle tri = { v0, v1, v2 }; | |
| 863 | − partTriangleSet.insert(tri); | |
| 858 | + transformedPoints = triangleListMesh.vertices; | |
| 859 | + if (worldFromMeshTransform != glm::mat4()) { | |
| 860 | + for (auto& point : transformedPoints) { | |
| 861 | + point = glm::vec3(worldFromMeshTransform * glm::vec4(point, 1.0f)); | |
| 864 | 862 | } |
| 865 | 863 | } |
| 866 | 864 | } |
| 865 | + auto& meshTriangleSets = _modelSpaceMeshTriangleSets.back(); | |
| 866 | + meshTriangleSets.emplace_back(); | |
| 867 | + auto& partTriangleSet = meshTriangleSets.back(); | |
| 868 | + | |
| 869 | + const static size_t INDICES_PER_TRIANGLE = 3; | |
| 870 | + const size_t triangleCount = (size_t)(part.y) / INDICES_PER_TRIANGLE; | |
| 871 | + partTriangleSet.reserve(triangleCount); | |
| 872 | + const size_t indexStart = (uint32_t)part.x; | |
| 873 | + const size_t indexEnd = indexStart + (triangleCount * INDICES_PER_TRIANGLE); | |
| 874 | + for (size_t i = indexStart; i < indexEnd; i += INDICES_PER_TRIANGLE) { | |
| 875 | + const int i0 = triangleListMesh.indices[i]; | |
| 876 | + const int i1 = triangleListMesh.indices[i + 1]; | |
| 877 | + const int i2 = triangleListMesh.indices[i + 2]; | |
| 878 | + | |
| 879 | + const glm::vec3 v0 = transformedPoints[i0]; | |
| 880 | + const glm::vec3 v1 = transformedPoints[i1]; | |
| 881 | + const glm::vec3 v2 = transformedPoints[i2]; | |
| 882 | + | |
| 883 | + const Triangle tri = { v0, v1, v2 }; | |
| 884 | + partTriangleSet.insert(tri); | |
| 885 | + } | |
| 867 | 886 | } |
| 868 | 887 | } |
| 869 | 888 | |
| @@ -874,8 +893,8 @@ void Model::updateRenderItemsKey(const render::ScenePointer& scene) { · 4 unmodified lines | ||
| 874 | 893 | } |
| 875 | 894 | auto renderItemsKey = _renderItemKeyGlobalFlags; |
| 876 | 895 | render::Transaction transaction; |
| 877 | − foreach(auto item, _modelMeshRenderItemsMap.keys()) { | |
| 878 | − transaction.updateItem<ModelMeshPartPayload>(item, [renderItemsKey](ModelMeshPartPayload& data) { | |
| 896 | + for(auto itemID: _modelMeshRenderItemIDs) { | |
| 897 | + transaction.updateItem<ModelMeshPartPayload>(itemID, [renderItemsKey](ModelMeshPartPayload& data) { | |
| 879 | 898 | data.updateKey(renderItemsKey); |
| 880 | 899 | }); |
| 881 | 900 | } |
| @@ -945,8 +964,8 @@ void Model::setCauterized(bool cauterized, const render::ScenePointer& scene) { · 63 unmodified lines | ||
| 945 | 964 | return; |
| 946 | 965 | } |
| 947 | 966 | render::Transaction transaction; |
| 948 | − foreach (auto item, _modelMeshRenderItemsMap.keys()) { | |
| 949 | − transaction.updateItem<ModelMeshPartPayload>(item, [cauterized](ModelMeshPartPayload& data) { | |
| 967 | + for (auto itemID : _modelMeshRenderItemIDs) { | |
| 968 | + transaction.updateItem<ModelMeshPartPayload>(itemID, [cauterized](ModelMeshPartPayload& data) { | |
| 950 | 969 | data.setCauterized(cauterized); |
| 951 | 970 | }); |
| 952 | 971 | } |
| @@ -976,7 +995,9 @@ bool Model::addToScene(const render::ScenePointer& scene, · 23 unmodified lines | ||
| 976 | 995 | render::Transaction& transaction, |
| 977 | 996 | render::Item::Status::Getters& statusGetters, |
| 978 | 997 | BlendShapeOperator modelBlendshapeOperator) { |
| 998 | + | |
| 979 | 999 | if (!_addedToScene && isLoaded()) { |
| 1000 | + updateGeometry(); | |
| 980 | 1001 | updateClusterMatrices(); |
| 981 | 1002 | if (_modelMeshRenderItems.empty()) { |
| 982 | 1003 | createRenderItemSet(); |
| @@ -987,26 +1008,25 @@ bool Model::addToScene(const render::ScenePointer& scene, · 4 unmodified lines | ||
| 987 | 1008 | |
| 988 | 1009 | bool somethingAdded = false; |
| 989 | 1010 | |
| 990 | − if (_modelMeshRenderItemsMap.empty()) { | |
| 1011 | + if (_modelMeshRenderItemIDs.empty()) { | |
| 991 | 1012 | bool hasTransparent = false; |
| 992 | 1013 | size_t verticesCount = 0; |
| 993 | 1014 | foreach(auto renderItem, _modelMeshRenderItems) { |
| 994 | 1015 | auto item = scene->allocateID(); |
| 995 | 1016 | auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem); |
| 996 | − if (_modelMeshRenderItemsMap.empty() && statusGetters.size()) { | |
| 1017 | + if (_modelMeshRenderItemIDs.empty() && statusGetters.size()) { | |
| 997 | 1018 | renderPayload->addStatusGetters(statusGetters); |
| 998 | 1019 | } |
| 999 | 1020 | transaction.resetItem(item, renderPayload); |
| 1000 | 1021 | |
| 1001 | 1022 | hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent(); |
| 1002 | 1023 | verticesCount += renderItem.get()->getVerticesCount(); |
| 1003 | − _modelMeshRenderItemsMap.insert(item, renderPayload); | |
| 1004 | 1024 | _modelMeshRenderItemIDs.emplace_back(item); |
| 1005 | 1025 | } |
| 1006 | − somethingAdded = !_modelMeshRenderItemsMap.empty(); | |
| 1026 | + somethingAdded = !_modelMeshRenderItemIDs.empty(); | |
| 1007 | 1027 | |
| 1008 | 1028 | _renderInfoVertexCount = verticesCount; |
| 1009 | − _renderInfoDrawCalls = _modelMeshRenderItemsMap.count(); | |
| 1029 | + _renderInfoDrawCalls = (uint32_t) _modelMeshRenderItemIDs.size(); | |
| 1010 | 1030 | _renderInfoHasTransparent = hasTransparent; |
| 1011 | 1031 | } |
| 1012 | 1032 | |
| @@ -1021,19 +1041,14 @@ bool Model::addToScene(const render::ScenePointer& scene, · 8 unmodified lines | ||
| 1021 | 1041 | } |
| 1022 | 1042 | |
| 1023 | 1043 | void Model::removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction) { |
| 1024 | − foreach (auto item, _modelMeshRenderItemsMap.keys()) { | |
| 1025 | − transaction.removeItem(item); | |
| 1044 | + for (auto itemID: _modelMeshRenderItemIDs) { | |
| 1045 | + transaction.removeItem(itemID); | |
| 1026 | 1046 | } |
| 1027 | 1047 | _modelMeshRenderItemIDs.clear(); |
| 1028 | − _modelMeshRenderItemsMap.clear(); | |
| 1029 | 1048 | _modelMeshRenderItems.clear(); |
| 1030 | 1049 | _modelMeshMaterialNames.clear(); |
| 1031 | − _modelMeshRenderItemShapes.clear(); | |
| 1032 | 1050 | _priorityMap.clear(); |
| 1033 | 1051 | |
| 1034 | − _blendshapeOffsets.clear(); | |
| 1035 | − _blendshapeOffsetsInitialized = false; | |
| 1036 | − | |
| 1037 | 1052 | _addedToScene = false; |
| 1038 | 1053 | |
| 1039 | 1054 | _renderInfoVertexCount = 0; |
| @@ -1042,7 +1057,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti · 2 unmodified lines | ||
| 1042 | 1057 | _renderInfoHasTransparent = false; |
| 1043 | 1058 | } |
| 1044 | 1059 | |
| 1045 | −void Model::renderDebugMeshBoxes(gpu::Batch& batch) { | |
| 1060 | +void Model::renderDebugMeshBoxes(gpu::Batch& batch, bool forward) { | |
| 1046 | 1061 | int colorNdx = 0; |
| 1047 | 1062 | _mutex.lock(); |
| 1048 | 1063 | |
| @@ -1051,7 +1066,7 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) { · 2 unmodified lines | ||
| 1051 | 1066 | Transform meshToWorld(meshToWorldMatrix); |
| 1052 | 1067 | batch.setModelTransform(meshToWorld); |
| 1053 | 1068 | |
| 1054 | − DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, true, true); | |
| 1069 | + DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, true, true, forward); | |
| 1055 | 1070 | |
| 1056 | 1071 | for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { |
| 1057 | 1072 | for (auto &partTriangleSet : meshTriangleSets) { |
| @@ -1210,7 +1225,7 @@ void Model::setURL(const QUrl& url) { · 152 unmodified lines | ||
| 1210 | 1225 | invalidCalculatedMeshBoxes(); |
| 1211 | 1226 | deleteGeometry(); |
| 1212 | 1227 | |
| 1213 | − auto resource = DependencyManager::get<ModelCache>()->getGeometryResource(url); | |
| 1228 | + auto resource = DependencyManager::get<ModelCache>()->getModelResource(url); | |
| 1214 | 1229 | if (resource) { |
| 1215 | 1230 | resource->setLoadPriority(this, _loadingPriority); |
| 1216 | 1231 | _renderWatcher.setResource(resource); |
| @@ -1381,8 +1396,6 @@ void Model::simulate(float deltaTime, bool fullUpdate) { · 164 unmodified lines | ||
| 1381 | 1396 | // update the world space transforms for all joints |
| 1382 | 1397 | glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); |
| 1383 | 1398 | updateRig(deltaTime, parentTransform); |
| 1384 | − | |
| 1385 | − computeMeshPartLocalBounds(); | |
| 1386 | 1399 | } |
| 1387 | 1400 | } |
| 1388 | 1401 | |
| @@ -1393,17 +1406,6 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { · 4 unmodified lines | ||
| 1393 | 1406 | _rig.updateAnimations(deltaTime, parentTransform, rigToWorldTransform); |
| 1394 | 1407 | } |
| 1395 | 1408 | |
| 1396 | −void Model::computeMeshPartLocalBounds() { | |
| 1397 | − for (auto& part : _modelMeshRenderItems) { | |
| 1398 | − const Model::MeshState& state = _meshStates.at(part->_meshIndex); | |
| 1399 | − if (_useDualQuaternionSkinning) { | |
| 1400 | − part->computeAdjustedLocalBound(state.clusterDualQuaternions); | |
| 1401 | − } else { | |
| 1402 | − part->computeAdjustedLocalBound(state.clusterMatrices); | |
| 1403 | − } | |
| 1404 | − } | |
| 1405 | −} | |
| 1406 | − | |
| 1407 | 1409 | // virtual |
| 1408 | 1410 | void Model::updateClusterMatrices() { |
| 1409 | 1411 | DETAILED_PERFORMANCE_TIMER("Model::updateClusterMatrices"); |
| @@ -1412,32 +1414,32 @@ void Model::updateClusterMatrices() { · 2 unmodified lines | ||
| 1412 | 1414 | return; |
| 1413 | 1415 | } |
| 1414 | 1416 | |
| 1417 | + updateShapeStatesFromRig(); | |
| 1418 | + | |
| 1415 | 1419 | _needsUpdateClusterMatrices = false; |
| 1416 | − const HFMModel& hfmModel = getHFMModel(); | |
| 1417 | − for (int i = 0; i < (int) _meshStates.size(); i++) { | |
| 1418 | − MeshState& state = _meshStates[i]; | |
| 1419 | − int meshIndex = i; | |
| 1420 | − const HFMMesh& mesh = hfmModel.meshes.at(i); | |
| 1421 | − for (int j = 0; j < mesh.clusters.size(); j++) { | |
| 1422 | − const HFMCluster& cluster = mesh.clusters.at(j); | |
| 1423 | − int clusterIndex = j; | |
| 1420 | + | |
| 1421 | + for (int skinDeformerIndex = 0; skinDeformerIndex < (int)_meshStates.size(); skinDeformerIndex++) { | |
| 1422 | + MeshState& state = _meshStates[skinDeformerIndex]; | |
| 1423 | + auto numClusters = state.getNumClusters(); | |
| 1424 | + for (uint32_t clusterIndex = 0; clusterIndex < numClusters; clusterIndex++) { | |
| 1425 | + const auto& cbmov = _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(skinDeformerIndex, clusterIndex); | |
| 1424 | 1426 | |
| 1425 | 1427 | if (_useDualQuaternionSkinning) { |
| 1426 | − auto jointPose = _rig.getJointPose(cluster.jointIndex); | |
| 1428 | + auto jointPose = _rig.getJointPose(cbmov.jointIndex); | |
| 1427 | 1429 | Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); |
| 1428 | 1430 | Transform clusterTransform; |
| 1429 | − Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); | |
| 1430 | − state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); | |
| 1431 | + Transform::mult(clusterTransform, jointTransform, cbmov.inverseBindTransform); | |
| 1432 | + state.clusterDualQuaternions[clusterIndex] = Model::TransformDualQuaternion(clusterTransform); | |
| 1431 | 1433 | } else { |
| 1432 | − auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); | |
| 1433 | − glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); | |
| 1434 | + auto jointMatrix = _rig.getJointTransform(cbmov.jointIndex); | |
| 1435 | + glm_mat4u_mul(jointMatrix, cbmov.inverseBindMatrix, state.clusterMatrices[clusterIndex]); | |
| 1434 | 1436 | } |
| 1435 | 1437 | } |
| 1436 | 1438 | } |
| 1437 | 1439 | |
| 1438 | 1440 | // post the blender if we're not currently waiting for one to finish |
| 1439 | 1441 | auto modelBlender = DependencyManager::get<ModelBlender>(); |
| 1440 | − if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { | |
| 1442 | + if (modelBlender->shouldComputeBlendshapes() && getHFMModel().hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { | |
| 1441 | 1443 | _blendedBlendshapeCoefficients = _blendshapeCoefficients; |
| 1442 | 1444 | modelBlender->noteRequiresBlend(getThisPointer()); |
| 1443 | 1445 | } |
| @@ -1445,8 +1447,7 @@ void Model::updateClusterMatrices() { · 1 unmodified line | ||
| 1445 | 1447 | |
| 1446 | 1448 | void Model::deleteGeometry() { |
| 1447 | 1449 | _deleteGeometryCounter++; |
| 1448 | − _blendshapeOffsets.clear(); | |
| 1449 | − _blendshapeOffsetsInitialized = false; | |
| 1450 | + _shapeStates.clear(); | |
| 1450 | 1451 | _meshStates.clear(); |
| 1451 | 1452 | _rig.destroyAnimGraph(); |
| 1452 | 1453 | _blendedBlendshapeCoefficients.clear(); |
| @@ -1480,20 +1481,12 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const { · 27 unmodified lines | ||
| 1480 | 1481 | |
| 1481 | 1482 | void Model::createRenderItemSet() { |
| 1482 | 1483 | assert(isLoaded()); |
| 1483 | − const auto& meshes = _renderGeometry->getMeshes(); | |
| 1484 | − | |
| 1485 | − // all of our mesh vectors must match in size | |
| 1486 | − if (meshes.size() != _meshStates.size()) { | |
| 1487 | − qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! " << meshes.size() << _meshStates.size() << " We will not segregate mesh groups yet."; | |
| 1488 | − return; | |
| 1489 | − } | |
| 1490 | 1484 | |
| 1491 | 1485 | // We should not have any existing renderItems if we enter this section of code |
| 1492 | 1486 | Q_ASSERT(_modelMeshRenderItems.isEmpty()); |
| 1493 | 1487 | |
| 1494 | 1488 | _modelMeshRenderItems.clear(); |
| 1495 | 1489 | _modelMeshMaterialNames.clear(); |
| 1496 | − _modelMeshRenderItemShapes.clear(); | |
| 1497 | 1490 | |
| 1498 | 1491 | Transform transform; |
| 1499 | 1492 | transform.setTranslation(_translation); |
| @@ -1504,31 +1497,19 @@ void Model::createRenderItemSet() { · 4 unmodified lines | ||
| 1504 | 1497 | offset.postTranslate(_offset); |
| 1505 | 1498 | |
| 1506 | 1499 | // Run through all of the meshes, and place them into their segregated, but unsorted buckets |
| 1507 | − int shapeID = 0; | |
| 1508 | − uint32_t numMeshes = (uint32_t)meshes.size(); | |
| 1509 | − auto& hfmModel = getHFMModel(); | |
| 1510 | − for (uint32_t i = 0; i < numMeshes; i++) { | |
| 1511 | − const auto& mesh = meshes.at(i); | |
| 1512 | − if (!mesh) { | |
| 1513 | − continue; | |
| 1514 | − } | |
| 1500 | + const auto& shapes = _renderGeometry->getHFMModel().shapes; | |
| 1501 | + for (uint32_t shapeID = 0; shapeID < shapes.size(); shapeID++) { | |
| 1502 | + const auto& shape = shapes[shapeID]; | |
| 1515 | 1503 | |
| 1516 | − // Create the render payloads | |
| 1517 | − int numParts = (int)mesh->getNumParts(); | |
| 1518 | − for (int partIndex = 0; partIndex < numParts; partIndex++) { | |
| 1519 | − initializeBlendshapes(hfmModel.meshes[i], i); | |
| 1520 | − _modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset); | |
| 1521 | − auto material = getGeometry()->getShapeMaterial(shapeID); | |
| 1522 | − _modelMeshMaterialNames.push_back(material ? material->getName() : ""); | |
| 1523 | − _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i }); | |
| 1524 | − shapeID++; | |
| 1525 | − } | |
| 1504 | + _modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), shape.mesh, shape.meshPart, shapeID, transform, offset, _created); | |
| 1505 | + | |
| 1506 | + auto material = getNetworkModel()->getShapeMaterial(shapeID); | |
| 1507 | + _modelMeshMaterialNames.push_back(material ? material->getName() : ""); | |
| 1526 | 1508 | } |
| 1527 | − _blendshapeOffsetsInitialized = true; | |
| 1528 | 1509 | } |
| 1529 | 1510 | |
| 1530 | 1511 | bool Model::isRenderable() const { |
| 1531 | − return !_meshStates.empty() || (isLoaded() && _renderGeometry->getMeshes().empty()); | |
| 1512 | + return (!_shapeStates.empty()) || (isLoaded() && _renderGeometry->getMeshes().empty()); | |
| 1532 | 1513 | } |
| 1533 | 1514 | |
| 1534 | 1515 | std::set<unsigned int> Model::getMeshIDsFromMaterialID(QString parentMaterialName) { |
| @@ -1583,6 +1564,13 @@ void Model::applyMaterialMapping() { · 48 unmodified lines | ||
| 1583 | 1564 | auto renderItemsKey = _renderItemKeyGlobalFlags; |
| 1584 | 1565 | PrimitiveMode primitiveMode = getPrimitiveMode(); |
| 1585 | 1566 | bool useDualQuaternionSkinning = _useDualQuaternionSkinning; |
| 1567 | + auto modelMeshRenderItemIDs = _modelMeshRenderItemIDs; | |
| 1568 | + auto shapeStates = _shapeStates; | |
| 1569 | + std::unordered_map<int, bool> shouldInvalidatePayloadShapeKeyMap; | |
| 1570 | + | |
| 1571 | + for (auto& shape : _shapeStates) { | |
| 1572 | + shouldInvalidatePayloadShapeKeyMap[shape._meshIndex] = shouldInvalidatePayloadShapeKey(shape._meshIndex); | |
| 1573 | + } | |
| 1586 | 1574 | |
| 1587 | 1575 | auto& materialMapping = getMaterialMapping(); |
| 1588 | 1576 | for (auto& mapping : materialMapping) { |
| @@ -1602,8 +1590,11 @@ void Model::applyMaterialMapping() { · 13 unmodified lines | ||
| 1602 | 1590 | priorityMapPerResource[shapeID] = ++_priorityMap[shapeID]; |
| 1603 | 1591 | } |
| 1604 | 1592 | |
| 1605 | − auto materialLoaded = [this, networkMaterialResource, shapeIDs, priorityMapPerResource, renderItemsKey, primitiveMode, useDualQuaternionSkinning]() { | |
| 1606 | − if (networkMaterialResource->isFailed() || networkMaterialResource->parsedMaterials.names.size() == 0) { | |
| 1593 | + std::weak_ptr<Model> weakSelf = shared_from_this(); | |
| 1594 | + auto materialLoaded = [networkMaterialResource, shapeIDs, priorityMapPerResource, renderItemsKey, primitiveMode, useDualQuaternionSkinning, | |
| 1595 | + modelMeshRenderItemIDs, shapeStates, shouldInvalidatePayloadShapeKeyMap, weakSelf]() { | |
| 1596 | + std::shared_ptr<Model> self = weakSelf.lock(); | |
| 1597 | + if (!self || networkMaterialResource->isFailed() || networkMaterialResource->parsedMaterials.names.size() == 0) { | |
| 1607 | 1598 | return; |
| 1608 | 1599 | } |
| 1609 | 1600 | render::Transaction transaction; |
| @@ -1625,12 +1616,15 @@ void Model::applyMaterialMapping() { · 15 unmodified lines | ||
| 1625 | 1616 | } |
| 1626 | 1617 | } |
| 1627 | 1618 | for (auto shapeID : shapeIDs) { |
| 1628 | − if (shapeID < _modelMeshRenderItemIDs.size()) { | |
| 1629 | − auto itemID = _modelMeshRenderItemIDs[shapeID]; | |
| 1630 | − auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex; | |
| 1631 | − bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex); | |
| 1619 | + if (shapeID < modelMeshRenderItemIDs.size()) { | |
| 1620 | + auto itemID = modelMeshRenderItemIDs[shapeID]; | |
| 1621 | + auto meshIndex = shapeStates[shapeID]._meshIndex; | |
| 1622 | + bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKeyMap.at(meshIndex); | |
| 1632 | 1623 | graphics::MaterialLayer material = graphics::MaterialLayer(networkMaterial, priorityMapPerResource.at(shapeID)); |
| 1633 | − _materialMapping[shapeID].push_back(material); | |
| 1624 | + { | |
| 1625 | + std::unique_lock<std::mutex> lock(self->_materialMappingMutex); | |
| 1626 | + self->_materialMapping[shapeID].push_back(material); | |
| 1627 | + } | |
| 1634 | 1628 | transaction.updateItem<ModelMeshPartPayload>(itemID, [material, renderItemsKey, |
| 1635 | 1629 | invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) { |
| 1636 | 1630 | data.addMaterial(material); |
| @@ -1662,7 +1656,7 @@ void Model::addMaterial(graphics::MaterialLayer material, const std::string& par · 25 unmodified lines | ||
| 1662 | 1656 | for (auto shapeID : shapeIDs) { |
| 1663 | 1657 | if (shapeID < _modelMeshRenderItemIDs.size()) { |
| 1664 | 1658 | auto itemID = _modelMeshRenderItemIDs[shapeID]; |
| 1665 | − auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex; | |
| 1659 | + auto meshIndex = _shapeStates[shapeID]._meshIndex; | |
| 1666 | 1660 | bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex); |
| 1667 | 1661 | transaction.updateItem<ModelMeshPartPayload>(itemID, [material, renderItemsKey, |
| 1668 | 1662 | invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) { |
| @@ -1684,7 +1678,7 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string · 15 unmodified lines | ||
| 1684 | 1678 | auto itemID = _modelMeshRenderItemIDs[shapeID]; |
| 1685 | 1679 | auto renderItemsKey = _renderItemKeyGlobalFlags; |
| 1686 | 1680 | PrimitiveMode primitiveMode = getPrimitiveMode(); |
| 1687 | − auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex; | |
| 1681 | + auto meshIndex = _shapeStates[shapeID]._meshIndex; | |
| 1688 | 1682 | bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex); |
| 1689 | 1683 | bool useDualQuaternionSkinning = _useDualQuaternionSkinning; |
| 1690 | 1684 | transaction.updateItem<ModelMeshPartPayload>(itemID, [material, renderItemsKey, |
| @@ -1699,21 +1693,17 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string · 8 unmodified lines | ||
| 1699 | 1693 | AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); |
| 1700 | 1694 | } |
| 1701 | 1695 | |
| 1702 | −class CollisionRenderGeometry : public Geometry { | |
| 1696 | +class CollisionRenderGeometry : public NetworkModel { | |
| 1703 | 1697 | public: |
| 1704 | 1698 | CollisionRenderGeometry(graphics::MeshPointer mesh) { |
| 1705 | 1699 | _hfmModel = std::make_shared<HFMModel>(); |
| 1706 | 1700 | std::shared_ptr<GeometryMeshes> meshes = std::make_shared<GeometryMeshes>(); |
| 1707 | 1701 | meshes->push_back(mesh); |
| 1708 | 1702 | _meshes = meshes; |
| 1709 | − _meshParts = std::shared_ptr<const GeometryMeshParts>(); | |
| 1710 | 1703 | } |
| 1711 | 1704 | }; |
| 1712 | 1705 | |
| 1713 | − | |
| 1714 | −using packBlendshapeOffsetTo = void(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked); | |
| 1715 | − | |
| 1716 | −void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) { | |
| 1706 | +static void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) { | |
| 1717 | 1707 | float len = glm::compMax(glm::abs(unpacked.positionOffset)); |
| 1718 | 1708 | glm::vec3 normalizedPos(unpacked.positionOffset); |
| 1719 | 1709 | if (len > 0.0f) { |
| @@ -1730,125 +1720,143 @@ void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& pac · 10 unmodified lines | ||
| 1730 | 1720 | ); |
| 1731 | 1721 | } |
| 1732 | 1722 | |
| 1723 | +static void packBlendshapeOffsets_ref(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) { | |
| 1724 | + for (int i = 0; i < size; ++i) { | |
| 1725 | + packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked)); | |
| 1726 | + ++unpacked; | |
| 1727 | + ++packed; | |
| 1728 | + } | |
| 1729 | +} | |
| 1730 | + | |
| 1731 | +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) | |
| 1732 | +// | |
| 1733 | +// Runtime CPU dispatch | |
| 1734 | +// | |
| 1735 | +#include <CPUDetect.h> | |
| 1736 | + | |
| 1737 | +void packBlendshapeOffsets_AVX2(float (*unpacked)[9], uint32_t (*packed)[4], int size); | |
| 1738 | + | |
| 1739 | +static void packBlendshapeOffsets(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) { | |
| 1740 | + static bool _cpuSupportsAVX2 = cpuSupportsAVX2(); | |
| 1741 | + if (_cpuSupportsAVX2) { | |
| 1742 | + static_assert(sizeof(BlendshapeOffsetUnpacked) == 9 * sizeof(float), "struct BlendshapeOffsetUnpacked size doesn't match."); | |
| 1743 | + static_assert(sizeof(BlendshapeOffsetPacked) == 4 * sizeof(uint32_t), "struct BlendshapeOffsetPacked size doesn't match."); | |
| 1744 | + packBlendshapeOffsets_AVX2((float(*)[9])unpacked, (uint32_t(*)[4])packed, size); | |
| 1745 | + } else { | |
| 1746 | + packBlendshapeOffsets_ref(unpacked, packed, size); | |
| 1747 | + } | |
| 1748 | +} | |
| 1749 | + | |
| 1750 | +#else // portable reference code | |
| 1751 | +static auto& packBlendshapeOffsets = packBlendshapeOffsets_ref; | |
| 1752 | +#endif | |
| 1753 | + | |
| 1733 | 1754 | class Blender : public QRunnable { |
| 1734 | 1755 | public: |
| 1735 | 1756 | |
| 1736 | − Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, const QVector<float>& blendshapeCoefficients); | |
| 1757 | + Blender(ModelPointer model, HFMModel::ConstPointer hfmModel, int blendNumber, const QVector<float>& blendshapeCoefficients); | |
| 1737 | 1758 | |
| 1738 | 1759 | virtual void run() override; |
| 1739 | 1760 | |
| 1740 | 1761 | private: |
| 1741 | − | |
| 1742 | 1762 | ModelPointer _model; |
| 1763 | + HFMModel::ConstPointer _hfmModel; | |
| 1743 | 1764 | int _blendNumber; |
| 1744 | − Geometry::WeakPointer _geometry; | |
| 1745 | 1765 | QVector<float> _blendshapeCoefficients; |
| 1746 | 1766 | }; |
| 1747 | 1767 | |
| 1748 | −Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, const QVector<float>& blendshapeCoefficients) : | |
| 1768 | +Blender::Blender(ModelPointer model, HFMModel::ConstPointer hfmModel, int blendNumber, const QVector<float>& blendshapeCoefficients) : | |
| 1749 | 1769 | _model(model), |
| 1770 | + _hfmModel(hfmModel), | |
| 1750 | 1771 | _blendNumber(blendNumber), |
| 1751 | − _geometry(geometry), | |
| 1752 | 1772 | _blendshapeCoefficients(blendshapeCoefficients) { |
| 1753 | 1773 | } |
| 1754 | 1774 | |
| 1755 | 1775 | void Blender::run() { |
| 1756 | − QVector<BlendshapeOffset> blendshapeOffsets; | |
| 1757 | − QVector<int> blendedMeshSizes; | |
| 1758 | − if (_model && _model->isLoaded()) { | |
| 1759 | − DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); | |
| 1760 | − int offset = 0; | |
| 1761 | − auto meshes = _model->getHFMModel().meshes; | |
| 1762 | − int meshIndex = 0; | |
| 1763 | − foreach(const HFMMesh& mesh, meshes) { | |
| 1764 | − auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++); | |
| 1765 | − if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) { | |
| 1766 | − // Not blendshaped or not initialized | |
| 1767 | − blendedMeshSizes.push_back(0); | |
| 1768 | − continue; | |
| 1769 | − } | |
| 1770 | − | |
| 1771 | − if (mesh.vertices.size() != modelMeshBlendshapeOffsets->second.size()) { | |
| 1772 | − // Mesh sizes don't match. Something has gone wrong | |
| 1773 | − blendedMeshSizes.push_back(0); | |
| 1774 | − continue; | |
| 1775 | − } | |
| 1776 | − | |
| 1777 | − blendshapeOffsets += modelMeshBlendshapeOffsets->second; | |
| 1778 | − BlendshapeOffset* meshBlendshapeOffsets = blendshapeOffsets.data() + offset; | |
| 1779 | − int numVertices = modelMeshBlendshapeOffsets->second.size(); | |
| 1780 | − blendedMeshSizes.push_back(numVertices); | |
| 1781 | − offset += numVertices; | |
| 1782 | − std::vector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets(modelMeshBlendshapeOffsets->second.size()); | |
| 1783 | − | |
| 1784 | − const float NORMAL_COEFFICIENT_SCALE = 0.01f; | |
| 1785 | − for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) { | |
| 1786 | − float vertexCoefficient = _blendshapeCoefficients.at(i); | |
| 1787 | − const float EPSILON = 0.0001f; | |
| 1788 | − if (vertexCoefficient < EPSILON) { | |
| 1789 | − continue; | |
| 1790 | − } | |
| 1776 | + DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); | |
| 1777 | + int numBlendshapeOffsets = 0; // number of offsets required for all meshes. | |
| 1778 | + int maxBlendshapeOffsets = 0; // number of offsets in the largest mesh. | |
| 1779 | + int numMeshes = 0; // number of meshes in this model. | |
| 1780 | + for (auto meshIter = _hfmModel->meshes.cbegin(); meshIter != _hfmModel->meshes.cend(); ++meshIter) { | |
| 1781 | + numMeshes++; | |
| 1782 | + if (meshIter->blendshapes.isEmpty()) { | |
| 1783 | + continue; | |
| 1784 | + } | |
| 1785 | + int numVertsInMesh = meshIter->vertices.size(); | |
| 1786 | + numBlendshapeOffsets += numVertsInMesh; | |
| 1787 | + maxBlendshapeOffsets = std::max(maxBlendshapeOffsets, numVertsInMesh); | |
| 1788 | + } | |
| 1791 | 1789 | |
| 1792 | − float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; | |
| 1793 | − const HFMBlendshape& blendshape = mesh.blendshapes.at(i); | |
| 1790 | + // allocate the required sizes | |
| 1791 | + QVector<int> blendedMeshSizes; | |
| 1792 | + blendedMeshSizes.reserve(numMeshes); | |
| 1794 | 1793 | |
| 1795 | − tbb::parallel_for(tbb::blocked_range<int>(0, blendshape.indices.size()), [&](const tbb::blocked_range<int>& range) { | |
| 1796 | − for (auto j = range.begin(); j < range.end(); j++) { | |
| 1797 | − int index = blendshape.indices.at(j); | |
| 1794 | + QVector<BlendshapeOffset> packedBlendshapeOffsets; | |
| 1795 | + packedBlendshapeOffsets.resize(numBlendshapeOffsets); | |
| 1798 | 1796 | |
| 1799 | − auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[index]; | |
| 1800 | − currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient; | |
| 1797 | + QVector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets; | |
| 1798 | + unpackedBlendshapeOffsets.resize(maxBlendshapeOffsets); // reuse for all meshes | |
| 1801 | 1799 | |
| 1802 | − currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient; | |
| 1803 | − if (j < blendshape.tangents.size()) { | |
| 1804 | − currentBlendshapeOffset.tangentOffset += blendshape.tangents.at(j) * normalCoefficient; | |
| 1805 | − } | |
| 1806 | − } | |
| 1807 | − }); | |
| 1800 | + int offset = 0; | |
| 1801 | + for (auto meshIter = _hfmModel->meshes.cbegin(); meshIter != _hfmModel->meshes.cend(); ++meshIter) { | |
| 1802 | + if (meshIter->blendshapes.isEmpty()) { | |
| 1803 | + blendedMeshSizes.push_back(0); | |
| 1804 | + continue; | |
| 1805 | + } | |
| 1806 | + int numVertsInMesh = meshIter->vertices.size(); | |
| 1807 | + blendedMeshSizes.push_back(numVertsInMesh); | |
| 1808 | + | |
| 1809 | + // initialize offsets to zero | |
| 1810 | + memset(unpackedBlendshapeOffsets.data(), 0, numVertsInMesh * sizeof(BlendshapeOffsetUnpacked)); | |
| 1811 | + | |
| 1812 | + // for each blendshape in this mesh, accumulate the offsets into unpackedBlendshapeOffsets. | |
| 1813 | + const float NORMAL_COEFFICIENT_SCALE = 0.01f; | |
| 1814 | + for (int i = 0, n = qMin(_blendshapeCoefficients.size(), meshIter->blendshapes.size()); i < n; i++) { | |
| 1815 | + float vertexCoefficient = _blendshapeCoefficients.at(i); | |
| 1816 | + const float EPSILON = 0.0001f; | |
| 1817 | + if (vertexCoefficient < EPSILON) { | |
| 1818 | + continue; | |
| 1808 | 1819 | } |
| 1809 | 1820 | |
| 1810 | − // Blendshape offsets are generrated, now let's pack it on its way to gpu | |
| 1811 | − tbb::parallel_for(tbb::blocked_range<int>(0, (int) unpackedBlendshapeOffsets.size()), [&](const tbb::blocked_range<int>& range) { | |
| 1812 | − auto unpacked = unpackedBlendshapeOffsets.data() + range.begin(); | |
| 1813 | − auto packed = meshBlendshapeOffsets + range.begin(); | |
| 1814 | − for (auto j = range.begin(); j < range.end(); j++) { | |
| 1815 | − packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked)); | |
| 1821 | + float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; | |
| 1822 | + const HFMBlendshape& blendshape = meshIter->blendshapes.at(i); | |
| 1823 | + for (int j = 0; j < blendshape.indices.size(); ++j) { | |
| 1824 | + int index = blendshape.indices.at(j); | |
| 1816 | 1825 | |
| 1817 | − unpacked++; | |
| 1818 | − packed++; | |
| 1826 | + auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[index]; | |
| 1827 | + currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient; | |
| 1828 | + currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient; | |
| 1829 | + if (j < blendshape.tangents.size()) { | |
| 1830 | + currentBlendshapeOffset.tangentOffset += blendshape.tangents.at(j) * normalCoefficient; | |
| 1819 | 1831 | } |
| 1820 | − }); | |
| 1832 | + } | |
| 1821 | 1833 | } |
| 1834 | + | |
| 1835 | + // convert unpackedBlendshapeOffsets into packedBlendshapeOffsets for the gpu. | |
| 1836 | + auto unpacked = unpackedBlendshapeOffsets.data(); | |
| 1837 | + auto packed = packedBlendshapeOffsets.data() + offset; | |
| 1838 | + packBlendshapeOffsets(unpacked, packed, numVertsInMesh); | |
| 1839 | + | |
| 1840 | + offset += numVertsInMesh; | |
| 1822 | 1841 | } |
| 1842 | + Q_ASSERT(offset == numBlendshapeOffsets); | |
| 1843 | + | |
| 1823 | 1844 | // post the result to the ModelBlender, which will dispatch to the model if still alive |
| 1824 | 1845 | QMetaObject::invokeMethod(DependencyManager::get<ModelBlender>().data(), "setBlendedVertices", |
| 1825 | − Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector<BlendshapeOffset>, blendshapeOffsets), Q_ARG(QVector<int>, blendedMeshSizes)); | |
| 1846 | + Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), | |
| 1847 | + Q_ARG(QVector<BlendshapeOffset>, packedBlendshapeOffsets), | |
| 1848 | + Q_ARG(QVector<int>, blendedMeshSizes)); | |
| 1826 | 1849 | } |
| 1827 | 1850 | |
| 1828 | 1851 | bool Model::maybeStartBlender() { |
| 1829 | 1852 | if (isLoaded()) { |
| 1830 | − QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _renderGeometry, _blendshapeCoefficients)); | |
| 1853 | + QThreadPool::globalInstance()->start(new Blender(getThisPointer(), getNetworkModel()->getConstHFMModelPointer(), | |
| 1854 | + ++_blendNumber, _blendshapeCoefficients)); | |
| 1831 | 1855 | return true; |
| 1832 | 1856 | } |
| 1833 | 1857 | return false; |
| 1834 | 1858 | } |
| 1835 | 1859 | |
| 1836 | −void Model::initializeBlendshapes(const HFMMesh& mesh, int index) { | |
| 1837 | − if (mesh.blendshapes.empty()) { | |
| 1838 | − // mesh doesn't have blendshape, did we allocate one though ? | |
| 1839 | − if (_blendshapeOffsets.find(index) != _blendshapeOffsets.end()) { | |
| 1840 | − qWarning() << "Mesh does not have Blendshape yet the blendshapeOffsets are allocated ?"; | |
| 1841 | − } | |
| 1842 | − return; | |
| 1843 | − } | |
| 1844 | − // Mesh has blendshape, let s allocate the local buffer if not done yet | |
| 1845 | − if (_blendshapeOffsets.find(index) == _blendshapeOffsets.end()) { | |
| 1846 | − QVector<BlendshapeOffset> blendshapeOffset; | |
| 1847 | − blendshapeOffset.fill(BlendshapeOffset(), mesh.vertices.size()); | |
| 1848 | − _blendshapeOffsets[index] = blendshapeOffset; | |
| 1849 | − } | |
| 1850 | −} | |
| 1851 | − | |
| 1852 | 1860 | ModelBlender::ModelBlender() : |
| 1853 | 1861 | _pendingBlenders(0) { |
| 1854 | 1862 | } |