Model.h#

Contains the class representing an entire Scene, or Model.

Includes#

  • PVRAssets/model/Animation.h

  • PVRAssets/model/Camera.h

  • PVRAssets/model/Light.h

  • PVRAssets/model/Mesh.h

Included By#

Namespaces#

Classes#

Enums#

Functions#

Typedefs#

Source Code#

#pragma once
#include "PVRAssets/model/Camera.h"
#include "PVRAssets/model/Animation.h"
#include "PVRAssets/model/Light.h"
#include "PVRAssets/model/Mesh.h"

namespace pvr {
class Stream;
namespace assets {

class Model;
class Mesh;
class Camera;
class Light;
typedef std::shared_ptr<Model> ModelHandle;
typedef std::shared_ptr<Mesh> MeshHandle;
typedef std::shared_ptr<Camera> CameraHandle;
typedef std::shared_ptr<Light> LightHandle;

struct Skeleton
{
    std::string name;
    std::vector<uint32_t> bones;
    std::vector<glm::mat4> invBindMatrices;
};

enum class ModelFileFormat
{
    UNKNOWN = 0,
    POD,
    GLTF,
};

class Model
{
public:
    typedef assets::Mesh Mesh;

    class Node
    {
    public:
        struct InternalData
        {
            StringHash name;
            uint32_t objectIndex;
            uint32_t materialIndex;
            uint32_t parentIndex;
            UInt8Buffer userData;

            enum TransformFlags
            {
                Identity = 0,
                Scale = 0x01,
                Rotate = 0x02,
                Translate = 0x04,
                SRT = Scale | Rotate | Translate,
                Matrix = 64,
            };

            // CONTAINS
            float frameTransform[16];

            // NODE'S LOCAL SPACE SRT
            glm::vec3 scale;
            glm::quat rotation;
            glm::vec3 translation;

            uint32_t transformFlags;
            int32_t skin;

            bool hasAnimation;

            glm::vec3& getFrameScaleAnimation() { return *(glm::vec3*)frameTransform; }

            glm::quat& getFrameRotationAnimation() { return *(glm::quat*)(&frameTransform[3]); }

            glm::vec3& getFrameTranslationAnimation() { return *(glm::vec3*)(&frameTransform[7]); }

            const glm::vec3& getFrameScaleAnimation() const { return *(glm::vec3*)frameTransform; }

            const glm::quat& getFrameRotationAnimation() const { return *(glm::quat*)(&frameTransform[3]); }

            const glm::vec3& getFrameTranslationAnimation() const { return *(glm::vec3*)(&frameTransform[7]); }

            glm::vec3& getScale() { return scale; }

            glm::quat& getRotate() { return rotation; }

            glm::vec3& getTranslation() { return translation; }

            const glm::vec3& getScale() const { return scale; }

            const glm::quat& getRotate() const { return rotation; }

            const glm::vec3& getTranslation() const { return translation; }

            InternalData()
                : objectIndex(static_cast<uint32_t>(-1)), materialIndex(static_cast<uint32_t>(-1)), parentIndex(static_cast<uint32_t>(-1)), scale(1.0f), translation(0.0f)
            {
                transformFlags = TransformFlags::Identity;
                hasAnimation = false;
            }
        };

    public:
        uint32_t getObjectId() const { return _data.objectIndex; }

        const StringHash& getName() const { return _data.name; }

        uint32_t getParentID() const { return _data.parentIndex; }

        uint32_t getMaterialIndex() const { return _data.materialIndex; }

        void setMaterialIndex(uint32_t materialId) { _data.materialIndex = materialId; }

        const uint8_t* getUserData() const { return _data.userData.data(); }

        uint32_t getUserDataSize() const { return static_cast<uint32_t>(_data.userData.size()); }

        void setIndex(uint32_t index) { _data.objectIndex = index; }

        void setName(const StringHash& name) { _data.name = name; }

        void setParentID(uint32_t parentID) { _data.parentIndex = parentID; }

