427 lines · 16.1 KB · C
RawDownload
////  Model.h//  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// #ifndef hifi_Model_h#define hifi_Model_h #include <QBitArray>#include <QObject>#include <QUrl>#include <QMutex> #include <unordered_map>#include <unordered_set>#include <functional> #include <AABox.h>#include <DependencyManager.h>#include <GeometryUtil.h>#include <gpu/Batch.h>#include <render/Scene.h>#include <Transform.h> #include "GeometryCache.h"#include "TextureCache.h"#include "Rig.h" class AbstractViewStateInterface;class QScriptEngine; #include "RenderArgs.h"class ViewFrustum; namespace render {    class Scene;    class PendingChanges;    typedef unsigned int ItemID;}class MeshPartPayload;class ModelMeshPartPayload;class ModelRenderLocations; inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) {    return qHash(a.get(), seed);} class Model;using ModelPointer = std::shared_ptr<Model>;using ModelWeakPointer = std::weak_ptr<Model>;  /// A generic 3D model displaying geometry loaded from a URL.class Model : public QObject, public std::enable_shared_from_this<Model> {    Q_OBJECT public:     typedef RenderArgs::RenderMode RenderMode;     static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; }     Model(RigPointer rig, QObject* parent = nullptr);    virtual ~Model();     inline ModelPointer getThisPointer() const {        return std::static_pointer_cast<Model>(std::const_pointer_cast<Model>(shared_from_this()));    }     /// Sets the URL of the model to render.    // Should only be called from the model's rendering thread to avoid access violations of changed geometry.    Q_INVOKABLE void setURL(const QUrl& url);    const QUrl& getURL() const { return _url; }     // new Scene/Engine rendering support    void setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scene);    bool needsFixupInScene() const;    bool readyToAddToScene(RenderArgs* renderArgs = nullptr) const {        return !_needsReload && isRenderable() && isActive();    }    bool initWhenReady(render::ScenePointer scene);    bool addToScene(std::shared_ptr<render::Scene> scene,                    render::PendingChanges& pendingChanges,                    bool showCollisionHull = false) {        auto getters = render::Item::Status::Getters(0);        return addToScene(scene, pendingChanges, getters, showCollisionHull);    }    bool addToScene(std::shared_ptr<render::Scene> scene,                    render::PendingChanges& pendingChanges,                    render::Item::Status::Getters& statusGetters,                    bool showCollisionHull = false);    void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);    void renderSetup(RenderArgs* args);    bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && getGeometry()->getGeometry()->getMeshes().empty()); }     bool isVisible() const { return _isVisible; }     void updateRenderItems();    AABox getRenderableMeshBound() const;     bool maybeStartBlender();     /// Sets blended vertices computed in a separate thread.    void setBlendedVertices(int blendNumber, const std::weak_ptr<NetworkGeometry>& geometry,        const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);     bool isLoaded() const { return _geometry && _geometry->getGeometry(); }    bool isCollisionLoaded() const { return _collisionGeometry && _collisionGeometry->getGeometry(); }     void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; }    bool isWireframe() const { return _isWireframe; }     void init();    void reset();     void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions);     void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint);    bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }     virtual void simulate(float deltaTime, bool fullUpdate = true);    virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation);     /// Returns a reference to the shared geometry.    const NetworkGeometry::Pointer& getGeometry() const { return _geometry; }    /// Returns a reference to the shared collision geometry.    const NetworkGeometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; }     const QVariantMap getTextures() const { assert(isLoaded()); return _geometry->getGeometry()->getTextures(); }    void setTextures(const QVariantMap& textures);     /// Provided as a convenience, will crash if !isLoaded()    // And so that getGeometry() isn't chained everywhere    const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return getGeometry()->getGeometry()->getGeometry(); }    /// Provided as a convenience, will crash if !isCollisionLoaded()    const FBXGeometry& getCollisionFBXGeometry() const { assert(isCollisionLoaded()); return getCollisionGeometry()->getGeometry()->getGeometry(); }     // Set the model to use for collisions.    // Should only be called from the model's rendering thread to avoid access violations of changed geometry.    Q_INVOKABLE void setCollisionModelURL(const QUrl& url);    const QUrl& getCollisionURL() const { return _collisionUrl; }      bool isActive() const { return isLoaded(); }     bool convexHullContains(glm::vec3 point);     QStringList getJointNames() const;     /// Sets the joint state at the specified index.    void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority);    void setJointRotation(int index, bool valid, const glm::quat& rotation, float priority);    void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority);     bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,                                             BoxFace& face, glm::vec3& surfaceNormal,                                              QString& extraInfo, bool pickAgainstTriangles = false);     void setOffset(const glm::vec3& offset);    const glm::vec3& getOffset() const { return _offset; }     void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false);    bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled     void setSnapModelToCenter(bool snapModelToCenter) {        setSnapModelToRegistrationPoint(snapModelToCenter, glm::vec3(0.5f,0.5f,0.5f));    };    bool getSnapModelToCenter() {        return _snapModelToRegistrationPoint && _registrationPoint == glm::vec3(0.5f,0.5f,0.5f);    }     /// Returns the number of joint states in the model.    int getJointStateCount() const { return (int)_rig->getJointStateCount(); }    bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;    bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;    bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;    /// \param jointIndex index of joint in model structure    /// \param rotation[out] rotation of joint in model-frame    /// \return true if joint exists    bool getJointRotation(int jointIndex, glm::quat& rotation) const;    bool getJointTranslation(int jointIndex, glm::vec3& translation) const;     // model frame    bool getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotationOut) const;    bool getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translationOut) const;     bool getRelativeDefaultJointRotation(int jointIndex, glm::quat& rotationOut) const;    bool getRelativeDefaultJointTranslation(int jointIndex, glm::vec3& translationOut) const;     /// Returns the index of the parent of the indexed joint, or -1 if not found.    int getParentJointIndex(int jointIndex) const;     void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority);     /// Returns the extents of the model in its bind pose.    Extents getBindExtents() const;     /// Returns the extents of the model's mesh    Extents getMeshExtents() const;     void setTranslation(const glm::vec3& translation);    void setRotation(const glm::quat& rotation);     const glm::vec3& getTranslation() const { return _translation; }    const glm::quat& getRotation() const { return _rotation; }     void setScale(const glm::vec3& scale);    const glm::vec3& getScale() const { return _scale; }     /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension    bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit    glm::vec3 getScaleToFitDimensions() const; /// the dimensions model is scaled to, including inferred y/z     void setCauterizeBones(bool flag) { _cauterizeBones = flag; }    bool getCauterizeBones() const { return _cauterizeBones; }     const std::unordered_set<int>& getCauterizeBoneSet() const { return _cauterizeBoneSet; }    void setCauterizeBoneSet(const std::unordered_set<int>& boneSet) { _cauterizeBoneSet = boneSet; }     int getBlendshapeCoefficientsNum() const { return _blendshapeCoefficients.size(); }    float getBlendshapeCoefficient(int index) const {        return ((index < 0) && (index >= _blendshapeCoefficients.size())) ? 0.0f : _blendshapeCoefficients.at(index);     }     virtual RigPointer getRig() const { return _rig; }     const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } protected:     void setPupilDilation(float dilation) { _pupilDilation = dilation; }    float getPupilDilation() const { return _pupilDilation; }     void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }    const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }     /// Returns the unscaled extents of the model's mesh    Extents getUnscaledMeshExtents() const;     /// Returns the scaled equivalent of some extents in model space.    Extents calculateScaledOffsetExtents(const Extents& extents, glm::vec3 modelPosition, glm::quat modelOrientation) const;     /// Returns the world space equivalent of some box in model space.    AABox calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPosition, glm::quat modelOrientation) const;     /// Returns the scaled equivalent of a point in model space.    glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const;     /// Clear the joint states    void clearJointState(int index);     /// Returns the index of the last free ancestor of the indexed joint, or -1 if not found.    int getLastFreeJointIndex(int jointIndex) const;     /// \param jointIndex index of joint in model structure    /// \param position[out] position of joint in model-frame    /// \return true if joint exists    bool getJointPosition(int jointIndex, glm::vec3& position) const;     NetworkGeometry::Pointer _geometry;     glm::vec3 _translation;    glm::quat _rotation;    glm::vec3 _scale;    glm::vec3 _offset;     static float FAKE_DIMENSION_PLACEHOLDER;     bool _scaleToFit; /// If you set scaleToFit, we will calculate scale based on MeshExtents    glm::vec3 _scaleToFitDimensions; /// this is the dimensions that scale to fit will use    bool _scaledToFit; /// have we scaled to fit     bool _snapModelToRegistrationPoint; /// is the model's offset automatically adjusted to a registration point in model space    bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point    glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to     class MeshState {    public:        QVector<glm::mat4> clusterMatrices;        QVector<glm::mat4> cauterizedClusterMatrices;        gpu::BufferPointer clusterBuffer;        gpu::BufferPointer cauterizedClusterBuffer;     };     QVector<MeshState> _meshStates;    std::unordered_set<int> _cauterizeBoneSet;    bool _cauterizeBones;     // returns 'true' if needs fullUpdate after geometry change    bool updateGeometry();     virtual void initJointStates();     void setScaleInternal(const glm::vec3& scale);    void scaleToFit();    void snapToRegistrationPoint();     void simulateInternal(float deltaTime);    virtual void updateRig(float deltaTime, glm::mat4 parentTransform);     /// Restores the indexed joint to its default position.    /// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to    /// the original position    /// \return true if the joint was found    bool restoreJointPosition(int jointIndex, float fraction = 1.0f, float priority = 0.0f);     /// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's    /// first free ancestor.    float getLimbLength(int jointIndex) const;     /// Allow sub classes to force invalidating the bboxes    void invalidCalculatedMeshBoxes() {        _calculatedMeshBoxesValid = false;        _calculatedMeshPartBoxesValid = false;        _calculatedMeshTrianglesValid = false;    }     // hook for derived classes to be notified when setUrl invalidates the current model.    virtual void onInvalidate() {}; protected:     void deleteGeometry();    void initJointTransforms();     NetworkGeometry::Pointer _collisionGeometry;     float _pupilDilation;    QVector<float> _blendshapeCoefficients;     QUrl _url;    QUrl _collisionUrl;    bool _isVisible;     gpu::Buffers _blendedVertexBuffers;     QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;     QVector<float> _blendedBlendshapeCoefficients;    int _blendNumber;    int _appliedBlendNumber;     QHash<QPair<int,int>, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes     bool _calculatedMeshPartBoxesValid;    QVector<AABox> _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes    bool _calculatedMeshBoxesValid;     QVector< QVector<Triangle> > _calculatedMeshTriangles; // world coordinate triangles for all sub meshes    bool _calculatedMeshTrianglesValid;    QMutex _mutex;     void recalculateMeshBoxes(bool pickAgainstTriangles = false);     void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes    static model::MaterialPointer _collisionHullMaterial;     bool _meshGroupsKnown;    bool _isWireframe;      // debug rendering support    void renderDebugMeshBoxes(gpu::Batch& batch);    int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;      static AbstractViewStateInterface* _viewState;     bool _renderCollisionHull;      QSet<std::shared_ptr<MeshPartPayload>> _collisionRenderItemsSet;    QMap<render::ItemID, render::PayloadPointer> _collisionRenderItems;     QSet<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItemsSet;    QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItems;     bool _readyWhenAdded { false };    bool _needsReload { true };    bool _needsUpdateClusterMatrices { true };    bool _showCollisionHull { false };    mutable bool _needsUpdateTextures { true };     friend class ModelMeshPartPayload;    RigPointer _rig;     uint32_t _deleteGeometryCounter { 0 };}; Q_DECLARE_METATYPE(ModelPointer)Q_DECLARE_METATYPE(std::weak_ptr<NetworkGeometry>) /// Handle management of pending models that need blendingclass ModelBlender : public QObject, public Dependency {    Q_OBJECT    SINGLETON_DEPENDENCY public:     /// Adds the specified model to the list requiring vertex blends.    void noteRequiresBlend(ModelPointer model); public slots:    void setBlendedVertices(ModelPointer model, int blendNumber, const std::weak_ptr<NetworkGeometry>& geometry,        const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals); private:    using Mutex = std::mutex;    using Lock = std::unique_lock<Mutex>;     ModelBlender();    virtual ~ModelBlender();     std::set<ModelWeakPointer, std::owner_less<ModelWeakPointer>> _modelsRequiringBlends;    int _pendingBlenders;    Mutex _mutex;};  #endif // hifi_Model_h