@ -114,9 +114,11 @@ static Model LoadOBJ(const char *fileName); // Load OBJ mesh data
# endif
# endif
# if defined(SUPPORT_FILEFORMAT_IQM)
# if defined(SUPPORT_FILEFORMAT_IQM)
static Model LoadIQM ( const char * fileName ) ; / / Load IQM mesh data
static Model LoadIQM ( const char * fileName ) ; / / Load IQM mesh data
static ModelAnimation * LoadIQMModelAnimations ( const char * fileName , int * animCount ) ; / / Load IQM animation data
# endif
# endif
# if defined(SUPPORT_FILEFORMAT_GLTF)
# if defined(SUPPORT_FILEFORMAT_GLTF)
static Model LoadGLTF ( const char * fileName ) ; / / Load GLTF mesh data
static Model LoadGLTF ( const char * fileName ) ; / / Load GLTF mesh data
static ModelAnimation * LoadGLTFModelAnimations ( const char * fileName , int * animCount ) ; / / Load GLTF animation data
# endif
# endif
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -852,7 +854,7 @@ Mesh *LoadMeshes(const char *fileName, int *meshCount)
/ / Upload mesh vertex data to GPU
/ / Upload mesh vertex data to GPU
void UploadMesh ( Mesh * mesh )
void UploadMesh ( Mesh * mesh )
{
{
rlLoadMesh ( o">& mesh , false ) ; / / Static mesh by default
rlLoadMesh ( mesh , false ) ; / / Static mesh by default
}
}
/ / Unload mesh from memory ( RAM and / or VRAM )
/ / Unload mesh from memory ( RAM and / or VRAM )
@ -1014,211 +1016,15 @@ void SetModelMeshMaterial(Model *model, int meshId, int materialId)
/ / Load model animations from file
/ / Load model animations from file
ModelAnimation * LoadModelAnimations ( const char * fileName , int * animCount )
ModelAnimation * LoadModelAnimations ( const char * fileName , int * animCount )
{
{
# define IQM_MAGIC "INTERQUAKEMODEL" / / IQM file magic number
# define IQM_VERSION 2 / / only IQM version 2 supported
unsigned int fileSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & fileSize ) ;
unsigned char * fileDataPtr = fileData ;
typedef struct IQMHeader {
char magic [ 16 ] ;
unsigned int version ;
unsigned int filesize ;
unsigned int flags ;
unsigned int num_text , ofs_text ;
unsigned int num_meshes , ofs_meshes ;
unsigned int num_vertexarrays , num_vertexes , ofs_vertexarrays ;
unsigned int num_triangles , ofs_triangles , ofs_adjacency ;
unsigned int num_joints , ofs_joints ;
unsigned int num_poses , ofs_poses ;
unsigned int num_anims , ofs_anims ;
unsigned int num_frames , num_framechannels , ofs_frames , ofs_bounds ;
unsigned int num_comment , ofs_comment ;
unsigned int num_extensions , ofs_extensions ;
} IQMHeader ;
typedef struct IQMPose {
int parent ;
unsigned int mask ;
float channeloffset [ 10 ] ;
float channelscale [ 10 ] ;
} IQMPose ;
typedef struct IQMAnim {
unsigned int name ;
unsigned int first_frame , num_frames ;
float framerate ;
unsigned int flags ;
} IQMAnim ;
/ / In case file can not be read , return an empty model
if ( fileDataPtr = = NULL ) return NULL ;
/ / Read IQM header
IQMHeader * iqmHeader = ( IQMHeader * ) fileDataPtr ;
if ( memcmp ( iqmHeader - > magic , IQM_MAGIC , sizeof ( IQM_MAGIC ) ) ! = 0 )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] IQM file is not a valid model " , fileName ) ;
return NULL ;
}
if ( iqmHeader - > version ! = IQM_VERSION )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] IQM file version not supported (%i) " , fileName , iqmHeader - > version ) ;
return NULL ;
}
/ / Get bones data
IQMPose * poses = RL_MALLOC ( iqmHeader - > num_poses * sizeof ( IQMPose ) ) ;
/ / fseek ( iqmFile , iqmHeader - > ofs_poses , SEEK_SET ) ;
/ / fread ( poses , iqmHeader - > num_poses * sizeof ( IQMPose ) , 1 , iqmFile ) ;
memcpy ( poses , fileDataPtr + iqmHeader - > ofs_poses , iqmHeader - > num_poses * sizeof ( IQMPose ) ) ;
/ / Get animations data
* animCount = iqmHeader - > num_anims ;
IQMAnim * anim = RL_MALLOC ( iqmHeader - > num_anims * sizeof ( IQMAnim ) ) ;
/ / fseek ( iqmFile , iqmHeader - > ofs_anims , SEEK_SET ) ;
/ / fread ( anim , iqmHeader - > num_anims * sizeof ( IQMAnim ) , 1 , iqmFile ) ;
memcpy ( anim , fileDataPtr + iqmHeader - > ofs_anims , iqmHeader - > num_anims * sizeof ( IQMAnim ) ) ;
ModelAnimation * animations = RL_MALLOC ( iqmHeader - > num_anims * sizeof ( ModelAnimation ) ) ;
/ / frameposes
unsigned short * framedata = RL_MALLOC ( iqmHeader - > num_frames * iqmHeader - > num_framechannels * sizeof ( unsigned short ) ) ;
/ / fseek ( iqmFile , iqmHeader - > ofs_frames , SEEK_SET ) ;
/ / fread ( framedata , iqmHeader - > num_frames * iqmHeader - > num_framechannels * sizeof ( unsigned short ) , 1 , iqmFile ) ;
memcpy ( framedata , fileDataPtr + iqmHeader - > ofs_frames , iqmHeader - > num_frames * iqmHeader - > num_framechannels * sizeof ( unsigned short ) ) ;
for ( unsigned int a = 0 ; a < iqmHeader - > num_anims ; a + + )
{
animations [ a ] . frameCount = anim [ a ] . num_frames ;
animations [ a ] . boneCount = iqmHeader - > num_poses ;
animations [ a ] . bones = RL_MALLOC ( iqmHeader - > num_poses * sizeof ( BoneInfo ) ) ;
animations [ a ] . framePoses = RL_MALLOC ( anim [ a ] . num_frames * sizeof ( Transform * ) ) ;
/ / animations [ a ] . framerate = anim . framerate ; / / TODO : Use framerate ?
for ( unsigned int j = 0 ; j < iqmHeader - > num_poses ; j + + )
{
strcpy ( animations [ a ] . bones [ j ] . name , " ANIMJOINTNAME " ) ;
animations [ a ] . bones [ j ] . parent = poses [ j ] . parent ;
}
for ( unsigned int j = 0 ; j < anim [ a ] . num_frames ; j + + ) animations [ a ] . framePoses [ j ] = RL_MALLOC ( iqmHeader - > num_poses * sizeof ( Transform ) ) ;
int dcounter = anim [ a ] . first_frame * iqmHeader - > num_framechannels ;
for ( unsigned int frame = 0 ; frame < anim [ a ] . num_frames ; frame + + )
{
for ( unsigned int i = 0 ; i < iqmHeader - > num_poses ; i + + )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . x = poses [ i ] . channeloffset [ 0 ] ;
if ( poses [ i ] . mask & 0x01 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 0 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . translation . y = poses [ i ] . channeloffset [ 1 ] ;
if ( poses [ i ] . mask & 0x02 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 1 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . translation . z = poses [ i ] . channeloffset [ 2 ] ;
if ( poses [ i ] . mask & 0x04 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 2 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation . x = poses [ i ] . channeloffset [ 3 ] ;
if ( poses [ i ] . mask & 0x08 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 3 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation . y = poses [ i ] . channeloffset [ 4 ] ;
if ( poses [ i ] . mask & 0x10 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 4 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation . z = poses [ i ] . channeloffset [ 5 ] ;
if ( poses [ i ] . mask & 0x20 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 5 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation . w = poses [ i ] . channeloffset [ 6 ] ;
if ( poses [ i ] . mask & 0x40 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . w + = framedata [ dcounter ] * poses [ i ] . channelscale [ 6 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . scale . x = poses [ i ] . channeloffset [ 7 ] ;
if ( poses [ i ] . mask & 0x80 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 7 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . scale . y = poses [ i ] . channeloffset [ 8 ] ;
if ( poses [ i ] . mask & 0x100 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 8 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . scale . z = poses [ i ] . channeloffset [ 9 ] ;
if ( poses [ i ] . mask & 0x200 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 9 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation = QuaternionNormalize ( animations [ a ] . framePoses [ frame ] [ i ] . rotation ) ;
}
}
/ / Build frameposes
for ( unsigned int frame = 0 ; frame < anim [ a ] . num_frames ; frame + + )
{
for ( int i = 0 ; i < animations [ a ] . boneCount ; i + + )
{
if ( animations [ a ] . bones [ i ] . parent > = 0 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation = QuaternionMultiply ( animations [ a ] . framePoses [ frame ] [ animations [ a ] . bones [ i ] . parent ] . rotation , animations [ a ] . framePoses [ frame ] [ i ] . rotation ) ;
animations [ a ] . framePoses [ frame ] [ i ] . translation = Vector3RotateByQuaternion ( animations [ a ] . framePoses [ frame ] [ i ] . translation , animations [ a ] . framePoses [ frame ] [ animations [ a ] . bones [ i ] . parent ] . rotation ) ;
animations [ a ] . framePoses [ frame ] [ i ] . translation = Vector3Add ( animations [ a ] . framePoses [ frame ] [ i ] . translation , animations [ a ] . framePoses [ frame ] [ animations [ a ] . bones [ i ] . parent ] . translation ) ;
animations [ a ] . framePoses [ frame ] [ i ] . scale = Vector3Multiply ( animations [ a ] . framePoses [ frame ] [ i ] . scale , animations [ a ] . framePoses [ frame ] [ animations [ a ] . bones [ i ] . parent ] . scale ) ;
}
}
}
}
RL_FREE ( fileData ) ;
RL_FREE ( framedata ) ;
RL_FREE ( poses ) ;
RL_FREE ( anim ) ;
ModelAnimation * animations = NULL ;
# if defined(SUPPORT_FILEFORMAT_IQM)
if ( IsFileExtension ( fileName , " .iqm " ) ) animations = LoadIQMModelAnimations ( fileName , animCount ) ;
# endif
# if defined(SUPPORT_FILEFORMAT_GLTF)
if ( IsFileExtension ( fileName , " .gltf;.glb " ) ) animations = LoadGLTFModelAnimations ( fileName , animCount ) ;
# endif
return animations ;
return animations ;
}
}
@ -3554,6 +3360,218 @@ static Model LoadIQM(const char *fileName)
return model ;
return model ;
}
}
/ / Load IQM animation data
static ModelAnimation * LoadIQMModelAnimations ( const char * fileName , int * animCount )
{
# define IQM_MAGIC "INTERQUAKEMODEL" / / IQM file magic number
# define IQM_VERSION 2 / / only IQM version 2 supported
unsigned int fileSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & fileSize ) ;
unsigned char * fileDataPtr = fileData ;
typedef struct IQMHeader {
char magic [ 16 ] ;
unsigned int version ;
unsigned int filesize ;
unsigned int flags ;
unsigned int num_text , ofs_text ;
unsigned int num_meshes , ofs_meshes ;
unsigned int num_vertexarrays , num_vertexes , ofs_vertexarrays ;
unsigned int num_triangles , ofs_triangles , ofs_adjacency ;
unsigned int num_joints , ofs_joints ;
unsigned int num_poses , ofs_poses ;
unsigned int num_anims , ofs_anims ;
unsigned int num_frames , num_framechannels , ofs_frames , ofs_bounds ;
unsigned int num_comment , ofs_comment ;
unsigned int num_extensions , ofs_extensions ;
} IQMHeader ;
typedef struct IQMPose {
int parent ;
unsigned int mask ;
float channeloffset [ 10 ] ;
float channelscale [ 10 ] ;
} IQMPose ;
typedef struct IQMAnim {
unsigned int name ;
unsigned int first_frame , num_frames ;
float framerate ;
unsigned int flags ;
} IQMAnim ;
/ / In case file can not be read , return an empty model
if ( fileDataPtr = = NULL ) return NULL ;
/ / Read IQM header
IQMHeader * iqmHeader = ( IQMHeader * ) fileDataPtr ;
if ( memcmp ( iqmHeader - > magic , IQM_MAGIC , sizeof ( IQM_MAGIC ) ) ! = 0 )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] IQM file is not a valid model " , fileName ) ;
return NULL ;
}
if ( iqmHeader - > version ! = IQM_VERSION )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] IQM file version not supported (%i) " , fileName , iqmHeader - > version ) ;
return NULL ;
}
/ / Get bones data
IQMPose * poses = RL_MALLOC ( iqmHeader - > num_poses * sizeof ( IQMPose ) ) ;
/ / fseek ( iqmFile , iqmHeader - > ofs_poses , SEEK_SET ) ;
/ / fread ( poses , iqmHeader - > num_poses * sizeof ( IQMPose ) , 1 , iqmFile ) ;
memcpy ( poses , fileDataPtr + iqmHeader - > ofs_poses , iqmHeader - > num_poses * sizeof ( IQMPose ) ) ;
/ / Get animations data
* animCount = iqmHeader - > num_anims ;
IQMAnim * anim = RL_MALLOC ( iqmHeader - > num_anims * sizeof ( IQMAnim ) ) ;
/ / fseek ( iqmFile , iqmHeader - > ofs_anims , SEEK_SET ) ;
/ / fread ( anim , iqmHeader - > num_anims * sizeof ( IQMAnim ) , 1 , iqmFile ) ;
memcpy ( anim , fileDataPtr + iqmHeader - > ofs_anims , iqmHeader - > num_anims * sizeof ( IQMAnim ) ) ;
ModelAnimation * animations = RL_MALLOC ( iqmHeader - > num_anims * sizeof ( ModelAnimation ) ) ;
/ / frameposes
unsigned short * framedata = RL_MALLOC ( iqmHeader - > num_frames * iqmHeader - > num_framechannels * sizeof ( unsigned short ) ) ;
/ / fseek ( iqmFile , iqmHeader - > ofs_frames , SEEK_SET ) ;
/ / fread ( framedata , iqmHeader - > num_frames * iqmHeader - > num_framechannels * sizeof ( unsigned short ) , 1 , iqmFile ) ;
memcpy ( framedata , fileDataPtr + iqmHeader - > ofs_frames , iqmHeader - > num_frames * iqmHeader - > num_framechannels * sizeof ( unsigned short ) ) ;
for ( unsigned int a = 0 ; a < iqmHeader - > num_anims ; a + + )
{
animations [ a ] . frameCount = anim [ a ] . num_frames ;
animations [ a ] . boneCount = iqmHeader - > num_poses ;
animations [ a ] . bones = RL_MALLOC ( iqmHeader - > num_poses * sizeof ( BoneInfo ) ) ;
animations [ a ] . framePoses = RL_MALLOC ( anim [ a ] . num_frames * sizeof ( Transform * ) ) ;
/ / animations [ a ] . framerate = anim . framerate ; / / TODO : Use framerate ?
for ( unsigned int j = 0 ; j < iqmHeader - > num_poses ; j + + )
{
strcpy ( animations [ a ] . bones [ j ] . name , " ANIMJOINTNAME " ) ;
animations [ a ] . bones [ j ] . parent = poses [ j ] . parent ;
}
for ( unsigned int j = 0 ; j < anim [ a ] . num_frames ; j + + ) animations [ a ] . framePoses [ j ] = RL_MALLOC ( iqmHeader - > num_poses * sizeof ( Transform ) ) ;
int dcounter = anim [ a ] . first_frame * iqmHeader - > num_framechannels ;
for ( unsigned int frame = 0 ; frame < anim [ a ] . num_frames ; frame + + )
{
for ( unsigned int i = 0 ; i < iqmHeader - > num_poses ; i + + )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . x = poses [ i ] . channeloffset [ 0 ] ;
if ( poses [ i ] . mask & 0x01 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 0 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . translation . y = poses [ i ] . channeloffset [ 1 ] ;
if ( poses [ i ] . mask & 0x02 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 1 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . translation . z = poses [ i ] . channeloffset [ 2 ] ;
if ( poses [ i ] . mask & 0x04 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 2 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation . x = poses [ i ] . channeloffset [ 3 ] ;
if ( poses [ i ] . mask & 0x08 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 3 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation . y = poses [ i ] . channeloffset [ 4 ] ;
if ( poses [ i ] . mask & 0x10 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 4 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation . z = poses [ i ] . channeloffset [ 5 ] ;
if ( poses [ i ] . mask & 0x20 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 5 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation . w = poses [ i ] . channeloffset [ 6 ] ;
if ( poses [ i ] . mask & 0x40 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . w + = framedata [ dcounter ] * poses [ i ] . channelscale [ 6 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . scale . x = poses [ i ] . channeloffset [ 7 ] ;
if ( poses [ i ] . mask & 0x80 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 7 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . scale . y = poses [ i ] . channeloffset [ 8 ] ;
if ( poses [ i ] . mask & 0x100 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 8 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . scale . z = poses [ i ] . channeloffset [ 9 ] ;
if ( poses [ i ] . mask & 0x200 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 9 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . rotation = QuaternionNormalize ( animations [ a ] . framePoses [ frame ] [ i ] . rotation ) ;
}
}
/ / Build frameposes
for ( unsigned int frame = 0 ; frame < anim [ a ] . num_frames ; frame + + )
{
for ( int i = 0 ; i < animations [ a ] . boneCount ; i + + )
{
if ( animations [ a ] . bones [ i ] . parent > = 0 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation = QuaternionMultiply ( animations [ a ] . framePoses [ frame ] [ animations [ a ] . bones [ i ] . parent ] . rotation , animations [ a ] . framePoses [ frame ] [ i ] . rotation ) ;
animations [ a ] . framePoses [ frame ] [ i ] . translation = Vector3RotateByQuaternion ( animations [ a ] . framePoses [ frame ] [ i ] . translation , animations [ a ] . framePoses [ frame ] [ animations [ a ] . bones [ i ] . parent ] . rotation ) ;
animations [ a ] . framePoses [ frame ] [ i ] . translation = Vector3Add ( animations [ a ] . framePoses [ frame ] [ i ] . translation , animations [ a ] . framePoses [ frame ] [ animations [ a ] . bones [ i ] . parent ] . translation ) ;
animations [ a ] . framePoses [ frame ] [ i ] . scale = Vector3Multiply ( animations [ a ] . framePoses [ frame ] [ i ] . scale , animations [ a ] . framePoses [ frame ] [ animations [ a ] . bones [ i ] . parent ] . scale ) ;
}
}
}
}
RL_FREE ( fileData ) ;
RL_FREE ( framedata ) ;
RL_FREE ( poses ) ;
RL_FREE ( anim ) ;
return animations ;
}
# endif
# endif
# if defined(SUPPORT_FILEFORMAT_GLTF)
# if defined(SUPPORT_FILEFORMAT_GLTF)
@ -3763,7 +3781,8 @@ static Model LoadGLTF(const char *fileName)
int primitivesCount = 0 ;
int primitivesCount = 0 ;
for ( unsigned int i = 0 ; i < data - > meshes_count ; i + + ) primitivesCount + = ( int ) data - > meshes [ i ] . primitives_count ;
for ( unsigned int i = 0 ; i < data - > meshes_count ; i + + )
primitivesCount + = ( int ) data - > meshes [ i ] . primitives_count ;
/ / Process glTF data and map to model
/ / Process glTF data and map to model
model . meshCount = primitivesCount ;
model . meshCount = primitivesCount ;
@ -3771,9 +3790,65 @@ static Model LoadGLTF(const char *fileName)
model . materialCount = ( int ) data - > materials_count + 1 ;
model . materialCount = ( int ) data - > materials_count + 1 ;
model . materials = RL_MALLOC ( model . materialCount * sizeof ( Material ) ) ;
model . materials = RL_MALLOC ( model . materialCount * sizeof ( Material ) ) ;
model . meshMaterial = RL_MALLOC ( model . meshCount * sizeof ( int ) ) ;
model . meshMaterial = RL_MALLOC ( model . meshCount * sizeof ( int ) ) ;
for ( int i = 0 ; i < model . meshCount ; i + + ) model . meshes [ i ] . vboId = ( unsigned int * ) RL_CALLOC ( DEFAULT_MESH_VERTEX_BUFFERS , sizeof ( unsigned int ) ) ;
model . boneCount = data - > nodes_count ;
model . bones = RL_CALLOC ( model . boneCount , sizeof ( BoneInfo ) ) ;
model . bindPose = RL_CALLOC ( model . boneCount , sizeof ( Transform ) ) ;
for ( int i = 0 ; i < model . meshCount ; i + + )
model . meshes [ i ] . vboId = ( unsigned int * ) RL_CALLOC ( DEFAULT_MESH_VERTEX_BUFFERS , sizeof ( unsigned int ) ) ;
for ( unsigned int j = 0 ; j < data - > nodes_count ; j + + )
{
strcpy ( model . bones [ j ] . name , data - > nodes [ j ] . name = = 0 ? " ANIMJOINT " : data - > nodes [ j ] . name ) ;
model . bones [ j ] . parent = j ! = 0 ? data - > nodes [ j ] . parent - data - > nodes : 0 ;
}
for ( unsigned int i = 0 ; i < data - > nodes_count ; i + + )
{
if ( data - > nodes [ i ] . has_translation )
{
memcpy ( & model . bindPose [ i ] . translation , data - > nodes [ i ] . translation , 3 * sizeof ( float ) ) ;
}
else
{
model . bindPose [ i ] . translation = Vector3Zero ( ) ;
}
if ( data - > nodes [ i ] . has_rotation )
{
memcpy ( & model . bindPose [ i ] . rotation , data - > nodes [ i ] . rotation , 4 * sizeof ( float ) ) ;
}
else
{
model . bindPose [ i ] . rotation = QuaternionIdentity ( ) ;
}
model . bindPose [ i ] . rotation = QuaternionNormalize ( model . bindPose [ i ] . rotation ) ;
if ( data - > nodes [ i ] . has_scale )
{
memcpy ( & model . bindPose [ i ] . scale , data - > nodes [ i ] . scale , 3 * sizeof ( float ) ) ;
}
else
{
model . bindPose [ i ] . scale = Vector3One ( ) ;
}
}
for ( int i = 0 ; i < model . boneCount ; i + + )
{
Transform * currentTransform = model . bindPose + i ;
BoneInfo * currentBone = model . bones + i ;
Transform * parentTransform = model . bindPose + currentBone - > parent ;
if ( currentBone - > parent > = 0 )
{
currentTransform - > rotation = QuaternionMultiply ( parentTransform - > rotation , currentTransform - > rotation ) ;
currentTransform - > translation = Vector3RotateByQuaternion ( currentTransform - > translation , parentTransform - > rotation ) ;
currentTransform - > translation = Vector3Add ( currentTransform - > translation , parentTransform - > translation ) ;
currentTransform - > scale = Vector3Multiply ( parentTransform - > scale , parentTransform - > scale ) ;
}
}
for ( int i = 0 ; i < model . materialCount - 1 ; i + + )
for ( int i = 0 ; i < model . materialCount - 1 ; i + + )
{
{
model . materials [ i ] = LoadMaterialDefault ( ) ;
model . materials [ i ] = LoadMaterialDefault ( ) ;
@ -3854,16 +3929,22 @@ static Model LoadGLTF(const char *fileName)
{
{
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
model . meshes [ primitiveIndex ] . vertexCount = ( int ) acc - > count ;
model . meshes [ primitiveIndex ] . vertexCount = ( int ) acc - > count ;
model . meshes [ primitiveIndex ] . vertices = RL_MALLOC ( model . meshes [ primitiveIndex ] . vertexCount * 3 * sizeof ( float ) ) ;
int bufferSize = model . meshes [ primitiveIndex ] . vertexCount * 3 * sizeof ( float ) ;
model . meshes [ primitiveIndex ] . vertices = RL_MALLOC ( bufferSize ) ;
model . meshes [ primitiveIndex ] . animVertices = RL_MALLOC ( bufferSize ) ;
LOAD_ACCESSOR ( float , 3 , acc , model . meshes [ primitiveIndex ] . vertices )
LOAD_ACCESSOR ( float , 3 , acc , model . meshes [ primitiveIndex ] . vertices ) ;
memcpy ( model . meshes [ primitiveIndex ] . animVertices , model . meshes [ primitiveIndex ] . vertices , bufferSize ) ;
}
}
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_normal )
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_normal )
{
{
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
model . meshes [ primitiveIndex ] . normals = RL_MALLOC ( acc - > count * 3 * sizeof ( float ) ) ;
int bufferSize = acc - > count * 3 * sizeof ( float ) ;
model . meshes [ primitiveIndex ] . normals = RL_MALLOC ( bufferSize ) ;
model . meshes [ primitiveIndex ] . animNormals = RL_MALLOC ( bufferSize ) ;
LOAD_ACCESSOR ( float , 3 , acc , model . meshes [ primitiveIndex ] . normals )
LOAD_ACCESSOR ( float , 3 , acc , model . meshes [ primitiveIndex ] . normals ) ;
memcpy ( model . meshes [ primitiveIndex ] . animNormals , model . meshes [ primitiveIndex ] . normals , bufferSize ) ;
}
}
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_texcoord )
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_texcoord )
{
{
@ -3880,6 +3961,45 @@ static Model LoadGLTF(const char *fileName)
TRACELOG ( LOG_WARNING , " MODEL: [%s] glTF texture coordinates must be float " , fileName ) ;
TRACELOG ( LOG_WARNING , " MODEL: [%s] glTF texture coordinates must be float " , fileName ) ;
}
}
}
}
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_joints )
{
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
if ( acc - > component_type = = cgltf_component_type_r_16u )
{
model . meshes [ primitiveIndex ] . boneIds = RL_MALLOC ( sizeof ( int ) * acc - > count * 4 ) ;
short * bones = RL_MALLOC ( sizeof ( short ) * acc - > count * 4 ) ;
LOAD_ACCESSOR ( short , 4 , acc , bones ) ;
for ( int a = 0 ; a < acc - > count * 4 ; a + + )
{
cgltf_node * skinJoint = data - > skins - > joints [ bones [ a ] ] ;
for ( int k = 0 ; k < data - > nodes_count ; k + + )
{
if ( & ( data - > nodes [ k ] ) = = skinJoint )
{
model . meshes [ primitiveIndex ] . boneIds [ a ] = k ;
break ;
}
}
}
RL_FREE ( bones ) ;
}
else
{
/ / TODO : Support other size of bone index ?
TRACELOG ( LOG_WARNING , " MODEL: [%s] glTF bones in unexpected format " , fileName ) ;
}
}
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_weights )
{
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
model . meshes [ primitiveIndex ] . boneWeights = RL_MALLOC ( acc - > count * 4 * sizeof ( float ) ) ;
LOAD_ACCESSOR ( float , 4 , acc , model . meshes [ primitiveIndex ] . boneWeights )
}
}
}
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . indices ;
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . indices ;
@ -3913,9 +4033,12 @@ static Model LoadGLTF(const char *fileName)
{
{
model . meshMaterial [ primitiveIndex ] = model . materialCount - 1 ; ;
model . meshMaterial [ primitiveIndex ] = model . materialCount - 1 ; ;
}
}
/ / if ( data - > meshes [ i ] . )
primitiveIndex + + ;
primitiveIndex + + ;
}
}
}
}
cgltf_free ( data ) ;
cgltf_free ( data ) ;
@ -3926,4 +4049,146 @@ static Model LoadGLTF(const char *fileName)
return model ;
return model ;
}
}
/ / LoadGLTF loads in animation data from given filename
static ModelAnimation * LoadGLTFModelAnimations ( const char * fileName , int * animCount )
{
/***********************************************************************************
Function implemented by Hristo Stamenov ( @ object71 )
Features :
- Supports . gltf and . glb files
Some restrictions ( not exhaustive ) :
- . . .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/ / glTF file loading
unsigned int dataSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
ModelAnimation * animations = NULL ;
if ( fileData = = NULL ) return animations ;
/ / glTF data loading
cgltf_options options = { 0 } ;
cgltf_data * data = NULL ;
cgltf_result result = cgltf_parse ( & options , fileData , dataSize , & data ) ;
if ( result = = cgltf_result_success )
{
TRACELOG ( LOG_INFO , " MODEL: [%s] glTF animations (%s) count: %i " , fileName , ( data - > file_type = = 2 ) ? " glb " :
" gltf " , data - > animations_count ) ;
animations = RL_MALLOC ( data - > animations_count * sizeof ( ModelAnimation ) ) ;
for ( unsigned int a = 0 ; a < data - > animations_count ; a + + )
{
cgltf_animation * animation = data - > animations + a ;
ModelAnimation * output = animations + a ;
output - > frameCount = animation - > channels - > sampler - > input - > count ;
output - > boneCount = data - > nodes_count ;
output - > bones = RL_MALLOC ( output - > boneCount * sizeof ( BoneInfo ) ) ;
output - > framePoses = RL_MALLOC ( output - > frameCount * sizeof ( Transform * ) ) ;
for ( unsigned int j = 0 ; j < data - > nodes_count ; j + + )
{
strcpy ( output - > bones [ j ] . name , data - > nodes [ j ] . name = = 0 ? " ANIMJOINT " : data - > nodes [ j ] . name ) ;
output - > bones [ j ] . parent = j ! = 0 ? ( int ) ( data - > nodes [ j ] . parent - data - > nodes ) : 0 ;
}
for ( unsigned int j = 0 ; j < output - > frameCount ; j + + )
output - > framePoses [ j ] = RL_MALLOC ( output - > frameCount * data - > nodes_count * sizeof ( Transform ) ) ;
for ( unsigned int frame = 0 ; frame < output - > frameCount ; frame + + )
{
for ( unsigned int i = 0 ; i < data - > nodes_count ; i + + )
{
output - > framePoses [ frame ] [ i ] . translation = Vector3Zero ( ) ;
output - > framePoses [ frame ] [ i ] . rotation = QuaternionIdentity ( ) ;
output - > framePoses [ frame ] [ i ] . rotation = QuaternionNormalize ( output - > framePoses [ frame ] [ i ] . rotation ) ;
output - > framePoses [ frame ] [ i ] . scale = Vector3One ( ) ;
}
}
for ( int channelId = 0 ; channelId < animation - > channels_count ; channelId + + )
{
cgltf_animation_channel * channel = animation - > channels + channelId ;
cgltf_animation_sampler * sampler = channel - > sampler ;
int boneId = channel - > target_node - data - > nodes ;
for ( int frame = 0 ; frame < output - > frameCount ; frame + + )
{
if ( channel - > target_path = = cgltf_animation_path_type_translation ) {
Vector3 translation ;
if ( cgltf_accessor_read_float ( sampler - > output , frame , ( float * ) & translation , 3 ) )
{
output - > framePoses [ frame ] [ boneId ] . translation = translation ;
}
else if ( output - > frameCount = = 2 )
{
memcpy ( & translation , frame = = 0 ? & ( sampler - > output - > min ) : & ( sampler - > output - > max ) , 3 * sizeof ( float ) ) ;
output - > framePoses [ frame ] [ boneId ] . translation = translation ;
}
}
if ( channel - > target_path = = cgltf_animation_path_type_rotation ) {
Quaternion rotation ;
if ( cgltf_accessor_read_float ( sampler - > output , frame , ( float * ) & rotation , 4 ) )
{
output - > framePoses [ frame ] [ boneId ] . rotation = rotation ;
output - > framePoses [ frame ] [ boneId ] . rotation = QuaternionNormalize ( output - > framePoses [ frame ] [ boneId ] . rotation ) ;
}
else if ( output - > frameCount = = 2 )
{
memcpy ( & rotation , frame = = 0 ? & ( sampler - > output - > min ) : & ( sampler - > output - > max ) , 4 * sizeof ( float ) ) ;
output - > framePoses [ frame ] [ boneId ] . rotation = rotation ;
output - > framePoses [ frame ] [ boneId ] . rotation = QuaternionNormalize ( output - > framePoses [ frame ] [ boneId ] . rotation ) ;
}
}
if ( channel - > target_path = = cgltf_animation_path_type_scale ) {
Vector3 scale ;
if ( cgltf_accessor_read_float ( sampler - > output , frame , ( float * ) & scale , 4 ) )
{
output - > framePoses [ frame ] [ boneId ] . scale = scale ;
}
else if ( output - > frameCount = = 2 )
{
memcpy ( & scale , frame = = 0 ? & ( sampler - > output - > min ) : & ( sampler - > output - > max ) , 3 * sizeof ( float ) ) ;
output - > framePoses [ frame ] [ boneId ] . scale = scale ;
}
}
}
}
/ / Build frameposes
for ( unsigned int frame = 0 ; frame < output - > frameCount ; frame + + )
{
for ( int i = 0 ; i < output - > boneCount ; i + + )
{
if ( output - > bones [ i ] . parent > = 0 )
{
output - > framePoses [ frame ] [ i ] . rotation = QuaternionMultiply ( output - > framePoses [ frame ] [ output - > bones [ i ] . parent ] . rotation , output - > framePoses [ frame ] [ i ] . rotation ) ;
output - > framePoses [ frame ] [ i ] . translation = Vector3RotateByQuaternion ( output - > framePoses [ frame ] [ i ] . translation , output - > framePoses [ frame ] [ output - > bones [ i ] . parent ] . rotation ) ;
output - > framePoses [ frame ] [ i ] . translation = Vector3Add ( output - > framePoses [ frame ] [ i ] . translation , output - > framePoses [ frame ] [ output - > bones [ i ] . parent ] . translation ) ;
output - > framePoses [ frame ] [ i ] . scale = Vector3Multiply ( output - > framePoses [ frame ] [ i ] . scale , output - > framePoses [ frame ] [ output - > bones [ i ] . parent ] . scale ) ;
}
}
}
}
cgltf_free ( data ) ;
}
else TRACELOG ( LOG_WARNING , " : [%s] Failed to load glTF data " , fileName ) ;
RL_FREE ( fileData ) ;
return animations ;
}
# endif
# endif