/*	Golaem Crowd IO API - v0.00 - Copyright (C) Golaem SA.  All Rights Reserved.

The implementation of this library is in the glmCrowdIO dynamic library redistributed with Golaem Crowd

QUICK NOTES:
Primarily of interest to pipeline developers to generate Golaem Crowd entities geometry

Brief documentation under "DOCUMENTATION" below.

Revision 0.00 (2015-11-09) release notes:

- Functions and structures for creation, read/write and destruction of Golaem Crowd entities geometry
*/
#pragma once

#include <glmCrowdIOAPI.h>
#include "glmGeometryFile.h"
#include "glmCrowdTerrainMesh.h"

#include "glmSimulationData.h"
#include "glmFrameData.h"
#include "glmHistory.h"
#include "glmFurCache.h"
#include "glmArray.h"
#include "glmMap.h"
#include "glmString.h"

#include "glmDeepInferenceWrapper.h"
#include "glmCrowdIOUtils.h"

namespace at
{
    class Tensor;
}

namespace glm
{
    namespace deep
    {
        class DeepModelData;
        class DeepModelUserData;
    };

    namespace crowdio
    {
        class CrowdFBXStorage;
        class CrowdFBXBaker;
        class CrowdFBXCharacter;
        class CrowdGcgCharacter;
		class SimulationCacheFactory;
        class BonesSkinningData;
        struct GeometryContextData;

        //-------------------------------------------------------------------------
        // Types and Structures
        //-------------------------------------------------------------------------

        typedef enum
        {
            GLM_OID_OBJECT,
            GLM_OID_SHADER,
            GLM_OID_MESH,
            GLM_OID_ENTITY,
            GLM_OID_ENTITYTYPE,
            GLM_OID_CHARACTERFILE,
            GLM_OID_CROWDFIELD,
            GLM_OID_ALL
        } GlmObjectIdMode;

        typedef float GlmMeshInstanceMatrix[4][4];

        // Render data for a mesh: vertices, polygons, normals, UVs and shader attributes values
        struct GlmMesh_0
        {
            int32_t _instanceGroup;
            int32_t _instanceIndex;
            int16_t _instanceFirstMatrixIndex;

            char _meshName[GIO_NAME_LENGTH];      // name of the mesh
            char _transformName[GIO_NAME_LENGTH]; // name of the mesh transform (used for mesh asset matching)
			char _meshAlias[GIO_NAME_LENGTH];     // export alias of the mesh
            uint8_t _meshId;                      // id of the mesh
            uint32_t _uniqueMeshIndex;            // used at isntancing to know if same name must be different instance

            // shaders
            uint16_t _shaderGroupIdx;

            // vertices
            uint32_t _verticeCount; // number of vertices per mesh
            float (**_vertices)[3]; // vertex positions, array size first dimension = _frameToProcessCounts, second dimension = _verticeCount

            // polygons
            uint32_t _polygonCount;             // number of polygons per mesh
            uint32_t* _verticePerPolygonCount;  // number of vertices by polygon, array size = _polygonCount
            uint32_t* _vertexIndicesPerPolygon; // id of vertices by polygon, array size = _polygonCount * _verticePerPolygonCount

            // OPTIONAL triangle edges visibility (for VRay n-gon subdiv management)
            uint8_t (*_triangleEdgeVisibility)[3]; // visibility of vertices by triangle edge, only computed when _computeEdgeVisibility == 1 && _maxVerticesPerFace == 3 (triangles). Edges visibility set in order triangleVertId 0-1, 1-2 and 2-0