        void setUserData(uint32_t size, const char* data)
        {
            _data.userData.resize(size);
            memcpy(_data.userData.data(), data, size);
        }

        InternalData& getInternalData() { return _data; }

        const InternalData& getInternalData() const { return _data; }

    private:
        InternalData _data;
    };

    class Texture
    {
    public:
        Texture() {}

        Texture(const StringHash&& name) : _name(std::move(name)) {}

        const pvr::StringHash& getName() const { return _name; }

        pvr::StringHash& getName() { return _name; }

        void setName(const StringHash& name) { this->_name = name; }

    private:
        pvr::StringHash _name;
    };

    class Material
    {
    public:
        Material() : _defaultSemantics(*this) {}

        enum BlendFunction
        {
            BlendFuncZero = 0,
            BlendFuncOne,
            BlendFuncFactor,
            BlendFuncOneMinusBlendFactor,

            BlendFuncSrcColor = 0x0300,
            BlendFuncOneMinusSrcColor,
            BlendFuncSrcAlpha,
            BlendFuncOneMinusSrcAlpha,
            BlendFuncDstAlpha,
            BlendFuncOneMinusDstAlpha,
            BlendFuncDstColor,
            BlendFuncOneMinusDstColor,
            BlendFuncSrcAlphaSaturate,

            BlendFuncConstantColor = 0x8001,
            BlendFuncOneMinusConstantColor,
            BlendFuncConstantAlpha,
            BlendFuncOneMinusConstantAlpha
        };

        enum BlendOperation
        {
            BlendOpAdd = 0x8006,
            BlendOpMin,
            BlendOpMax,
            BlendOpSubtract = 0x800A,
            BlendOpReverseSubtract
        };

        //"BLENDFUNCTION", BlendFunction
        //"BLENDOP", BlendOperation
        //"DIFFUSETEXTURE",
        //"SPECULARTEXTURE",
        //"NORMALTEXTURE",
        //"EMISSIVETEXTURE",
        //"GLOSSINESSTEXTURE",
        //"OPACITYTEXTURE",
        //"REFLECTIONTEXTURE",
        //"REFRACTIONTEXTURE",
        //"OPACITY",
        //"AMBIENTCOLOR",
        //"DIFFUSECOLOR",
        //"SPECULARCOLOR",
        //"SHININESS",
        //"EFFECTFILE",
        //"EFFECTNAME",
        //"BLENDFUNCSRCCOLOR"
        //"BLENDFUNCSRCALPHA"
        //"BLENDFUNCDSTCOLOR"
        //"BLENDFUNCDSTALPHA"
        //"BLENDCOLOR"
        //"BLENDFACTOR"
        //"FLAGS"
        //"USERDATA"

        //--------  PBR COMMON SEMANTICS ---------
        //"METALLICITY"
        //"ROUGHNESS"
        //"METALLICITYTEXTURE"
        //"ROUGHNESSTEXTURE"

        //-------- PBR POD SEMANTICS -------------
        //"IOR"
        //"FRESENEL"
        //"SSSCATERING"
        //"SSCATERINGDEPTH"
        //"SSCATERINGCOLOR"
        //"EMISSIONLUMINANCE"
        //"EMISSIONKELVIN"
        //"ANISTROPHY"

        //-------- PBR GLTF SEMANTICS ------------
        // "METALLICBASECOLOR"
        // "EMISSIVECOLOR"
        // "ALPHACUTOFF"
        // "DOUBLESIDED"
        // "NORMALTEXTURE"
        // "OCCLUSIONTEXTURE"

        //------- PBR SEMANTICS ----------
        //"REFLECTIVITY"
        //"EMISSION"

        struct InternalData
        {
            std::map<StringHash, FreeValue> materialSemantics;
            std::map<StringHash, uint32_t> textureIndices;

            StringHash name;
            StringHash effectFile;
            StringHash effectName;

            UInt8Buffer userData;
        };

        class PBRSemantics
        {
        public:
            virtual ~PBRSemantics() {}

