} Extents modelMeshExtents = getUnscaledMeshExtents(); // size is our "target size in world space" // we need to set our model scale so that the extents of the mesh, fit in a box that size... glm::vec3 meshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum; glm::vec3 rescaleDimensions = _scaleToFitDimensions / meshDimensions; setScaleInternal(rescaleDimensions); _scaledToFit = true;} void Model::setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint) { glm::vec3 clampedRegistrationPoint = glm::clamp(registrationPoint, 0.0f, 1.0f); if (_snapModelToRegistrationPoint != snapModelToRegistrationPoint || _registrationPoint != clampedRegistrationPoint) { _snapModelToRegistrationPoint = snapModelToRegistrationPoint; _registrationPoint = clampedRegistrationPoint; _snappedToRegistrationPoint = false; // force re-centering }} void Model::snapToRegistrationPoint() { Extents modelMeshExtents = getUnscaledMeshExtents(); glm::vec3 dimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum); glm::vec3 offset = -modelMeshExtents.minimum - (dimensions * _registrationPoint); _offset = offset; _snappedToRegistrationPoint = true;} void Model::simulate(float deltaTime, bool fullUpdate) { PROFILE_RANGE(__FUNCTION__); fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); if (isActive() && fullUpdate) { // NOTE: This is overly aggressive and we are invalidating the MeshBoxes when in fact they may not be invalid // they really only become invalid if something about the transform to world space has changed. This is // not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking // because ray picking needs valid boxes to work _calculatedMeshBoxesValid = false; _calculatedMeshTrianglesValid = false; onInvalidate(); // check for scale to fit if (_scaleToFit && !_scaledToFit) { scaleToFit(); } if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) { snapToRegistrationPoint(); } simulateInternal(deltaTime); }} //virtualvoid Model::updateRig(float deltaTime, glm::mat4 parentTransform) { _needsUpdateClusterMatrices = true; _rig->updateAnimations(deltaTime, parentTransform);} void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); updateRig(deltaTime, parentTransform);} // virtualvoid Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) { PerformanceTimer perfTimer("Model::updateClusterMatrices"); if (!_needsUpdateClusterMatrices || !isLoaded()) { return; } _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); 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; glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation); for (int i = 0; i < _meshStates.size(); i++) { 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); state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; } } // Once computed the cluster matrices, update the buffer(s) if (mesh.clusters.size() > 1) { if (!state.clusterBuffer) { state.clusterBuffer = std::make_shared<gpu::Buffer>(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()); } if (!_cauterizeBoneSet.empty() && (state.cauterizedClusterMatrices.size() > 1)) { if (!state.cauterizedClusterBuffer) { state.cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(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()); } } } } // post the blender if we're not currently waiting for one to finish if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; DependencyManager::get<ModelBlender>()->noteRequiresBlend(getThisPointer()); }} void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { const FBXGeometry& geometry = getFBXGeometry(); const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage; glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform);} bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { const FBXGeometry& geometry = getFBXGeometry(); const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage; return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage);} float Model::getLimbLength(int jointIndex) const { const FBXGeometry& geometry = getFBXGeometry(); const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage; return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints);} bool Model::maybeStartBlender() { if (isLoaded()) { const FBXGeometry& fbxGeometry = getFBXGeometry(); if (fbxGeometry.hasBlendedMeshes()) { QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _geometry, fbxGeometry.meshes, _blendshapeCoefficients)); return true; } } return false;} void Model::setBlendedVertices(int blendNumber, const std::weak_ptr<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) { auto geometryRef = geometry.lock(); if (!geometryRef || _geometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { return; } _appliedBlendNumber = blendNumber; const FBXGeometry& fbxGeometry = getFBXGeometry(); int index = 0; for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.blendshapes.isEmpty()) { continue; } gpu::BufferPointer& buffer = _blendedVertexBuffers[i]; buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) vertices.constData() + index*sizeof(glm::vec3)); buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) normals.constData() + index*sizeof(glm::vec3)); index += mesh.vertices.size(); }} void Model::deleteGeometry() { _deleteGeometryCounter++; _blendedVertexBuffers.clear(); _meshStates.clear(); _rig->destroyAnimGraph(); _blendedBlendshapeCoefficients.clear();} AABox Model::getRenderableMeshBound() const { if (!isLoaded()) { return AABox(); } else { // Build a bound using the last known bound from all the renderItems. AABox totalBound; for (auto& renderItem : _modelMeshRenderItemsSet) { totalBound += renderItem->getBound(); } return totalBound; }} void Model::segregateMeshGroups() { NetworkGeometry::Pointer networkGeometry; bool showingCollisionHull = false; if (_showCollisionHull && _collisionGeometry) { if (isCollisionLoaded()) { networkGeometry = _collisionGeometry; showingCollisionHull = true; } else { return; } } else { assert(isLoaded()); networkGeometry = _geometry; } const FBXGeometry& geometry = networkGeometry->getGeometry()->getGeometry(); const auto& networkMeshes = networkGeometry->getGeometry()->getMeshes(); // all of our mesh vectors must match in size auto geoMeshesSize = geometry.meshes.size(); if ((int)networkMeshes.size() != geoMeshesSize || // geometry.meshes.size() != _meshStates.size()) { geoMeshesSize > _meshStates.size()) { qDebug() << "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(_modelMeshRenderItems.isEmpty()); Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); Q_ASSERT(_collisionRenderItems.isEmpty()); Q_ASSERT(_collisionRenderItemsSet.isEmpty()); _modelMeshRenderItemsSet.clear(); _collisionRenderItemsSet.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; for (int i = 0; i < (int)networkMeshes.size(); i++) { const FBXMesh& mesh = geometry.meshes.at(i); const auto& networkMesh = networkMeshes.at(i); // Create the render payloads int totalParts = mesh.parts.size(); for (int partIndex = 0; partIndex < totalParts; partIndex++) { if (showingCollisionHull) { if (_collisionHullMaterials.empty()) { initCollisionHullMaterials(); } _collisionRenderItemsSet << std::make_shared<MeshPartPayload>(networkMesh, partIndex, _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS], transform, offset); } else { _modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset); } shapeID++; } } _meshGroupsKnown = true;} bool Model::initWhenReady(render::ScenePointer scene) { if (isActive() && isRenderable() && !_meshGroupsKnown && isLoaded()) { segregateMeshGroups(); render::PendingChanges pendingChanges; Transform transform; transform.setTranslation(_translation); transform.setRotation(_rotation); Transform offset; offset.setScale(_scale); offset.postTranslate(_offset); foreach (auto renderItem, _modelMeshRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem); _modelMeshRenderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); } foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem); _collisionRenderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); } scene->enqueuePendingChanges(pendingChanges); updateRenderItems(); _readyWhenAdded = true; return true; } return false;} ModelBlender::ModelBlender() : _pendingBlenders(0) {} ModelBlender::~ModelBlender() {} void ModelBlender::noteRequiresBlend(ModelPointer model) { if (_pendingBlenders < QThread::idealThreadCount()) { if (model->maybeStartBlender()) { _pendingBlenders++; } return; } { Lock lock(_mutex); _modelsRequiringBlends.insert(model); }} void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const std::weak_ptr<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) { if (model) { model->setBlendedVertices(blendNumber, geometry, vertices, normals); } _pendingBlenders--; { Lock lock(_mutex); for (auto i = _modelsRequiringBlends.begin(); i != _modelsRequiringBlends.end();) { auto weakPtr = *i; _modelsRequiringBlends.erase(i++); // remove front of the set ModelPointer nextModel = weakPtr.lock(); if (nextModel && nextModel->maybeStartBlender()) { _pendingBlenders++; return; } } }} Lines 1001–1348 of 1348. Previous lines
} Extents modelMeshExtents = getUnscaledMeshExtents(); // size is our "target size in world space" // we need to set our model scale so that the extents of the mesh, fit in a box that size... glm::vec3 meshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum; glm::vec3 rescaleDimensions = _scaleToFitDimensions / meshDimensions; setScaleInternal(rescaleDimensions); _scaledToFit = true; } void Model::setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint) { glm::vec3 clampedRegistrationPoint = glm::clamp(registrationPoint, 0.0f, 1.0f); if (_snapModelToRegistrationPoint != snapModelToRegistrationPoint || _registrationPoint != clampedRegistrationPoint) { _snapModelToRegistrationPoint = snapModelToRegistrationPoint; _registrationPoint = clampedRegistrationPoint; _snappedToRegistrationPoint = false; // force re-centering } } void Model::snapToRegistrationPoint() { Extents modelMeshExtents = getUnscaledMeshExtents(); glm::vec3 dimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum); glm::vec3 offset = -modelMeshExtents.minimum - (dimensions * _registrationPoint); _offset = offset; _snappedToRegistrationPoint = true; } void Model::simulate(float deltaTime, bool fullUpdate) { PROFILE_RANGE(__FUNCTION__); fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); if (isActive() && fullUpdate) { // NOTE: This is overly aggressive and we are invalidating the MeshBoxes when in fact they may not be invalid // they really only become invalid if something about the transform to world space has changed. This is // not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking // because ray picking needs valid boxes to work _calculatedMeshBoxesValid = false; _calculatedMeshTrianglesValid = false; onInvalidate(); // check for scale to fit if (_scaleToFit && !_scaledToFit) { scaleToFit(); } if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) { snapToRegistrationPoint(); } simulateInternal(deltaTime); } } //virtual void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { _needsUpdateClusterMatrices = true; _rig->updateAnimations(deltaTime, parentTransform); } void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); updateRig(deltaTime, parentTransform); } // virtual void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) { PerformanceTimer perfTimer("Model::updateClusterMatrices"); if (!_needsUpdateClusterMatrices || !isLoaded()) { return; } _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); 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; glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation); for (int i = 0; i < _meshStates.size(); i++) { 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); state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; } } // Once computed the cluster matrices, update the buffer(s) if (mesh.clusters.size() > 1) { if (!state.clusterBuffer) { state.clusterBuffer = std::make_shared<gpu::Buffer>(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()); } if (!_cauterizeBoneSet.empty() && (state.cauterizedClusterMatrices.size() > 1)) { if (!state.cauterizedClusterBuffer) { state.cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(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()); } } } } // post the blender if we're not currently waiting for one to finish if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; DependencyManager::get<ModelBlender>()->noteRequiresBlend(getThisPointer()); } } void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { const FBXGeometry& geometry = getFBXGeometry(); const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage; glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); } bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { const FBXGeometry& geometry = getFBXGeometry(); const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage; return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage); } float Model::getLimbLength(int jointIndex) const { const FBXGeometry& geometry = getFBXGeometry(); const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage; return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); } bool Model::maybeStartBlender() { if (isLoaded()) { const FBXGeometry& fbxGeometry = getFBXGeometry(); if (fbxGeometry.hasBlendedMeshes()) { QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _geometry, fbxGeometry.meshes, _blendshapeCoefficients)); return true; } } return false; } void Model::setBlendedVertices(int blendNumber, const std::weak_ptr<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) { auto geometryRef = geometry.lock(); if (!geometryRef || _geometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { return; } _appliedBlendNumber = blendNumber; const FBXGeometry& fbxGeometry = getFBXGeometry(); int index = 0; for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.blendshapes.isEmpty()) { continue; } gpu::BufferPointer& buffer = _blendedVertexBuffers[i]; buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) vertices.constData() + index*sizeof(glm::vec3)); buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) normals.constData() + index*sizeof(glm::vec3)); index += mesh.vertices.size(); } } void Model::deleteGeometry() { _deleteGeometryCounter++; _blendedVertexBuffers.clear(); _meshStates.clear(); _rig->destroyAnimGraph(); _blendedBlendshapeCoefficients.clear(); } AABox Model::getRenderableMeshBound() const { if (!isLoaded()) { return AABox(); } else { // Build a bound using the last known bound from all the renderItems. AABox totalBound; for (auto& renderItem : _modelMeshRenderItemsSet) { totalBound += renderItem->getBound(); } return totalBound; } } void Model::segregateMeshGroups() { NetworkGeometry::Pointer networkGeometry; bool showingCollisionHull = false; if (_showCollisionHull && _collisionGeometry) { if (isCollisionLoaded()) { networkGeometry = _collisionGeometry; showingCollisionHull = true; } else { return; } } else { assert(isLoaded()); networkGeometry = _geometry; } const FBXGeometry& geometry = networkGeometry->getGeometry()->getGeometry(); const auto& networkMeshes = networkGeometry->getGeometry()->getMeshes(); // all of our mesh vectors must match in size auto geoMeshesSize = geometry.meshes.size(); if ((int)networkMeshes.size() != geoMeshesSize || // geometry.meshes.size() != _meshStates.size()) { geoMeshesSize > _meshStates.size()) { qDebug() << "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(_modelMeshRenderItems.isEmpty()); Q_ASSERT(_modelMeshRenderItemsSet.isEmpty()); Q_ASSERT(_collisionRenderItems.isEmpty()); Q_ASSERT(_collisionRenderItemsSet.isEmpty()); _modelMeshRenderItemsSet.clear(); _collisionRenderItemsSet.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; for (int i = 0; i < (int)networkMeshes.size(); i++) { const FBXMesh& mesh = geometry.meshes.at(i); const auto& networkMesh = networkMeshes.at(i); // Create the render payloads int totalParts = mesh.parts.size(); for (int partIndex = 0; partIndex < totalParts; partIndex++) { if (showingCollisionHull) { if (_collisionHullMaterials.empty()) { initCollisionHullMaterials(); } _collisionRenderItemsSet << std::make_shared<MeshPartPayload>(networkMesh, partIndex, _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS], transform, offset); } else { _modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset); } shapeID++; } } _meshGroupsKnown = true; } bool Model::initWhenReady(render::ScenePointer scene) { if (isActive() && isRenderable() && !_meshGroupsKnown && isLoaded()) { segregateMeshGroups(); render::PendingChanges pendingChanges; Transform transform; transform.setTranslation(_translation); transform.setRotation(_rotation); Transform offset; offset.setScale(_scale); offset.postTranslate(_offset); foreach (auto renderItem, _modelMeshRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem); _modelMeshRenderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); } foreach (auto renderItem, _collisionRenderItemsSet) { auto item = scene->allocateID(); auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem); _collisionRenderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); } scene->enqueuePendingChanges(pendingChanges); updateRenderItems(); _readyWhenAdded = true; return true; } return false; } ModelBlender::ModelBlender() : _pendingBlenders(0) { } ModelBlender::~ModelBlender() { } void ModelBlender::noteRequiresBlend(ModelPointer model) { if (_pendingBlenders < QThread::idealThreadCount()) { if (model->maybeStartBlender()) { _pendingBlenders++; } return; } { Lock lock(_mutex); _modelsRequiringBlends.insert(model); } } void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const std::weak_ptr<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) { if (model) { model->setBlendedVertices(blendNumber, geometry, vertices, normals); } _pendingBlenders--; { Lock lock(_mutex); for (auto i = _modelsRequiringBlends.begin(); i != _modelsRequiringBlends.end();) { auto weakPtr = *i; _modelsRequiringBlends.erase(i++); // remove front of the set ModelPointer nextModel = weakPtr.lock(); if (nextModel && nextModel->maybeStartBlender()) { _pendingBlenders++; return; } } } }