            // normals
            uint8_t _hasNormals;   // 1 if the character has normals / control point. Normals are always extracted as mapped per polygon vertex
            float (**_normals)[3]; // normals per polygon vertex, array size first dimension = _frameToProcessCounts, second dimension = _polygonCount * _verticePerPolygonCount
            // tangents
            uint8_t _hasTangents; // 1 if the character has tangents / mesh
            uint8_t _tangentsByControlPoint;
            float (**_tangents)[6]; // tangents and binormals. If _tangentsByControlPoint == 1 then array size == _verticeCount, else array size = _polygonCount * _verticePerPolygonCount
            // UVs
            size_t _uvSetCount;                          // number of uv set for this mesh
            char (*_uvSetNames)[GIO_SHADER_NAME_LENGTH]; // array size dimension = _uvSetCount, names of the uv set for this mesh
            uint8_t* _uvsByControlPoint;                 // array size dimension = _uvSetCount, contains 1 if the UVs are mapped by control point / mesh
			float (**_uvs)[2];							 // UV coordinates. array size first dimension = _uvSetCount, second dimension if _uvsByControlPoint == 1 then array size == _verticeCount, else array size = _polygonCount * _verticePerPolygonCount
                                                         // object id
            uint32_t _objectId;
			int8_t _subdivOverride;
        };
        typedef GlmMesh_0 GlmMesh;

        struct GlmMeshFur_0
        {
            char _furName[GIO_NAME_LENGTH];     // name of the fur
			char _furAlias[GIO_NAME_LENGTH];	// fur alias
            uint64_t _curveCount;    // total number of curves
            uint32_t* _numVertices;  // number of vertices for each curve, dimension = _curveCount
            float (**_positions)[3]; // vertex positions, array size first dimension = _frameToProcessCounts, second dimension = _curveCount * _numVertices
            float* _widths;          // widths of vertices for each curve, dimension = _curveCount * _numVertices
            float (*_uvs)[2];        // uvs of vertices for each curve, dimension = _curveCount * _numVertices

            Array<GlmString> _floatPropertiesNames;
            Array<Array<float>> _floatProperties;
            Array<GlmString> _vector3PropertiesNames;
            Array<Array<Vector3>> _vector3Properties;

            uint16_t _shaderGroupIdx;

            // object id
            uint32_t _objectId;
        };

        typedef GlmMeshFur_0 GlmMeshFur;

        // Render data for an entity: meshes
        struct GlmEntityGeometry_0
        {
            uint16_t _meshCount;
            GlmMesh* _meshes;

            uint16_t _furCount;
            GlmMeshFur* _meshFurs;

            int32_t **_intShaderAttributeValues;					// use GlmShaderGroup::_intShaderAttributeCount and GlmShaderGroup::_intShaderAttributeIndexes to access it
            float **_floatShaderAttributeValues;					// use GlmShaderGroup::_floatShaderAttributeCount and GlmShaderGroup::_floatShaderAttributeIndexes to access it
            char (**_stringShaderAttributeValues)[GIO_NAME_LENGTH];	// use GlmShaderGroup::_fileShaderAttributeCount and GlmShaderGroup::_fileShaderAttributeIndexes to access it
            float (**_vectorShaderAttributeValues)[3];				// use GlmShaderGroup::_vectorShaderAttributeCount and GlmShaderGroup::_vectorShaderAttributeIndexes to access it
        };
        typedef GlmEntityGeometry_0 GlmEntityGeometry;

        // instanced meshes
        struct GlmMeshInstanceGroup_0
        {
            GlmMesh* _referenceMesh; // points on first geometry instanciated
            uint32_t _instanceCount; // crowdField wide, used to set instanceIndex, used by renderer to know if it makes the reference mesh or an instance of it
            void* _rendererSpecificData;
            uint8_t _definitionComplete; // says that the mesh can be used in renderer. Could occur that a thread is preparing the reference mesh, while another is already further on same mesh and wants to use the reference.
        };
        typedef GlmMeshInstanceGroup_0 GlmMeshInstanceGroup;

        // helper to find right mesh instance group according to hashkey then name. This will be sorted
        struct GlmMeshInstanceGroupSortEntry_0
        {
            // identifiers
            uint32_t _nameAndShaderHashKey; // done by name hash + shader index
            uint32_t _timeHashKey;          // for vertex cache
            const char* _name;

            // index to instance group in the GlmMeshInstanceGroupPerCrowdField
            int32_t _instanceGroup;
        };
        typedef GlmMeshInstanceGroupSortEntry_0 GlmMeshInstanceGroupSortEntry;

        // static allocation of instanceMatrices to avoid reallocating (and thus locking) matrices
        struct GlmEntityInstanceMatrices
        {
            GlmMeshInstanceMatrix _instanceMatrices[GIO_MAX_INSTANCE_MATRIX_PER_ENTITY]; // by default 64 ko = 16 float * 1k
            uint16_t _instanceMatrixUsedCount;
        };