            PBRSemantics(Material& mat) : _material(mat) {}

            uint32_t getOcclusionTextureIndex() const { return _material.getTextureIndex("OCCLUSIONTEXTURE"); }

            void setOcclusionTextureIndex(uint32_t index) { _material.setTextureIndex("OCCLUSIONTEXTURE", index); }

            uint32_t getNormalTextureIndex() const { return _material.getTextureIndex("NORMALTEXTURE"); }

            void setNormalTextureIndex(uint32_t index) { _material.setTextureIndex("NORMALTEXTURE", index); }

            glm::vec3 getEmissiveColor() const { return _material.getMaterialAttributeWithDefault("EMISSIVECOLOR", glm::vec3(0.f)); }

            void setEmissiveColor(const glm::vec3& color)
            {
                FreeValue value;
                value.setValue(color);
                _material.setMaterialAttribute("EMISSIVECOLOR", value);
            }

            void setEmissiveTextureIndex(uint32_t index) { _material.setTextureIndex("EMISSIVETEXTURE", index); }

            uint32_t getEmissiveTextureIndex() const { return _material.getTextureIndex("EMISSIVETEXTURE"); }

            void setRoughnessTextureIndex(uint32_t index) { _material.setTextureIndex("ROUGHNESSTEXTURE", index); }

            uint32_t getRoughnessTextureIndex() { return _material.getTextureIndex("ROUGHNESSTEXTURE"); }

            void setMetallicityTextureIndex(uint32_t index) { _material.setTextureIndex("METALLICITYTEXTURE", index); }

            uint32_t getMetallicityTextureIndex() { return _material.getTextureIndex("METALLICITYTEXTURE"); }

        protected:
            Material& _material;
        };

        class PODMetallicRoughnessSemantics : public PBRSemantics
        {
        public:
            PODMetallicRoughnessSemantics(Material& mat) : PBRSemantics(mat) {}

            void setEmissionLuminance(float luminance) { _material.setMaterialAttribute("EMISSIONLUMINANCE", FreeValue(luminance)); }

            float getEmissionLuminance() const { return _material.getMaterialAttributeWithDefault("EMISSIONLUMINANCE", 0.f); }

            void setEmissionKelvin(float kelvin) { _material.setMaterialAttribute("EMISSIONKELVIN", kelvin); }

            float getEmissionKelvin() const { return _material.getMaterialAttributeWithDefault("EMISSIONKELVIN", 1.f); }
        };

        enum class GLTFAlphaMode
        {
            Opaque, //< The alpha value is ignored and the rendered output is fully opaque.
            Mask, //< The rendered output is either fully opaque or fully transparent depending on the alpha value and the specified alpha cutoff value.
            Blend, //<The alpha value is used to composite the source and destination areas. The rendered output is combined with the background using the normal painting operation
                   //(i.e. the Porter and Duff over operator).
        };

        class GLTFMetallicRoughnessSemantics : public PBRSemantics
        {
        public:
            GLTFMetallicRoughnessSemantics(Material& material) : PBRSemantics(material) {}

            void setBaseColor(const glm::vec4& color)
            {
                FreeValue value;
                value.setValue(color);
                _material.setMaterialAttribute("METALLICBASECOLOR", value);
            }

            glm::vec4 getBaseColor() const { return _material.getMaterialAttributeWithDefault<glm::vec4>("METALLICBASECOLOR", glm::vec4(1.f)); }

            void setBaseColorTextureIndex(uint32_t index) { _material.setTextureIndex("DIFFUSETEXTURE", index); }

            uint32_t getBaseColorTextureIndex() const { return _material.getTextureIndex("DIFFUSETEXTURE"); }

            void setMetallicity(float metallic)
            {
                FreeValue value;
                value.setValue(metallic);
                _material.setMaterialAttribute("METALLICITY", value);
            }

            float getMetallicity() const { return _material.getMaterialAttributeWithDefault<float>("METALLICITY", 0); }

