diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 627c42ffa8..f644968ff8 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -582,7 +582,9 @@ void Avatar::simulateAttachments(float deltaTime) { _skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) { model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale); model->setRotation(jointRotation * attachment.rotation); - model->setScaleToFit(true, _scale * attachment.scale); + model->setScaleToFit(true, _scale * attachment.scale, true); // hack to force rescale + model->setSnapModelToCenter(false); // hack to force resnap + model->setSnapModelToCenter(true); model->simulate(deltaTime); } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index afe0311a29..65b927c2c0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1196,23 +1196,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo } scene->enqueuePendingChanges(pendingChanges); - Camera *camera = Application::getInstance()->getCamera(); - const glm::vec3 cameraPos = camera->getPosition(); - - - // HACK: comment this block which possibly change the near and break the rendering 5/6/2015 - // Only tweak the frustum near far if it's not shadow - /* if (renderMode != RenderArgs::SHADOW_RENDER_MODE) { - // Set near clip distance according to skeleton model dimensions if first person and there is no separate head model. - if (shouldRenderHead(cameraPos, renderMode) || !getHead()->getFaceModel().getURL().isEmpty()) { - renderFrustum->setNearClip(DEFAULT_NEAR_CLIP); - } else { - float clipDistance = _skeletonModel.getHeadClipDistance(); - clipDistance = glm::length(getEyePosition() - + camera->getOrientation() * glm::vec3(0.0f, 0.0f, -clipDistance) - cameraPos); - renderFrustum->setNearClip(clipDistance); - } - }*/ + const glm::vec3 cameraPos = Application::getInstance()->getCamera()->getPosition(); // Render head so long as the camera isn't inside it if (shouldRenderHead(renderArgs, cameraPos)) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 3cea5c9777..8d234cdef5 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -435,6 +435,7 @@ bool Model::updateGeometry() { QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); if (_geometry != geometry) { + // NOTE: it is theoretically impossible to reach here after passing through the applyNextGeometry() call above. // Which means we don't need to worry about calling deleteGeometry() below immediately after creating new geometry. @@ -811,71 +812,41 @@ void Model::renderSetup(RenderArgs* args) { } -class TransparentMeshPart { +class MeshPartPayload { public: - TransparentMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { } - typedef render::Payload Payload; + MeshPartPayload(bool transparent, Model* model, int meshIndex, int partIndex) : + transparent(transparent), model(model), url(model->getURL()), meshIndex(meshIndex), partIndex(partIndex) { } + typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - Model* model; + bool transparent; + Model* model; + QUrl url; int meshIndex; int partIndex; }; namespace render { - template <> const ItemKey payloadGetKey(const TransparentMeshPart::Pointer& payload) { + template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { if (!payload->model->isVisible()) { return ItemKey::Builder().withInvisible().build(); } - return ItemKey::Builder::transparentShape(); + return payload->transparent ? ItemKey::Builder::transparentShape() : ItemKey::Builder::opaqueShape(); } - template <> const Item::Bound payloadGetBound(const TransparentMeshPart::Pointer& payload) { + template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { if (payload) { return payload->model->getPartBounds(payload->meshIndex, payload->partIndex); } return render::Item::Bound(); } - template <> void payloadRender(const TransparentMeshPart::Pointer& payload, RenderArgs* args) { + template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) { if (args) { - return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, true); + return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, payload->transparent); } } -} -class OpaqueMeshPart { -public: - OpaqueMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { } - typedef render::Payload Payload; - typedef Payload::DataPointer Pointer; - - Model* model; - int meshIndex; - int partIndex; -}; - -namespace render { - template <> const ItemKey payloadGetKey(const OpaqueMeshPart::Pointer& payload) { - if (!payload->model->isVisible()) { - return ItemKey::Builder().withInvisible().build(); - } - return ItemKey::Builder::opaqueShape(); - } - - template <> const Item::Bound payloadGetBound(const OpaqueMeshPart::Pointer& payload) { - if (payload) { - Item::Bound result = payload->model->getPartBounds(payload->meshIndex, payload->partIndex); - //qDebug() << "payloadGetBound(OpaqueMeshPart) " << result; - return result; - } - return render::Item::Bound(); - } - template <> void payloadRender(const OpaqueMeshPart::Pointer& payload, RenderArgs* args) { - if (args) { - return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, false); - } - } - /* template <> const model::MaterialKey& shapeGetMaterialKey(const OpaqueMeshPart::Pointer& payload) { + /* template <> const model::MaterialKey& shapeGetMaterialKey(const MeshPartPayload::Pointer& payload) { return payload->model->getPartMaterial(payload->meshIndex, payload->partIndex); }*/ } @@ -902,16 +873,17 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan foreach (auto renderItem, _transparentRenderItems) { auto item = scene->allocateID(); - auto renderData = TransparentMeshPart::Pointer(renderItem); - auto renderPayload = render::PayloadPointer(new TransparentMeshPart::Payload(renderData)); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); pendingChanges.resetItem(item, renderPayload); _renderItems.insert(item, renderPayload); somethingAdded = true; } + foreach (auto renderItem, _opaqueRenderItems) { auto item = scene->allocateID(); - auto renderData = OpaqueMeshPart::Pointer(renderItem); - auto renderPayload = render::PayloadPointer(new OpaqueMeshPart::Payload(renderData)); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); pendingChanges.resetItem(item, renderPayload); _renderItems.insert(item, renderPayload); somethingAdded = true; @@ -1036,12 +1008,12 @@ Extents Model::calculateScaledOffsetExtents(const Extents& extents) const { Extents translatedExtents = { rotatedExtents.minimum + _translation, rotatedExtents.maximum + _translation }; + return translatedExtents; } /// Returns the world space equivalent of some box in model space. AABox Model::calculateScaledOffsetAABox(const AABox& box) const { - return AABox(calculateScaledOffsetExtents(Extents(box))); } @@ -1110,9 +1082,10 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo if (_url == url && _geometry && _geometry->getURL() == url) { return; } - + _readyWhenAdded = false; // reset out render items. _needsReload = true; + invalidCalculatedMeshBoxes(); _url = url; @@ -1301,7 +1274,7 @@ void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) { } } -void Model::setScaleToFit(bool scaleToFit, float largestDimension) { +void Model::setScaleToFit(bool scaleToFit, float largestDimension, bool forceRescale) { // NOTE: if the model is not active, then it means we don't actually know the true/natural dimensions of the // mesh, and so we can't do the needed calculations for scaling to fit to a single largest dimension. In this // case we will record that we do want to do this, but we will stick our desired single dimension into the @@ -1314,7 +1287,7 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) { return; } - if (_scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) { + if (forceRescale || _scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) { _scaleToFit = scaleToFit; // we only need to do this work if we're "turning on" scale to fit. @@ -1324,7 +1297,7 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) { float maxScale = largestDimension / maxDimension; glm::vec3 modelMeshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum; glm::vec3 dimensions = modelMeshDimensions * maxScale; - + _scaleToFitDimensions = dimensions; _scaledToFit = false; // force rescaling } @@ -1822,7 +1795,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran glm::mat4 scale = glm::scale(partBounds.getDimensions()); glm::mat4 modelToWorldMatrix = translation * scale; batch.setModelTransform(modelToWorldMatrix); - //qDebug() << "partBounds:" << partBounds; DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); } #endif //def DEBUG_BOUNDING_PARTS @@ -1912,16 +1884,18 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // guard against partially loaded meshes if (partIndex >= networkMesh.parts.size() || partIndex >= mesh.parts.size()) { - return; + return; } const NetworkMeshPart& networkPart = networkMesh.parts.at(partIndex); const FBXMeshPart& part = mesh.parts.at(partIndex); model::MaterialPointer material = part._material; + #ifdef WANT_DEBUG if (material == nullptr) { - // qCDebug(renderutils) << "WARNING: material == nullptr!!!"; + qCDebug(renderutils) << "WARNING: material == nullptr!!!"; } + #endif if (material != nullptr) { @@ -2023,8 +1997,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } void Model::segregateMeshGroups() { - _renderBuckets.clear(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& networkMeshes = _geometry->getMeshes(); @@ -2034,6 +2006,9 @@ void Model::segregateMeshGroups() { qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } + + _transparentRenderItems.clear(); + _opaqueRenderItems.clear(); // Run through all of the meshes, and place them into their segregated, but unsorted buckets for (int i = 0; i < networkMeshes.size(); i++) { @@ -2058,43 +2033,12 @@ void Model::segregateMeshGroups() { for (int partIndex = 0; partIndex < totalParts; partIndex++) { // this is a good place to create our renderPayloads if (translucentMesh) { - _transparentRenderItems << std::shared_ptr(new TransparentMeshPart(this, i, partIndex)); + _transparentRenderItems << std::shared_ptr(new MeshPartPayload(true, this, i, partIndex)); } else { - _opaqueRenderItems << std::shared_ptr(new OpaqueMeshPart(this, i, partIndex)); + _opaqueRenderItems << std::shared_ptr(new MeshPartPayload(false, this, i, partIndex)); } } - - - QString materialID; - - // create a material name from all the parts. If there's one part, this will be a single material and its - // true name. If however the mesh has multiple parts the name will be all the part's materials mashed together - // which will result in those parts being sorted away from single material parts. - QString lastPartMaterialID; - foreach(FBXMeshPart part, mesh.parts) { - if (part.materialID != lastPartMaterialID) { - materialID += part.materialID; - } - lastPartMaterialID = part.materialID; - } - const bool wantDebug = false; - if (wantDebug) { - qCDebug(renderutils) << "materialID:" << materialID << "parts:" << mesh.parts.size(); - } - - RenderKey key(translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe); - - // reuse or create the bucket corresponding to that key and insert the mesh as unsorted - _renderBuckets[key.getRaw()]._unsortedMeshes.insertMulti(materialID, i); } - - for(auto& b : _renderBuckets) { - foreach(auto i, b.second._unsortedMeshes) { - b.second._meshes.append(i); - } - b.second._unsortedMeshes.clear(); - } - _meshGroupsKnown = true; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index c96e006aa8..7c1572418e 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -51,17 +51,11 @@ namespace render { class PendingChanges; typedef unsigned int ItemID; } -class OpaqueMeshPart; -class TransparentMeshPart; +class MeshPartPayload; -inline uint qHash(const std::shared_ptr& a, uint seed) { +inline uint qHash(const std::shared_ptr& a, uint seed) { return qHash(a.get(), seed); } -inline uint qHash(const std::shared_ptr& a, uint seed) { - return qHash(a.get(), seed); -} - - /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public PhysicsEntity { @@ -77,7 +71,7 @@ public: virtual ~Model(); /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension - void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f); + void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false); bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to @@ -511,24 +505,11 @@ private: }; static RenderPipelineLib _renderPipelineLib; - - class RenderBucket { - public: - QVector _meshes; - QMap _unsortedMeshes; - }; - typedef std::unordered_map BaseRenderBucketMap; - class RenderBucketMap : public BaseRenderBucketMap { - public: - typedef RenderKey Key; - }; - RenderBucketMap _renderBuckets; - bool _renderCollisionHull; - QSet> _transparentRenderItems; - QSet> _opaqueRenderItems; + QSet> _transparentRenderItems; + QSet> _opaqueRenderItems; QMap _renderItems; bool _readyWhenAdded = false; bool _needsReload = true;