        // instances per crowdField, to be able to dimension it according to frame count
        struct GlmMeshInstanceGroupPerCrowdField_0
        {
            uint32_t _meshInstanceGroupCount;                                  // number of instance groups used in the current frame
            GlmMeshInstanceGroup _meshInstanceGroups[GIO_MAX_INSTANCE_MESHES]; // all meshInstanceGroups, one instance group per character file / per rigid mesh
            void* _meshInstanceGroupSet;                                       // for internal use
        };
        typedef GlmMeshInstanceGroupPerCrowdField_0 GlmMeshInstanceGroupPerCrowdField;

        // Entity ID and bounding box data, created by glmBeginGeometryGeneration
        struct GlmEntityBoundingBox_0
        {
            uint8_t _isVisible;               // is the character visible? Used if _enableFrustumCulling is on in the GlmProcessCharactersParameters
            uint64_t _entityId;               // entity id of the character in the simulation
            uint32_t _entityIndex;            // entity index of the character in the simulation cache
            uint8_t _crowdFieldIndex;         // index of the crowdField this character is a part of
			uint8_t _isMirrored;              // is the geometry mirrored for this entity
			float _geometryMirrorCenterPos[3];        // mirror plane position transform
			float _geometryMirrorCenterOri[4];        // mirror plane orientation transform
            float _boundingBoxHalfExtents[3]; // bounding box distances from the origin
            float _entityOrigin[3];           // origin of the entity for this frame
        };
        typedef GlmEntityBoundingBox_0 GlmEntityBoundingBox;

        // Shader name and category, attributes/context are stored in a ShaderGroup to mutualize attributes
        struct GlmShader_0
        {
            char _name[GIO_NAME_LENGTH];		// name of the shader
			char _previewName[GIO_NAME_LENGTH]; // name of the shader to use in preview (mesh drive)
            char _category[GIO_NAME_LENGTH];	// category of the shader: surface, displacement, etc..
        };
        typedef GlmShader_0 GlmShader;

        // Shader group contains an array of shaders and an array of attributes names, values are stored per mesh in GlmMesh
        struct GlmShaderGroup_0
        {
            char _name[GIO_NAME_LENGTH];
            uint16_t _intShaderAttributeCount;
            char (*_intShaderAttributeNames)[GIO_NAME_LENGTH]; // array size = _intShaderAttributeCount
            uint16_t* _intShaderAttributeIndexes;              // array size = _intShaderAttributeCount

            uint16_t _floatShaderAttributeCount;
            char (*_floatShaderAttributeNames)[GIO_NAME_LENGTH]; // array size = _floatShaderAttributeCount
            uint16_t* _floatShaderAttributeIndexes;              // array size = _floatShaderAttributeCount

            uint16_t _stringShaderAttributeCount;
            char (*_stringShaderAttributeNames)[GIO_NAME_LENGTH]; // array size = _stringShaderAttributeCount
            uint16_t* _stringShaderAttributeIndexes;              // array size = _stringShaderAttributeCount

            uint16_t _vectorShaderAttributeCount;
            char (*_vectorShaderAttributeNames)[GIO_NAME_LENGTH]; // array size = _vectorShaderAttributeCount
            uint16_t* _vectorShaderAttributeIndexes;              // array size = _vectorShaderAttributeCount

            uint16_t _shaderCount;
            GlmShader* _shaders;
        };
        typedef GlmShaderGroup_0 GlmShaderGroup;

        // Input and ouput parameters passed as a context between functions declared below
        struct GlmPPAttributes_0
        {
            uint8_t _floatAttributeCount;                   // number of Float attributes
            char (*_floatAttributeNames)[GIO_NAME_LENGTH];  // name of the float per particle attribute, array size first dimension = _floatAttributeCount, second dimension = _entityCountPerCrowdField
            float** _floatAttributeData;                    // float data per particle attributes, array size first dimension = _floatAttributeCount
            uint8_t _vectorAttributeCount;                  // number of Vector attributes
            char (*_vectorAttributeNames)[GIO_NAME_LENGTH]; // name of the vector per particle attribute, array size first dimension = _vectorAttributeCount, second dimension = _entityCountPerCrowdField
            float (**_vectorAttributeData)[3];              // vector data per particle attributes, array size = _vectorAttributeCount
        };
        typedef GlmPPAttributes_0 GlmPPAttributes;