            void setRoughness(float roughness)
            {
                FreeValue value;
                value.setValue(roughness);
                _material.setMaterialAttribute("ROUGHNESS", value);
            }

            float getRoughness() const { return _material.getMaterialAttributeWithDefault<float>("ROUGHNESS", 0); }

            float getAlphaCutOff() const { return _material.getMaterialAttributeWithDefault("ALPHACUTOFF", 0.5f); }

            void setAlphaCutOff(float cutoff)
            {
                FreeValue val;
                val.setValue(cutoff);
                _material.setMaterialAttribute("ALPHACUTOFF", val);
            }

            bool isDoubleSided() const { return static_cast<bool>(_material.getMaterialAttributeWithDefault("DOUBLESIDED", 1)); }

            void setDoubleSided(bool doubleSided)
            {
                FreeValue val;
                val.setValue(static_cast<uint32_t>(doubleSided));
                _material.setMaterialAttribute("DOUBLESIDED", val);
            }

            GLTFAlphaMode getAlphaMode()
            {
                return static_cast<GLTFAlphaMode>(_material.getMaterialAttributeWithDefault("ALPHAMODE", static_cast<uint32_t>(GLTFAlphaMode::Opaque)));
            }

            void setAlphaMode(GLTFAlphaMode alphaMode) const
            {
                FreeValue val;
                val.setValue(static_cast<uint32_t>(alphaMode));
                _material.setMaterialAttribute("ALPHAMODE", val);
            }
        };

        class DefaultMaterialSemantics
        {
        public:
            DefaultMaterialSemantics(const Material& material) : material(&material) {}

            glm::vec3 getAmbient() const { return material->getMaterialAttributeWithDefault<glm::vec3>("AMBIENT", glm::vec3(0.f, 0.f, 0.f)); }

            glm::vec3 getDiffuse() const { return material->getMaterialAttributeWithDefault<glm::vec3>("DIFFUSE", glm::vec3(1.f, 1.f, 1.f)); }

            glm::vec3 getSpecular() const { return material->getMaterialAttributeWithDefault<glm::vec3>("SPECULAR", glm::vec3(0.f, 0.f, 0.f)); }

            float getShininess() const { return material->getMaterialAttributeWithDefault<float>("SHININESS", 0.f); }

