2061 lines · 85.2 KB · C++
RawDownload
////  Model.cpp//  interface/src/renderer////  Created by Andrzej Kapolka on 10/18/13.//  Copyright 2013 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "Model.h" #include <QMetaType>#include <QRunnable>#include <QThreadPool> #include <glm/gtx/transform.hpp>#include <glm/gtx/norm.hpp> #include <shared/QtHelpers.h>#include <GeometryUtil.h>#include <PathUtils.h>#include <PerfStat.h>#include <ViewFrustum.h>#include <GLMHelpers.h>#include <TBBHelpers.h> #include <model-networking/SimpleMeshProxy.h>#include <graphics-scripting/Forward.h>#include <graphics/BufferViewHelpers.h>#include <DualQuaternion.h> #include <glm/gtc/packing.hpp> #include "AbstractViewStateInterface.h"#include "MeshPartPayload.h" #include "RenderUtilsLogging.h"#include <Trace.h> #include <BlendshapeConstants.h> using namespace std; int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>();int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType<Geometry::WeakPointer>();int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3>>();int normalTypeVecTypeId = qRegisterMetaType<QVector<NormalType>>("QVector<NormalType>");float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;#define HTTP_INVALID_COM "http://invalid.com" Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride, uint64_t created) :    QObject(parent),    _renderGeometry(),    _renderWatcher(_renderGeometry),    _spatiallyNestableOverride(spatiallyNestableOverride),    _translation(0.0f),    _rotation(),    _scale(1.0f, 1.0f, 1.0f),    _scaleToFit(false),    _scaleToFitDimensions(1.0f),    _scaledToFit(false),    _snapModelToRegistrationPoint(false),    _snappedToRegistrationPoint(false),    _url(HTTP_INVALID_COM),    _renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build()),    _created(created){    // we may have been created in the network thread, but we live in the main thread    if (_viewState) {        moveToThread(_viewState->getMainThread());    }     setSnapModelToRegistrationPoint(true, glm::vec3(0.5f));     connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished);} Model::~Model() {    deleteGeometry();} AbstractViewStateInterface* Model::_viewState = NULL; bool Model::needsFixupInScene() const {    return (_needsFixupInScene || !_addedToScene) && !_needsReload && isLoaded();} void Model::setTranslation(const glm::vec3& translation) {    _translation = translation;    updateRenderItems();} void Model::setRotation(const glm::quat& rotation) {    _rotation = rotation;    updateRenderItems();} // temporary HACK: set transform while avoiding implicit calls to updateRenderItems()// TODO: make setRotation() and friends set flag to be used later to decide to updateRenderItems()void Model::setTransformNoUpdateRenderItems(const Transform& transform) {    _translation = transform.getTranslation();    _rotation = transform.getRotation();    // DO NOT call updateRenderItems() here!} Transform Model::getTransform() const {    if (_overrideModelTransform) {        Transform transform;        transform.setTranslation(getOverrideTranslation());        transform.setRotation(getOverrideRotation());        transform.setScale(getScale());        return transform;    } else if (_spatiallyNestableOverride) {        bool success;        Transform transform = _billboardMode == BillboardMode::NONE ? _spatiallyNestableOverride->getTransform(success) :            _spatiallyNestableOverride->getTransformWithOnlyLocalRotation(success);        if (success) {            transform.setScale(getScale());            return transform;        }    }     Transform transform;    transform.setScale(getScale());    transform.setTranslation(getTranslation());    transform.setRotation(getRotation());    return transform;} void Model::setScale(const glm::vec3& scale) {    setScaleInternal(scale);    // if anyone sets scale manually, then we are no longer scaled to fit    _scaleToFit = false;    _scaledToFit = false;} const float SCALE_CHANGE_EPSILON = 0.0000001f; void Model::setScaleInternal(const glm::vec3& scale) {    if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) {        _scale = scale;        assert(_scale.x != 0.0f && scale.y != 0.0f && scale.z != 0.0f);        simulate(0.0f, true);    }} void Model::setOffset(const glm::vec3& offset) {    _offset = offset;     // if someone manually sets our offset, then we are no longer snapped to center    _snapModelToRegistrationPoint = false;    _snappedToRegistrationPoint = false;    _forceOffset = true;} void Model::calculateTextureInfo() {    if (!_hasCalculatedTextureInfo && isLoaded() && getGeometry()->areTexturesLoaded() && !_modelMeshRenderItems.isEmpty()) {        size_t textureSize = 0;        int textureCount = 0;        bool allTexturesLoaded = true;        foreach(auto renderItem, _modelMeshRenderItems) {            auto meshPart = renderItem.get();            textureSize += meshPart->getMaterialTextureSize();            textureCount += meshPart->getMaterialTextureCount();            allTexturesLoaded = allTexturesLoaded & meshPart->hasTextureInfo();        }        _renderInfoTextureSize = textureSize;        _renderInfoTextureCount = textureCount;        _hasCalculatedTextureInfo = allTexturesLoaded; // only do this once    }} size_t Model::getRenderInfoTextureSize() {    calculateTextureInfo();    return _renderInfoTextureSize;} int Model::getRenderInfoTextureCount() {    calculateTextureInfo();    return _renderInfoTextureCount;} bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) {    if (!getGeometry()) {        return true;    }     const HFMModel& hfmModel = getHFMModel();    const auto& networkMeshes = getGeometry()->getMeshes();    // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown    // to false to rebuild out mesh groups.    if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)hfmModel.meshes.size() || meshIndex >= (int)_meshStates.size()) {        _needsFixupInScene = true;     // trigger remove/add cycle        invalidCalculatedMeshBoxes();  // if we have to reload, we need to assume our mesh boxes are all invalid        return true;    }     return false;} void Model::updateRenderItems() {    if (!_addedToScene) {        return;    }     _needsUpdateClusterMatrices = true;    _renderItemsNeedUpdate = false;     // queue up this work for later processing, at the end of update and just before rendering.    // the application will ensure only the last lambda is actually invoked.    void* key = (void*)this;    std::weak_ptr<Model> weakSelf = shared_from_this();    AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() {         // do nothing, if the model has already been destroyed.        auto self = weakSelf.lock();        if (!self || !self->isLoaded()) {            return;        }         // lazy update of cluster matrices used for rendering.        // We need to update them here so we can correctly update the bounding box.        self->updateClusterMatrices();         Transform modelTransform = self->getTransform();        modelTransform.setScale(glm::vec3(1.0f));         PrimitiveMode primitiveMode = self->getPrimitiveMode();        BillboardMode billboardMode = self->getBillboardMode();        auto renderWithZones = self->getRenderWithZones();        auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags();        bool cauterized = self->isCauterized();        auto mirrorMode = self->getMirrorMode();        const QUuid& portalExitID = self->getPortalExitID();         render::Transaction transaction;        for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) {             auto itemID = self->_modelMeshRenderItemIDs[i];            auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;             const auto& meshState = self->getMeshState(meshIndex);             bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);            bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();             transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, meshState, useDualQuaternionSkinning,                                                                  invalidatePayloadShapeKey, primitiveMode, billboardMode, renderItemKeyGlobalFlags,                                                                  cauterized, renderWithZones, mirrorMode, portalExitID](ModelMeshPartPayload& data) {                if (useDualQuaternionSkinning) {                    data.updateClusterBuffer(meshState.clusterDualQuaternions);                    data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);                } else {                    data.updateClusterBuffer(meshState.clusterMatrices);                    data.computeAdjustedLocalBound(meshState.clusterMatrices);                }                 data.updateTransformForSkinnedMesh(modelTransform, meshState, useDualQuaternionSkinning);                 data.setCauterized(cauterized);                data.setRenderWithZones(renderWithZones);                data.setBillboardMode(billboardMode);                data.setMirrorMode(mirrorMode);                data.setPortalExitID(portalExitID);                data.updateKey(renderItemKeyGlobalFlags);                data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);            });        }         AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);    });} void Model::setRenderItemsNeedUpdate() {    _renderItemsNeedUpdate = true;    emit requestRenderUpdate();} void Model::reset() {    if (isLoaded()) {        const HFMModel& hfmModel = getHFMModel();        _rig.reset(hfmModel);        emit rigReset();        emit rigReady();    }} bool Model::updateGeometry() {    bool needFullUpdate = false;     if (!isLoaded()) {        return false;    }     _needsReload = false;     // TODO: should all Models have a valid _rig?    if (_rig.jointStatesEmpty() && getHFMModel().joints.size() > 0) {        initJointStates();         if (!_meshStates.empty()) {            // See https://github.com/vircadia/vircadia/issues/958 for a discussion of the issues found here with            // a previously existing assert, and the likely reasons for things going wrong here.            //            // TL;DR: There may be a threading issue, or it could be due to something lacking joints, which would cause            // initJointStates() to fail to make _rig.jointStatesEmpty() false, causing things to end up here twice.            //            // In any case it appears to be safe to simply clear _meshStates here, even though this shouldn't happen.             qCWarning(renderutils) << "_meshStates has" << _meshStates.size() << "items when it should have none. Model with URL "                                   << _url.toString() << "; translation" << _translation << "; rotation" << _rotation << "; scale" << _scale                                   << "; joint state count" << _rig.getJointStateCount() << "; type" << (modelProviderType == NestableType::Avatar ? "Avatar" : "Entity");            _meshStates.clear();        }         const HFMModel& hfmModel = getHFMModel();        foreach (const HFMMesh& mesh, hfmModel.meshes) {            MeshState state;            state.clusterDualQuaternions.resize(mesh.clusters.size());            state.clusterMatrices.resize(mesh.clusters.size());            _meshStates.push_back(state);        }        needFullUpdate = true;        emit rigReady();    }     return needFullUpdate;} // virtualvoid Model::initJointStates() {    const HFMModel& hfmModel = getHFMModel();    glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);     _rig.initJointStates(hfmModel, modelOffset);} bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& viewFrustumPos,                                                float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,                                                bool pickAgainstTriangles, bool allowBackface) {    bool intersectedSomething = false;     // if we aren't active, we can't pick yet...    if (!isLoaded()) {        return intersectedSomething;    }     // extents is the entity relative, scaled, centered extents of the entity    glm::quat rotation = BillboardModeHelpers::getBillboardRotation(_translation, _rotation, _billboardMode, viewFrustumPos);    glm::mat4 transRot = createMatFromQuatAndPos(rotation, _translation);    glm::mat4 modelToWorldMatrix = transRot;    if (!_snapModelToRegistrationPoint) {        modelToWorldMatrix = modelToWorldMatrix * glm::translate(getOriginalOffset());    }    glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix);     Extents modelExtents = getMeshExtents(); // NOTE: unrotated     glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum;    glm::vec3 corner = -(dimensions * _registrationPoint); // since we're going to do the picking in the model frame of reference    AABox modelFrameBox(corner, dimensions);     glm::vec3 modelFrameOrigin = glm::vec3(worldToModelMatrix * glm::vec4(origin, 1.0f));    glm::vec3 modelFrameDirection = glm::vec3(worldToModelMatrix * glm::vec4(direction, 0.0f));     // we can use the AABox's intersection by mapping our origin and direction into the model frame    // and testing intersection there.    if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, 1.0f / modelFrameDirection, distance, face, surfaceNormal)) {        QMutexLocker locker(&_mutex);         float bestDistance = FLT_MAX;        BoxFace bestFace;        Triangle bestModelTriangle;        Triangle bestWorldTriangle;        glm::vec3 bestWorldIntersectionPoint;        glm::vec3 bestMeshIntersectionPoint;        int bestPartIndex = 0;        int bestShapeID = 0;        int bestSubMeshIndex = 0;         const HFMModel& hfmModel = getHFMModel();        if (!_triangleSetsValid) {            calculateTriangleSets(hfmModel);        }         glm::mat4 meshToWorldMatrix = transRot;        if (_snapModelToRegistrationPoint || _forceOffset) {            meshToWorldMatrix = meshToWorldMatrix * (glm::scale(_scale) * glm::translate(_offset));        } else {            meshToWorldMatrix = meshToWorldMatrix * (glm::scale(_scale) * glm::translate(getNaturalDimensions() * (0.5f - _registrationPoint)));        }        glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);         glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));        glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));        glm::vec3 meshFrameInvDirection = 1.0f / meshFrameDirection;         int shapeID = 0;        int subMeshIndex = 0;         std::vector<SortedTriangleSet> sortedTriangleSets;        for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) {            int partIndex = 0;            for (auto& partTriangleSet : meshTriangleSets) {                float priority = FLT_MAX;                if (partTriangleSet.getBounds().contains(meshFrameOrigin)) {                    priority = 0.0f;                } else {                    float partBoundDistance = FLT_MAX;                    BoxFace partBoundFace;                    glm::vec3 partBoundNormal;                    if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection,                                                                        partBoundDistance, partBoundFace, partBoundNormal)) {                        priority = partBoundDistance;                    }                }                 if (priority < FLT_MAX) {                    sortedTriangleSets.emplace_back(priority, &partTriangleSet, partIndex, shapeID, subMeshIndex);                }                partIndex++;                shapeID++;            }            subMeshIndex++;        }         if (sortedTriangleSets.size() > 1) {            static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance < right.distance; };            std::sort(sortedTriangleSets.begin(), sortedTriangleSets.end(), comparator);        }         for (auto it = sortedTriangleSets.begin(); it != sortedTriangleSets.end(); ++it) {            const SortedTriangleSet& sortedTriangleSet = *it;            // We can exit once triangleSetDistance > bestDistance            if (sortedTriangleSet.distance > bestDistance) {                break;            }            float triangleSetDistance = FLT_MAX;            BoxFace triangleSetFace;            Triangle triangleSetTriangle;            if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection, triangleSetDistance, triangleSetFace,                                                                   triangleSetTriangle, pickAgainstTriangles, allowBackface)) {                if (triangleSetDistance < bestDistance) {                    bestDistance = triangleSetDistance;                    intersectedSomething = true;                    bestFace = triangleSetFace;                    bestModelTriangle = triangleSetTriangle;                    bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix;                    glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);                    glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));                    bestWorldIntersectionPoint = worldIntersectionPoint;                    bestMeshIntersectionPoint = meshIntersectionPoint;                    bestPartIndex = sortedTriangleSet.partIndex;                    bestShapeID = sortedTriangleSet.shapeID;                    bestSubMeshIndex = sortedTriangleSet.subMeshIndex;                }            }        }         /*@jsdoc         * A submesh intersection point.         * @typedef {object} SubmeshIntersection         * @property {Vec3} worldIntersectionPoint - The intersection point in world coordinates.         * @property {Vec3} meshIntersectionPoint - The intersection point in model coordinates.         * @property {number} partIndex - The index of the intersected mesh part within the submesh.         * @property {number} shapeID - The index of the mesh part within the model.         * @property {number} subMeshIndex - The index of the intersected submesh within the model.         * @property {string} subMeshName - The name of the intersected submesh.         * @property {Triangle} subMeshTriangleWorld - The vertices of the intersected mesh part triangle in world coordinates.         * @property {Vec3} subMeshNormal - The normal of the intersected mesh part triangle in model coordinates.         * @property {Triangle} subMeshTriangle - The vertices of the intersected mesh part triangle in model coordinates.         */        if (intersectedSomething) {            distance = bestDistance;            face = bestFace;            surfaceNormal = bestWorldTriangle.getNormal();            extraInfo["worldIntersectionPoint"] = vec3toVariant(bestWorldIntersectionPoint);            extraInfo["meshIntersectionPoint"] = vec3toVariant(bestMeshIntersectionPoint);            extraInfo["partIndex"] = bestPartIndex;            extraInfo["shapeID"] = bestShapeID;            if (pickAgainstTriangles) {                extraInfo["subMeshIndex"] = bestSubMeshIndex;                extraInfo["subMeshName"] = hfmModel.getModelNameOfMesh(bestSubMeshIndex);                extraInfo["subMeshTriangleWorld"] = QVariantMap{                    { "v0", vec3toVariant(bestWorldTriangle.v0) },                    { "v1", vec3toVariant(bestWorldTriangle.v1) },                    { "v2", vec3toVariant(bestWorldTriangle.v2) },                };                extraInfo["subMeshNormal"] = vec3toVariant(bestModelTriangle.getNormal());                extraInfo["subMeshTriangle"] = QVariantMap{                    { "v0", vec3toVariant(bestModelTriangle.v0) },                    { "v1", vec3toVariant(bestModelTriangle.v1) },                    { "v2", vec3toVariant(bestModelTriangle.v2) },                };            }        }    } 

Lines 1–500 of 2061.