        class DeepInputsMatching
        {
            
        public:            
            
            class JointInputMatchingElement
            {
                public :
                    JointInputMatchingElement() : _index(-1), _parentIndex(-1)
                    {
                    }                    
                    int _index; // boneIndex in cache (helper, but specific entity offset must be added)
					int _parentIndex; // needed for bone rotation to take out parent rotation and obtain joint local one
#ifndef NDEBUG
                    std::string _name;
#endif
            };

            class MatrixInputMatchingElement
            {
            public:
                MatrixInputMatchingElement() : _blindDataIndex(0)
                {
                }
                int _blindDataIndex; // first ppIndex in cache, size = 12 (helper, but specific entity offset must be added)
#ifndef NDEBUG
                std::string _name;
#endif
            };

            class FloatInputMatchingElement
            {
            public:
                FloatInputMatchingElement() : _blindDataIndex(0)
                {
                }

                int _blindDataIndex; // ppIndex in cache (helper, but specific entity offset must be added)
#ifndef NDEBUG
                std::string _name;
#endif
            };

            Array<JointInputMatchingElement> _rigJoints;
            Array<MatrixInputMatchingElement> _rigMatrices;
            Array<FloatInputMatchingElement> _rigFloats;
        };

        class DeepContextModelData
        {
        public:
            Array< DeepInputsMatching > _deepInputsMatching; // access with _deepInputMatching[iCrowdField]
            GlmMap< GlmString, DeepMeshVertexDefinition > _deepMeshVertexDef;
            int _modelTotalVertexCount;

            GlmMap<int64_t, size_t> _entityOffsetInArrays; // order of entities in the _deepInputPerEntityFrame
            size_t _deepBatchCount = 0;
            PODArray<float> _deepInputPerEntityFrame; // (model input size serial) * (entity1001 frame 0, .. entity1001 frameN, entity2001 frame0...entity2001 frameN...etc)
            PODArray<float> _deepOutputPerEntityFrame;
        };

        class DeepContextData
        {
        public:
            DeepContextData()
            {}

            ~DeepContextData()
            {
                DeepInferenceWrapper& deepWrapper = Singleton<DeepInferenceWrapper>::getInstance();
                for (auto modelDataIte = _deepContextModelData .begin(); modelDataIte != _deepContextModelData.end(); modelDataIte++)
                {
                    deepWrapper.unloadModel(modelDataIte.getKey()); // will actually unload only if not used elswewhere as load/unload count as one user
                }
            }

            GlmMap< GlmString, DeepContextModelData > _deepContextModelData; // per model, access with _deepContextModelData[ioFilePath];
        };

        // Input and ouput parameters passed as a context between functions declared below
        // Input paramters must be allocated/deallocated and set by the user
        // Output parameters are allocated/deallocated by glmBeginGeometryGeneration and glmFinishProcessEntityRenderData
        struct GlmGeometryGenerationContext_0
        {
            uint32_t _contextId;

