@ -4895,6 +4895,7 @@ static Model LoadGLTF(const char *fileName)
/*********************************************************************************************
/*********************************************************************************************
Function implemented by Wilhem Barbier ( @ wbrbr ) , with modifications by Tyler Bezera ( @ gamerfiend )
Function implemented by Wilhem Barbier ( @ wbrbr ) , with modifications by Tyler Bezera ( @ gamerfiend )
Transform handling implemented by Paul Melis ( @ paulmelis ) .
Reviewed by Ramon Santamaria ( @ raysan5 )
Reviewed by Ramon Santamaria ( @ raysan5 )
FEATURES :
FEATURES :
@ -4904,6 +4905,10 @@ static Model LoadGLTF(const char *fileName)
PBR specular / glossiness flow and extended texture flows not supported
PBR specular / glossiness flow and extended texture flows not supported
- Supports multiple meshes per model ( every primitives is loaded as a separate mesh )
- Supports multiple meshes per model ( every primitives is loaded as a separate mesh )
- Supports basic animations
- Supports basic animations
- Transforms , including parent - child relations , are applied on the mesh data , but the
hierarchy is not kept ( as it can ' t be represented ) .
- Mesh instances in the glTF file ( i . e . same mesh linked from multiple nodes )
are turned into separate raylib Meshes .
RESTRICTIONS :
RESTRICTIONS :
- Only triangle meshes supported
- Only triangle meshes supported
@ -4913,7 +4918,8 @@ static Model LoadGLTF(const char *fileName)
> Texcoords : vec2 : float
> Texcoords : vec2 : float
> Colors : vec4 : u8 , u16 , f32 ( normalized )
> Colors : vec4 : u8 , u16 , f32 ( normalized )
> Indices : u16 , u32 ( truncated to u16 )
> Indices : u16 , u32 ( truncated to u16 )
- Node hierarchies or transforms not supported
- Scenes defined in the glTF file are ignored . All nodes in the file
are used .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@ -4965,8 +4971,22 @@ static Model LoadGLTF(const char *fileName)
if ( result ! = cgltf_result_success ) TRACELOG ( LOG_INFO , " MODEL: [%s] Failed to load mesh/material buffers " , fileName ) ;
if ( result ! = cgltf_result_success ) TRACELOG ( LOG_INFO , " MODEL: [%s] Failed to load mesh/material buffers " , fileName ) ;
int primitivesCount = 0 ;
int primitivesCount = 0 ;
/ / NOTE : We will load every primitive in the glTF as a separate raylib mesh
for ( unsigned int i = 0 ; i < data - > meshes_count ; i + + ) primitivesCount + = ( int ) data - > meshes [ i ] . primitives_count ;
/ / NOTE : We will load every primitive in the glTF as a separate raylib Mesh .
/ / Determine total number of meshes needed from the node hierarchy .
for ( unsigned int i = 0 ; i < data - > nodes_count ; i + + )
{
cgltf_node * node = & ( data - > nodes [ i ] ) ;
cgltf_mesh * mesh = node - > mesh ;
if ( ! mesh )
continue ;
for ( unsigned int p = 0 ; p < mesh - > primitives_count ; p + + )
{
if ( mesh - > primitives [ p ] . type = = cgltf_primitive_type_triangles )
primitivesCount + + ;
}
}
TRACELOG ( LOG_DEBUG , " > Primitives (triangles only) count based on hierarchy : %i " , primitivesCount ) ;
/ / Load our model data : meshes and materials
/ / Load our model data : meshes and materials
model . meshCount = primitivesCount ;
model . meshCount = primitivesCount ;
@ -5069,27 +5089,51 @@ static Model LoadGLTF(const char *fileName)
/ / has_clearcoat , has_transmission , has_volume , has_ior , has specular , has_sheen
/ / has_clearcoat , has_transmission , has_volume , has_ior , has specular , has_sheen
}
}
/ / Load meshes data
/ / Visit each node in the hierarchy and process any mesh linked from it .
/ / Each primitive within a glTF node becomes a Raylib Mesh .
/ / The local - to - world transform of each node is used to transform the
/ / points / normals / tangents of the created Mesh ( es ) .
/ / Any glTF mesh linked from more than one Node ( i . e . instancing )
/ / is turned into multiple Mesh ' s , as each Node will have its own
/ / transform applied .
/ / Note : the code below disregards the scenes defined in the file , all nodes are used .
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
for ( unsigned int i = 0 , meshIndex = 0 ; i < data - > meshes_count ; i + + )
int meshIndex = 0 ;
for ( unsigned int i = 0 ; i < data - > nodes_count ; i + + )
{
{
/ / NOTE : meshIndex accumulates primitives
n">cgltf_node * node = & ( data - > nodes [ i ] ) ;
for ( unsigned int p = 0 ; p < data - > meshes [ i ] . primitives_count ; p + + )
cgltf_mesh * mesh = node - > mesh ;
if ( ! mesh )
continue ;
cgltf_float worldTransform [ 16 ] ;
cgltf_node_transform_world ( node , worldTransform ) ;
Matrix worldMatrix = {
worldTransform [ 0 ] , worldTransform [ 4 ] , worldTransform [ 8 ] , worldTransform [ 12 ] ,
worldTransform [ 1 ] , worldTransform [ 5 ] , worldTransform [ 9 ] , worldTransform [ 13 ] ,
worldTransform [ 2 ] , worldTransform [ 6 ] , worldTransform [ 10 ] , worldTransform [ 14 ] ,
worldTransform [ 3 ] , worldTransform [ 7 ] , worldTransform [ 11 ] , worldTransform [ 15 ]
} ;
Matrix worldMatrixNormals = MatrixTranspose ( MatrixInvert ( worldMatrix ) ) ;
for ( unsigned int p = 0 ; p < mesh - > primitives_count ; p + + )
{
{
/ / NOTE : We only support primitives defined by triangles
/ / NOTE : We only support primitives defined by triangles
/ / Other alternatives : points , lines , line_strip , triangle_strip
/ / Other alternatives : points , lines , line_strip , triangle_strip
if ( data - > meshes [ i ] . primitives [ p ] . type ! = cgltf_primitive_type_triangles ) continue ;
if ( mesh - > primitives [ p ] . type ! = cgltf_primitive_type_triangles ) continue ;
/ / NOTE : Attributes data could be provided in several data formats ( 8 , 8u , 16u , 32. . . ) ,
/ / NOTE : Attributes data could be provided in several data formats ( 8 , 8u , 16u , 32. . . ) ,
/ / Only some formats for each attribute type are supported , read info at the top of this function !
/ / Only some formats for each attribute type are supported , read info at the top of this function !
for ( unsigned int j = 0 ; j < data - > meshes [ i ] . primitives [ p ] . attributes_count ; j + + )
for ( unsigned int j = 0 ; j < mesh - > primitives [ p ] . attributes_count ; j + + )
{
{
/ / Check the different attributes for every primitive
/ / Check the different attributes for every primitive
if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_position ) / / POSITION , vec3 , float
if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_position ) / / POSITION , vec3 , float
{
{
cgltf_accessor * attribute = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
/ / WARNING : SPECS : POSITION accessor MUST have its min and max properties defined
/ / WARNING : SPECS : POSITION accessor MUST have its min and max properties defined
@ -5101,12 +5145,22 @@ static Model LoadGLTF(const char *fileName)
/ / Load 3 components of float data type into mesh . vertices
/ / Load 3 components of float data type into mesh . vertices
LOAD_ATTRIBUTE ( attribute , 3 , float , model . meshes [ meshIndex ] . vertices )
LOAD_ATTRIBUTE ( attribute , 3 , float , model . meshes [ meshIndex ] . vertices )
/ / Transform the vertices
float * vertices = model . meshes [ meshIndex ] . vertices ;
for ( int k = 0 ; k < attribute - > count ; k + + )
{
Vector3 vt = Vector3Transform ( ( Vector3 ) { vertices [ 3 * k ] , vertices [ 3 * k + 1 ] , vertices [ 3 * k + 2 ] } , worldMatrix ) ;
vertices [ 3 * k ] = vt . x ;
vertices [ 3 * k + 1 ] = vt . y ;
vertices [ 3 * k + 2 ] = vt . z ;
}
}
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Vertices attribute data format not supported, use vec3 float " , fileName ) ;
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Vertices attribute data format not supported, use vec3 float " , fileName ) ;
}
}
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_normal ) / / NORMAL , vec3 , float
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_normal ) / / NORMAL , vec3 , float
{
{
cgltf_accessor * attribute = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
if ( ( attribute - > type = = cgltf_type_vec3 ) & & ( attribute - > component_type = = cgltf_component_type_r_32f ) )
if ( ( attribute - > type = = cgltf_type_vec3 ) & & ( attribute - > component_type = = cgltf_component_type_r_32f ) )
{
{
@ -5115,12 +5169,22 @@ static Model LoadGLTF(const char *fileName)
/ / Load 3 components of float data type into mesh . normals
/ / Load 3 components of float data type into mesh . normals
LOAD_ATTRIBUTE ( attribute , 3 , float , model . meshes [ meshIndex ] . normals )
LOAD_ATTRIBUTE ( attribute , 3 , float , model . meshes [ meshIndex ] . normals )
/ / Transform the normals
float * normals = model . meshes [ meshIndex ] . normals ;
for ( int k = 0 ; k < attribute - > count ; k + + )
{
Vector3 nt = Vector3Transform ( ( Vector3 ) { normals [ 3 * k ] , normals [ 3 * k + 1 ] , normals [ 3 * k + 2 ] } , worldMatrixNormals ) ;
normals [ 3 * k ] = nt . x ;
normals [ 3 * k + 1 ] = nt . y ;
normals [ 3 * k + 2 ] = nt . z ;
}
}
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Normal attribute data format not supported, use vec3 float " , fileName ) ;
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Normal attribute data format not supported, use vec3 float " , fileName ) ;
}
}
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_tangent ) / / TANGENT , vec3 , float
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_tangent ) / / TANGENT , vec3 , float
{
{
cgltf_accessor * attribute = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
if ( ( attribute - > type = = cgltf_type_vec4 ) & & ( attribute - > component_type = = cgltf_component_type_r_32f ) )
if ( ( attribute - > type = = cgltf_type_vec4 ) & & ( attribute - > component_type = = cgltf_component_type_r_32f ) )
{
{
@ -5129,15 +5193,25 @@ static Model LoadGLTF(const char *fileName)
/ / Load 4 components of float data type into mesh . tangents
/ / Load 4 components of float data type into mesh . tangents
LOAD_ATTRIBUTE ( attribute , 4 , float , model . meshes [ meshIndex ] . tangents )
LOAD_ATTRIBUTE ( attribute , 4 , float , model . meshes [ meshIndex ] . tangents )
/ / Transform the tangents
float * tangents = model . meshes [ meshIndex ] . tangents ;
for ( int k = 0 ; k < attribute - > count ; k + + )
{
Vector3 tt = Vector3Transform ( ( Vector3 ) { tangents [ 3 * k ] , tangents [ 3 * k + 1 ] , tangents [ 3 * k + 2 ] } , worldMatrix ) ;
tangents [ 3 * k ] = tt . x ;
tangents [ 3 * k + 1 ] = tt . y ;
tangents [ 3 * k + 2 ] = tt . z ;
}
}
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Tangent attribute data format not supported, use vec4 float " , fileName ) ;
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Tangent attribute data format not supported, use vec4 float " , fileName ) ;
}
}
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_texcoord ) / / TEXCOORD_n , vec2 , float / u8n / u16n
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_texcoord ) / / TEXCOORD_n , vec2 , float / u8n / u16n
{
{
/ / Support up to 2 texture coordinates attributes
/ / Support up to 2 texture coordinates attributes
float * texcoordPtr = NULL ;
float * texcoordPtr = NULL ;
cgltf_accessor * attribute = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
if ( attribute - > type = = cgltf_type_vec2 )
if ( attribute - > type = = cgltf_type_vec2 )
{
{
@ -5181,7 +5255,7 @@ static Model LoadGLTF(const char *fileName)
}
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Texcoords attribute data format not supported, use vec2 float " , fileName ) ;
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Texcoords attribute data format not supported, use vec2 float " , fileName ) ;
int index = data - >meshes [ i ] . primitives [ p ] . attributes [ j ] . index ;
int index = mesh - > primitives [ p ] . attributes [ j ] . index ;
if ( index = = 0 ) model . meshes [ meshIndex ] . texcoords = texcoordPtr ;
if ( index = = 0 ) model . meshes [ meshIndex ] . texcoords = texcoordPtr ;
else if ( index = = 1 ) model . meshes [ meshIndex ] . texcoords2 = texcoordPtr ;
else if ( index = = 1 ) model . meshes [ meshIndex ] . texcoords2 = texcoordPtr ;
else
else
@ -5190,9 +5264,9 @@ static Model LoadGLTF(const char *fileName)
if ( texcoordPtr ! = NULL ) RL_FREE ( texcoordPtr ) ;
if ( texcoordPtr ! = NULL ) RL_FREE ( texcoordPtr ) ;
}
}
}
}
else if ( data - >meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_color ) / / COLOR_n , vec3 / vec4 , float / u8n / u16n
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_color ) / / COLOR_n , vec3 / vec4 , float / u8n / u16n
{
{
cgltf_accessor * attribute = data - >meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
/ / WARNING : SPECS : All components of each COLOR_n accessor element MUST be clamped to [ 0.0 , 1.0 ] range
/ / WARNING : SPECS : All components of each COLOR_n accessor element MUST be clamped to [ 0.0 , 1.0 ] range
@ -5309,9 +5383,9 @@ static Model LoadGLTF(const char *fileName)
}
}
/ / Load primitive indices data ( if provided )
/ / Load primitive indices data ( if provided )
if ( data - >meshes [ i ] . primitives [ p ] . indices ! = NULL )
if ( mesh - > primitives [ p ] . indices ! = NULL )
{
{
cgltf_accessor * attribute = data - >meshes [ i ] . primitives [ p ] . indices ;
cgltf_accessor * attribute = mesh - > primitives [ p ] . indices ;
model . meshes [ meshIndex ] . triangleCount = ( int ) attribute - > count / 3 ;
model . meshes [ meshIndex ] . triangleCount = ( int ) attribute - > count / 3 ;
@ -5351,7 +5425,7 @@ static Model LoadGLTF(const char *fileName)
/ / raylib instead assigns to the mesh the by its index , as loaded in model . materials array
/ / raylib instead assigns to the mesh the by its index , as loaded in model . materials array
/ / To get the index , we check if material pointers match , and we assign the corresponding index ,
/ / To get the index , we check if material pointers match , and we assign the corresponding index ,
/ / skipping index 0 , the default material
/ / skipping index 0 , the default material
if ( & data - > materials [ m ] = = data - >meshes [ i ] . primitives [ p ] . material )
if ( & data - > materials [ m ] = = mesh - > primitives [ p ] . material )
{
{
model . meshMaterial [ meshIndex ] = m + 1 ;
model . meshMaterial [ meshIndex ] = m + 1 ;
break ;
break ;
@ -5401,20 +5475,27 @@ static Model LoadGLTF(const char *fileName)
TRACELOG ( LOG_ERROR , " MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i " , fileName , data - > skins_count ) ;
TRACELOG ( LOG_ERROR , " MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i " , fileName , data - > skins_count ) ;
}
}
for ( unsigned int i = 0 , meshIndex = 0 ; i < data - > meshes_count ; i + + )
meshIndex = 0 ;
for ( unsigned int i = 0 ; i < data - > nodes_count ; i + + )
{
{
for ( unsigned int p = 0 ; p < data - > meshes [ i ] . primitives_count ; p + + )
cgltf_node * node = & ( data - > nodes [ i ] ) ;
cgltf_mesh * mesh = node - > mesh ;
if ( ! mesh )
continue ;
for ( unsigned int p = 0 ; p < mesh - > primitives_count ; p + + )
{
{
/ / NOTE : We only support primitives defined by triangles
/ / NOTE : We only support primitives defined by triangles
if ( data - > meshes [ i ] . primitives [ p ] . type ! = cgltf_primitive_type_triangles ) continue ;
if ( mesh - > primitives [ p ] . type ! = cgltf_primitive_type_triangles ) continue ;
for ( unsigned int j = 0 ; j < data - > meshes [ i ] . primitives [ p ] . attributes_count ; j + + )
for ( unsigned int j = 0 ; j < mesh - > primitives [ p ] . attributes_count ; j + + )
{
{
/ / NOTE : JOINTS_1 + WEIGHT_1 will be used for + 4 joints influencing a vertex - > Not supported by raylib
/ / NOTE : JOINTS_1 + WEIGHT_1 will be used for + 4 joints influencing a vertex - > Not supported by raylib
if ( data - >meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_joints ) / / JOINTS_n ( vec4 : 4 bones max per vertex / u8 , u16 )
if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_joints ) / / JOINTS_n ( vec4 : 4 bones max per vertex / u8 , u16 )
{
{
cgltf_accessor * attribute = data - >meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
/ / NOTE : JOINTS_n can only be vec4 and u8 / u16
/ / NOTE : JOINTS_n can only be vec4 and u8 / u16
/ / SPECS : https : / / registry . khronos . org / glTF / specs / 2.0 / glTF - 2.0 . html # meshes - overview
/ / SPECS : https : / / registry . khronos . org / glTF / specs / 2.0 / glTF - 2.0 . html # meshes - overview
@ -5462,9 +5543,9 @@ static Model LoadGLTF(const char *fileName)
}
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Joint attribute data format not supported " , fileName ) ;
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Joint attribute data format not supported " , fileName ) ;
}
}
else if ( data - >meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_weights ) / / WEIGHTS_n ( vec4 , u8n / u16n / f32 )
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_weights ) / / WEIGHTS_n ( vec4 , u8n / u16n / f32 )
{
{
cgltf_accessor * attribute = data - >meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
if ( attribute - > type = = cgltf_type_vec4 )
if ( attribute - > type = = cgltf_type_vec4 )
{
{