            uint32_t getDiffuseTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("DIFFUSETEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            uint32_t getAmbientTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("AMBIENTTEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            uint32_t getSpecularColorTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("SPECULARCOLORTEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            uint32_t getSpecularLevelTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("SPECULARLEVELTEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            uint32_t getBumpMapTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("NORMALTEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            uint32_t getEmissiveTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("EMISSIVETEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            uint32_t getGlossinessTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("GLOSSINESSTEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            uint32_t getOpacityTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("OPACITYTEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            uint32_t getReflectionTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("REFLECTIONTEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            uint32_t getRefractionTextureIndex() const
            {
                static const StringHash diffuseTexSemantic("REFRACTIONTEXTURE");
                return material->getTextureIndex(diffuseTexSemantic);
            }

            float getOpacity() const { return material->getMaterialAttributeWithDefault<float>("OPACITY", 1.f); }

            BlendFunction getBlendSrcRGB() const { return material->getMaterialAttributeWithDefault<BlendFunction>("BLENDSRCCOLOR", BlendFunction::BlendFuncOne); }

            BlendFunction getBlendSrcA() const { return material->getMaterialAttributeWithDefault<BlendFunction>("BLENDSRCALPHA", BlendFunction::BlendFuncOne); }

            BlendFunction getBlendDstRGB() const { return material->getMaterialAttributeWithDefault<BlendFunction>("BLENDDSTCOLOR", BlendFunction::BlendFuncZero); }

            BlendFunction getBlendDstA() const { return material->getMaterialAttributeWithDefault<BlendFunction>("BLENDDSTALPHA", BlendFunction::BlendFuncZero); }

            BlendOperation getBlendOpRGB() const { return material->getMaterialAttributeWithDefault<BlendOperation>("BLENDOPCOLOR", BlendOperation::BlendOpAdd); }

            BlendOperation getBlendOpA() const { return material->getMaterialAttributeWithDefault<BlendOperation>("BLENDOPALPHA", BlendOperation::BlendOpAdd); }

            glm::vec4 getBlendColor() const { return material->getMaterialAttributeWithDefault<glm::vec4>("BLENDCOLOR", glm::vec4(0.f, 0.f, 0.f, 0.f)); }

            glm::vec4 getBlendFactor() const { return material->getMaterialAttributeWithDefault<glm::vec4>("BLENDFACTOR", glm::vec4(0.f, 0.f, 0.f, 0.f)); }

        private:
            const Material* material;
        };

    public:
        const DefaultMaterialSemantics& defaultSemantics() const { return _defaultSemantics; }

        void setMaterialAttribute(const StringHash& semantic, const FreeValue& value) { _data.materialSemantics[semantic] = value; }

        const FreeValue* getMaterialAttribute(const StringHash& semantic) const
        {
            auto it = _data.materialSemantics.find(semantic);
            if (it != _data.materialSemantics.end()) { return &it->second; }
            return nullptr;
        }

        template<typename Type>
        const Type getMaterialAttributeWithDefault(const StringHash& semantic, const Type& defaultAttrib) const
        {
            auto* val = getMaterialAttribute(semantic);
            if (val) { return val->interpretValueAs<Type>(); }
            return defaultAttrib;
        }

        template<typename Type>
        const Type* getMaterialAttributeAs(const StringHash& semantic) const
        {
            auto* val = getMaterialAttribute(semantic);
            if (val) { return &val->interpretValueAs<Type>(); }
            return NULL;
        }

        bool hasSemantic(const StringHash& semantic) const { return hasMaterialTexture(semantic) || hasMaterialAttribute(semantic); }

        bool hasMaterialTexture(const StringHash& semantic) const { return getTextureIndex(semantic) != static_cast<uint32_t>(-1); }
        bool hasMaterialAttribute(const StringHash& semantic) const { return getMaterialAttribute(semantic) != NULL; }

        void setEffectName(const StringHash& name) { _data.effectName = name; }

        void setEffectFile(const StringHash& name) { _data.effectFile = name; }

        uint32_t getTextureIndex(const StringHash& semantic) const
        {
            auto it = _data.textureIndices.find(semantic);
            return (it == _data.textureIndices.end()) ? -1 : it->second;
        }

        void setTextureIndex(const StringHash& semantic, uint32_t index) { _data.textureIndices[semantic] = index; }

        const StringHash& getName() const { return this->_data.name; }

        const StringHash& getEffectFile() const { return _data.effectFile; }

        const StringHash& getEffectName() const { return _data.effectName; }

        InternalData& getInternalData() { return _data; }

    private:
        UInt8Buffer userData;
        InternalData _data;
        DefaultMaterialSemantics _defaultSemantics;
    };
    struct InternalData
    {
        std::map<StringHash, FreeValue> semantics;

        float clearColor[3];
        float ambientColor[3];

        std::vector<Mesh> meshes;
        std::vector<Camera> cameras;
        std::vector<Light> lights;
        std::vector<Texture> textures;
        std::vector<Material> materials;
        std::vector<Node> nodes;
        std::vector<Skeleton> skeletons;

        std::vector<AnimationData> animationsData;
        std::vector<AnimationInstance> animationInstances;
        uint32_t numMeshNodes;
        uint32_t numLightNodes;
        uint32_t numCameraNodes;

        uint32_t numFrames;
        float currentFrame;
        float FPS;

        UInt8Buffer userData;

        float units;
        uint32_t flags;
        std::shared_ptr<void> userDataPtr;

        InternalData() : numMeshNodes(0), numLightNodes(0), numCameraNodes(0), numFrames(0), currentFrame(0), FPS(30), units(1), flags(0)
        {
            memset(clearColor, 0, sizeof(clearColor));
            memset(ambientColor, 0, sizeof(ambientColor));
        }
    };

private:
    std::vector<float> cachedFrame;
    std::vector<glm::mat4x4> worldMatrixFrameN;
    std::vector<glm::mat4x4> worldMatrixFrameZero;
    InternalData _data;
public:
    const FreeValue* getModelSemantic(const StringHash& semantic) const
    {
        auto it = _data.semantics.find(semantic);
        if (it == _data.semantics.end()) { return NULL; }
        return &it->second;
    }

    const std::shared_ptr<void>& getUserDataPtr() const { return this->_data.userDataPtr; }

    std::shared_ptr<void> getUserDataPtr() { return this->_data.userDataPtr; }

    void setUserDataPtr(const std::shared_ptr<void>& ptr) { _data.userDataPtr = ptr; }

public:
    void releaseVertexData()
    {
        for (uint32_t i = 0; i < getNumMeshes(); ++i) { releaseVertexData(i); }
    }

    void releaseVertexData(uint32_t meshId)
    {
        Mesh& mesh = getMesh(meshId);
        for (uint32_t i = 0; i < mesh.getNumDataElements(); ++i) { mesh.removeData(i); }
        mesh.getFaces().setData(0, 0);
    }

    glm::vec3 getLightPosition(uint32_t lightId) const;

    size_t getNumAnimationData() const { return _data.animationsData.size(); }

    const AnimationData& getAnimationData(uint32_t index) const { return _data.animationsData[index]; }

    const AnimationData* getAnimationData(const char* name) const
    {
        const auto& it =
            std::find_if(_data.animationsData.begin(), _data.animationsData.end(), [&](const AnimationData& anim) { return strcmp(name, anim.getAnimationName().c_str()) == 0; });
        if (it != _data.animationsData.end()) { return &(*it); }
        return nullptr;
    }

    const AnimationInstance& getAnimationInstance(uint32_t index) const { return _data.animationInstances[index]; }

    AnimationInstance& getAnimationInstance(uint32_t index) { return _data.animationInstances[index]; }

    size_t getNumAnimationInstances() const { return _data.animationInstances.size(); }

    size_t addAnimationInstance(const AnimationInstance& animationInstance)
    {
        _data.animationInstances.emplace_back(animationInstance);
        return _data.animationInstances.size() - 1;
    }

    glm::mat4x4 getWorldMatrix(uint32_t nodeId) const;

    glm::mat4x4 getWorldMatrixNoCache(uint32_t nodeId) const;

    glm::mat4x4 getBoneWorldMatrix(uint32_t skinNodeID, uint32_t boneId) const;

    glm::mat4x4 toWorldMatrix(uint32_t nodeId, const glm::mat4& localMatrix) const
    {
        uint32_t parentID = _data.nodes[nodeId].getParentID();
        if (parentID == static_cast<uint32_t>(-1)) { return localMatrix; }
        else
        {
            glm::mat4 parentWorld = getWorldMatrix(parentID);
            return parentWorld * localMatrix;
        }
    }

    const Skeleton& getSkeleton(uint32_t skeletonIndex) const { return _data.skeletons[skeletonIndex]; }

    size_t getNumSkeletons() const { return _data.skeletons.size(); }

    const float* getBackgroundColor() const { return _data.clearColor; }

    uint32_t getNumCameras() const { return static_cast<uint32_t>(_data.cameras.size()); }

    uint32_t getNumCameraNodes() const { return getNumCameras(); /* Will be changed at a future revision */ }

    const Camera& getCamera(uint32_t cameraIndex) const
    {
        assertion(cameraIndex < getNumCameras(), "Invalid camera index");
        return _data.cameras[cameraIndex];
    }
    Camera& getCamera(uint32_t cameraIndex)
    {
        assertion(cameraIndex < getNumCameras(), "Invalid camera index");
        return _data.cameras[cameraIndex];
    }

    const Node& getCameraNode(uint32_t cameraNodeIndex) const
    {
        assertion(cameraNodeIndex < getNumCameraNodes(), "Invalid camera node index");
        // Camera nodes are after the mesh and light nodes in the array
        return getNode(getNodeIdFromCameraId(cameraNodeIndex));
    }

    Node& getCameraNode(uint32_t cameraNodeIndex)
    {
        assertion(cameraNodeIndex < getNumCameraNodes(), "Invalid camera node index");
        // Camera nodes are after the mesh and light nodes in the array
        return getNode(getNodeIdFromCameraId(cameraNodeIndex));
    }

    size_t getNumAnimations() const { return _data.animationsData.size(); }

    uint32_t getNodeIdFromCameraId(uint32_t cameraNodeIndex) const
    {
        // Camera nodes are after the mesh and light nodes in the array
        assertion(cameraNodeIndex < getNumCameraNodes(), "Invalid camera node index");
        return getNumMeshNodes() + getNumLights() + cameraNodeIndex;
    }

    uint32_t getNumLights() const { return static_cast<uint32_t>(_data.lights.size()); }

    uint32_t getNumLightNodes() const { return getNumLights(); /* Will be changed at a future revision */ }

    const Light& getLight(uint32_t lightIndex) const
    {
        assertion(lightIndex < getNumLights(), "Invalid light index");
        return _data.lights[lightIndex];
    }
    Light& getLight(uint32_t lightIndex)
    {
        assertion(lightIndex < getNumLights(), "Invalid light index");
        return _data.lights[lightIndex];
    }

    const Node& getLightNode(uint32_t lightNodeIndex) const
    {
        assertion(lightNodeIndex < getNumLights(), "Invalid light node index");
        // Light nodes are after the mesh nodes in the array
        return getNode(getNodeIdFromLightNodeId(lightNodeIndex));
    }

    uint32_t getNodeIdFromLightNodeId(uint32_t lightNodeIndex) const
    {
        assertion(lightNodeIndex < getNumLightNodes(), "Invalid light node index");
        // Light nodes are after the mesh nodes in the array
        return getNumMeshNodes() + lightNodeIndex;
    }

    uint32_t getNumMeshes() const { return static_cast<uint32_t>(_data.meshes.size()); }

    uint32_t getNumMeshNodes() const { return _data.numMeshNodes; }

    const Mesh& getMesh(uint32_t meshIndex) const { return _data.meshes[meshIndex]; }

    void allocateAnimationsData(uint32_t numAnimation) { _data.animationsData.resize(numAnimation); }

    void allocateAnimationInstances(uint32_t numAnimation) { _data.animationInstances.resize(numAnimation); }

    Mesh& getMesh(uint32_t index)
    {
        assertion(index < getNumMeshes(), "Invalid mesh index");
        return _data.meshes[index];
    }

    const Node& getMeshNode(uint32_t meshIndex) const
    {
        assertion(meshIndex < getNumMeshNodes(), "Invalid mesh index");
        // Mesh nodes are at the start of the array
        return getNode(meshIndex);
    }

    Node& getMeshNode(uint32_t meshIndex)
    {
        assertion(meshIndex < getNumMeshNodes(), "Invalid mesh index");
        // Mesh nodes are at the start of the array
        return getNode(meshIndex);
    }

    void connectMeshWithMeshNode(uint32_t meshId, uint32_t meshNodeId) { getMeshNode(meshNodeId).setIndex(meshId); }

    void connectMeshWithMeshNodes(uint32_t meshId, uint32_t beginMeshNodeId, uint32_t endMeshNodeId)
    {
        for (uint32_t i = beginMeshNodeId; i <= endMeshNodeId; ++i) { connectMeshWithMeshNode(meshId, i); }
    }

    void assignMaterialToMeshNodes(uint32_t materialIndex, uint32_t beginMeshNodeId, uint32_t endMeshNodeId)
    {
        for (uint32_t i = beginMeshNodeId; i <= endMeshNodeId; ++i) { getMeshNode(i).setMaterialIndex(materialIndex); }
    }

    uint32_t getNodeIdForMeshNodeId(uint32_t meshNodeIndex) const
    {
        debug_assertion(meshNodeIndex < getNumMeshNodes(), "invalid mesh node index");
        // Camera nodes are after the mesh and light nodes in the array
        return meshNodeIndex;
    }

    std::vector<Mesh>::iterator beginMeshes() { return _data.meshes.begin(); }

    std::vector<Mesh>::iterator endMeshes() { return _data.meshes.end(); }

    std::vector<Mesh>::const_iterator beginMeshes() const { return _data.meshes.begin(); }

    std::vector<Mesh>::const_iterator endMeshes() const { return _data.meshes.end(); }

    uint32_t getNumNodes() const { return static_cast<uint32_t>(_data.nodes.size()); }

    const Node& getNode(uint32_t index) const { return _data.nodes[index]; }

    Node& getNode(uint32_t index) { return _data.nodes[index]; }

    uint32_t getNumTextures() const { return static_cast<uint32_t>(_data.textures.size()); }

    const Texture& getTexture(uint32_t index) const { return _data.textures[index]; }

    uint32_t getNumMaterials() const { return static_cast<uint32_t>(_data.materials.size()); }

    const Material& getMaterial(uint32_t index) const { return _data.materials[index]; }

    uint32_t addMaterial(const Material& material)
    {
        _data.materials.emplace_back(material);
        return static_cast<uint32_t>(_data.materials.size() - 1);
    }

    Material& getMaterial(uint32_t index) { return _data.materials[index]; }

    uint32_t getNumFrames() const { return _data.numFrames ? _data.numFrames : 1; }

    float getCurrentFrame();

    void setFPS(float fps) { _data.FPS = fps; }

    float getFPS() const { return _data.FPS; }

    void setUserData(uint32_t size, const char* data);

    void allocCameras(uint32_t count);

    void allocLights(uint32_t count);

    void allocMeshes(uint32_t count);

    void allocNodes(uint32_t count);

    InternalData& getInternalData() { return _data; }

    void getCameraProperties(uint32_t cameraIdx, float& fov, glm::vec3& from, glm::vec3& to, glm::vec3& up, float frameTimeInMs = 0.f) const;

    void getCameraProperties(uint32_t cameraIdx, float& fov, glm::vec3& from, glm::vec3& to, glm::vec3& up, float& nearClip, float& farClip, float frameTimeInMs = 0.f) const;

    void getLightDirection(uint32_t lightIdx, glm::vec3& direction) const;

    void getLightPosition(uint32_t lightIdx, glm::vec3& position) const;

    void getLightPosition(uint32_t lightIdx, glm::vec4& position) const;

    void destroy() { _data = InternalData(); }

    void allocMeshNodes(uint32_t no);

    int32_t addTexture(const Texture& tex)
    {
        _data.textures.emplace_back(tex);
        return static_cast<int32_t>(_data.textures.size()) - 1;
    }
};

typedef Model::Material Material;
typedef Model::Node Node;
typedef Model::Mesh::VertexAttributeData VertexAttributeData;

typedef std::shared_ptr<Node> NodeHandle;
typedef std::shared_ptr<Material> MaterialHandle;

inline MeshHandle getMeshHandle(ModelHandle model, uint32_t meshId) { return std::shared_ptr<Mesh>(model, &model->getMesh(meshId)); }

inline MaterialHandle getMaterialHandle(ModelHandle model, uint32_t materialId) { return std::shared_ptr<Material>(model, &model->getMaterial(materialId)); }

inline LightHandle getLightHandle(ModelHandle model, uint32_t lightId) { return std::shared_ptr<Light>(model, &model->getLight(lightId)); }

inline CameraHandle getCameraHandle(ModelHandle model, uint32_t cameraId) { return std::shared_ptr<Camera>(model, &model->getCamera(cameraId)); }

inline NodeHandle getNodeHandle(ModelHandle model, uint32_t nodeId) { return std::shared_ptr<Node>(model, &model->getNode(nodeId)); }
} // namespace assets
} // namespace pvr