            // input context
            float _frame; // the frame to process
            // crowd fields
            uint8_t _crowdFieldCount;                  // number of crowd fields
            char (*_crowdFieldNames)[GIO_NAME_LENGTH]; // crowdFields names, array size = _crowdFieldCount
            // simulation directories
            char _cacheName[GIO_NAME_LENGTH];           // maya scene name
            uint8_t _cacheDirectoryCount;               // number of directories to look for the golaem simulation cache files, .gscs and .gscf
            char (*_cacheDirectories)[GIO_NAME_LENGTH]; // directories to look for the golaem simulation cache files, .gscs and .gscf, array size = _cacheDirectoryCount
            uint8_t _characterFilesCount;               // number of directories to look for the golaem character files, .gcha
            char (*_characterFiles)[GIO_NAME_LENGTH];   // directories to look for the golaem character files, .gcha
            uint8_t _enableLayout;                      // 1 if layout is enabled
            char _layoutFile[GIO_LAYOUTS_LENGTH];       // layout file name
            char _terrainFile[GIO_NAME_LENGTH];         // destination terrain file
            // flip texture UVs
            uint8_t _relativeVertPos;       // 1 if vertex positions are relative to root position
            uint8_t _flipUs;                // 1 if texture U axis needs to be flipped
            uint8_t _flipVs;                // 1 if texture V axis needs to be flipped
            uint32_t _maxVerticesPerFace;   // replace _triangulate, _maxVerticesPerFace could be 3 (triangulate), 4 (tri and quad only, fall back to tri if more) or UINT_MAX (more than 4 vertices per poly OK)
            uint8_t _computeEdgeVisibility; // only for triangles (when _maxVerticesPerFace == 3), if _computeEdgeVisibility is 1, fill true with triangle edges from the original poly boundary, false else
            uint8_t _generateTangents;
            // motion blur
            uint8_t _motionBlurEnabled;  // 1 if motion blur is enabled
            float _motionBlurStartFrame; // motion blur start frame
            float _motionBlurWindowSize; // motion blur window size (in frames)
            uint32_t _motionBlurSamples; // number of motion blur samples
            // frustum culling
            uint8_t _enableFrustumCulling; // 1 if frustum culling is used
            float _frustumMargin;          // grow the view frustum
            float _cameraMargin;           // radius around camera eye where there is be no frustum culling
            float _cameraWorldPosition[3]; // world-position of the camera, Y-up, used to compute frustum culling and/or LOD
            float _viewMatrix[4][4];       // view matrix, column-major, used to compute frustum culling
            float _projectionMatrix[4][4]; // projection matrix, column-major, used to compute frustum culling
            uint8_t _enableLOD;            // 1 if LOD is used
            // object id
            uint32_t _objectIdBase;        // Start id for the ObjectId pass
            GlmObjectIdMode _objectIdMode; // ObjectId pass mode
            // render percent
            float _renderPercent; // Render Percent
            // geometry tag
            short _geometryTag; // Geometry Tag
            // dirmap rules
            char _dirmap[GIO_NAME_LENGTH]; // dirmaps , array size = _dirMapCount
            // shading
            char _shadingNamespaces[GIO_SHADING_LENGTH]; // character file shading namespaces
            // proxy matrix
            float _proxyMatrix[4][4];
            float _proxyMatrixInverse[4][4];

            uint8_t _instancingEnabled;

            uint64_t _engineVersion; // (internal) used to experiment with different computation algorithms

            // output context
            uint32_t _entityCount;                      // number of entities for the current frame
            uint32_t* _entityCountPerCrowdField;        // number of entities per crowd field for the current frame, array size = _crowdFieldCount
            GlmEntityBoundingBox* _entityBBoxes;        // entities bounding boxes and id information, array size = _entityCount
            GlmEntityGeometry* _entityGeometry;         // entity geometry, array size = _entityCount
            uint8_t* _frameToProcessCounts;             // number of frames computed for motion blur, array size = _crowdFieldCount
            double** _framesToProcess;                  // frames computed for motion blur, array size first dimension = _crowdFieldCount, second dimension = _frameToProcessCounts
            const GlmFrameData*** _framesDataToProcess; // frame data to process, array size first dimension = _crowdFieldCount, second dimension = _frameToProcessCounts
            float** _timesToProcess;                    // times computed for motion blur, array size first dimension = _crowdFieldCount, second dimension = _frameToProcessCounts
            GlmPPAttributes* _ppAttributes;             // user per-particle attributes, array size = _crowdFieldCount
            uint16_t _shaderGroupCount;                 // number of shader groups used in the current frame
            GlmShaderGroup* _shaderGroups;              // shader groups used in the current frame, array size = _shaderGroupCount

            DeepContextData* _deepData;
            uint8_t _enableDeepDeformer;
            uint8_t _enableDeepDeformerBatch;
            uint16_t _deepDevice;
            uint16_t _deepSubspacesDevice;
            uint8_t _deepPCAOnCPU;
            float _deepQuality;

            GlmMeshInstanceGroupPerCrowdField* _meshInstances;

			SimulationCacheFactory* _factory;

