//================================================================
// E3D library
//
// Mesh Vertex skinning
//
// AUTHOR:  Gabor Nagy
// DATE:    2023-May-28 15:54:36
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#ifndef _E3D_SkinMesh_h
#define _E3D_SkinMesh_h

#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif



//------------------------------------------------
// Skinning data on Meshes
//------------------------------------------------

typedef struct
{
	EIndex  Index;float  Weight;
} E3dIndexAndWeight;


// This struct stores all the points influenced by a particular bone.
// It is used for editing skinning / rigging. It's not necessary for playback.
// It allows to efficiently visualize and edit Vertex weights for a particular bone.
// It's also used by some exporters, for better efficiency.
//
typedef struct
{
	EListA(, E3dIndexAndWeight, PointIndicesAndWeights);
	EBool  Uptodate;
} E3dDeformerCluster;


// Per joint / bone information
//
typedef struct
{
	E3dNode*   Node;                    // Pointer to the Node.
	E3dIndex   IndexInDrivingNodes;
	E3dMatrix  WorldToBindPoseMatrix;   // The inverse of the LocalToWorld Matrix at the time of binding. At render time, the LocalToWorldMatrix of the joint is multiplied by this, to get SkinningMatrix: E3dMatrix_Mult4x4_3(LSkinJoint->WorldToBindPoseMatrix, LNode->LocalToWorldMatrix, LSkinJoint->SkinningMatrix);
	E3dMatrix  SkinningMatrix,          // LocalToWorldMatrix * WorldToBindPoseMatrix
	           SkinningNormalMatrix;    // Similar to above, but for normals, we need the transpose-adjoint, rather than the inverse (plus, we only use the top-left 3x3 sub-matrix)

	E3dDeformerCluster  Cluster;
} E3dSkinJoint, *E3dSkinJointPtr;
EListA_t(E3dSkinJoint);

static inline void E3dSkinJoint_Init(E3dSkinJoint* PSkinJoint)
{
	EClr_p(PSkinJoint);
	PSkinJoint->IndexInDrivingNodes = -1;
}


// Per Vertex information
//
typedef struct
{
	E3dCoord3       BindPosition;    // Position of the Vertex at the time of binding
	E3dSkinJoint**  Bones;EIndex  NumOfBones;
	E3dWeight*      Weights;         // Weight values for the bones affecting this SkinVertex. The number of values here is NumOfBones
} E3dSkinVertex;


typedef struct
{
	void*     WeightColors;               // Used for visualizing skinning weights
	uint16_t  WeightColorsComponentType;  // EResFLOAT32, EResINT16 etc.

	E3dNode*  ShowingWeightsForNode;
} E3dSkinMeshGPUData;


typedef struct
{
	E3dObjectComponentCore;

// E3dSkinMeshInfo part
//
	EListA_s(, E3dSkinJoint, Joints);
	E3dSkinVertex*    SkinVertices;               // The number of entries in this array, matches the number of Vertices on the Mesh, so there's no "Count" member here.

	EBool             RecomputeNormals;
	EBool             DeformedSinceNormalUpdate;

// GPU skinning
//
	EList(, E3dNode*, DrivingNodes);              // Nodes that actually affect the Vertices (the ones with non-zero weights)
	E3dGLCoordinate*  SkinningVertexMatrices;
	E3dGLCoordinate*  SkinningNormalMatrices;
	EBool             UseGPU;
	uint16_t          ShowWeights;    // 0: no visualization, EpPaletteGRAY, EpPalletteTURBO

	EInterface        InputInterface;             // This is added to the Joint Nodesas an output

	E3dSkinMeshGPUData*  GPUData;
} E3dSkinMeshInfo;



//------------------------------------------------
// Skinning data on Nodes
//------------------------------------------------
// Note that "typedef struct E3dModel_S	E3dModel;" is in Geometry.h, because
// this file includes it and E3dGeometry needs E3dModel
//
typedef struct
{
	E3dGeometry*  Geometry;
	EBool         ShowWeights;
} E3dNodeSkinShape;

typedef struct
{
	E3dNodeComponentCore;

	EList(, E3dNodeSkinShape*, SkinnedShapes);
} E3dSkinningJointInfo;


extern E3dObjectComponentClass*  E3dSkinMeshInfo_class;
extern E3dNodeComponentClass*    E3dSkinningJointInfo_class;

extern E3dSkinVertex*  E3dSkinVertices_New(const EIndex PNumOfVertices, const EBool PInitialize);

extern void           E3dSkinJoint_UpdateClusterData(E3dSkinJoint* PSkinJoint, E3dObject* PObject, E3dSkinMeshInfo* PSkin);
extern void           E3dSkinMesh_AssignSkeleton(E3dMesh* PMesh, E3dNode* PSkeletonRoot);


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif

#endif	// _E3D_SkinMesh_h