            //default constructor must at least initialise pointers to null
            GlmGeometryGenerationContext_0()
                : _relativeVertPos(0)
                , _deepData(NULL)
                , _enableDeepDeformer(0)
                , _enableDeepDeformerBatch(1)
                , _deepDevice(3)
                , _deepSubspacesDevice(0)
                , _deepPCAOnCPU(0)
                , _deepQuality(1.f)
                , _meshInstances(NULL)
                , _factory(NULL)
            {
            }

            //default constructor must at least initialise pointers to null
            ~GlmGeometryGenerationContext_0()
            {
                GLM_DELETE(_deepData, glm::MemoryStatIds::GolaemDeep);
            }
        };
        typedef GlmGeometryGenerationContext_0 GlmGeometryGenerationContext;

        struct GeometryBehaviorInfo
        {
            int _idGeometryFileIdx;
            bool _blendingModeIdx;
            int _geoDataIndex;
        };

        //-------------------------------------------------------------------------
        // Functions
        //-------------------------------------------------------------------------

        // check if a GlmGeometryGenerationContext is valid
        extern GLM_CROWDIO_API bool isGeometryGenerationValid(const GlmGeometryGenerationContext* context);

        // initialize the entities geometry generation for a frame, to be called before glmCreateEntityGeometry
        // context input parameters must be allocated and set before calling
        // context output parameters will be allocated and set by this function
        extern GLM_CROWDIO_API GlmGeometryGenerationStatus glmBeginGeometryGeneration(GlmGeometryGenerationContext* context, const char* layoutJSONContent=NULL, size_t layoutJSONContentSize=0, int layoutJSONContentIndex=-1);

        // create an entity geometry, allocates geometry
        // context output parameter _shaderGroups will be modified by this function
        // mode parameter is used to either generate mesh and shader names, geometry or both of them
        extern GLM_CROWDIO_API GlmGeometryGenerationStatus glmCreateEntityGeometry(GlmEntityGeometry* geometry, GlmGeometryGenerationContext* context, const GlmEntityBoundingBox* bbox, GlmEntityInstanceMatrices& instanceMatrices);

        struct GLM_CROWDIO_API InputEntityGeoData
        {
            const GlmSimulationData* _simuData = NULL;
            int32_t _characterIdx = -1;
            const GolaemCharacter* _character = NULL;
            const glm::PODArray<int>* _assets = NULL;
            const glm::PODArray<int>* _deepAssets = NULL;
            const ShaderAssetDataContainer* _shaderDataContainer = NULL;
            uint32_t _entityIndex = 0;
            int32_t _entityToBakeIndex = -1;
            int64_t _entityId = -1;
            glm::PODArray<double> _frames;
            glm::PODArray<const GlmFrameData*> _frameDatas;
            Array<GlmString> _dirMapRules;
            bool _enableLOD = false;
            const float* _entityPos = NULL;
            const float* _cameraWorldPosition = NULL;
            short _geometryTag = 0;
            CrowdFBXStorage* _fbxStorage = NULL;
            CrowdFBXBaker* _fbxBaker = NULL;
            uint32_t _maxVerticesPerFace = UINT_MAX;
            bool _generateFur = false;
            bool _generateTangents = false;
            int32_t _geoFileIndex = -1;
        };

        struct GeometryType
        {
            enum Value
            {
                FBX,
                GCG,
                END
            };
        };

        class GLM_CROWDIO_API OutputEntityGeoData
        {
        public:
            uint16_t _entityType;
            glm::PODArray<size_t> _geometryFileIndexes;
            glm::PODArray<CrowdFBXCharacter*> _fbxCharacters;
            glm::PODArray<CrowdGcgCharacter*> _gcgCharacters;
            GeometryType::Value _geoType;
            GeometryBehaviorInfo _geoBeInfo;
            glm::PODArray<glm::crowdio::FurIds> _furIdsArray;
            GlmMap<GlmString, size_t> _furCacheMap;
            Array<FurCache::SP> _furCacheArray;
            glm::Array<glm::GlmString> _meshAssetNames;
            glm::Array<glm::GlmString> _meshAssetAliases;
            glm::PODArray<size_t> _meshAssetNameIndices;
            glm::PODArray<int> _meshAssetMaterialIndices;
            glm::PODArray<int> _gchaMeshIds;
            glm::Array<glm::GlmString> _gchaMeshDeepConfFile;
            glm::PODArray<int> _furAssetIds;
            glm::PODArray<uint32_t> _transformIndicesInGcgFile;
            glm::Array<glm::Array<glm::Array<glm::Vector3>>> _deformedVertices; // _deformedVertices[iFrame][iMesh][Vertex0...n]
            glm::Array<glm::Array<glm::Array<glm::Vector3>>> _deformedNormals;
            glm::Array<glm::Array<glm::Array<glm::Vector3>>> _deformedTangents;
            glm::Array<glm::Array<glm::Array<glm::Vector3>>> _deformedFurVertices;

            glm::Array<glm::Array<glm::Array<glm::Vector3>>> _deepVerticesDelta; // _deepVerticesDelta[iFrame][iMesh][Vertex0...n] to be used like additive blendshapes before skinning

            glm::PODArray<int> _meshShadingGroups;
            glm::PODArray<int> _furShadingGroups;

            virtual ~OutputEntityGeoData();

            // releases fbx locks if needed
            void finish();

            virtual void setSkinningDataCount(size_t count);
            virtual BonesSkinningData* getSkinningData(size_t index);
        };

        extern GLM_CROWDIO_API GlmGeometryGenerationStatus glmPrepareEntityGeometry(const InputEntityGeoData* inputData, OutputEntityGeoData* outputData,
            GlmGeometryGenerationContext* context = 0,
            int crowdFieldIndex = -1);

        // free a single mesh after it has been used to initiate render data :
        extern GLM_CROWDIO_API void glmDestroyEntityGeometryMesh(GlmMesh* renderMesh, size_t frameCount);

        // free a single fur after it has been used to initiate render data :
        extern GLM_CROWDIO_API void glmDestroyEntityGeometryFur(GlmMeshFur* renderFur, size_t frameCount);

        // destroy an entity geometry attributes
        extern GLM_CROWDIO_API void glmDestroyEntityGeometryAttributes(GlmEntityGeometry* geometry, size_t frameCount);

        // destroy an entity geometry
        extern GLM_CROWDIO_API void glmDestroyEntityGeometry(GlmEntityGeometry* geometry, const GlmGeometryGenerationContext* context, const GlmEntityBoundingBox* bbox);

        // deallocate the context output parameters, context input parameters must be deallocated by the user
        extern GLM_CROWDIO_API GlmGeometryGenerationStatus glmEndGeometryGeneration(GlmGeometryGenerationContext* context);

        //-----------------------------------------------------------------------------
        extern GLM_CROWDIO_API void getLodOverridesFromCache(
            PODArray<float>& overrideMinLodDistances,
            PODArray<float>& overrideMaxLodDistances,
            const GolaemCharacter* character,
            const GlmSimulationData* simuData,
            const PODArray<const GlmFrameData*>& frameDatas,
            int32_t entityToBakeIndex,
            short geometryTag);

        extern GLM_CROWDIO_API void getLodOverridesFromCache(PODArray<float>& overrideMinLodDistances, PODArray<float>& overrideMaxLodDistances, const InputEntityGeoData* inputData);

        /**
         * Initializes the InputEntityGeoData structure with relevant data for geometry generation.
         *
         * @param inputData Reference to the InputEntityGeoData structure to initialize.
         * @param geometryContext Pointer to the geometry generation context containing input and output parameters.
         * @param contextData Pointer to additional geometry context data.
         * @param entityBbox Pointer to the entity bounding box data for the entity being processed.
         *
         * Preconditions:
         * - geometryContext, contextData, and entityBbox must be valid pointers.
         *
         * Postconditions:
         * - inputData will be populated with simulation, character, asset, and geometry information for the entity.
         * - inputData._character may be NULL if the character is not found; callers should check for this case.
         */
        extern GLM_CROWDIO_API void initInputEntityGeoData(InputEntityGeoData& inputData, GlmGeometryGenerationContext* geometryContext, GeometryContextData* contextData, const GlmEntityBoundingBox* entityBbox);

    } //namespace crowdio
} //namespace glm
