2014-09-16 22:51:31 +02:00
/**********************************************************************************************
2013-11-18 23:38:44 +01:00
*
2021-09-22 00:15:06 +02:00
* rmodels - Basic functions to draw 3 d shapes and load and draw 3 d models
2013-11-18 23:38:44 +01:00
*
2017-02-16 00:50:02 +01:00
* CONFIGURATION :
2023-05-01 14:04:22 +02:00
* # define SUPPORT_MODULE_RMODELS
* rmodels module is included in the build
*
* # define SUPPORT_FILEFORMAT_OBJ
* # define SUPPORT_FILEFORMAT_MTL
* # define SUPPORT_FILEFORMAT_IQM
* # define SUPPORT_FILEFORMAT_GLTF
* # define SUPPORT_FILEFORMAT_VOX
* # define SUPPORT_FILEFORMAT_M3D
* Selected desired fileformats to be supported for model data loading .
*
* # define SUPPORT_MESH_GENERATION
* Support procedural mesh generation functions , uses external par_shapes . h library
* NOTE : Some generated meshes DO NOT include generated texture coordinates
2017-09-18 00:59:22 +02:00
*
2017-02-16 00:50:02 +01:00
*
* LICENSE : zlib / libpng
2016-11-16 18:46:13 +01:00
*
2024-01-02 20:58:12 +01:00
* Copyright ( c ) 2013 - 2024 Ramon Santamaria ( @ raysan5 )
2014-09-03 16:51:28 +02:00
*
* This software is provided " as-is " , without any express or implied warranty . In no event
2013-11-23 13:30:54 +01:00
* will the authors be held liable for any damages arising from the use of this software .
2013-11-18 23:38:44 +01:00
*
2014-09-03 16:51:28 +02:00
* Permission is granted to anyone to use this software for any purpose , including commercial
2013-11-23 13:30:54 +01:00
* applications , and to alter it and redistribute it freely , subject to the following restrictions :
2013-11-18 23:38:44 +01:00
*
2014-09-03 16:51:28 +02:00
* 1. The origin of this software must not be misrepresented ; you must not claim that you
* wrote the original software . If you use this software in a product , an acknowledgment
2013-11-23 13:30:54 +01:00
* in the product documentation would be appreciated but is not required .
2013-11-18 23:38:44 +01:00
*
2013-11-23 13:30:54 +01:00
* 2. Altered source versions must be plainly marked as such , and must not be misrepresented
* as being the original software .
2013-11-18 23:38:44 +01:00
*
2013-11-23 13:30:54 +01:00
* 3. This notice may not be removed or altered from any source distribution .
2013-11-18 23:38:44 +01:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-05-17 00:04:36 +02:00
# include "raylib.h" // Declares module functions
2013-11-18 23:38:44 +01:00
2018-12-18 00:20:08 +01:00
// Check if config flags have been externally provided on compilation line
# if !defined(EXTERNAL_CONFIG_FLAGS)
2021-07-29 21:57:50 +02:00
# include "config.h" // Defines module configuration flags
2018-12-18 00:20:08 +01:00
# endif
2021-12-04 19:56:02 +01:00
# if defined(SUPPORT_MODULE_RMODELS)
2021-07-29 21:57:50 +02:00
# include "utils.h" // Required for: TRACELOG(), LoadFileData(), LoadFileText(), SaveFileText()
# include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
# include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality
2014-09-16 22:51:31 +02:00
2020-06-23 00:33:51 +02:00
# include <stdio.h> // Required for: sprintf()
2024-02-29 18:50:44 +01:00
# include <stdlib.h> // Required for: malloc(), calloc(), free()
# include <string.h> // Required for: memcmp(), strlen(), strncpy()
2020-02-28 12:54:39 +01:00
# include <math.h> // Required for: sinf(), cosf(), sqrtf(), fabsf()
2014-03-25 12:40:35 +01:00
2019-03-29 17:15:22 +01:00
# if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
2020-03-17 13:40:07 +01:00
# define TINYOBJ_MALLOC RL_MALLOC
# define TINYOBJ_CALLOC RL_CALLOC
# define TINYOBJ_REALLOC RL_REALLOC
# define TINYOBJ_FREE RL_FREE
2019-03-29 16:48:23 +01:00
# define TINYOBJ_LOADER_C_IMPLEMENTATION
2019-03-29 17:15:22 +01:00
# include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading
2019-03-29 16:48:23 +01:00
# endif
2018-09-05 10:59:05 +02:00
# if defined(SUPPORT_FILEFORMAT_GLTF)
2020-03-17 13:40:07 +01:00
# define CGLTF_MALLOC RL_MALLOC
# define CGLTF_FREE RL_FREE
2020-03-24 13:27:49 +01:00
2018-09-05 10:59:05 +02:00
# define CGLTF_IMPLEMENTATION
# include "external/cgltf.h" // glTF file format loading
# endif
2021-09-04 19:55:09 +02:00
# if defined(SUPPORT_FILEFORMAT_VOX)
2021-09-10 15:37:31 +02:00
# define VOX_MALLOC RL_MALLOC
# define VOX_CALLOC RL_CALLOC
# define VOX_REALLOC RL_REALLOC
# define VOX_FREE RL_FREE
2021-09-05 20:39:34 +02:00
2021-09-04 19:55:09 +02:00
# define VOX_LOADER_IMPLEMENTATION
2021-10-14 15:38:50 +02:00
# include "external/vox_loader.h" // VOX file format loading (MagikaVoxel)
2021-09-04 19:55:09 +02:00
# endif
2022-08-20 14:01:54 +02:00
# if defined(SUPPORT_FILEFORMAT_M3D)
# define M3D_MALLOC RL_MALLOC
# define M3D_REALLOC RL_REALLOC
# define M3D_FREE RL_FREE
2022-08-22 11:11:05 +02:00
2022-08-20 14:01:54 +02:00
# define M3D_IMPLEMENTATION
# include "external/m3d.h" // Model3D file format loading
# endif
2018-09-05 10:59:05 +02:00
# if defined(SUPPORT_MESH_GENERATION)
2024-12-03 19:14:14 +01:00
# define PAR_MALLOC(T, N) ((T *)RL_MALLOC(N*sizeof(T)))
# define PAR_CALLOC(T, N) ((T *)RL_CALLOC(N*sizeof(T), 1))
# define PAR_REALLOC(T, BUF, N) ((T *)RL_REALLOC(BUF, sizeof(T)*(N)))
2020-03-17 13:40:07 +01:00
# define PAR_FREE RL_FREE
2020-03-24 13:27:49 +01:00
2023-08-09 10:00:26 +02:00
# if defined(_MSC_VER) // Disable some MSVC warning
# pragma warning(push)
# pragma warning(disable : 4244)
# pragma warning(disable : 4305)
# endif
2023-01-20 07:05:19 -08:00
2018-09-05 10:59:05 +02:00
# define PAR_SHAPES_IMPLEMENTATION
# include "external/par_shapes.h" // Shapes 3d parametric generation
2023-01-20 07:05:19 -08:00
2023-08-10 22:45:25 +02:00
# if defined(_MSC_VER)
2023-08-09 10:00:26 +02:00
# pragma warning(pop) // Disable MSVC warning suppression
# endif
2018-09-05 10:59:05 +02:00
# endif
2017-09-02 17:42:22 +02:00
2021-07-29 21:57:50 +02:00
# if defined(_WIN32)
# include <direct.h> // Required for: _chdir() [Used in LoadOBJ()]
# define CHDIR _chdir
# else
# include <unistd.h> // Required for: chdir() (POSIX) [Used in LoadOBJ()]
# define CHDIR chdir
# endif
2013-11-18 23:38:44 +01:00
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
2021-07-31 19:46:44 +02:00
# ifndef MAX_MATERIAL_MAPS
# define MAX_MATERIAL_MAPS 12 // Maximum number of maps supported
# endif
# ifndef MAX_MESH_VERTEX_BUFFERS
2024-09-20 11:30:37 -04:00
# define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh
2021-07-31 19:46:44 +02:00
# endif
2013-11-18 23:38:44 +01:00
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
2014-04-04 20:11:57 +02:00
// ...
2013-11-18 23:38:44 +01:00
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
2016-06-11 12:41:03 +02:00
// ...
2013-11-18 23:38:44 +01:00
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
2017-03-26 22:49:01 +02:00
# if defined(SUPPORT_FILEFORMAT_OBJ)
2019-03-29 16:48:23 +01:00
static Model LoadOBJ ( const char * fileName ) ; // Load OBJ mesh data
2017-03-26 22:49:01 +02:00
# endif
2019-06-12 12:12:59 +02:00
# if defined(SUPPORT_FILEFORMAT_IQM)
2019-03-29 16:48:23 +01:00
static Model LoadIQM ( const char * fileName ) ; // Load IQM mesh data
2023-09-02 12:05:34 +02:00
static ModelAnimation * LoadModelAnimationsIQM ( const char * fileName , int * animCount ) ; // Load IQM animation data
2018-09-05 10:59:05 +02:00
# endif
# if defined(SUPPORT_FILEFORMAT_GLTF)
2019-03-29 16:48:23 +01:00
static Model LoadGLTF ( const char * fileName ) ; // Load GLTF mesh data
2023-09-02 12:05:34 +02:00
static ModelAnimation * LoadModelAnimationsGLTF ( const char * fileName , int * animCount ) ; // Load GLTF animation data
2018-09-05 10:59:05 +02:00
# endif
2021-09-04 19:55:09 +02:00
# if defined(SUPPORT_FILEFORMAT_VOX)
2021-09-05 20:39:34 +02:00
static Model LoadVOX ( const char * filename ) ; // Load VOX mesh data
2021-09-04 19:55:09 +02:00
# endif
2022-08-20 14:01:54 +02:00
# if defined(SUPPORT_FILEFORMAT_M3D)
static Model LoadM3D ( const char * filename ) ; // Load M3D mesh data
2023-09-02 12:05:34 +02:00
static ModelAnimation * LoadModelAnimationsM3D ( const char * fileName , int * animCount ) ; // Load M3D animation data
2022-08-20 14:01:54 +02:00
# endif
2023-01-24 17:16:35 +01:00
# if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
static void ProcessMaterialsOBJ ( Material * rayMaterials , tinyobj_material_t * materials , int materialCount ) ; // Process obj materials
# endif
2016-05-07 18:07:15 +02:00
2013-11-18 23:38:44 +01:00
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
2016-05-21 18:10:06 +02:00
// Draw a line in 3D world space
2016-08-10 12:20:46 +02:00
void DrawLine3D ( Vector3 startPos , Vector3 endPos , Color color )
2016-05-21 18:10:06 +02:00
{
rlBegin ( RL_LINES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
rlVertex3f ( startPos . x , startPos . y , startPos . z ) ;
rlVertex3f ( endPos . x , endPos . y , endPos . z ) ;
rlEnd ( ) ;
}
2020-01-29 12:21:22 +01:00
// Draw a point in 3D space, actually a small line
2019-11-22 00:33:50 +01:00
void DrawPoint3D ( Vector3 position , Color color )
{
2019-11-21 18:30:19 -05:00
rlPushMatrix ( ) ;
2019-11-22 00:33:50 +01:00
rlTranslatef ( position . x , position . y , position . z ) ;
rlBegin ( RL_LINES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2020-05-06 19:12:09 +02:00
rlVertex3f ( 0.0f , 0.0f , 0.0f ) ;
rlVertex3f ( 0.0f , 0.0f , 0.1f ) ;
2019-11-22 00:33:50 +01:00
rlEnd ( ) ;
2019-11-21 18:30:19 -05:00
rlPopMatrix ( ) ;
}
2016-05-30 19:18:11 +02:00
// Draw a circle in 3D world space
2016-11-17 12:55:30 +01:00
void DrawCircle3D ( Vector3 center , float radius , Vector3 rotationAxis , float rotationAngle , Color color )
2016-05-30 19:18:11 +02:00
{
rlPushMatrix ( ) ;
rlTranslatef ( center . x , center . y , center . z ) ;
2016-11-17 12:55:30 +01:00
rlRotatef ( rotationAngle , rotationAxis . x , rotationAxis . y , rotationAxis . z ) ;
2016-08-16 11:09:55 +02:00
2016-05-30 19:18:11 +02:00
rlBegin ( RL_LINES ) ;
for ( int i = 0 ; i < 360 ; i + = 10 )
{
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2016-08-16 11:09:55 +02:00
2016-10-17 00:03:38 +02:00
rlVertex3f ( sinf ( DEG2RAD * i ) * radius , cosf ( DEG2RAD * i ) * radius , 0.0f ) ;
rlVertex3f ( sinf ( DEG2RAD * ( i + 10 ) ) * radius , cosf ( DEG2RAD * ( i + 10 ) ) * radius , 0.0f ) ;
2016-05-30 19:18:11 +02:00
}
rlEnd ( ) ;
rlPopMatrix ( ) ;
}
2020-05-07 12:46:45 +02:00
// Draw a color-filled triangle (vertex in counter-clockwise order!)
void DrawTriangle3D ( Vector3 v1 , Vector3 v2 , Vector3 v3 , Color color )
{
rlBegin ( RL_TRIANGLES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
rlVertex3f ( v1 . x , v1 . y , v1 . z ) ;
rlVertex3f ( v2 . x , v2 . y , v2 . z ) ;
rlVertex3f ( v3 . x , v3 . y , v3 . z ) ;
rlEnd ( ) ;
}
// Draw a triangle strip defined by points
2024-06-11 02:51:49 -07:00
void DrawTriangleStrip3D ( const Vector3 * points , int pointCount , Color color )
2020-05-07 12:46:45 +02:00
{
2024-04-20 13:53:13 +02:00
if ( pointCount < 3 ) return ; // Security check
2020-05-07 12:46:45 +02:00
2022-09-05 13:20:09 +02:00
rlBegin ( RL_TRIANGLES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2020-05-07 12:46:45 +02:00
2022-09-05 13:20:09 +02:00
for ( int i = 2 ; i < pointCount ; i + + )
{
if ( ( i % 2 ) = = 0 )
2020-05-07 12:46:45 +02:00
{
2022-09-05 13:20:09 +02:00
rlVertex3f ( points [ i ] . x , points [ i ] . y , points [ i ] . z ) ;
rlVertex3f ( points [ i - 2 ] . x , points [ i - 2 ] . y , points [ i - 2 ] . z ) ;
rlVertex3f ( points [ i - 1 ] . x , points [ i - 1 ] . y , points [ i - 1 ] . z ) ;
2020-05-07 12:46:45 +02:00
}
2022-09-05 13:20:09 +02:00
else
{
rlVertex3f ( points [ i ] . x , points [ i ] . y , points [ i ] . z ) ;
rlVertex3f ( points [ i - 1 ] . x , points [ i - 1 ] . y , points [ i - 1 ] . z ) ;
rlVertex3f ( points [ i - 2 ] . x , points [ i - 2 ] . y , points [ i - 2 ] . z ) ;
}
}
rlEnd ( ) ;
2020-05-07 12:46:45 +02:00
}
2013-11-18 23:38:44 +01:00
// Draw cube
2014-03-16 20:59:02 +01:00
// NOTE: Cube position is the center position
2015-12-21 16:42:13 +01:00
void DrawCube ( Vector3 position , float width , float height , float length , Color color )
2013-11-18 23:38:44 +01:00
{
2014-12-16 11:15:56 +01:00
float x = 0.0f ;
float y = 0.0f ;
float z = 0.0f ;
2019-02-21 18:45:19 +01:00
2014-03-25 12:40:35 +01:00
rlPushMatrix ( ) ;
2017-08-04 18:34:51 +02:00
// NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
2014-12-16 11:15:56 +01:00
rlTranslatef ( position . x , position . y , position . z ) ;
2017-08-04 18:34:51 +02:00
//rlRotatef(45, 0, 1, 0);
//rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
rlBegin ( RL_TRIANGLES ) ;
2014-09-03 16:51:28 +02:00
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2017-08-04 18:34:51 +02:00
// Front face
2024-04-23 20:22:29 +08:00
rlNormal3f ( 0.0f , 0.0f , 1.0f ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Left
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Right
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Top Left
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Top Right
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Top Left
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Right
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
// Back face
2024-04-23 20:22:29 +08:00
rlNormal3f ( 0.0f , 0.0f , - 1.0f ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Left
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top Left
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Right
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ; // Top Right
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Right
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top Left
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
// Top face
2024-04-23 20:22:29 +08:00
rlNormal3f ( 0.0f , 1.0f , 0.0f ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top Left
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Bottom Left
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Bottom Right
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ; // Top Right
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top Left
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Bottom Right
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
// Bottom face
2024-04-23 20:22:29 +08:00
rlNormal3f ( 0.0f , - 1.0f , 0.0f ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Top Left
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Right
rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Left
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Top Right
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Right
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Top Left
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
// Right face
2024-04-23 20:22:29 +08:00
rlNormal3f ( 1.0f , 0.0f , 0.0f ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Right
rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ; // Top Right
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Top Left
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Left
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Right
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Top Left
// Left face
2024-04-23 20:22:29 +08:00
rlNormal3f ( - 1.0f , 0.0f , 0.0f ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Right
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Top Left
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top Right
rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Left
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Top Left
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Right
2014-04-04 20:11:57 +02:00
rlEnd ( ) ;
rlPopMatrix ( ) ;
}
// Draw cube (Vector version)
void DrawCubeV ( Vector3 position , Vector3 size , Color color )
{
DrawCube ( position , size . x , size . y , size . z , color ) ;
}
// Draw cube wires
2015-12-21 16:42:13 +01:00
void DrawCubeWires ( Vector3 position , float width , float height , float length , Color color )
2014-04-04 20:11:57 +02:00
{
2014-12-16 11:15:56 +01:00
float x = 0.0f ;
float y = 0.0f ;
float z = 0.0f ;
2019-02-21 18:45:19 +01:00
2014-04-04 20:11:57 +02:00
rlPushMatrix ( ) ;
2017-07-21 17:19:28 +02:00
rlTranslatef ( position . x , position . y , position . z ) ;
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
rlBegin ( RL_LINES ) ;
2014-09-03 16:51:28 +02:00
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2022-09-05 13:20:09 +02:00
// Front face
//------------------------------------------------------------------
2021-10-12 12:08:12 +02:00
// Bottom line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom left
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom right
2021-10-12 12:08:12 +02:00
// Left line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom right
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Top right
2021-10-12 12:08:12 +02:00
// Top line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Top right
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Top left
2021-10-12 12:08:12 +02:00
// Right line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Top left
rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom left
2021-10-12 12:08:12 +02:00
2022-09-05 13:20:09 +02:00
// Back face
//------------------------------------------------------------------
2021-10-12 12:08:12 +02:00
// Bottom line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom left
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom right
2021-10-12 12:08:12 +02:00
// Left line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom right
rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ; // Top right
2021-10-12 12:08:12 +02:00
// Top line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ; // Top right
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top left
2021-10-12 12:08:12 +02:00
// Right line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top left
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom left
2021-10-12 12:08:12 +02:00
2022-09-05 13:20:09 +02:00
// Top face
//------------------------------------------------------------------
2021-10-12 12:08:12 +02:00
// Left line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Top left front
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top left back
2021-10-12 12:08:12 +02:00
// Right line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Top right front
rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ; // Top right back
2021-10-12 12:08:12 +02:00
2022-09-05 13:20:09 +02:00
// Bottom face
//------------------------------------------------------------------
2021-10-12 12:08:12 +02:00
// Left line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ; // Top left front
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Top left back
2021-10-12 12:08:12 +02:00
// Right line
2022-09-05 13:20:09 +02:00
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Top right front
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Top right back
2014-04-04 20:11:57 +02:00
rlEnd ( ) ;
rlPopMatrix ( ) ;
}
2019-03-08 15:06:17 -03:00
// Draw cube wires (vector version)
void DrawCubeWiresV ( Vector3 position , Vector3 size , Color color )
{
DrawCubeWires ( position , size . x , size . y , size . z , color ) ;
}
2013-11-18 23:38:44 +01:00
// Draw sphere
void DrawSphere ( Vector3 centerPos , float radius , Color color )
{
2013-11-23 13:30:54 +01:00
DrawSphereEx ( centerPos , radius , 16 , 16 , color ) ;
2013-11-18 23:38:44 +01:00
}
// Draw sphere with extended parameters
void DrawSphereEx ( Vector3 centerPos , float radius , int rings , int slices , Color color )
{
2024-06-30 11:15:45 +02:00
#if 0
// Basic implementation, do not use it!
2024-10-21 17:00:52 +02:00
// For a sphere with 16 rings and 16 slices it requires 8640 cos()/sin() function calls!
2024-06-30 11:15:45 +02:00
// New optimized version below only requires 4 cos()/sin() calls
2024-10-21 17:00:52 +02:00
2024-06-30 11:15:45 +02:00
rlPushMatrix ( ) ;
// NOTE: Transformation is applied in inverse order (scale -> translate)
rlTranslatef ( centerPos . x , centerPos . y , centerPos . z ) ;
rlScalef ( radius , radius , radius ) ;
2024-10-21 17:00:52 +02:00
2024-06-30 11:15:45 +02:00
rlBegin ( RL_TRIANGLES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
for ( int i = 0 ; i < ( rings + 2 ) ; i + + )
{
for ( int j = 0 ; j < slices ; j + + )
{
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) * sinf ( DEG2RAD * ( 360.0f * j / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) * cosf ( DEG2RAD * ( 360.0f * j / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( 360.0f * j / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( 360.0f * j / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) * sinf ( DEG2RAD * ( 360.0f * j / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) * cosf ( DEG2RAD * ( 360.0f * j / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i ) ) ) * sinf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i ) ) ) * cosf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ) ;
}
}
rlEnd ( ) ;
rlPopMatrix ( ) ;
# endif
2014-03-25 12:40:35 +01:00
rlPushMatrix ( ) ;
2017-08-04 18:34:51 +02:00
// NOTE: Transformation is applied in inverse order (scale -> translate)
2017-07-21 17:19:28 +02:00
rlTranslatef ( centerPos . x , centerPos . y , centerPos . z ) ;
2017-08-04 18:34:51 +02:00
rlScalef ( radius , radius , radius ) ;
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
rlBegin ( RL_TRIANGLES ) ;
2014-03-25 12:40:35 +01:00
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2014-09-03 16:51:28 +02:00
2024-06-30 05:09:21 -04:00
float ringangle = DEG2RAD * ( 180.0f / ( rings + 1 ) ) ; // Angle between latitudinal parallels
float sliceangle = DEG2RAD * ( 360.0f / slices ) ; // Angle between longitudinal meridians
float cosring = cosf ( ringangle ) ;
float sinring = sinf ( ringangle ) ;
float cosslice = cosf ( sliceangle ) ;
float sinslice = sinf ( sliceangle ) ;
2024-06-30 11:15:45 +02:00
Vector3 vertices [ 4 ] = { 0 } ; // Required to store face vertices
vertices [ 2 ] = ( Vector3 ) { 0 , 1 , 0 } ;
vertices [ 3 ] = ( Vector3 ) { sinring , cosring , 0 } ;
2024-06-30 05:09:21 -04:00
2024-06-30 11:15:45 +02:00
for ( int i = 0 ; i < rings + 1 ; i + + )
{
2024-10-21 17:00:52 +02:00
for ( int j = 0 ; j < slices ; j + + )
2024-06-30 11:15:45 +02:00
{
2024-06-30 05:09:21 -04:00
vertices [ 0 ] = vertices [ 2 ] ; // Rotate around y axis to set up vertices for next face
vertices [ 1 ] = vertices [ 3 ] ;
2024-06-30 11:15:45 +02:00
vertices [ 2 ] = ( Vector3 ) { cosslice * vertices [ 2 ] . x - sinslice * vertices [ 2 ] . z , vertices [ 2 ] . y , sinslice * vertices [ 2 ] . x + cosslice * vertices [ 2 ] . z } ; // Rotation matrix around y axis
vertices [ 3 ] = ( Vector3 ) { cosslice * vertices [ 3 ] . x - sinslice * vertices [ 3 ] . z , vertices [ 3 ] . y , sinslice * vertices [ 3 ] . x + cosslice * vertices [ 3 ] . z } ;
2024-06-30 05:09:21 -04:00
rlVertex3f ( vertices [ 0 ] . x , vertices [ 0 ] . y , vertices [ 0 ] . z ) ;
rlVertex3f ( vertices [ 3 ] . x , vertices [ 3 ] . y , vertices [ 3 ] . z ) ;
rlVertex3f ( vertices [ 1 ] . x , vertices [ 1 ] . y , vertices [ 1 ] . z ) ;
rlVertex3f ( vertices [ 0 ] . x , vertices [ 0 ] . y , vertices [ 0 ] . z ) ;
rlVertex3f ( vertices [ 2 ] . x , vertices [ 2 ] . y , vertices [ 2 ] . z ) ;
rlVertex3f ( vertices [ 3 ] . x , vertices [ 3 ] . y , vertices [ 3 ] . z ) ;
2013-11-23 13:30:54 +01:00
}
2024-06-30 05:09:21 -04:00
vertices [ 2 ] = vertices [ 3 ] ; // Rotate around z axis to set up starting vertices for next ring
2024-06-30 11:15:45 +02:00
vertices [ 3 ] = ( Vector3 ) { cosring * vertices [ 3 ] . x + sinring * vertices [ 3 ] . y , - sinring * vertices [ 3 ] . x + cosring * vertices [ 3 ] . y , vertices [ 3 ] . z } ; // Rotation matrix around z axis
2013-11-23 13:30:54 +01:00
}
2014-03-25 12:40:35 +01:00
rlEnd ( ) ;
rlPopMatrix ( ) ;
2013-11-18 23:38:44 +01:00
}
// Draw sphere wires
2014-04-04 20:11:57 +02:00
void DrawSphereWires ( Vector3 centerPos , float radius , int rings , int slices , Color color )
2013-11-18 23:38:44 +01:00
{
2014-04-04 20:11:57 +02:00
rlPushMatrix ( ) ;
2017-08-04 18:34:51 +02:00
// NOTE: Transformation is applied in inverse order (scale -> translate)
2017-07-21 17:19:28 +02:00
rlTranslatef ( centerPos . x , centerPos . y , centerPos . z ) ;
2017-08-04 18:34:51 +02:00
rlScalef ( radius , radius , radius ) ;
2015-04-06 14:02:29 +02:00
2014-04-04 20:11:57 +02:00
rlBegin ( RL_LINES ) ;
2014-03-25 12:40:35 +01:00
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2014-09-03 16:51:28 +02:00
2016-05-21 18:22:15 +02:00
for ( int i = 0 ; i < ( rings + 2 ) ; i + + )
2014-03-25 12:40:35 +01:00
{
2016-05-21 18:22:15 +02:00
for ( int j = 0 ; j < slices ; j + + )
2014-04-04 20:11:57 +02:00
{
2021-05-14 14:01:42 +02:00
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) * sinf ( DEG2RAD * ( 360.0f * j / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) * cosf ( DEG2RAD * ( 360.0f * j / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( 360.0f * ( j + 1 ) / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( 360.0f * j / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( 360.0f * j / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( 360.0f * j / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( 360.0f * j / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) * sinf ( DEG2RAD * ( 360.0f * j / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180.0f / ( rings + 1 ) ) * i ) ) * cosf ( DEG2RAD * ( 360.0f * j / slices ) ) ) ;
2014-04-04 20:11:57 +02:00
}
2014-03-25 12:40:35 +01:00
}
rlEnd ( ) ;
2014-04-04 20:11:57 +02:00
rlPopMatrix ( ) ;
}
// Draw a cylinder
// NOTE: It could be also used for pyramid and cone
void DrawCylinder ( Vector3 position , float radiusTop , float radiusBottom , float height , int sides , Color color )
{
if ( sides < 3 ) sides = 3 ;
2019-09-13 12:29:52 +01:00
2024-06-04 11:46:57 +02:00
const float angleStep = 360.0f / sides ;
2014-04-04 20:11:57 +02:00
rlPushMatrix ( ) ;
rlTranslatef ( position . x , position . y , position . z ) ;
rlBegin ( RL_TRIANGLES ) ;
2014-03-25 12:40:35 +01:00
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2014-04-04 20:11:57 +02:00
if ( radiusTop > 0 )
2014-03-25 12:40:35 +01:00
{
2014-04-04 20:11:57 +02:00
// Draw Body -------------------------------------------------------------------------------------
2024-06-04 11:46:57 +02:00
for ( int i = 0 ; i < sides ; i + + )
2014-04-04 20:11:57 +02:00
{
2024-06-04 11:46:57 +02:00
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * i * angleStep ) * radiusBottom ) ; //Bottom Left
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom ) ; //Bottom Right
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop , height , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop ) ; //Top Right
2014-09-03 16:51:28 +02:00
2024-06-04 11:46:57 +02:00
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusTop , height , cosf ( DEG2RAD * i * angleStep ) * radiusTop ) ; //Top Left
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * i * angleStep ) * radiusBottom ) ; //Bottom Left
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop , height , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop ) ; //Top Right
2014-04-04 20:11:57 +02:00
}
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Draw Cap --------------------------------------------------------------------------------------
2024-06-04 11:46:57 +02:00
for ( int i = 0 ; i < sides ; i + + )
2014-04-04 20:11:57 +02:00
{
rlVertex3f ( 0 , height , 0 ) ;
2024-06-04 11:46:57 +02:00
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusTop , height , cosf ( DEG2RAD * i * angleStep ) * radiusTop ) ;
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop , height , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop ) ;
2014-04-04 20:11:57 +02:00
}
2014-03-25 12:40:35 +01:00
}
2014-04-04 20:11:57 +02:00
else
2014-03-25 12:40:35 +01:00
{
2014-04-04 20:11:57 +02:00
// Draw Cone -------------------------------------------------------------------------------------
2024-06-04 11:46:57 +02:00
for ( int i = 0 ; i < sides ; i + + )
2014-04-04 20:11:57 +02:00
{
rlVertex3f ( 0 , height , 0 ) ;
2024-06-04 11:46:57 +02:00
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * i * angleStep ) * radiusBottom ) ;
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom ) ;
2014-04-04 20:11:57 +02:00
}
2014-03-25 12:40:35 +01:00
}
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Draw Base -----------------------------------------------------------------------------------------
2024-06-04 11:46:57 +02:00
for ( int i = 0 ; i < sides ; i + + )
2014-03-25 12:40:35 +01:00
{
2014-04-04 20:11:57 +02:00
rlVertex3f ( 0 , 0 , 0 ) ;
2024-11-10 13:04:58 -08:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom ) ;
2024-06-04 11:46:57 +02:00
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * i * angleStep ) * radiusBottom ) ;
2014-03-25 12:40:35 +01:00
}
2024-06-04 11:46:57 +02:00
2014-09-03 16:51:28 +02:00
rlEnd ( ) ;
2014-04-04 20:11:57 +02:00
rlPopMatrix ( ) ;
}
2021-10-12 19:36:06 +02:00
// Draw a cylinder with base at startPos and top at endPos
// NOTE: It could be also used for pyramid and cone
void DrawCylinderEx ( Vector3 startPos , Vector3 endPos , float startRadius , float endRadius , int sides , Color color )
{
if ( sides < 3 ) sides = 3 ;
Vector3 direction = { endPos . x - startPos . x , endPos . y - startPos . y , endPos . z - startPos . z } ;
2024-04-20 13:53:13 +02:00
if ( ( direction . x = = 0 ) & & ( direction . y = = 0 ) & & ( direction . z = = 0 ) ) return ; // Security check
2021-10-13 19:32:30 +02:00
2021-10-12 19:36:06 +02:00
// Construct a basis of the base and the top face:
Vector3 b1 = Vector3Normalize ( Vector3Perpendicular ( direction ) ) ;
Vector3 b2 = Vector3Normalize ( Vector3CrossProduct ( b1 , direction ) ) ;
2021-10-13 20:47:42 +02:00
float baseAngle = ( 2.0f * PI ) / sides ;
2021-10-12 19:36:06 +02:00
rlBegin ( RL_TRIANGLES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2024-04-19 21:13:52 +02:00
for ( int i = 0 ; i < sides ; i + + )
{
// Compute the four vertices
2021-10-12 19:36:06 +02:00
float s1 = sinf ( baseAngle * ( i + 0 ) ) * startRadius ;
float c1 = cosf ( baseAngle * ( i + 0 ) ) * startRadius ;
Vector3 w1 = { startPos . x + s1 * b1 . x + c1 * b2 . x , startPos . y + s1 * b1 . y + c1 * b2 . y , startPos . z + s1 * b1 . z + c1 * b2 . z } ;
float s2 = sinf ( baseAngle * ( i + 1 ) ) * startRadius ;
float c2 = cosf ( baseAngle * ( i + 1 ) ) * startRadius ;
Vector3 w2 = { startPos . x + s2 * b1 . x + c2 * b2 . x , startPos . y + s2 * b1 . y + c2 * b2 . y , startPos . z + s2 * b1 . z + c2 * b2 . z } ;
float s3 = sinf ( baseAngle * ( i + 0 ) ) * endRadius ;
float c3 = cosf ( baseAngle * ( i + 0 ) ) * endRadius ;
Vector3 w3 = { endPos . x + s3 * b1 . x + c3 * b2 . x , endPos . y + s3 * b1 . y + c3 * b2 . y , endPos . z + s3 * b1 . z + c3 * b2 . z } ;
float s4 = sinf ( baseAngle * ( i + 1 ) ) * endRadius ;
float c4 = cosf ( baseAngle * ( i + 1 ) ) * endRadius ;
Vector3 w4 = { endPos . x + s4 * b1 . x + c4 * b2 . x , endPos . y + s4 * b1 . y + c4 * b2 . y , endPos . z + s4 * b1 . z + c4 * b2 . z } ;
2024-04-19 21:13:52 +02:00
if ( startRadius > 0 )
{
2021-10-12 19:36:06 +02:00
rlVertex3f ( startPos . x , startPos . y , startPos . z ) ; // |
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ; // T0
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ; // |
2024-04-19 21:13:52 +02:00
}
2021-10-12 19:36:06 +02:00
// w2 x.-----------x startPos
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ; // | |\'. T0 /
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ; // T1 | \ '. /
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ; // | |T \ '. /
// | 2 \ T 'x w1
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ; // | w4 x.---\-1-|---x endPos
rlVertex3f ( w4 . x , w4 . y , w4 . z ) ; // T2 '. \ |T3/
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ; // | '. \ | /
// '.\|/
2024-04-19 21:13:52 +02:00
if ( endRadius > 0 ) // 'x w3
{
2021-10-12 19:36:06 +02:00
rlVertex3f ( endPos . x , endPos . y , endPos . z ) ; // |
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ; // T3
rlVertex3f ( w4 . x , w4 . y , w4 . z ) ; // |
} //
}
rlEnd ( ) ;
}
2014-04-04 20:11:57 +02:00
// Draw a wired cylinder
// NOTE: It could be also used for pyramid and cone
void DrawCylinderWires ( Vector3 position , float radiusTop , float radiusBottom , float height , int sides , Color color )
{
2016-05-21 18:22:15 +02:00
if ( sides < 3 ) sides = 3 ;
2019-09-13 12:29:52 +01:00
2024-06-04 11:46:57 +02:00
const float angleStep = 360.0f / sides ;
2014-04-04 20:11:57 +02:00
rlPushMatrix ( ) ;
rlTranslatef ( position . x , position . y , position . z ) ;
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
rlBegin ( RL_LINES ) ;
2014-03-25 12:40:35 +01:00
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2014-09-03 16:51:28 +02:00
2024-06-04 11:46:57 +02:00
for ( int i = 0 ; i < sides ; i + + )
2014-03-25 12:40:35 +01:00
{
2024-06-04 11:46:57 +02:00
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * i * angleStep ) * radiusBottom ) ;
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom ) ;
2014-09-03 16:51:28 +02:00
2024-06-04 11:46:57 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusBottom ) ;
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop , height , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop ) ;
2014-09-03 16:51:28 +02:00
2024-06-04 11:46:57 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop , height , cosf ( DEG2RAD * ( i + 1 ) * angleStep ) * radiusTop ) ;
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusTop , height , cosf ( DEG2RAD * i * angleStep ) * radiusTop ) ;
2014-09-03 16:51:28 +02:00
2024-06-04 11:46:57 +02:00
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusTop , height , cosf ( DEG2RAD * i * angleStep ) * radiusTop ) ;
rlVertex3f ( sinf ( DEG2RAD * i * angleStep ) * radiusBottom , 0 , cosf ( DEG2RAD * i * angleStep ) * radiusBottom ) ;
2014-03-25 12:40:35 +01:00
}
rlEnd ( ) ;
2014-04-04 20:11:57 +02:00
rlPopMatrix ( ) ;
2013-11-18 23:38:44 +01:00
}
2021-10-12 19:36:06 +02:00
// Draw a wired cylinder with base at startPos and top at endPos
// NOTE: It could be also used for pyramid and cone
void DrawCylinderWiresEx ( Vector3 startPos , Vector3 endPos , float startRadius , float endRadius , int sides , Color color )
{
if ( sides < 3 ) sides = 3 ;
2021-10-13 19:32:30 +02:00
Vector3 direction = { endPos . x - startPos . x , endPos . y - startPos . y , endPos . z - startPos . z } ;
2024-04-20 13:53:13 +02:00
if ( ( direction . x = = 0 ) & & ( direction . y = = 0 ) & & ( direction . z = = 0 ) ) return ; // Security check
2021-10-13 19:32:30 +02:00
2021-10-12 19:36:06 +02:00
// Construct a basis of the base and the top face:
2021-10-13 19:32:30 +02:00
Vector3 b1 = Vector3Normalize ( Vector3Perpendicular ( direction ) ) ;
Vector3 b2 = Vector3Normalize ( Vector3CrossProduct ( b1 , direction ) ) ;
2021-10-12 19:36:06 +02:00
2021-10-13 20:47:42 +02:00
float baseAngle = ( 2.0f * PI ) / sides ;
2021-10-12 19:36:06 +02:00
rlBegin ( RL_LINES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2024-04-19 21:13:52 +02:00
for ( int i = 0 ; i < sides ; i + + )
{
// Compute the four vertices
2021-10-12 19:36:06 +02:00
float s1 = sinf ( baseAngle * ( i + 0 ) ) * startRadius ;
float c1 = cosf ( baseAngle * ( i + 0 ) ) * startRadius ;
Vector3 w1 = { startPos . x + s1 * b1 . x + c1 * b2 . x , startPos . y + s1 * b1 . y + c1 * b2 . y , startPos . z + s1 * b1 . z + c1 * b2 . z } ;
float s2 = sinf ( baseAngle * ( i + 1 ) ) * startRadius ;
float c2 = cosf ( baseAngle * ( i + 1 ) ) * startRadius ;
Vector3 w2 = { startPos . x + s2 * b1 . x + c2 * b2 . x , startPos . y + s2 * b1 . y + c2 * b2 . y , startPos . z + s2 * b1 . z + c2 * b2 . z } ;
float s3 = sinf ( baseAngle * ( i + 0 ) ) * endRadius ;
float c3 = cosf ( baseAngle * ( i + 0 ) ) * endRadius ;
Vector3 w3 = { endPos . x + s3 * b1 . x + c3 * b2 . x , endPos . y + s3 * b1 . y + c3 * b2 . y , endPos . z + s3 * b1 . z + c3 * b2 . z } ;
float s4 = sinf ( baseAngle * ( i + 1 ) ) * endRadius ;
float c4 = cosf ( baseAngle * ( i + 1 ) ) * endRadius ;
Vector3 w4 = { endPos . x + s4 * b1 . x + c4 * b2 . x , endPos . y + s4 * b1 . y + c4 * b2 . y , endPos . z + s4 * b1 . z + c4 * b2 . z } ;
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ;
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ;
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
rlVertex3f ( w4 . x , w4 . y , w4 . z ) ;
}
rlEnd ( ) ;
}
2022-10-17 02:36:53 -07:00
// Draw a capsule with the center of its sphere caps at startPos and endPos
void DrawCapsule ( Vector3 startPos , Vector3 endPos , float radius , int slices , int rings , Color color )
{
if ( slices < 3 ) slices = 3 ;
Vector3 direction = { endPos . x - startPos . x , endPos . y - startPos . y , endPos . z - startPos . z } ;
2023-02-05 16:30:23 +01:00
2022-10-17 02:36:53 -07:00
// draw a sphere if start and end points are the same
bool sphereCase = ( direction . x = = 0 ) & & ( direction . y = = 0 ) & & ( direction . z = = 0 ) ;
if ( sphereCase ) direction = ( Vector3 ) { 0.0f , 1.0f , 0.0f } ;
// Construct a basis of the base and the caps:
Vector3 b0 = Vector3Normalize ( direction ) ;
Vector3 b1 = Vector3Normalize ( Vector3Perpendicular ( direction ) ) ;
Vector3 b2 = Vector3Normalize ( Vector3CrossProduct ( b1 , direction ) ) ;
Vector3 capCenter = endPos ;
float baseSliceAngle = ( 2.0f * PI ) / slices ;
2024-04-21 12:29:09 +02:00
float baseRingAngle = PI * 0.5f / rings ;
2022-10-17 02:36:53 -07:00
rlBegin ( RL_TRIANGLES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
// render both caps
for ( int c = 0 ; c < 2 ; c + + )
{
for ( int i = 0 ; i < rings ; i + + )
{
2023-02-05 16:30:23 +01:00
for ( int j = 0 ; j < slices ; j + + )
2022-10-17 02:36:53 -07:00
{
// we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier
// as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i))
// as we iterate through the rings they must get smaller by the cos(angle(i))
// compute the four vertices
2024-04-21 12:29:09 +02:00
float ringSin1 = sinf ( baseSliceAngle * ( j + 0 ) ) * cosf ( baseRingAngle * ( i + 0 ) ) ;
float ringCos1 = cosf ( baseSliceAngle * ( j + 0 ) ) * cosf ( baseRingAngle * ( i + 0 ) ) ;
2023-02-05 16:30:23 +01:00
Vector3 w1 = ( Vector3 ) {
2024-04-21 12:29:09 +02:00
capCenter . x + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . x + ringSin1 * b1 . x + ringCos1 * b2 . x ) * radius ,
capCenter . y + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . y + ringSin1 * b1 . y + ringCos1 * b2 . y ) * radius ,
capCenter . z + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . z + ringSin1 * b1 . z + ringCos1 * b2 . z ) * radius
2022-10-17 02:36:53 -07:00
} ;
2024-04-21 12:29:09 +02:00
float ringSin2 = sinf ( baseSliceAngle * ( j + 1 ) ) * cosf ( baseRingAngle * ( i + 0 ) ) ;
float ringCos2 = cosf ( baseSliceAngle * ( j + 1 ) ) * cosf ( baseRingAngle * ( i + 0 ) ) ;
2023-02-05 16:30:23 +01:00
Vector3 w2 = ( Vector3 ) {
2024-04-21 12:29:09 +02:00
capCenter . x + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . x + ringSin2 * b1 . x + ringCos2 * b2 . x ) * radius ,
capCenter . y + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . y + ringSin2 * b1 . y + ringCos2 * b2 . y ) * radius ,
capCenter . z + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . z + ringSin2 * b1 . z + ringCos2 * b2 . z ) * radius
2022-10-17 02:36:53 -07:00
} ;
2024-04-21 12:29:09 +02:00
float ringSin3 = sinf ( baseSliceAngle * ( j + 0 ) ) * cosf ( baseRingAngle * ( i + 1 ) ) ;
float ringCos3 = cosf ( baseSliceAngle * ( j + 0 ) ) * cosf ( baseRingAngle * ( i + 1 ) ) ;
2023-02-05 16:30:23 +01:00
Vector3 w3 = ( Vector3 ) {
2024-04-21 12:29:09 +02:00
capCenter . x + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . x + ringSin3 * b1 . x + ringCos3 * b2 . x ) * radius ,
capCenter . y + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . y + ringSin3 * b1 . y + ringCos3 * b2 . y ) * radius ,
capCenter . z + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . z + ringSin3 * b1 . z + ringCos3 * b2 . z ) * radius
2022-10-17 02:36:53 -07:00
} ;
2024-04-21 12:29:09 +02:00
float ringSin4 = sinf ( baseSliceAngle * ( j + 1 ) ) * cosf ( baseRingAngle * ( i + 1 ) ) ;
float ringCos4 = cosf ( baseSliceAngle * ( j + 1 ) ) * cosf ( baseRingAngle * ( i + 1 ) ) ;
2023-02-05 16:30:23 +01:00
Vector3 w4 = ( Vector3 ) {
2024-04-21 12:29:09 +02:00
capCenter . x + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . x + ringSin4 * b1 . x + ringCos4 * b2 . x ) * radius ,
capCenter . y + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . y + ringSin4 * b1 . y + ringCos4 * b2 . y ) * radius ,
capCenter . z + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . z + ringSin4 * b1 . z + ringCos4 * b2 . z ) * radius
2022-10-17 02:36:53 -07:00
} ;
2024-04-20 13:53:13 +02:00
// Make sure cap triangle normals are facing outwards
if ( c = = 0 )
2022-10-17 02:36:53 -07:00
{
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ;
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
2023-02-05 16:30:23 +01:00
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
rlVertex3f ( w4 . x , w4 . y , w4 . z ) ;
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
2022-10-17 02:36:53 -07:00
}
else
{
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ;
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
2023-02-05 16:30:23 +01:00
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
rlVertex3f ( w4 . x , w4 . y , w4 . z ) ;
2022-10-17 02:36:53 -07:00
}
}
}
capCenter = startPos ;
b0 = Vector3Scale ( b0 , - 1.0f ) ;
}
// render middle
if ( ! sphereCase )
{
2023-02-05 16:30:23 +01:00
for ( int j = 0 ; j < slices ; j + + )
2022-10-17 02:36:53 -07:00
{
// compute the four vertices
float ringSin1 = sinf ( baseSliceAngle * ( j + 0 ) ) * radius ;
float ringCos1 = cosf ( baseSliceAngle * ( j + 0 ) ) * radius ;
2023-02-05 16:30:23 +01:00
Vector3 w1 = {
2022-10-17 02:36:53 -07:00
startPos . x + ringSin1 * b1 . x + ringCos1 * b2 . x ,
2023-02-05 16:30:23 +01:00
startPos . y + ringSin1 * b1 . y + ringCos1 * b2 . y ,
startPos . z + ringSin1 * b1 . z + ringCos1 * b2 . z
2022-10-17 02:36:53 -07:00
} ;
float ringSin2 = sinf ( baseSliceAngle * ( j + 1 ) ) * radius ;
float ringCos2 = cosf ( baseSliceAngle * ( j + 1 ) ) * radius ;
2023-02-05 16:30:23 +01:00
Vector3 w2 = {
startPos . x + ringSin2 * b1 . x + ringCos2 * b2 . x ,
startPos . y + ringSin2 * b1 . y + ringCos2 * b2 . y ,
startPos . z + ringSin2 * b1 . z + ringCos2 * b2 . z
2022-10-17 02:36:53 -07:00
} ;
float ringSin3 = sinf ( baseSliceAngle * ( j + 0 ) ) * radius ;
float ringCos3 = cosf ( baseSliceAngle * ( j + 0 ) ) * radius ;
2023-02-05 16:30:23 +01:00
Vector3 w3 = {
endPos . x + ringSin3 * b1 . x + ringCos3 * b2 . x ,
endPos . y + ringSin3 * b1 . y + ringCos3 * b2 . y ,
endPos . z + ringSin3 * b1 . z + ringCos3 * b2 . z
2022-10-17 02:36:53 -07:00
} ;
float ringSin4 = sinf ( baseSliceAngle * ( j + 1 ) ) * radius ;
float ringCos4 = cosf ( baseSliceAngle * ( j + 1 ) ) * radius ;
2023-02-05 16:30:23 +01:00
Vector3 w4 = {
endPos . x + ringSin4 * b1 . x + ringCos4 * b2 . x ,
endPos . y + ringSin4 * b1 . y + ringCos4 * b2 . y ,
endPos . z + ringSin4 * b1 . z + ringCos4 * b2 . z
2022-10-17 02:36:53 -07:00
} ;
// w2 x.-----------x startPos
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ; // | |\'. T0 /
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ; // T1 | \ '. /
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ; // | |T \ '. /
// | 2 \ T 'x w1
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ; // | w4 x.---\-1-|---x endPos
rlVertex3f ( w4 . x , w4 . y , w4 . z ) ; // T2 '. \ |T3/
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ; // | '. \ | /
// '.\|/
// 'x w3
}
}
rlEnd ( ) ;
}
// Draw capsule wires with the center of its sphere caps at startPos and endPos
void DrawCapsuleWires ( Vector3 startPos , Vector3 endPos , float radius , int slices , int rings , Color color )
{
if ( slices < 3 ) slices = 3 ;
Vector3 direction = { endPos . x - startPos . x , endPos . y - startPos . y , endPos . z - startPos . z } ;
// draw a sphere if start and end points are the same
bool sphereCase = ( direction . x = = 0 ) & & ( direction . y = = 0 ) & & ( direction . z = = 0 ) ;
if ( sphereCase ) direction = ( Vector3 ) { 0.0f , 1.0f , 0.0f } ;
// Construct a basis of the base and the caps:
Vector3 b0 = Vector3Normalize ( direction ) ;
Vector3 b1 = Vector3Normalize ( Vector3Perpendicular ( direction ) ) ;
Vector3 b2 = Vector3Normalize ( Vector3CrossProduct ( b1 , direction ) ) ;
Vector3 capCenter = endPos ;
float baseSliceAngle = ( 2.0f * PI ) / slices ;
2024-04-21 12:29:09 +02:00
float baseRingAngle = PI * 0.5f / rings ;
2022-10-17 02:36:53 -07:00
rlBegin ( RL_LINES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
// render both caps
for ( int c = 0 ; c < 2 ; c + + )
{
for ( int i = 0 ; i < rings ; i + + )
{
2023-02-05 16:30:23 +01:00
for ( int j = 0 ; j < slices ; j + + )
2022-10-17 02:36:53 -07:00
{
// we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier
// as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i))
// as we iterate through the rings they must get smaller by the cos(angle(i))
// compute the four vertices
2024-04-21 12:29:09 +02:00
float ringSin1 = sinf ( baseSliceAngle * ( j + 0 ) ) * cosf ( baseRingAngle * ( i + 0 ) ) ;
float ringCos1 = cosf ( baseSliceAngle * ( j + 0 ) ) * cosf ( baseRingAngle * ( i + 0 ) ) ;
2023-02-05 16:30:23 +01:00
Vector3 w1 = ( Vector3 ) {
2024-04-21 12:29:09 +02:00
capCenter . x + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . x + ringSin1 * b1 . x + ringCos1 * b2 . x ) * radius ,
capCenter . y + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . y + ringSin1 * b1 . y + ringCos1 * b2 . y ) * radius ,
capCenter . z + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . z + ringSin1 * b1 . z + ringCos1 * b2 . z ) * radius
2022-10-17 02:36:53 -07:00
} ;
2024-04-21 12:29:09 +02:00
float ringSin2 = sinf ( baseSliceAngle * ( j + 1 ) ) * cosf ( baseRingAngle * ( i + 0 ) ) ;
float ringCos2 = cosf ( baseSliceAngle * ( j + 1 ) ) * cosf ( baseRingAngle * ( i + 0 ) ) ;
2023-02-05 16:30:23 +01:00
Vector3 w2 = ( Vector3 ) {
2024-04-21 12:29:09 +02:00
capCenter . x + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . x + ringSin2 * b1 . x + ringCos2 * b2 . x ) * radius ,
capCenter . y + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . y + ringSin2 * b1 . y + ringCos2 * b2 . y ) * radius ,
capCenter . z + ( sinf ( baseRingAngle * ( i + 0 ) ) * b0 . z + ringSin2 * b1 . z + ringCos2 * b2 . z ) * radius
2022-10-17 02:36:53 -07:00
} ;
2024-04-21 12:29:09 +02:00
float ringSin3 = sinf ( baseSliceAngle * ( j + 0 ) ) * cosf ( baseRingAngle * ( i + 1 ) ) ;
float ringCos3 = cosf ( baseSliceAngle * ( j + 0 ) ) * cosf ( baseRingAngle * ( i + 1 ) ) ;
2023-02-05 16:30:23 +01:00
Vector3 w3 = ( Vector3 ) {
2024-04-21 12:29:09 +02:00
capCenter . x + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . x + ringSin3 * b1 . x + ringCos3 * b2 . x ) * radius ,
capCenter . y + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . y + ringSin3 * b1 . y + ringCos3 * b2 . y ) * radius ,
capCenter . z + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . z + ringSin3 * b1 . z + ringCos3 * b2 . z ) * radius
2022-10-17 02:36:53 -07:00
} ;
2024-04-21 12:29:09 +02:00
float ringSin4 = sinf ( baseSliceAngle * ( j + 1 ) ) * cosf ( baseRingAngle * ( i + 1 ) ) ;
float ringCos4 = cosf ( baseSliceAngle * ( j + 1 ) ) * cosf ( baseRingAngle * ( i + 1 ) ) ;
2023-02-05 16:30:23 +01:00
Vector3 w4 = ( Vector3 ) {
2024-04-21 12:29:09 +02:00
capCenter . x + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . x + ringSin4 * b1 . x + ringCos4 * b2 . x ) * radius ,
capCenter . y + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . y + ringSin4 * b1 . y + ringCos4 * b2 . y ) * radius ,
capCenter . z + ( sinf ( baseRingAngle * ( i + 1 ) ) * b0 . z + ringSin4 * b1 . z + ringCos4 * b2 . z ) * radius
2022-10-17 02:36:53 -07:00
} ;
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ;
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ;
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
2023-02-05 16:30:23 +01:00
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
rlVertex3f ( w4 . x , w4 . y , w4 . z ) ;
2022-10-17 02:36:53 -07:00
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
2023-02-05 16:30:23 +01:00
rlVertex3f ( w4 . x , w4 . y , w4 . z ) ;
2022-10-17 02:36:53 -07:00
}
}
capCenter = startPos ;
b0 = Vector3Scale ( b0 , - 1.0f ) ;
}
// render middle
if ( ! sphereCase )
{
2023-02-05 16:30:23 +01:00
for ( int j = 0 ; j < slices ; j + + )
2022-10-17 02:36:53 -07:00
{
// compute the four vertices
float ringSin1 = sinf ( baseSliceAngle * ( j + 0 ) ) * radius ;
float ringCos1 = cosf ( baseSliceAngle * ( j + 0 ) ) * radius ;
2023-02-05 16:30:23 +01:00
Vector3 w1 = {
2022-10-17 02:36:53 -07:00
startPos . x + ringSin1 * b1 . x + ringCos1 * b2 . x ,
2023-02-05 16:30:23 +01:00
startPos . y + ringSin1 * b1 . y + ringCos1 * b2 . y ,
startPos . z + ringSin1 * b1 . z + ringCos1 * b2 . z
2022-10-17 02:36:53 -07:00
} ;
float ringSin2 = sinf ( baseSliceAngle * ( j + 1 ) ) * radius ;
float ringCos2 = cosf ( baseSliceAngle * ( j + 1 ) ) * radius ;
2023-02-05 16:30:23 +01:00
Vector3 w2 = {
startPos . x + ringSin2 * b1 . x + ringCos2 * b2 . x ,
startPos . y + ringSin2 * b1 . y + ringCos2 * b2 . y ,
startPos . z + ringSin2 * b1 . z + ringCos2 * b2 . z
2022-10-17 02:36:53 -07:00
} ;
float ringSin3 = sinf ( baseSliceAngle * ( j + 0 ) ) * radius ;
float ringCos3 = cosf ( baseSliceAngle * ( j + 0 ) ) * radius ;
2023-02-05 16:30:23 +01:00
Vector3 w3 = {
endPos . x + ringSin3 * b1 . x + ringCos3 * b2 . x ,
endPos . y + ringSin3 * b1 . y + ringCos3 * b2 . y ,
endPos . z + ringSin3 * b1 . z + ringCos3 * b2 . z
2022-10-17 02:36:53 -07:00
} ;
float ringSin4 = sinf ( baseSliceAngle * ( j + 1 ) ) * radius ;
float ringCos4 = cosf ( baseSliceAngle * ( j + 1 ) ) * radius ;
2023-02-05 16:30:23 +01:00
Vector3 w4 = {
endPos . x + ringSin4 * b1 . x + ringCos4 * b2 . x ,
endPos . y + ringSin4 * b1 . y + ringCos4 * b2 . y ,
endPos . z + ringSin4 * b1 . z + ringCos4 * b2 . z
2022-10-17 02:36:53 -07:00
} ;
2023-02-05 16:30:23 +01:00
rlVertex3f ( w1 . x , w1 . y , w1 . z ) ;
2022-10-17 02:36:53 -07:00
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
2023-02-05 16:30:23 +01:00
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
rlVertex3f ( w4 . x , w4 . y , w4 . z ) ;
2022-10-17 02:36:53 -07:00
2023-02-05 16:30:23 +01:00
rlVertex3f ( w2 . x , w2 . y , w2 . z ) ;
2022-10-17 02:36:53 -07:00
rlVertex3f ( w3 . x , w3 . y , w3 . z ) ;
}
}
rlEnd ( ) ;
}
2013-11-18 23:38:44 +01:00
// Draw a plane
2015-04-06 14:02:29 +02:00
void DrawPlane ( Vector3 centerPos , Vector2 size , Color color )
2013-11-18 23:38:44 +01:00
{
2015-04-06 14:02:29 +02:00
// NOTE: Plane is always created on XZ ground
2014-03-25 12:40:35 +01:00
rlPushMatrix ( ) ;
2017-07-21 17:19:28 +02:00
rlTranslatef ( centerPos . x , centerPos . y , centerPos . z ) ;
2017-08-04 18:34:51 +02:00
rlScalef ( size . x , 1.0f , size . y ) ;
2014-09-03 16:51:28 +02:00
2018-12-19 11:55:58 +01:00
rlBegin ( RL_QUADS ) ;
2014-03-25 12:40:35 +01:00
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2014-09-03 16:51:28 +02:00
rlNormal3f ( 0.0f , 1.0f , 0.0f ) ;
2014-09-16 22:51:31 +02:00
2016-03-01 19:00:12 +01:00
rlVertex3f ( - 0.5f , 0.0f , - 0.5f ) ;
rlVertex3f ( - 0.5f , 0.0f , 0.5f ) ;
rlVertex3f ( 0.5f , 0.0f , 0.5f ) ;
rlVertex3f ( 0.5f , 0.0f , - 0.5f ) ;
rlEnd ( ) ;
rlPopMatrix ( ) ;
2013-11-18 23:38:44 +01:00
}
2015-04-06 14:02:29 +02:00
// Draw a ray line
2015-03-02 20:52:58 +01:00
void DrawRay ( Ray ray , Color color )
{
float scale = 10000 ;
2015-04-06 14:02:29 +02:00
2015-03-02 20:52:58 +01:00
rlBegin ( RL_LINES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
rlVertex3f ( ray . position . x , ray . position . y , ray . position . z ) ;
rlVertex3f ( ray . position . x + ray . direction . x * scale , ray . position . y + ray . direction . y * scale , ray . position . z + ray . direction . z * scale ) ;
rlEnd ( ) ;
}
2013-11-18 23:38:44 +01:00
// Draw a grid centered at (0, 0, 0)
void DrawGrid ( int slices , float spacing )
{
2016-09-12 19:36:41 +02:00
int halfSlices = slices / 2 ;
2013-11-23 13:30:54 +01:00
2014-03-25 12:40:35 +01:00
rlBegin ( RL_LINES ) ;
2016-05-21 18:22:15 +02:00
for ( int i = - halfSlices ; i < = halfSlices ; i + + )
2014-03-25 12:40:35 +01:00
{
if ( i = = 0 )
2013-11-23 13:30:54 +01:00
{
2014-03-25 12:40:35 +01:00
rlColor3f ( 0.5f , 0.5f , 0.5f ) ;
2013-11-23 13:30:54 +01:00
}
2014-03-25 12:40:35 +01:00
else
{
rlColor3f ( 0.75f , 0.75f , 0.75f ) ;
}
2014-09-03 16:51:28 +02:00
2014-03-25 12:40:35 +01:00
rlVertex3f ( ( float ) i * spacing , 0.0f , ( float ) - halfSlices * spacing ) ;
rlVertex3f ( ( float ) i * spacing , 0.0f , ( float ) halfSlices * spacing ) ;
rlVertex3f ( ( float ) - halfSlices * spacing , 0.0f , ( float ) i * spacing ) ;
rlVertex3f ( ( float ) halfSlices * spacing , 0.0f , ( float ) i * spacing ) ;
}
rlEnd ( ) ;
2013-11-18 23:38:44 +01:00
}
2017-07-17 00:33:40 +02:00
// Load model from files (mesh and material)
2016-12-25 01:59:23 +01:00
Model LoadModel ( const char * fileName )
{
Model model = { 0 } ;
2019-03-29 16:48:23 +01:00
# if defined(SUPPORT_FILEFORMAT_OBJ)
if ( IsFileExtension ( fileName , " .obj " ) ) model = LoadOBJ ( fileName ) ;
# endif
# if defined(SUPPORT_FILEFORMAT_IQM)
if ( IsFileExtension ( fileName , " .iqm " ) ) model = LoadIQM ( fileName ) ;
# endif
2019-06-12 12:14:36 +02:00
# if defined(SUPPORT_FILEFORMAT_GLTF)
2021-12-04 19:56:02 +01:00
if ( IsFileExtension ( fileName , " .gltf " ) | | IsFileExtension ( fileName , " .glb " ) ) model = LoadGLTF ( fileName ) ;
2019-06-12 12:14:36 +02:00
# endif
2021-09-04 19:55:09 +02:00
# if defined(SUPPORT_FILEFORMAT_VOX)
if ( IsFileExtension ( fileName , " .vox " ) ) model = LoadVOX ( fileName ) ;
# endif
2022-08-22 11:11:05 +02:00
# if defined(SUPPORT_FILEFORMAT_M3D)
if ( IsFileExtension ( fileName , " .m3d " ) ) model = LoadM3D ( fileName ) ;
# endif
2019-04-04 13:50:52 +02:00
2019-04-04 13:33:54 +02:00
// Make sure model transform is set to identity matrix!
model . transform = MatrixIdentity ( ) ;
2019-03-29 16:48:23 +01:00
2023-10-31 11:32:07 +01:00
if ( ( model . meshCount ! = 0 ) & & ( model . meshes ! = NULL ) )
2019-03-29 20:22:30 +01:00
{
2023-10-31 11:32:07 +01:00
// Upload vertex data to GPU (static meshes)
2021-03-25 14:28:12 +01:00
for ( int i = 0 ; i < model . meshCount ; i + + ) UploadMesh ( & model . meshes [ i ] , false ) ;
2019-04-04 13:33:54 +02:00
}
2023-10-31 11:32:07 +01:00
else TRACELOG ( LOG_WARNING , " MESH: [%s] Failed to load model mesh(es) data " , fileName) ;
2019-04-04 13:33:54 +02:00
2019-03-29 20:22:30 +01:00
if ( model . materialCount = = 0 )
{
2023-10-31 11:32:07 +01:00
TRACELOG ( LOG_WARNING , " MATERIAL: [%s] Failed to load model material data, default to white material " , fileName ) ;
2019-04-04 13:50:52 +02:00
2019-03-29 20:22:30 +01:00
model . materialCount = 1 ;
2019-04-23 14:55:35 +02:00
model . materials = ( Material * ) RL_CALLOC ( model . materialCount , sizeof ( Material ) ) ;
2019-03-29 20:22:30 +01:00
model . materials [ 0 ] = LoadMaterialDefault ( ) ;
2019-04-04 13:50:52 +02:00
2019-09-19 13:36:55 +02:00
if ( model . meshMaterial = = NULL ) model . meshMaterial = ( int * ) RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2019-03-29 20:22:30 +01:00
}
2021-04-01 20:24:33 +02:00
2015-04-06 14:02:29 +02:00
return model ;
}
2017-07-17 00:33:40 +02:00
// Load model from generated mesh
2017-07-21 09:34:09 +02:00
// WARNING: A shallow copy of mesh is generated, passed by value,
// as long as struct contains pointers to data and some values, we get a copy
// of mesh pointing to same data as original version... be careful!
Model LoadModelFromMesh ( Mesh mesh )
2016-03-05 16:17:54 +01:00
{
2016-05-07 18:07:15 +02:00
Model model = { 0 } ;
2019-02-21 18:45:19 +01:00
2016-05-07 18:07:15 +02:00
model . transform = MatrixIdentity ( ) ;
2019-04-04 13:50:52 +02:00
2019-03-29 16:48:23 +01:00
model . meshCount = 1 ;
2019-08-27 11:16:43 +02:00
model . meshes = ( Mesh * ) RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
2019-03-29 16:48:23 +01:00
model . meshes [ 0 ] = mesh ;
2019-04-04 13:50:52 +02:00
2019-03-29 16:48:23 +01:00
model . materialCount = 1 ;
2019-08-27 11:16:43 +02:00
model . materials = ( Material * ) RL_CALLOC ( model . materialCount , sizeof ( Material ) ) ;
2019-03-29 16:48:23 +01:00
model . materials [ 0 ] = LoadMaterialDefault ( ) ;
2019-04-04 13:50:52 +02:00
2019-08-27 11:16:43 +02:00
model . meshMaterial = ( int * ) RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2019-03-29 16:48:23 +01:00
model . meshMaterial [ 0 ] = 0 ; // First material index
2016-03-05 16:17:54 +01:00
return model ;
}
2017-01-28 23:02:30 +01:00
2024-10-16 19:26:12 +02:00
// Check if a model is valid (loaded in GPU, VAO/VBOs)
bool IsModelValid ( Model model )
2023-01-28 06:13:09 -05:00
{
2024-04-20 13:53:13 +02:00
bool result = false ;
2024-04-21 12:29:09 +02:00
2024-04-20 13:53:13 +02:00
if ( ( model . meshes ! = NULL ) & & // Validate model contains some mesh
( model . materials ! = NULL ) & & // Validate model contains some material (at least default one)
( model . meshMaterial ! = NULL ) & & // Validate mesh-material linkage
( model . meshCount > 0 ) & & // Validate mesh count
( model . materialCount > 0 ) ) result = true ; // Validate material count
2024-10-21 17:00:52 +02:00
2024-10-16 19:26:12 +02:00
// NOTE: Many elements could be validated from a model, including every model mesh VAO/VBOs
// but some VBOs could not be used, it depends on Mesh vertex data
for ( int i = 0 ; i < model . meshCount ; i + + )
{
if ( ( model . meshes [ i ] . vertices ! = NULL ) & & ( model . meshes [ i ] . vboId [ 0 ] = = 0 ) ) { result = false ; break ; } // Vertex position buffer not uploaded to GPU
if ( ( model . meshes [ i ] . texcoords ! = NULL ) & & ( model . meshes [ i ] . vboId [ 1 ] = = 0 ) ) { result = false ; break ; } // Vertex textcoords buffer not uploaded to GPU
if ( ( model . meshes [ i ] . normals ! = NULL ) & & ( model . meshes [ i ] . vboId [ 2 ] = = 0 ) ) { result = false ; break ; } // Vertex normals buffer not uploaded to GPU
if ( ( model . meshes [ i ] . colors ! = NULL ) & & ( model . meshes [ i ] . vboId [ 3 ] = = 0 ) ) { result = false ; break ; } // Vertex colors buffer not uploaded to GPU
if ( ( model . meshes [ i ] . tangents ! = NULL ) & & ( model . meshes [ i ] . vboId [ 4 ] = = 0 ) ) { result = false ; break ; } // Vertex tangents buffer not uploaded to GPU
if ( ( model . meshes [ i ] . texcoords2 ! = NULL ) & & ( model . meshes [ i ] . vboId [ 5 ] = = 0 ) ) { result = false ; break ; } // Vertex texcoords2 buffer not uploaded to GPU
if ( ( model . meshes [ i ] . indices ! = NULL ) & & ( model . meshes [ i ] . vboId [ 6 ] = = 0 ) ) { result = false ; break ; } // Vertex indices buffer not uploaded to GPU
if ( ( model . meshes [ i ] . boneIds ! = NULL ) & & ( model . meshes [ i ] . vboId [ 7 ] = = 0 ) ) { result = false ; break ; } // Vertex boneIds buffer not uploaded to GPU
if ( ( model . meshes [ i ] . boneWeights ! = NULL ) & & ( model . meshes [ i ] . vboId [ 8 ] = = 0 ) ) { result = false ; break ; } // Vertex boneWeights buffer not uploaded to GPU
2024-10-21 17:00:52 +02:00
2024-10-16 19:26:12 +02:00
// NOTE: Some OpenGL versions do not support VAO, so we don't check it
//if (model.meshes[i].vaoId == 0) { result = false; break }
}
2024-04-21 12:29:09 +02:00
2024-04-20 13:53:13 +02:00
return result ;
2023-01-28 06:13:09 -05:00
}
2020-12-01 23:44:10 +01:00
// Unload model (meshes/materials) from memory (RAM and/or VRAM)
// NOTE: This function takes care of all model elements, for a detailed control
// over them, use UnloadMesh() and UnloadMaterial()
2016-03-05 16:17:54 +01:00
void UnloadModel ( Model model )
{
2020-12-01 23:44:10 +01:00
// Unload meshes
2019-08-07 00:32:44 +02:00
for ( int i = 0 ; i < model . meshCount ; i + + ) UnloadMesh ( model . meshes [ i ] ) ;
2019-08-08 08:57:21 +01:00
2021-03-18 13:57:53 +01:00
// Unload materials maps
2020-12-01 23:44:10 +01:00
// NOTE: As the user could be sharing shaders and textures between models,
2023-02-09 07:17:47 -05:00
// we don't unload the material but just free its maps,
2020-12-01 23:44:10 +01:00
// the user is responsible for freeing models shaders and textures
2021-03-18 13:57:53 +01:00
for ( int i = 0 ; i < model . materialCount ; i + + ) RL_FREE ( model . materials [ i ] . maps ) ;
2020-12-01 23:44:10 +01:00
// Unload arrays
RL_FREE ( model . meshes ) ;
RL_FREE ( model . materials ) ;
RL_FREE ( model . meshMaterial ) ;
// Unload animation data
RL_FREE ( model . bones ) ;
RL_FREE ( model . bindPose ) ;
TRACELOG ( LOG_INFO , " MODEL: Unloaded model (and meshes) from RAM and VRAM " ) ;
}
2021-07-28 13:15:10 +02:00
// Compute model bounding box limits (considers all meshes)
BoundingBox GetModelBoundingBox ( Model model )
{
BoundingBox bounds = { 0 } ;
2021-08-16 23:23:16 +02:00
2021-07-28 13:15:10 +02:00
if ( model . meshCount > 0 )
2021-08-16 23:23:16 +02:00
{
2021-07-28 13:15:10 +02:00
Vector3 temp = { 0 } ;
bounds = GetMeshBoundingBox ( model . meshes [ 0 ] ) ;
2021-08-16 23:23:16 +02:00
2021-07-28 13:15:10 +02:00
for ( int i = 1 ; i < model . meshCount ; i + + )
{
BoundingBox tempBounds = GetMeshBoundingBox ( model . meshes [ i ] ) ;
2021-08-16 23:23:16 +02:00
2021-07-28 13:15:10 +02:00
temp . x = ( bounds . min . x < tempBounds . min . x ) ? bounds . min . x : tempBounds . min . x ;
temp . y = ( bounds . min . y < tempBounds . min . y ) ? bounds . min . y : tempBounds . min . y ;
temp . z = ( bounds . min . z < tempBounds . min . z ) ? bounds . min . z : tempBounds . min . z ;
bounds . min = temp ;
temp . x = ( bounds . max . x > tempBounds . max . x ) ? bounds . max . x : tempBounds . max . x ;
temp . y = ( bounds . max . y > tempBounds . max . y ) ? bounds . max . y : tempBounds . max . y ;
temp . z = ( bounds . max . z > tempBounds . max . z ) ? bounds . max . z : tempBounds . max . z ;
bounds . max = temp ;
}
}
2023-10-31 15:54:52 +01:00
2023-10-30 20:41:33 +01:00
// Apply model.transform to bounding box
// WARNING: Current BoundingBox structure design does not support rotation transformations,
// in those cases is up to the user to calculate the proper box bounds (8 vertices transformed)
bounds . min = Vector3Transform ( bounds . min , model . transform ) ;
bounds . max = Vector3Transform ( bounds . max , model . transform ) ;
2021-08-16 23:23:16 +02:00
2021-07-28 13:15:10 +02:00
return bounds ;
}
2021-03-25 14:28:12 +01:00
// Upload vertex data into a VAO (if supported) and VBO
void UploadMesh ( Mesh * mesh , bool dynamic )
2019-04-05 13:15:56 +02:00
{
2021-03-25 14:28:12 +01:00
if ( mesh - > vaoId > 0 )
{
// Check if mesh has already been loaded in GPU
TRACELOG ( LOG_WARNING , " VAO: [ID %i] Trying to re-load an already loaded mesh " , mesh - > vaoId ) ;
return ;
}
mesh - > vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VERTEX_BUFFERS , sizeof ( unsigned int ) ) ;
mesh - > vaoId = 0 ; // Vertex Array Object
2024-09-11 16:57:19 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION ] = 0 ; // Vertex buffer: positions
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD ] = 0 ; // Vertex buffer: texcoords
2024-09-20 11:30:37 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL ] = 0 ; // Vertex buffer: normals
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR ] = 0 ; // Vertex buffer: colors
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT ] = 0 ; // Vertex buffer: tangents
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 ] = 0 ; // Vertex buffer: texcoords2
2024-09-30 03:10:02 -07:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES ] = 0 ; // Vertex buffer: indices
2024-10-01 07:09:06 -03:00
2024-10-02 06:41:21 -03:00
# ifdef RL_SUPPORT_MESH_GPU_SKINNING
2024-09-20 11:30:37 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS ] = 0 ; // Vertex buffer: boneIds
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS ] = 0 ; // Vertex buffer: boneWeights
2024-10-01 07:09:06 -03:00
# endif
2024-09-30 03:10:02 -07:00
2021-03-25 14:28:12 +01:00
# if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
mesh - > vaoId = rlLoadVertexArray ( ) ;
rlEnableVertexArray ( mesh - > vaoId ) ;
2022-07-04 17:47:47 +02:00
// NOTE: Vertex attributes must be uploaded considering default locations points and available vertex data
2021-04-01 20:24:33 +02:00
2021-03-25 14:28:12 +01:00
// Enable vertex attributes: position (shader-location = 0)
2023-10-08 18:10:05 +02:00
void * vertices = ( mesh - > animVertices ! = NULL ) ? mesh - > animVertices : mesh - > vertices ;
2024-09-11 16:57:19 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION ] = rlLoadVertexBuffer ( vertices , mesh - > vertexCount * 3 * sizeof ( float ) , dynamic ) ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION , 3 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION ) ;
2021-03-25 14:28:12 +01:00
// Enable vertex attributes: texcoords (shader-location = 1)
2024-09-11 16:57:19 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD ] = rlLoadVertexBuffer ( mesh - > texcoords , mesh - > vertexCount * 2 * sizeof ( float ) , dynamic ) ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD , 2 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD ) ;
2021-03-25 14:28:12 +01:00
2022-08-13 22:28:47 +02:00
// WARNING: When setting default vertex attribute values, the values for each generic vertex attribute
2023-02-09 07:17:47 -05:00
// is part of current state, and it is maintained even if a different program object is used
2022-07-04 17:47:47 +02:00
2021-03-25 14:28:12 +01:00
if ( mesh - > normals ! = NULL )
{
// Enable vertex attributes: normals (shader-location = 2)
2023-10-08 18:10:05 +02:00
void * normals = ( mesh - > animNormals ! = NULL ) ? mesh - > animNormals : mesh - > normals ;
2024-09-11 16:57:19 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL ] = rlLoadVertexBuffer ( normals , mesh - > vertexCount * 3 * sizeof ( float ) , dynamic ) ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL , 3 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL ) ;
2021-03-25 14:28:12 +01:00
}
else
{
2022-07-04 17:47:47 +02:00
// Default vertex attribute: normal
// WARNING: Default value provided to shader if location available
2021-03-25 14:28:12 +01:00
float value [ 3 ] = { 1.0f , 1.0f , 1.0f } ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttributeDefault ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL , value , SHADER_ATTRIB_VEC3 , 3 ) ;
rlDisableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL ) ;
2021-03-25 14:28:12 +01:00
}
if ( mesh - > colors ! = NULL )
{
// Enable vertex attribute: color (shader-location = 3)
2024-09-11 16:57:19 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR ] = rlLoadVertexBuffer ( mesh - > colors , mesh - > vertexCount * 4 * sizeof ( unsigned char ) , dynamic ) ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR , 4 , RL_UNSIGNED_BYTE , 1 , 0 , 0 ) ;
rlEnableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR ) ;
2021-03-25 14:28:12 +01:00
}
else
{
2022-07-04 17:47:47 +02:00
// Default vertex attribute: color
// WARNING: Default value provided to shader if location available
float value [ 4 ] = { 1.0f , 1.0f , 1.0f , 1.0f } ; // WHITE
2024-04-23 20:22:29 +08:00
rlSetVertexAttributeDefault ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR , value , SHADER_ATTRIB_VEC4 , 4 ) ;
rlDisableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR ) ;
2021-03-25 14:28:12 +01:00
}
2019-09-13 12:29:52 +01:00
2021-03-25 14:28:12 +01:00
if ( mesh - > tangents ! = NULL )
{
// Enable vertex attribute: tangent (shader-location = 4)
2024-09-11 16:57:19 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT ] = rlLoadVertexBuffer ( mesh - > tangents , mesh - > vertexCount * 4 * sizeof ( float ) , dynamic ) ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT , 4 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT ) ;
2021-03-25 14:28:12 +01:00
}
else
{
2022-07-04 17:47:47 +02:00
// Default vertex attribute: tangent
// WARNING: Default value provided to shader if location available
2021-03-25 14:28:12 +01:00
float value [ 4 ] = { 0.0f , 0.0f , 0.0f , 0.0f } ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttributeDefault ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT , value , SHADER_ATTRIB_VEC4 , 4 ) ;
rlDisableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT ) ;
2021-03-25 14:28:12 +01:00
}
2019-09-13 12:29:52 +01:00
2021-03-25 14:28:12 +01:00
if ( mesh - > texcoords2 ! = NULL )
{
// Enable vertex attribute: texcoord2 (shader-location = 5)
2024-09-11 16:57:19 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 ] = rlLoadVertexBuffer ( mesh - > texcoords2 , mesh - > vertexCount * 2 * sizeof ( float ) , dynamic ) ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 , 2 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 ) ;
2021-03-25 14:28:12 +01:00
}
else
{
2022-07-04 17:47:47 +02:00
// Default vertex attribute: texcoord2
// WARNING: Default value provided to shader if location available
2021-03-25 14:28:12 +01:00
float value [ 2 ] = { 0.0f , 0.0f } ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttributeDefault ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 , value , SHADER_ATTRIB_VEC2 , 2 ) ;
rlDisableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 ) ;
2021-03-25 14:28:12 +01:00
}
2024-09-30 03:10:02 -07:00
2024-10-02 01:49:56 -07:00
# ifdef RL_SUPPORT_MESH_GPU_SKINNING
2024-09-20 11:30:37 -04:00
if ( mesh - > boneIds ! = NULL )
{
2024-10-21 00:26:15 +02:00
// Enable vertex attribute: boneIds (shader-location = 7)
2024-09-20 11:30:37 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS ] = rlLoadVertexBuffer ( mesh - > boneIds , mesh - > vertexCount * 4 * sizeof ( unsigned char ) , dynamic ) ;
rlSetVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS , 4 , RL_UNSIGNED_BYTE , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS ) ;
}
else
{
// Default vertex attribute: boneIds
// WARNING: Default value provided to shader if location available
float value [ 4 ] = { 0.0f , 0.0f , 0.0f , 0.0f } ;
rlSetVertexAttributeDefault ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS , value , SHADER_ATTRIB_VEC4 , 4 ) ;
rlDisableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS ) ;
}
2024-10-21 17:00:52 +02:00
2024-09-20 11:30:37 -04:00
if ( mesh - > boneWeights ! = NULL )
{
2024-10-21 00:26:15 +02:00
// Enable vertex attribute: boneWeights (shader-location = 8)
2024-09-20 11:30:37 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS ] = rlLoadVertexBuffer ( mesh - > boneWeights , mesh - > vertexCount * 4 * sizeof ( float ) , dynamic ) ;
rlSetVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS , 4 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS ) ;
}
else
{
// Default vertex attribute: boneWeights
// WARNING: Default value provided to shader if location available
float value [ 4 ] = { 0.0f , 0.0f , 0.0f , 0.0f } ;
rlSetVertexAttributeDefault ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS , value , SHADER_ATTRIB_VEC4 , 2 ) ;
rlDisableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS ) ;
}
2024-09-30 03:10:02 -07:00
# endif
2021-03-25 14:28:12 +01:00
if ( mesh - > indices ! = NULL )
{
2024-09-11 16:57:19 -04:00
mesh - > vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES ] = rlLoadVertexBufferElement ( mesh - > indices , mesh - > triangleCount * 3 * sizeof ( unsigned short ) , dynamic ) ;
2021-03-25 14:28:12 +01:00
}
if ( mesh - > vaoId > 0 ) TRACELOG ( LOG_INFO , " VAO: [ID %i] Mesh uploaded successfully to VRAM (GPU) " , mesh - > vaoId ) ;
else TRACELOG ( LOG_INFO , " VBO: Mesh uploaded successfully to VRAM (GPU) " ) ;
2021-04-01 20:24:33 +02:00
2021-03-25 14:28:12 +01:00
rlDisableVertexArray ( ) ;
# endif
2019-04-05 13:15:56 +02:00
}
2021-04-20 12:16:16 +02:00
// Update mesh vertex data in GPU for a specific buffer index
2022-02-20 20:35:28 +01:00
void UpdateMeshBuffer ( Mesh mesh , int index , const void * data , int dataSize , int offset )
2021-04-20 12:16:16 +02:00
{
2021-04-20 12:19:04 +02:00
rlUpdateVertexBuffer ( mesh . vboId [ index ] , data , dataSize , offset ) ;
2021-04-20 12:16:16 +02:00
}
2021-03-25 14:28:12 +01:00
// Draw a 3d mesh with material and transform
void DrawMesh ( Mesh mesh , Material material , Matrix transform )
2021-01-22 12:16:19 +01:00
{
2021-03-25 14:28:12 +01:00
# if defined(GRAPHICS_API_OPENGL_11)
2021-03-25 20:35:48 +01:00
# define GL_VERTEX_ARRAY 0x8074
# define GL_NORMAL_ARRAY 0x8075
# define GL_COLOR_ARRAY 0x8076
# define GL_TEXTURE_COORD_ARRAY 0x8078
2021-03-25 14:28:12 +01:00
2021-03-25 20:35:48 +01:00
rlEnableTexture ( material . maps [ MATERIAL_MAP_DIFFUSE ] . texture . id ) ;
2021-03-25 14:28:12 +01:00
2021-03-25 20:35:48 +01:00
rlEnableStatePointer ( GL_VERTEX_ARRAY , mesh . vertices ) ;
rlEnableStatePointer ( GL_TEXTURE_COORD_ARRAY , mesh . texcoords ) ;
rlEnableStatePointer ( GL_NORMAL_ARRAY , mesh . normals ) ;
rlEnableStatePointer ( GL_COLOR_ARRAY , mesh . colors ) ;
2021-04-01 20:24:33 +02:00
2021-03-25 14:28:12 +01:00
rlPushMatrix ( ) ;
2021-08-27 12:13:44 +02:00
rlMultMatrixf ( MatrixToFloat ( transform ) ) ;
2021-04-01 20:24:33 +02:00
rlColor4ub ( material . maps [ MATERIAL_MAP_DIFFUSE ] . color . r ,
material . maps [ MATERIAL_MAP_DIFFUSE ] . color . g ,
material . maps [ MATERIAL_MAP_DIFFUSE ] . color . b ,
2021-03-25 20:35:48 +01:00
material . maps [ MATERIAL_MAP_DIFFUSE ] . color . a ) ;
2021-03-25 14:28:12 +01:00
2021-03-25 20:35:48 +01:00
if ( mesh . indices ! = NULL ) rlDrawVertexArrayElements ( 0 , mesh . triangleCount * 3 , mesh . indices ) ;
else rlDrawVertexArray ( 0 , mesh . vertexCount ) ;
2021-03-25 14:28:12 +01:00
rlPopMatrix ( ) ;
2021-03-25 20:35:48 +01:00
rlDisableStatePointer ( GL_VERTEX_ARRAY ) ;
rlDisableStatePointer ( GL_TEXTURE_COORD_ARRAY ) ;
rlDisableStatePointer ( GL_NORMAL_ARRAY ) ;
rlDisableStatePointer ( GL_COLOR_ARRAY ) ;
2021-03-25 14:28:12 +01:00
2021-03-25 20:35:48 +01:00
rlDisableTexture ( ) ;
2021-03-25 14:28:12 +01:00
# endif
# if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// Bind shader program
rlEnableShader ( material . shader . id ) ;
2021-03-26 00:12:29 +01:00
// Send required data to shader (matrices, values)
2021-03-25 14:28:12 +01:00
//-----------------------------------------------------
// Upload to shader material.colDiffuse
if ( material . shader . locs [ SHADER_LOC_COLOR_DIFFUSE ] ! = - 1 )
{
float values [ 4 ] = {
( float ) material . maps [ MATERIAL_MAP_DIFFUSE ] . color . r / 255.0f ,
( float ) material . maps [ MATERIAL_MAP_DIFFUSE ] . color . g / 255.0f ,
( float ) material . maps [ MATERIAL_MAP_DIFFUSE ] . color . b / 255.0f ,
2021-04-01 20:24:33 +02:00
( float ) material . maps [ MATERIAL_MAP_DIFFUSE ] . color . a / 255.0f
2021-03-25 14:28:12 +01:00
} ;
2021-04-01 20:24:33 +02:00
2021-03-25 14:28:12 +01:00
rlSetUniform ( material . shader . locs [ SHADER_LOC_COLOR_DIFFUSE ] , values , SHADER_UNIFORM_VEC4 , 1 ) ;
}
2021-03-31 19:59:23 +02:00
// Upload to shader material.colSpecular (if location available)
2021-03-25 14:28:12 +01:00
if ( material . shader . locs [ SHADER_LOC_COLOR_SPECULAR ] ! = - 1 )
{
float values [ 4 ] = {
2024-01-23 12:37:54 +01:00
( float ) material . maps [ MATERIAL_MAP_SPECULAR ] . color . r / 255.0f ,
( float ) material . maps [ MATERIAL_MAP_SPECULAR ] . color . g / 255.0f ,
( float ) material . maps [ MATERIAL_MAP_SPECULAR ] . color . b / 255.0f ,
( float ) material . maps [ MATERIAL_MAP_SPECULAR ] . color . a / 255.0f
2021-03-25 14:28:12 +01:00
} ;
2021-04-01 20:24:33 +02:00
2021-03-25 14:28:12 +01:00
rlSetUniform ( material . shader . locs [ SHADER_LOC_COLOR_SPECULAR ] , values , SHADER_UNIFORM_VEC4 , 1 ) ;
}
2021-03-31 19:59:23 +02:00
// Get a copy of current matrices to work with,
2023-02-09 07:17:47 -05:00
// just in case stereo render is required, and we need to modify them
2021-03-31 19:59:23 +02:00
// NOTE: At this point the modelview matrix just contains the view matrix (camera)
// That's because BeginMode3D() sets it and there is no model-drawing function
// that modifies it, all use rlPushMatrix() and rlPopMatrix()
2021-07-16 17:12:22 +02:00
Matrix matModel = MatrixIdentity ( ) ;
2021-07-30 12:54:54 +02:00
Matrix matView = rlGetMatrixModelview ( ) ;
2021-07-16 17:12:22 +02:00
Matrix matModelView = MatrixIdentity ( ) ;
2021-07-30 12:54:54 +02:00
Matrix matProjection = rlGetMatrixProjection ( ) ;
2021-03-31 19:59:23 +02:00
// Upload view and projection matrices (if locations available)
2021-07-30 12:54:54 +02:00
if ( material . shader . locs [ SHADER_LOC_MATRIX_VIEW ] ! = - 1 ) rlSetUniformMatrix ( material . shader . locs [ SHADER_LOC_MATRIX_VIEW ] , matView ) ;
if ( material . shader . locs [ SHADER_LOC_MATRIX_PROJECTION ] ! = - 1 ) rlSetUniformMatrix ( material . shader . locs [ SHADER_LOC_MATRIX_PROJECTION ] , matProjection ) ;
2021-03-25 14:28:12 +01:00
2021-08-27 12:13:44 +02:00
// Accumulate several model transformations:
// transform: model transformation provided (includes DrawModel() params combined with model.transform)
// rlGetMatrixTransform(): rlgl internal transform matrix due to push/pop matrix stack
matModel = MatrixMultiply ( transform , rlGetMatrixTransform ( ) ) ;
2024-06-02 19:52:00 +10:00
// Model transformation matrix is sent to shader uniform location: SHADER_LOC_MATRIX_MODEL
if ( material . shader . locs [ SHADER_LOC_MATRIX_MODEL ] ! = - 1 ) rlSetUniformMatrix ( material . shader . locs [ SHADER_LOC_MATRIX_MODEL ] , matModel ) ;
2021-08-27 12:13:44 +02:00
// Get model-view matrix
matModelView = MatrixMultiply ( matModel , matView ) ;
// Upload model normal matrix (if locations available)
if ( material . shader . locs [ SHADER_LOC_MATRIX_NORMAL ] ! = - 1 ) rlSetUniformMatrix ( material . shader . locs [ SHADER_LOC_MATRIX_NORMAL ] , MatrixTranspose ( MatrixInvert ( matModel ) ) ) ;
2024-09-30 03:10:02 -07:00
2024-10-02 01:49:56 -07:00
# ifdef RL_SUPPORT_MESH_GPU_SKINNING
2024-10-21 17:00:52 +02:00
// Upload Bone Transforms
2024-11-03 13:12:01 +01:00
if ( ( material . shader . locs [ SHADER_LOC_BONE_MATRICES ] ! = - 1 ) & & mesh . boneMatrices )
2024-09-20 11:30:37 -04:00
{
rlSetUniformMatrices ( material . shader . locs [ SHADER_LOC_BONE_MATRICES ] , mesh . boneMatrices , mesh . boneCount ) ;
}
2024-09-30 03:10:02 -07:00
# endif
2021-08-27 12:13:44 +02:00
//-----------------------------------------------------
// Bind active texture maps (if available)
for ( int i = 0 ; i < MAX_MATERIAL_MAPS ; i + + )
2021-03-26 00:12:29 +01:00
{
2021-08-27 12:13:44 +02:00
if ( material . maps [ i ] . texture . id > 0 )
{
// Select current shader texture slot
rlActiveTextureSlot ( i ) ;
2021-03-25 14:28:12 +01:00
2021-08-27 12:13:44 +02:00
// Enable texture for active slot
if ( ( i = = MATERIAL_MAP_IRRADIANCE ) | |
( i = = MATERIAL_MAP_PREFILTER ) | |
( i = = MATERIAL_MAP_CUBEMAP ) ) rlEnableTextureCubemap ( material . maps [ i ] . texture . id ) ;
else rlEnableTexture ( material . maps [ i ] . texture . id ) ;
rlSetUniform ( material . shader . locs [ SHADER_LOC_MAP_DIFFUSE + i ] , & i , SHADER_UNIFORM_INT , 1 ) ;
}
}
2022-07-04 17:47:47 +02:00
// Try binding vertex array objects (VAO) or use VBOs if not possible
// WARNING: UploadMesh() enables all vertex attributes available in mesh and sets default attribute values
// for shader expected vertex attributes that are not provided by the mesh (i.e. colors)
// This could be a dangerous approach because different meshes with different shaders can enable/disable some attributes
2021-08-27 12:13:44 +02:00
if ( ! rlEnableVertexArray ( mesh . vaoId ) )
{
// Bind mesh VBO data: vertex position (shader-location = 0)
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION ] ) ;
2021-08-27 12:13:44 +02:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_POSITION ] , 3 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_POSITION ] ) ;
// Bind mesh VBO data: vertex texcoords (shader-location = 1)
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD ] ) ;
2021-08-27 12:13:44 +02:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD01 ] , 2 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD01 ] ) ;
2021-03-26 00:12:29 +01:00
2021-08-27 12:13:44 +02:00
if ( material . shader . locs [ SHADER_LOC_VERTEX_NORMAL ] ! = - 1 )
{
// Bind mesh VBO data: vertex normals (shader-location = 2)
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL ] ) ;
2021-08-27 12:13:44 +02:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_NORMAL ] , 3 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_NORMAL ] ) ;
}
2021-03-26 00:12:29 +01:00
2021-08-27 12:13:44 +02:00
// Bind mesh VBO data: vertex colors (shader-location = 3, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] ! = - 1 )
{
2024-09-11 16:57:19 -04:00
if ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR ] ! = 0 )
2021-08-27 12:13:44 +02:00
{
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR ] ) ;
2021-08-27 12:13:44 +02:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] , 4 , RL_UNSIGNED_BYTE , 1 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] ) ;
}
else
{
2022-07-04 17:47:47 +02:00
// Set default value for defined vertex attribute in shader but not provided by mesh
// WARNING: It could result in GPU undefined behaviour
2021-08-27 12:13:44 +02:00
float value [ 4 ] = { 1.0f , 1.0f , 1.0f , 1.0f } ;
2021-12-16 00:19:41 +01:00
rlSetVertexAttributeDefault ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] , value , SHADER_ATTRIB_VEC4 , 4 ) ;
2021-08-27 12:13:44 +02:00
rlDisableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] ) ;
}
}
2021-03-26 00:12:29 +01:00
2021-08-27 12:13:44 +02:00
// Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_TANGENT ] ! = - 1 )
2021-03-26 00:12:29 +01:00
{
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT ] ) ;
2021-08-27 12:13:44 +02:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TANGENT ] , 4 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TANGENT ] ) ;
2021-03-26 00:12:29 +01:00
}
2021-08-27 12:13:44 +02:00
// Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD02 ] ! = - 1 )
{
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 ] ) ;
2021-08-27 12:13:44 +02:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD02 ] , 2 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD02 ] ) ;
}
2024-09-30 03:10:02 -07:00
2024-10-02 01:49:56 -07:00
# ifdef RL_SUPPORT_MESH_GPU_SKINNING
2024-09-20 11:30:37 -04:00
// Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_BONEIDS ] ! = - 1 )
{
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS ] ) ;
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_BONEIDS ] , 4 , RL_UNSIGNED_BYTE , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_BONEIDS ] ) ;
}
2024-10-21 17:00:52 +02:00
2024-09-20 11:30:37 -04:00
// Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_BONEWEIGHTS ] ! = - 1 )
{
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS ] ) ;
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_BONEWEIGHTS ] , 4 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_BONEWEIGHTS ] ) ;
}
2024-09-30 03:10:02 -07:00
# endif
2021-04-01 20:24:33 +02:00
2024-09-11 16:57:19 -04:00
if ( mesh . indices ! = NULL ) rlEnableVertexBufferElement ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES ] ) ;
2021-03-26 00:12:29 +01:00
}
2021-08-27 12:13:44 +02:00
2021-09-01 23:09:30 +02:00
int eyeCount = 1 ;
if ( rlIsStereoRenderEnabled ( ) ) eyeCount = 2 ;
2021-08-27 12:13:44 +02:00
2021-09-01 23:09:30 +02:00
for ( int eye = 0 ; eye < eyeCount ; eye + + )
2021-08-27 12:13:44 +02:00
{
// Calculate model-view-projection matrix (MVP)
Matrix matModelViewProjection = MatrixIdentity ( ) ;
2021-09-01 23:09:30 +02:00
if ( eyeCount = = 1 ) matModelViewProjection = MatrixMultiply ( matModelView , matProjection ) ;
2021-08-27 12:13:44 +02:00
else
{
// Setup current eye viewport (half screen width)
rlViewport ( eye * rlGetFramebufferWidth ( ) / 2 , 0 , rlGetFramebufferWidth ( ) / 2 , rlGetFramebufferHeight ( ) ) ;
matModelViewProjection = MatrixMultiply ( MatrixMultiply ( matModelView , rlGetMatrixViewOffsetStereo ( eye ) ) , rlGetMatrixProjectionStereo ( eye ) ) ;
}
// Send combined model-view-projection matrix to shader
rlSetUniformMatrix ( material . shader . locs [ SHADER_LOC_MATRIX_MVP ] , matModelViewProjection ) ;
// Draw mesh
if ( mesh . indices ! = NULL ) rlDrawVertexArrayElements ( 0 , mesh . triangleCount * 3 , 0 ) ;
else rlDrawVertexArray ( 0 , mesh . vertexCount ) ;
}
2023-02-09 07:17:47 -05:00
// Unbind all bound texture maps
2021-08-27 12:13:44 +02:00
for ( int i = 0 ; i < MAX_MATERIAL_MAPS ; i + + )
{
2022-07-04 17:47:47 +02:00
if ( material . maps [ i ] . texture . id > 0 )
{
// Select current shader texture slot
rlActiveTextureSlot ( i ) ;
// Disable texture for active slot
if ( ( i = = MATERIAL_MAP_IRRADIANCE ) | |
( i = = MATERIAL_MAP_PREFILTER ) | |
( i = = MATERIAL_MAP_CUBEMAP ) ) rlDisableTextureCubemap ( ) ;
else rlDisableTexture ( ) ;
}
2021-08-27 12:13:44 +02:00
}
// Disable all possible vertex array objects (or VBOs)
rlDisableVertexArray ( ) ;
rlDisableVertexBuffer ( ) ;
rlDisableVertexBufferElement ( ) ;
// Disable shader program
rlDisableShader ( ) ;
// Restore rlgl internal modelview and projection matrices
rlSetMatrixModelview ( matView ) ;
rlSetMatrixProjection ( matProjection ) ;
# endif
}
// Draw multiple mesh instances with material and different transforms
2022-02-20 21:12:55 +02:00
void DrawMeshInstanced ( Mesh mesh , Material material , const Matrix * transforms , int instances )
2021-08-27 12:13:44 +02:00
{
# if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// Instancing required variables
float16 * instanceTransforms = NULL ;
unsigned int instancesVboId = 0 ;
// Bind shader program
rlEnableShader ( material . shader . id ) ;
// Send required data to shader (matrices, values)
//-----------------------------------------------------
// Upload to shader material.colDiffuse
if ( material . shader . locs [ SHADER_LOC_COLOR_DIFFUSE ] ! = - 1 )
{
float values [ 4 ] = {
( float ) material . maps [ MATERIAL_MAP_DIFFUSE ] . color . r / 255.0f ,
( float ) material . maps [ MATERIAL_MAP_DIFFUSE ] . color . g / 255.0f ,
( float ) material . maps [ MATERIAL_MAP_DIFFUSE ] . color . b / 255.0f ,
( float ) material . maps [ MATERIAL_MAP_DIFFUSE ] . color . a / 255.0f
} ;
rlSetUniform ( material . shader . locs [ SHADER_LOC_COLOR_DIFFUSE ] , values , SHADER_UNIFORM_VEC4 , 1 ) ;
}
// Upload to shader material.colSpecular (if location available)
if ( material . shader . locs [ SHADER_LOC_COLOR_SPECULAR ] ! = - 1 )
2021-03-26 00:12:29 +01:00
{
2021-08-27 12:13:44 +02:00
float values [ 4 ] = {
( float ) material . maps [ SHADER_LOC_COLOR_SPECULAR ] . color . r / 255.0f ,
( float ) material . maps [ SHADER_LOC_COLOR_SPECULAR ] . color . g / 255.0f ,
( float ) material . maps [ SHADER_LOC_COLOR_SPECULAR ] . color . b / 255.0f ,
( float ) material . maps [ SHADER_LOC_COLOR_SPECULAR ] . color . a / 255.0f
} ;
2021-03-26 00:12:29 +01:00
2021-08-27 12:13:44 +02:00
rlSetUniform ( material . shader . locs [ SHADER_LOC_COLOR_SPECULAR ] , values , SHADER_UNIFORM_VEC4 , 1 ) ;
}
2021-08-16 23:23:16 +02:00
2021-08-27 12:13:44 +02:00
// Get a copy of current matrices to work with,
2023-02-09 07:17:47 -05:00
// just in case stereo render is required, and we need to modify them
2021-08-27 12:13:44 +02:00
// NOTE: At this point the modelview matrix just contains the view matrix (camera)
// That's because BeginMode3D() sets it and there is no model-drawing function
// that modifies it, all use rlPushMatrix() and rlPopMatrix()
Matrix matModel = MatrixIdentity ( ) ;
Matrix matView = rlGetMatrixModelview ( ) ;
Matrix matModelView = MatrixIdentity ( ) ;
Matrix matProjection = rlGetMatrixProjection ( ) ;
// Upload view and projection matrices (if locations available)
if ( material . shader . locs [ SHADER_LOC_MATRIX_VIEW ] ! = - 1 ) rlSetUniformMatrix ( material . shader . locs [ SHADER_LOC_MATRIX_VIEW ] , matView ) ;
if ( material . shader . locs [ SHADER_LOC_MATRIX_PROJECTION ] ! = - 1 ) rlSetUniformMatrix ( material . shader . locs [ SHADER_LOC_MATRIX_PROJECTION ] , matProjection ) ;
// Create instances buffer
instanceTransforms = ( float16 * ) RL_MALLOC ( instances * sizeof ( float16 ) ) ;
// Fill buffer with instances transformations as float16 arrays
for ( int i = 0 ; i < instances ; i + + ) instanceTransforms [ i ] = MatrixToFloatV ( transforms [ i ] ) ;
// Enable mesh VAO to attach new buffer
rlEnableVertexArray ( mesh . vaoId ) ;
2024-04-20 20:31:06 +02:00
// This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData()
2021-08-27 12:13:44 +02:00
// It isn't clear which would be reliably faster in all cases and on all platforms,
// anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems
// no faster, since we're transferring all the transform matrices anyway
instancesVboId = rlLoadVertexBuffer ( instanceTransforms , instances * sizeof ( float16 ) , false ) ;
2024-12-08 12:48:54 +01:00
// Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCE_TX
2021-08-27 12:13:44 +02:00
for ( unsigned int i = 0 ; i < 4 ; i + + )
{
2024-12-08 12:48:54 +01:00
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_INSTANCE_TX ] + i ) ;
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_INSTANCE_TX ] + i , 4 , RL_FLOAT , 0 , sizeof ( Matrix ) , i * sizeof ( Vector4 ) ) ;
rlSetVertexAttributeDivisor ( material . shader . locs [ SHADER_LOC_VERTEX_INSTANCE_TX ] + i , 1 ) ;
2021-03-26 00:12:29 +01:00
}
2021-04-01 20:24:33 +02:00
2021-08-27 12:13:44 +02:00
rlDisableVertexBuffer ( ) ;
rlDisableVertexArray ( ) ;
// Accumulate internal matrix transform (push/pop) and view matrix
// NOTE: In this case, model instance transformation must be computed in the shader
matModelView = MatrixMultiply ( rlGetMatrixTransform ( ) , matView ) ;
2021-03-31 20:44:16 +02:00
// Upload model normal matrix (if locations available)
2021-07-30 12:54:54 +02:00
if ( material . shader . locs [ SHADER_LOC_MATRIX_NORMAL ] ! = - 1 ) rlSetUniformMatrix ( material . shader . locs [ SHADER_LOC_MATRIX_NORMAL ] , MatrixTranspose ( MatrixInvert ( matModel ) ) ) ;
2024-10-21 17:00:52 +02:00
2024-10-02 01:49:56 -07:00
# ifdef RL_SUPPORT_MESH_GPU_SKINNING
2024-10-21 17:00:52 +02:00
// Upload Bone Transforms
2024-11-03 13:12:01 +01:00
if ( ( material . shader . locs [ SHADER_LOC_BONE_MATRICES ] ! = - 1 ) & & mesh . boneMatrices )
2024-09-20 11:30:37 -04:00
{
rlSetUniformMatrices ( material . shader . locs [ SHADER_LOC_BONE_MATRICES ] , mesh . boneMatrices , mesh . boneCount ) ;
}
2024-09-30 03:10:02 -07:00
# endif
2024-10-21 17:00:52 +02:00
2021-03-25 14:28:12 +01:00
//-----------------------------------------------------
// Bind active texture maps (if available)
for ( int i = 0 ; i < MAX_MATERIAL_MAPS ; i + + )
{
if ( material . maps [ i ] . texture . id > 0 )
{
// Select current shader texture slot
rlActiveTextureSlot ( i ) ;
2021-04-01 20:24:33 +02:00
2021-03-25 14:28:12 +01:00
// Enable texture for active slot
2021-04-01 20:24:33 +02:00
if ( ( i = = MATERIAL_MAP_IRRADIANCE ) | |
( i = = MATERIAL_MAP_PREFILTER ) | |
2021-03-25 14:28:12 +01:00
( i = = MATERIAL_MAP_CUBEMAP ) ) rlEnableTextureCubemap ( material . maps [ i ] . texture . id ) ;
else rlEnableTexture ( material . maps [ i ] . texture . id ) ;
rlSetUniform ( material . shader . locs [ SHADER_LOC_MAP_DIFFUSE + i ] , & i , SHADER_UNIFORM_INT , 1 ) ;
}
}
// Try binding vertex array objects (VAO)
// or use VBOs if not possible
if ( ! rlEnableVertexArray ( mesh . vaoId ) )
{
// Bind mesh VBO data: vertex position (shader-location = 0)
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION ] ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_POSITION ] , 3 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_POSITION ] ) ;
2021-04-01 20:24:33 +02:00
2021-03-25 14:28:12 +01:00
// Bind mesh VBO data: vertex texcoords (shader-location = 1)
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD ] ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD01 ] , 2 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD01 ] ) ;
if ( material . shader . locs [ SHADER_LOC_VERTEX_NORMAL ] ! = - 1 )
{
// Bind mesh VBO data: vertex normals (shader-location = 2)
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL ] ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_NORMAL ] , 3 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_NORMAL ] ) ;
}
// Bind mesh VBO data: vertex colors (shader-location = 3, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] ! = - 1 )
{
2024-09-11 16:57:19 -04:00
if ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR ] ! = 0 )
2021-03-25 14:28:12 +01:00
{
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR ] ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] , 4 , RL_UNSIGNED_BYTE , 1 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] ) ;
}
else
{
// Set default value for unused attribute
// NOTE: Required when using default shader and no VAO support
float value [ 4 ] = { 1.0f , 1.0f , 1.0f , 1.0f } ;
2021-12-16 00:19:41 +01:00
rlSetVertexAttributeDefault ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] , value , SHADER_ATTRIB_VEC4 , 4 ) ;
2021-03-25 14:28:12 +01:00
rlDisableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] ) ;
}
}
// Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_TANGENT ] ! = - 1 )
{
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT ] ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TANGENT ] , 4 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TANGENT ] ) ;
}
// Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD02 ] ! = - 1 )
{
2024-09-11 16:57:19 -04:00
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 ] ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD02 ] , 2 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD02 ] ) ;
}
2024-10-01 07:09:06 -03:00
2024-10-02 06:41:21 -03:00
# ifdef RL_SUPPORT_MESH_GPU_SKINNING
2024-09-20 11:30:37 -04:00
// Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_BONEIDS ] ! = - 1 )
{
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS ] ) ;
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_BONEIDS ] , 4 , RL_UNSIGNED_BYTE , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_BONEIDS ] ) ;
}
2024-10-21 17:00:52 +02:00
2024-09-20 11:30:37 -04:00
// Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
if ( material . shader . locs [ SHADER_LOC_VERTEX_BONEWEIGHTS ] ! = - 1 )
{
rlEnableVertexBuffer ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS ] ) ;
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_BONEWEIGHTS ] , 4 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_BONEWEIGHTS ] ) ;
}
2024-10-01 07:09:06 -03:00
# endif
2021-03-25 14:28:12 +01:00
2024-09-11 16:57:19 -04:00
if ( mesh . indices ! = NULL ) rlEnableVertexBufferElement ( mesh . vboId [ RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES ] ) ;
2021-03-25 14:28:12 +01:00
}
2021-03-31 19:59:23 +02:00
2021-09-01 23:09:30 +02:00
int eyeCount = 1 ;
if ( rlIsStereoRenderEnabled ( ) ) eyeCount = 2 ;
2021-03-25 14:28:12 +01:00
2021-09-01 23:09:30 +02:00
for ( int eye = 0 ; eye < eyeCount ; eye + + )
2021-03-25 14:28:12 +01:00
{
2021-03-31 19:59:23 +02:00
// Calculate model-view-projection matrix (MVP)
2021-07-16 17:12:22 +02:00
Matrix matModelViewProjection = MatrixIdentity ( ) ;
2021-09-01 23:09:30 +02:00
if ( eyeCount = = 1 ) matModelViewProjection = MatrixMultiply ( matModelView , matProjection ) ;
2021-03-25 14:28:12 +01:00
else
{
// Setup current eye viewport (half screen width)
2021-03-28 21:35:58 +02:00
rlViewport ( eye * rlGetFramebufferWidth ( ) / 2 , 0 , rlGetFramebufferWidth ( ) / 2 , rlGetFramebufferHeight ( ) ) ;
2021-07-30 12:54:54 +02:00
matModelViewProjection = MatrixMultiply ( MatrixMultiply ( matModelView , rlGetMatrixViewOffsetStereo ( eye ) ) , rlGetMatrixProjectionStereo ( eye ) ) ;
2021-03-25 14:28:12 +01:00
}
2021-03-31 19:59:23 +02:00
2021-03-25 14:28:12 +01:00
// Send combined model-view-projection matrix to shader
2021-07-30 12:54:54 +02:00
rlSetUniformMatrix ( material . shader . locs [ SHADER_LOC_MATRIX_MVP ] , matModelViewProjection ) ;
2021-03-25 14:28:12 +01:00
2021-08-27 12:13:44 +02:00
// Draw mesh instanced
if ( mesh . indices ! = NULL ) rlDrawVertexArrayElementsInstanced ( 0 , mesh . triangleCount * 3 , 0 , instances ) ;
else rlDrawVertexArrayInstanced ( 0 , mesh . vertexCount , instances ) ;
2021-03-25 14:28:12 +01:00
}
2023-02-09 07:17:47 -05:00
// Unbind all bound texture maps
2021-03-25 14:28:12 +01:00
for ( int i = 0 ; i < MAX_MATERIAL_MAPS ; i + + )
{
2022-07-04 17:47:47 +02:00
if ( material . maps [ i ] . texture . id > 0 )
{
// Select current shader texture slot
rlActiveTextureSlot ( i ) ;
// Disable texture for active slot
if ( ( i = = MATERIAL_MAP_IRRADIANCE ) | |
( i = = MATERIAL_MAP_PREFILTER ) | |
( i = = MATERIAL_MAP_CUBEMAP ) ) rlDisableTextureCubemap ( ) ;
else rlDisableTexture ( ) ;
}
2021-03-25 14:28:12 +01:00
}
// Disable all possible vertex array objects (or VBOs)
rlDisableVertexArray ( ) ;
rlDisableVertexBuffer ( ) ;
rlDisableVertexBufferElement ( ) ;
// Disable shader program
rlDisableShader ( ) ;
2021-08-27 12:13:44 +02:00
// Remove instance transforms buffer
rlUnloadVertexBuffer ( instancesVboId ) ;
RL_FREE ( instanceTransforms ) ;
2021-03-25 14:28:12 +01:00
# endif
2021-01-22 12:16:19 +01:00
}
2021-03-25 14:28:12 +01:00
// Unload mesh from memory (RAM and VRAM)
2019-08-07 00:32:44 +02:00
void UnloadMesh ( Mesh mesh )
2017-07-17 00:33:40 +02:00
{
2021-02-21 12:09:52 +01:00
// Unload rlgl mesh vboId data
2021-03-25 14:28:12 +01:00
rlUnloadVertexArray ( mesh . vaoId ) ;
2021-12-24 21:54:25 +03:00
if ( mesh . vboId ! = NULL ) for ( int i = 0 ; i < MAX_MESH_VERTEX_BUFFERS ; i + + ) rlUnloadVertexBuffer ( mesh . vboId [ i ] ) ;
2021-03-25 14:28:12 +01:00
RL_FREE ( mesh . vboId ) ;
2021-03-19 19:43:44 +01:00
2021-02-21 12:09:52 +01:00
RL_FREE ( mesh . vertices ) ;
RL_FREE ( mesh . texcoords ) ;
RL_FREE ( mesh . normals ) ;
RL_FREE ( mesh . colors ) ;
RL_FREE ( mesh . tangents ) ;
RL_FREE ( mesh . texcoords2 ) ;
RL_FREE ( mesh . indices ) ;
RL_FREE ( mesh . animVertices ) ;
RL_FREE ( mesh . animNormals ) ;
RL_FREE ( mesh . boneWeights ) ;
RL_FREE ( mesh . boneIds ) ;
2024-09-20 11:30:37 -04:00
RL_FREE ( mesh . boneMatrices ) ;
2016-05-07 18:07:15 +02:00
}
2018-09-17 16:56:02 +02:00
// Export mesh data to file
2020-11-22 00:10:16 +01:00
bool ExportMesh ( Mesh mesh , const char * fileName )
2018-04-04 12:00:54 +02:00
{
2020-11-22 00:10:16 +01:00
bool success = false ;
2020-12-23 15:03:26 +01:00
2018-09-17 16:56:02 +02:00
if ( IsFileExtension ( fileName , " .obj " ) )
2018-04-04 12:00:54 +02:00
{
2020-05-23 19:48:40 +02:00
// Estimated data size, it should be enough...
2024-11-19 10:35:47 +01:00
int vc = mesh . vertexCount ;
int dataSize = vc * ( int ) strlen ( " v -0000.000000f -0000.000000f -0000.000000f \n " ) +
vc * ( int ) strlen ( " vt -0.000000f -0.000000f \n " ) +
vc * ( int ) strlen ( " vn -0.0000f -0.0000f -0.0000f \n " ) +
mesh . triangleCount * snprintf ( NULL , 0 , " f %i/%i/%i %i/%i/%i %i/%i/%i \n " , vc , vc , vc , vc , vc , vc , vc , vc , vc ) ;
2020-05-23 19:48:40 +02:00
// NOTE: Text data buffer size is estimated considering mesh data size
2024-11-19 10:35:47 +01:00
char * txtData = ( char * ) RL_CALLOC ( dataSize + 1000 , sizeof ( char ) ) ;
2020-05-23 19:48:40 +02:00
2021-09-01 23:09:30 +02:00
int byteCount = 0 ;
byteCount + = sprintf ( txtData + byteCount , " # ////////////////////////////////////////////////////////////////////////////////// \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " # // // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " # // rMeshOBJ exporter v1.0 - Mesh exported as triangle faces and not optimized // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " # // // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " # // more info and bugs-report: github.com/raysan5/raylib // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " # // feedback and support: ray[at]raylib.com // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " # // // \n " ) ;
2024-01-02 20:58:12 +01:00
byteCount + = sprintf ( txtData + byteCount , " # // Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) // \n " ) ;
2021-09-01 23:09:30 +02:00
byteCount + = sprintf ( txtData + byteCount , " # // // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " # ////////////////////////////////////////////////////////////////////////////////// \n \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " # Vertex Count: %i \n " , mesh . vertexCount ) ;
byteCount + = sprintf ( txtData + byteCount , " # Triangle Count: %i \n \n " , mesh . triangleCount ) ;
byteCount + = sprintf ( txtData + byteCount , " g mesh \n " ) ;
2019-02-21 18:45:19 +01:00
2018-09-17 16:56:02 +02:00
for ( int i = 0 , v = 0 ; i < mesh . vertexCount ; i + + , v + = 3 )
{
2024-11-19 10:35:47 +01:00
byteCount + = sprintf ( txtData + byteCount , " v %.6f %.6f %.6f \n " , mesh . vertices [ v ] , mesh . vertices [ v + 1 ] , mesh . vertices [ v + 2 ] ) ;
2018-09-17 16:56:02 +02:00
}
2019-02-21 18:45:19 +01:00
2018-09-17 16:56:02 +02:00
for ( int i = 0 , v = 0 ; i < mesh . vertexCount ; i + + , v + = 2 )
{
2024-11-19 10:35:47 +01:00
byteCount + = sprintf ( txtData + byteCount , " vt %.6f %.6f \n " , mesh . texcoords [ v ] , mesh . texcoords [ v + 1 ] ) ;
2018-09-17 16:56:02 +02:00
}
2019-02-21 18:45:19 +01:00
2018-09-17 16:56:02 +02:00
for ( int i = 0 , v = 0 ; i < mesh . vertexCount ; i + + , v + = 3 )
{
2024-11-19 10:35:47 +01:00
byteCount + = sprintf ( txtData + byteCount , " vn %.4f %.4f %.4f \n " , mesh . normals [ v ] , mesh . normals [ v + 1 ] , mesh . normals [ v + 2 ] ) ;
2018-09-17 16:56:02 +02:00
}
2019-02-21 18:45:19 +01:00
2021-12-17 20:55:38 +01:00
if ( mesh . indices ! = NULL )
2018-09-17 16:56:02 +02:00
{
2021-12-17 20:55:38 +01:00
for ( int i = 0 , v = 0 ; i < mesh . triangleCount ; i + + , v + = 3 )
{
2022-02-18 20:30:46 +01:00
byteCount + = sprintf ( txtData + byteCount , " f %i/%i/%i %i/%i/%i %i/%i/%i \n " ,
2021-12-17 20:55:38 +01:00
mesh . indices [ v ] + 1 , mesh . indices [ v ] + 1 , mesh . indices [ v ] + 1 ,
mesh . indices [ v + 1 ] + 1 , mesh . indices [ v + 1 ] + 1 , mesh . indices [ v + 1 ] + 1 ,
mesh . indices [ v + 2 ] + 1 , mesh . indices [ v + 2 ] + 1 , mesh . indices [ v + 2 ] + 1 ) ;
}
}
else
{
for ( int i = 0 , v = 1 ; i < mesh . triangleCount ; i + + , v + = 3 )
{
byteCount + = sprintf ( txtData + byteCount , " f %i/%i/%i %i/%i/%i %i/%i/%i \n " , v , v , v , v + 1 , v + 1 , v + 1 , v + 2 , v + 2 , v + 2 ) ;
}
2018-09-17 16:56:02 +02:00
}
2019-02-21 18:45:19 +01:00
2020-05-23 19:48:40 +02:00
// NOTE: Text data length exported is determined by '\0' (NULL) character
2020-11-22 00:10:16 +01:00
success = SaveFileText ( fileName , txtData ) ;
2019-02-21 18:45:19 +01:00
2020-05-23 19:48:40 +02:00
RL_FREE ( txtData ) ;
}
2020-11-03 23:47:33 +01:00
else if ( IsFileExtension ( fileName , " .raw " ) )
2020-05-23 19:48:40 +02:00
{
// TODO: Support additional file formats to export mesh vertex data
2018-04-04 12:00:54 +02:00
}
2020-12-23 15:03:26 +01:00
2020-11-22 00:10:16 +01:00
return success ;
2018-04-04 12:00:54 +02:00
}
2023-11-30 13:01:19 +01:00
// Export mesh as code file (.h) defining multiple arrays of vertex attributes
bool ExportMeshAsCode ( Mesh mesh , const char * fileName )
{
bool success = false ;
# ifndef TEXT_BYTES_PER_LINE
# define TEXT_BYTES_PER_LINE 20
# endif
// NOTE: Text data buffer size is fixed to 64MB
char * txtData = ( char * ) RL_CALLOC ( 64 * 1024 * 1024 , sizeof ( char ) ) ; // 64 MB
int byteCount = 0 ;
byteCount + = sprintf ( txtData + byteCount , " //////////////////////////////////////////////////////////////////////////////////////// \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " // // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " // MeshAsCode exporter v1.0 - Mesh vertex data exported as arrays // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " // // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " // more info and bugs-report: github.com/raysan5/raylib // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " // feedback and support: ray[at]raylib.com // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " // // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " // Copyright (c) 2023 Ramon Santamaria (@raysan5) // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " // // \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " //////////////////////////////////////////////////////////////////////////////////////// \n \n " ) ;
// Get file name from path and convert variable name to uppercase
char varFileName [ 256 ] = { 0 } ;
strcpy ( varFileName , GetFileNameWithoutExt ( fileName ) ) ;
for ( int i = 0 ; varFileName [ i ] ! = ' \0 ' ; i + + ) if ( ( varFileName [ i ] > = ' a ' ) & & ( varFileName [ i ] < = ' z ' ) ) { varFileName [ i ] = varFileName [ i ] - 32 ; }
// Add image information
byteCount + = sprintf ( txtData + byteCount , " // Mesh basic information \n " ) ;
byteCount + = sprintf ( txtData + byteCount , " #define %s_VERTEX_COUNT %i \n " , varFileName , mesh . vertexCount ) ;
byteCount + = sprintf ( txtData + byteCount , " #define %s_TRIANGLE_COUNT %i \n \n " , varFileName , mesh . triangleCount ) ;
2024-02-29 18:50:44 +01:00
2023-11-30 13:01:19 +01:00
// Define vertex attributes data as separate arrays
//-----------------------------------------------------------------------------------------
if ( mesh . vertices ! = NULL ) // Vertex position (XYZ - 3 components per vertex - float)
{
byteCount + = sprintf ( txtData + byteCount , " static float %s_VERTEX_DATA[%i] = { " , varFileName , mesh . vertexCount * 3 ) ;
for ( int i = 0 ; i < mesh . vertexCount * 3 - 1 ; i + + ) byteCount + = sprintf ( txtData + byteCount , ( ( i % TEXT_BYTES_PER_LINE = = 0 ) ? " %.3ff, \n " : " %.3ff, " ) , mesh . vertices [ i ] ) ;
byteCount + = sprintf ( txtData + byteCount , " %.3ff }; \n \n " , mesh . vertices [ mesh . vertexCount * 3 - 1 ] ) ;
}
2024-02-29 18:50:44 +01:00
if ( mesh . texcoords ! = NULL ) // Vertex texture coordinates (UV - 2 components per vertex - float)
2023-11-30 13:01:19 +01:00
{
byteCount + = sprintf ( txtData + byteCount , " static float %s_TEXCOORD_DATA[%i] = { " , varFileName , mesh . vertexCount * 2 ) ;
for ( int i = 0 ; i < mesh . vertexCount * 2 - 1 ; i + + ) byteCount + = sprintf ( txtData + byteCount , ( ( i % TEXT_BYTES_PER_LINE = = 0 ) ? " %.3ff, \n " : " %.3ff, " ) , mesh . texcoords [ i ] ) ;
byteCount + = sprintf ( txtData + byteCount , " %.3ff }; \n \n " , mesh . texcoords [ mesh . vertexCount * 2 - 1 ] ) ;
}
2024-02-29 18:50:44 +01:00
if ( mesh . texcoords2 ! = NULL ) // Vertex texture coordinates (UV - 2 components per vertex - float)
2023-11-30 13:01:19 +01:00
{
byteCount + = sprintf ( txtData + byteCount , " static float %s_TEXCOORD2_DATA[%i] = { " , varFileName , mesh . vertexCount * 2 ) ;
for ( int i = 0 ; i < mesh . vertexCount * 2 - 1 ; i + + ) byteCount + = sprintf ( txtData + byteCount , ( ( i % TEXT_BYTES_PER_LINE = = 0 ) ? " %.3ff, \n " : " %.3ff, " ) , mesh . texcoords2 [ i ] ) ;
byteCount + = sprintf ( txtData + byteCount , " %.3ff }; \n \n " , mesh . texcoords2 [ mesh . vertexCount * 2 - 1 ] ) ;
}
2024-02-29 18:50:44 +01:00
2023-11-30 13:01:19 +01:00
if ( mesh . normals ! = NULL ) // Vertex normals (XYZ - 3 components per vertex - float)
{
byteCount + = sprintf ( txtData + byteCount , " static float %s_NORMAL_DATA[%i] = { " , varFileName , mesh . vertexCount * 3 ) ;
for ( int i = 0 ; i < mesh . vertexCount * 3 - 1 ; i + + ) byteCount + = sprintf ( txtData + byteCount , ( ( i % TEXT_BYTES_PER_LINE = = 0 ) ? " %.3ff, \n " : " %.3ff, " ) , mesh . normals [ i ] ) ;
byteCount + = sprintf ( txtData + byteCount , " %.3ff }; \n \n " , mesh . normals [ mesh . vertexCount * 3 - 1 ] ) ;
}
2024-02-29 18:50:44 +01:00
2023-11-30 13:01:19 +01:00
if ( mesh . tangents ! = NULL ) // Vertex tangents (XYZW - 4 components per vertex - float)
{
byteCount + = sprintf ( txtData + byteCount , " static float %s_TANGENT_DATA[%i] = { " , varFileName , mesh . vertexCount * 4 ) ;
for ( int i = 0 ; i < mesh . vertexCount * 4 - 1 ; i + + ) byteCount + = sprintf ( txtData + byteCount , ( ( i % TEXT_BYTES_PER_LINE = = 0 ) ? " %.3ff, \n " : " %.3ff, " ) , mesh . tangents [ i ] ) ;
byteCount + = sprintf ( txtData + byteCount , " %.3ff }; \n \n " , mesh . tangents [ mesh . vertexCount * 4 - 1 ] ) ;
}
if ( mesh . colors ! = NULL ) // Vertex colors (RGBA - 4 components per vertex - unsigned char)
{
byteCount + = sprintf ( txtData + byteCount , " static unsigned char %s_COLOR_DATA[%i] = { " , varFileName , mesh . vertexCount * 4 ) ;
for ( int i = 0 ; i < mesh . vertexCount * 4 - 1 ; i + + ) byteCount + = sprintf ( txtData + byteCount , ( ( i % TEXT_BYTES_PER_LINE = = 0 ) ? " 0x%x, \n " : " 0x%x, " ) , mesh . colors [ i ] ) ;
byteCount + = sprintf ( txtData + byteCount , " 0x%x }; \n \n " , mesh . colors [ mesh . vertexCount * 4 - 1 ] ) ;
}
if ( mesh . indices ! = NULL ) // Vertex indices (3 index per triangle - unsigned short)
{
byteCount + = sprintf ( txtData + byteCount , " static unsigned short %s_INDEX_DATA[%i] = { " , varFileName , mesh . triangleCount * 3 ) ;
for ( int i = 0 ; i < mesh . triangleCount * 3 - 1 ; i + + ) byteCount + = sprintf ( txtData + byteCount , ( ( i % TEXT_BYTES_PER_LINE = = 0 ) ? " %i, \n " : " %i, " ) , mesh . indices [ i ] ) ;
byteCount + = sprintf ( txtData + byteCount , " %i }; \n " , mesh . indices [ mesh . triangleCount * 3 - 1 ] ) ;
}
//-----------------------------------------------------------------------------------------
// NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText ( fileName , txtData ) ;
RL_FREE ( txtData ) ;
//if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
//else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
return success ;
}
2023-01-24 17:16:35 +01:00
# if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
2023-01-20 07:13:19 -08:00
// Process obj materials
2023-06-24 13:32:13 +02:00
static void ProcessMaterialsOBJ ( Material * materials , tinyobj_material_t * mats , int materialCount )
2023-01-20 07:13:19 -08:00
{
2023-06-24 13:32:13 +02:00
// Init model mats
2023-02-04 20:19:51 +01:00
for ( int m = 0 ; m < materialCount ; m + + )
{
// Init material to default
// NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE
2023-06-24 13:32:13 +02:00
materials [ m ] = LoadMaterialDefault ( ) ;
2023-01-20 07:13:19 -08:00
2024-08-25 09:49:52 -07:00
if ( mats = = NULL ) continue ;
2023-02-04 20:19:51 +01:00
// Get default texture, in case no texture is defined
// NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
2023-06-24 13:32:13 +02:00
materials [ m ] . maps [ MATERIAL_MAP_DIFFUSE ] . texture = ( Texture2D ) { rlGetTextureIdDefault ( ) , 1 , 1 , 1 , PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 } ;
2023-01-20 07:13:19 -08:00
2023-06-24 13:32:13 +02:00
if ( mats [ m ] . diffuse_texname ! = NULL ) materials [ m ] . maps [ MATERIAL_MAP_DIFFUSE ] . texture = LoadTexture ( mats [ m ] . diffuse_texname ) ; //char *diffuse_texname; // map_Kd
2024-04-21 12:29:09 +02:00
else materials [ m ] . maps [ MATERIAL_MAP_DIFFUSE ] . color = ( Color ) { ( unsigned char ) ( mats [ m ] . diffuse [ 0 ] * 255.0f ) , ( unsigned char ) ( mats [ m ] . diffuse [ 1 ] * 255.0f ) , ( unsigned char ) ( mats [ m ] . diffuse [ 2 ] * 255.0f ) , 255 } ; //float diffuse[3];
2023-06-24 13:32:13 +02:00
materials [ m ] . maps [ MATERIAL_MAP_DIFFUSE ] . value = 0.0f ;
2023-01-20 07:13:19 -08:00
2023-06-24 13:32:13 +02:00
if ( mats [ m ] . specular_texname ! = NULL ) materials [ m ] . maps [ MATERIAL_MAP_SPECULAR ] . texture = LoadTexture ( mats [ m ] . specular_texname ) ; //char *specular_texname; // map_Ks
2024-04-21 12:29:09 +02:00
materials [ m ] . maps [ MATERIAL_MAP_SPECULAR ] . color = ( Color ) { ( unsigned char ) ( mats [ m ] . specular [ 0 ] * 255.0f ) , ( unsigned char ) ( mats [ m ] . specular [ 1 ] * 255.0f ) , ( unsigned char ) ( mats [ m ] . specular [ 2 ] * 255.0f ) , 255 } ; //float specular[3];
2023-06-24 13:32:13 +02:00
materials [ m ] . maps [ MATERIAL_MAP_SPECULAR ] . value = 0.0f ;
2023-01-20 07:13:19 -08:00
2023-06-24 13:32:13 +02:00
if ( mats [ m ] . bump_texname ! = NULL ) materials [ m ] . maps [ MATERIAL_MAP_NORMAL ] . texture = LoadTexture ( mats [ m ] . bump_texname ) ; //char *bump_texname; // map_bump, bump
materials [ m ] . maps [ MATERIAL_MAP_NORMAL ] . color = WHITE ;
materials [ m ] . maps [ MATERIAL_MAP_NORMAL ] . value = mats [ m ] . shininess ;
2023-01-20 07:13:19 -08:00
2024-04-21 12:29:09 +02:00
materials [ m ] . maps [ MATERIAL_MAP_EMISSION ] . color = ( Color ) { ( unsigned char ) ( mats [ m ] . emission [ 0 ] * 255.0f ) , ( unsigned char ) ( mats [ m ] . emission [ 1 ] * 255.0f ) , ( unsigned char ) ( mats [ m ] . emission [ 2 ] * 255.0f ) , 255 } ; //float emission[3];
2023-01-20 07:13:19 -08:00
2023-06-24 13:32:13 +02:00
if ( mats [ m ] . displacement_texname ! = NULL ) materials [ m ] . maps [ MATERIAL_MAP_HEIGHT ] . texture = LoadTexture ( mats [ m ] . displacement_texname ) ; //char *displacement_texname; // disp
2023-02-04 20:19:51 +01:00
}
2023-01-20 07:13:19 -08:00
}
2023-01-24 17:16:35 +01:00
# endif
2023-01-20 07:13:19 -08:00
2019-04-05 13:15:56 +02:00
// Load materials from model file
Material * LoadMaterials ( const char * fileName , int * materialCount )
{
Material * materials = NULL ;
unsigned int count = 0 ;
2019-09-13 12:29:52 +01:00
2019-04-05 13:15:56 +02:00
// TODO: Support IQM and GLTF for materials parsing
# if defined(SUPPORT_FILEFORMAT_MTL)
if ( IsFileExtension ( fileName , " .mtl " ) )
{
2020-10-08 19:31:59 +01:00
tinyobj_material_t * mats = NULL ;
2020-11-03 23:47:33 +01:00
2020-11-21 14:14:25 +01:00
int result = tinyobj_parse_mtl_file ( & mats , & count , fileName ) ;
2020-11-15 14:31:43 +01:00
if ( result ! = TINYOBJ_SUCCESS ) TRACELOG ( LOG_WARNING , " MATERIAL: [%s] Failed to parse materials file " , fileName ) ;
2019-04-05 13:15:56 +02:00
2023-09-02 12:58:47 +02:00
materials = RL_MALLOC ( count * sizeof ( Material ) ) ;
2023-01-24 17:16:35 +01:00
ProcessMaterialsOBJ ( materials , mats , count ) ;
2019-04-05 13:15:56 +02:00
tinyobj_materials_free ( mats , count ) ;
}
# else
2020-03-27 17:43:51 +01:00
TRACELOG ( LOG_WARNING , " FILEIO: [%s] Failed to load material file " , fileName ) ;
2019-04-05 13:15:56 +02:00
# endif
* materialCount = count ;
return materials ;
}
// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)
Material LoadMaterialDefault ( void )
{
Material material = { 0 } ;
2019-08-27 10:56:49 +02:00
material . maps = ( MaterialMap * ) RL_CALLOC ( MAX_MATERIAL_MAPS , sizeof ( MaterialMap ) ) ;
2019-04-05 13:15:56 +02:00
2021-07-29 21:57:50 +02:00
// Using rlgl default shader
material . shader . id = rlGetShaderIdDefault ( ) ;
material . shader . locs = rlGetShaderLocsDefault ( ) ;
2021-08-16 23:23:16 +02:00
2021-07-29 21:57:50 +02:00
// Using rlgl default texture (1x1 pixel, UNCOMPRESSED_R8G8B8A8, 1 mipmap)
material . maps [ MATERIAL_MAP_DIFFUSE ] . texture = ( Texture2D ) { rlGetTextureIdDefault ( ) , 1 , 1 , 1 , PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 } ;
2021-03-14 11:05:51 +01:00
//material.maps[MATERIAL_MAP_NORMAL].texture; // NOTE: By default, not set
//material.maps[MATERIAL_MAP_SPECULAR].texture; // NOTE: By default, not set
2019-04-05 13:15:56 +02:00
2021-03-14 11:05:51 +01:00
material . maps [ MATERIAL_MAP_DIFFUSE ] . color = WHITE ; // Diffuse color
material . maps [ MATERIAL_MAP_SPECULAR ] . color = WHITE ; // Specular color
2019-04-05 13:15:56 +02:00
return material ;
}
2024-10-16 19:26:12 +02:00
// Check if a material is valid (map textures loaded in GPU)
bool IsMaterialValid ( Material material )
2023-01-28 06:13:09 -05:00
{
2024-04-20 13:53:13 +02:00
bool result = false ;
2024-04-21 12:29:09 +02:00
2024-04-20 13:53:13 +02:00
if ( ( material . maps ! = NULL ) & & // Validate material contain some map
( material . shader . id > 0 ) ) result = true ; // Validate material shader is valid
2024-10-21 17:00:52 +02:00
2024-10-16 19:26:12 +02:00
// TODO: Check if available maps contain loaded textures
2024-04-21 12:29:09 +02:00
2024-04-20 13:53:13 +02:00
return result ;
2023-01-28 06:13:09 -05:00
}
2019-04-05 13:15:56 +02:00
// Unload material from memory
void UnloadMaterial ( Material material )
{
// Unload material shader (avoid unloading default shader, managed by raylib)
2021-07-29 21:57:50 +02:00
if ( material . shader . id ! = rlGetShaderIdDefault ( ) ) UnloadShader ( material . shader ) ;
2019-04-05 13:15:56 +02:00
// Unload loaded texture maps (avoid unloading default texture, managed by raylib)
2021-12-24 21:54:25 +03:00
if ( material . maps ! = NULL )
2019-04-05 13:15:56 +02:00
{
2021-12-24 21:54:25 +03:00
for ( int i = 0 ; i < MAX_MATERIAL_MAPS ; i + + )
{
if ( material . maps [ i ] . texture . id ! = rlGetTextureIdDefault ( ) ) rlUnloadTexture ( material . maps [ i ] . texture . id ) ;
}
2019-04-05 13:15:56 +02:00
}
2019-09-13 12:29:52 +01:00
2019-08-07 00:32:44 +02:00
RL_FREE ( material . maps ) ;
2019-04-05 13:15:56 +02:00
}
2021-03-14 11:05:51 +01:00
// Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)
2019-04-05 13:15:56 +02:00
// NOTE: Previous texture should be manually unloaded
void SetMaterialTexture ( Material * material , int mapType , Texture2D texture )
{
material - > maps [ mapType ] . texture = texture ;
}
// Set the material for a mesh
void SetModelMeshMaterial ( Model * model , int meshId , int materialId )
{
2020-03-27 17:43:51 +01:00
if ( meshId > = model - > meshCount ) TRACELOG ( LOG_WARNING , " MESH: Id greater than mesh count " ) ;
else if ( materialId > = model - > materialCount ) TRACELOG ( LOG_WARNING , " MATERIAL: Id greater than material count " ) ;
2019-04-05 13:15:56 +02:00
else model - > meshMaterial [ meshId ] = materialId ;
}
// Load model animations from file
2023-09-02 12:05:34 +02:00
ModelAnimation * LoadModelAnimations ( const char * fileName , int * animCount )
2019-04-05 13:15:56 +02:00
{
2021-01-28 12:29:06 +02:00
ModelAnimation * animations = NULL ;
2019-09-13 12:29:52 +01:00
2021-01-28 12:29:06 +02:00
# if defined(SUPPORT_FILEFORMAT_IQM)
2021-10-14 10:43:29 +02:00
if ( IsFileExtension ( fileName , " .iqm " ) ) animations = LoadModelAnimationsIQM ( fileName , animCount ) ;
2021-01-28 12:29:06 +02:00
# endif
2022-08-26 10:04:38 +02:00
# if defined(SUPPORT_FILEFORMAT_M3D)
if ( IsFileExtension ( fileName , " .m3d " ) ) animations = LoadModelAnimationsM3D ( fileName , animCount ) ;
# endif
2021-01-28 12:29:06 +02:00
# if defined(SUPPORT_FILEFORMAT_GLTF)
2023-01-02 14:23:48 -05:00
if ( IsFileExtension ( fileName , " .gltf;.glb " ) ) animations = LoadModelAnimationsGLTF ( fileName , animCount ) ;
2021-01-28 12:29:06 +02:00
# endif
2021-03-19 19:43:44 +01:00
2019-04-05 13:15:56 +02:00
return animations ;
}
2024-10-24 12:46:02 +02:00
// Update model animated bones transform matrices for a given frame
// NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId],
// to be uploaded to shader at drawing, in case GPU skinning is enabled
2024-11-03 13:12:01 +01:00
void UpdateModelAnimationBones ( Model model , ModelAnimation anim , int frame )
2024-09-20 11:30:37 -04:00
{
if ( ( anim . frameCount > 0 ) & & ( anim . bones ! = NULL ) & & ( anim . framePoses ! = NULL ) )
{
2024-10-21 17:00:52 +02:00
if ( frame > = anim . frameCount ) frame = frame % anim . frameCount ;
2024-12-18 16:09:50 +05:30
// Get first mesh which have bones
int firstMeshWithBones = - 1 ;
2024-09-20 11:30:37 -04:00
for ( int i = 0 ; i < model . meshCount ; i + + )
{
if ( model . meshes [ i ] . boneMatrices )
{
assert ( model . meshes [ i ] . boneCount = = anim . boneCount ) ;
2024-12-18 16:09:50 +05:30
if ( firstMeshWithBones = = - 1 )
{
firstMeshWithBones = i ;
2024-12-18 11:43:43 +01:00
break ;
2024-12-18 16:09:50 +05:30
}
}
}
2024-10-21 17:00:52 +02:00
2024-12-18 16:09:50 +05:30
// Update all bones and boneMatrices of first mesh with bones.
for ( int boneId = 0 ; boneId < anim . boneCount ; boneId + + )
{
Vector3 inTranslation = model . bindPose [ boneId ] . translation ;
Quaternion inRotation = model . bindPose [ boneId ] . rotation ;
Vector3 inScale = model . bindPose [ boneId ] . scale ;
Vector3 outTranslation = anim . framePoses [ frame ] [ boneId ] . translation ;
Quaternion outRotation = anim . framePoses [ frame ] [ boneId ] . rotation ;
Vector3 outScale = anim . framePoses [ frame ] [ boneId ] . scale ;
Vector3 invTranslation = Vector3RotateByQuaternion ( Vector3Negate ( inTranslation ) , QuaternionInvert ( inRotation ) ) ;
Quaternion invRotation = QuaternionInvert ( inRotation ) ;
Vector3 invScale = Vector3Divide ( ( Vector3 ) { 1.0f , 1.0f , 1.0f } , inScale ) ;
Vector3 boneTranslation = Vector3Add (
Vector3RotateByQuaternion ( Vector3Multiply ( outScale , invTranslation ) ,
outRotation ) , outTranslation ) ;
Quaternion boneRotation = QuaternionMultiply ( outRotation , invRotation ) ;
Vector3 boneScale = Vector3Multiply ( outScale , invScale ) ;
Matrix boneMatrix = MatrixMultiply ( MatrixMultiply (
QuaternionToMatrix ( boneRotation ) ,
MatrixTranslate ( boneTranslation . x , boneTranslation . y , boneTranslation . z ) ) ,
MatrixScale ( boneScale . x , boneScale . y , boneScale . z ) ) ;
model . meshes [ firstMeshWithBones ] . boneMatrices [ boneId ] = boneMatrix ;
}
2024-12-18 11:43:43 +01:00
// Update remaining meshes with bones
// NOTE: Using deep copy because shallow copy results in double free with 'UnloadModel()'
2024-12-18 16:09:50 +05:30
if ( firstMeshWithBones ! = - 1 )
{
for ( int i = firstMeshWithBones + 1 ; i < model . meshCount ; i + + )
{
if ( model . meshes [ i ] . boneMatrices )
2024-09-20 11:30:37 -04:00
{
2024-12-18 11:43:43 +01:00
memcpy ( model . meshes [ i ] . boneMatrices ,
2024-12-18 16:09:50 +05:30
model . meshes [ firstMeshWithBones ] . boneMatrices ,
2024-12-18 11:43:43 +01:00
model . meshes [ i ] . boneCount * sizeof ( model . meshes [ i ] . boneMatrices [ 0 ] ) ) ;
2024-09-20 11:30:37 -04:00
}
}
}
}
}
2024-12-03 19:14:14 +01:00
// at least 2x speed up vs the old method
2024-11-08 08:28:39 -05:00
// Update model animated vertex data (positions and normals) for a given frame
// NOTE: Updated data is uploaded to GPU
void UpdateModelAnimation ( Model model , ModelAnimation anim , int frame )
{
UpdateModelAnimationBones ( model , anim , frame ) ;
2024-12-18 11:43:43 +01:00
2024-11-08 08:28:39 -05:00
for ( int m = 0 ; m < model . meshCount ; m + + )
{
Mesh mesh = model . meshes [ m ] ;
Vector3 animVertex = { 0 } ;
Vector3 animNormal = { 0 } ;
int boneId = 0 ;
int boneCounter = 0 ;
float boneWeight = 0.0 ;
2024-12-18 11:43:43 +01:00
bool updated = false ; // Flag to check when anim vertex information is updated
2024-11-08 08:28:39 -05:00
const int vValues = mesh . vertexCount * 3 ;
2024-12-18 11:43:43 +01:00
2024-11-08 08:28:39 -05:00
for ( int vCounter = 0 ; vCounter < vValues ; vCounter + = 3 )
{
mesh . animVertices [ vCounter ] = 0 ;
mesh . animVertices [ vCounter + 1 ] = 0 ;
mesh . animVertices [ vCounter + 2 ] = 0 ;
if ( mesh . animNormals ! = NULL )
{
mesh . animNormals [ vCounter ] = 0 ;
mesh . animNormals [ vCounter + 1 ] = 0 ;
mesh . animNormals [ vCounter + 2 ] = 0 ;
}
// Iterates over 4 bones per vertex
for ( int j = 0 ; j < 4 ; j + + , boneCounter + + )
{
boneWeight = mesh . boneWeights [ boneCounter ] ;
boneId = mesh . boneIds [ boneCounter ] ;
2024-12-03 19:14:14 +01:00
2024-11-08 08:28:39 -05:00
// Early stop when no transformation will be applied
if ( boneWeight = = 0.0f ) continue ;
animVertex = ( Vector3 ) { mesh . vertices [ vCounter ] , mesh . vertices [ vCounter + 1 ] , mesh . vertices [ vCounter + 2 ] } ;
animVertex = Vector3Transform ( animVertex , model . meshes [ m ] . boneMatrices [ boneId ] ) ;
2024-12-03 19:14:14 +01:00
mesh . animVertices [ vCounter ] + = animVertex . x * boneWeight ;
mesh . animVertices [ vCounter + 1 ] + = animVertex . y * boneWeight ;
mesh . animVertices [ vCounter + 2 ] + = animVertex . z * boneWeight ;
2024-11-08 08:28:39 -05:00
updated = true ;
2024-12-03 19:14:14 +01:00
2024-11-08 08:28:39 -05:00
// Normals processing
// NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
if ( mesh . normals ! = NULL )
{
animNormal = ( Vector3 ) { mesh . normals [ vCounter ] , mesh . normals [ vCounter + 1 ] , mesh . normals [ vCounter + 2 ] } ;
animNormal = Vector3Transform ( animNormal , model . meshes [ m ] . boneMatrices [ boneId ] ) ;
mesh . animNormals [ vCounter ] + = animNormal . x * boneWeight ;
mesh . animNormals [ vCounter + 1 ] + = animNormal . y * boneWeight ;
mesh . animNormals [ vCounter + 2 ] + = animNormal . z * boneWeight ;
}
}
}
2024-12-03 19:14:14 +01:00
2024-11-08 08:28:39 -05:00
if ( updated )
{
rlUpdateVertexBuffer ( mesh . vboId [ 0 ] , mesh . animVertices , mesh . vertexCount * 3 * sizeof ( float ) , 0 ) ; // Update vertex position
rlUpdateVertexBuffer ( mesh . vboId [ 2 ] , mesh . animNormals , mesh . vertexCount * 3 * sizeof ( float ) , 0 ) ; // Update vertex normals
}
}
}
2021-03-14 20:30:18 +02:00
// Unload animation array data
2023-09-02 12:54:36 +02:00
void UnloadModelAnimations ( ModelAnimation * animations , int animCount )
2021-03-14 20:30:18 +02:00
{
2023-09-02 12:54:36 +02:00
for ( int i = 0 ; i < animCount ; i + + ) UnloadModelAnimation ( animations [ i ] ) ;
2021-03-14 20:30:18 +02:00
RL_FREE ( animations ) ;
}
2019-04-05 13:15:56 +02:00
// Unload animation data
void UnloadModelAnimation ( ModelAnimation anim )
{
2019-04-23 14:55:35 +02:00
for ( int i = 0 ; i < anim . frameCount ; i + + ) RL_FREE ( anim . framePoses [ i ] ) ;
2019-09-13 12:29:52 +01:00
2019-04-23 14:55:35 +02:00
RL_FREE ( anim . bones ) ;
RL_FREE ( anim . framePoses ) ;
2019-04-05 13:15:56 +02:00
}
// Check model animation skeleton match
// NOTE: Only number of bones and parent connections are checked
bool IsModelAnimationValid ( Model model , ModelAnimation anim )
{
int result = true ;
2019-09-13 12:29:52 +01:00
2019-04-05 13:15:56 +02:00
if ( model . boneCount ! = anim . boneCount ) result = false ;
else
{
for ( int i = 0 ; i < model . boneCount ; i + + )
{
if ( model . bones [ i ] . parent ! = anim . bones [ i ] . parent ) { result = false ; break ; }
}
}
return result ;
}
2017-09-18 00:59:22 +02:00
# if defined(SUPPORT_MESH_GENERATION)
2018-12-15 23:31:56 +01:00
// Generate polygonal mesh
Mesh GenMeshPoly ( int sides , float radius )
{
Mesh mesh = { 0 } ;
2020-12-23 15:03:26 +01:00
2024-04-20 13:53:13 +02:00
if ( sides < 3 ) return mesh ; // Security check
2020-12-23 15:03:26 +01:00
2018-12-15 23:31:56 +01:00
int vertexCount = sides * 3 ;
2019-02-21 18:45:19 +01:00
2018-12-15 23:31:56 +01:00
// Vertices definition
2019-04-23 14:55:35 +02:00
Vector3 * vertices = ( Vector3 * ) RL_MALLOC ( vertexCount * sizeof ( Vector3 ) ) ;
2020-06-05 18:13:31 +01:00
float d = 0.0f , dStep = 360.0f / sides ;
2023-11-01 15:28:18 +01:00
for ( int v = 0 ; v < vertexCount - 2 ; v + = 3 )
2018-12-15 23:31:56 +01:00
{
vertices [ v ] = ( Vector3 ) { 0.0f , 0.0f , 0.0f } ;
2020-06-05 18:13:31 +01:00
vertices [ v + 1 ] = ( Vector3 ) { sinf ( DEG2RAD * d ) * radius , 0.0f , cosf ( DEG2RAD * d ) * radius } ;
2023-11-01 15:28:18 +01:00
vertices [ v + 2 ] = ( Vector3 ) { sinf ( DEG2RAD * ( d + dStep ) ) * radius , 0.0f , cosf ( DEG2RAD * ( d + dStep ) ) * radius } ;
2020-06-05 18:13:31 +01:00
d + = dStep ;
2019-02-21 18:45:19 +01:00
}
2018-12-15 23:31:56 +01:00
// Normals definition
2019-04-23 14:55:35 +02:00
Vector3 * normals = ( Vector3 * ) RL_MALLOC ( vertexCount * sizeof ( Vector3 ) ) ;
2018-12-15 23:31:56 +01:00
for ( int n = 0 ; n < vertexCount ; n + + ) normals [ n ] = ( Vector3 ) { 0.0f , 1.0f , 0.0f } ; // Vector3.up;
2019-02-21 18:45:19 +01:00
// TexCoords definition
2019-04-23 14:55:35 +02:00
Vector2 * texcoords = ( Vector2 * ) RL_MALLOC ( vertexCount * sizeof ( Vector2 ) ) ;
2018-12-15 23:31:56 +01:00
for ( int n = 0 ; n < vertexCount ; n + + ) texcoords [ n ] = ( Vector2 ) { 0.0f , 0.0f } ;
mesh . vertexCount = vertexCount ;
mesh . triangleCount = sides ;
2019-04-23 14:55:35 +02:00
mesh . vertices = ( float * ) RL_MALLOC ( mesh . vertexCount * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( mesh . vertexCount * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( mesh . vertexCount * 3 * sizeof ( float ) ) ;
2019-02-21 18:45:19 +01:00
2018-12-15 23:31:56 +01:00
// Mesh vertices position array
for ( int i = 0 ; i < mesh . vertexCount ; i + + )
{
mesh . vertices [ 3 * i ] = vertices [ i ] . x ;
mesh . vertices [ 3 * i + 1 ] = vertices [ i ] . y ;
mesh . vertices [ 3 * i + 2 ] = vertices [ i ] . z ;
}
2019-02-21 18:45:19 +01:00
2018-12-15 23:31:56 +01:00
// Mesh texcoords array
for ( int i = 0 ; i < mesh . vertexCount ; i + + )
{
mesh . texcoords [ 2 * i ] = texcoords [ i ] . x ;
mesh . texcoords [ 2 * i + 1 ] = texcoords [ i ] . y ;
}
2019-02-21 18:45:19 +01:00
2018-12-15 23:31:56 +01:00
// Mesh normals array
for ( int i = 0 ; i < mesh . vertexCount ; i + + )
{
mesh . normals [ 3 * i ] = normals [ i ] . x ;
mesh . normals [ 3 * i + 1 ] = normals [ i ] . y ;
mesh . normals [ 3 * i + 2 ] = normals [ i ] . z ;
}
2019-02-21 18:45:19 +01:00
2019-04-23 14:55:35 +02:00
RL_FREE ( vertices ) ;
RL_FREE ( normals ) ;
RL_FREE ( texcoords ) ;
2018-12-15 23:31:56 +01:00
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
// NOTE: mesh.vboId array is allocated inside UploadMesh()
UploadMesh ( & mesh , false ) ;
2019-02-21 18:45:19 +01:00
2018-12-15 23:31:56 +01:00
return mesh ;
}
2017-09-18 00:59:22 +02:00
// Generate plane mesh (with subdivisions)
Mesh GenMeshPlane ( float width , float length , int resX , int resZ )
{
Mesh mesh = { 0 } ;
# define CUSTOM_MESH_GEN_PLANE
# if defined(CUSTOM_MESH_GEN_PLANE)
resX + + ;
resZ + + ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// Vertices definition
2018-09-06 21:11:00 +02:00
int vertexCount = resX * resZ ; // vertices get reused for the faces
2017-09-18 00:59:22 +02:00
2019-04-23 14:55:35 +02:00
Vector3 * vertices = ( Vector3 * ) RL_MALLOC ( vertexCount * sizeof ( Vector3 ) ) ;
2017-09-18 00:59:22 +02:00
for ( int z = 0 ; z < resZ ; z + + )
{
// [-length/2, length/2]
float zPos = ( ( float ) z / ( resZ - 1 ) - 0.5f ) * length ;
for ( int x = 0 ; x < resX ; x + + )
{
// [-width/2, width/2]
float xPos = ( ( float ) x / ( resX - 1 ) - 0.5f ) * width ;
vertices [ x + z * resX ] = ( Vector3 ) { xPos , 0.0f , zPos } ;
}
}
// Normals definition
2019-04-23 14:55:35 +02:00
Vector3 * normals = ( Vector3 * ) RL_MALLOC ( vertexCount * sizeof ( Vector3 ) ) ;
2017-09-18 00:59:22 +02:00
for ( int n = 0 ; n < vertexCount ; n + + ) normals [ n ] = ( Vector3 ) { 0.0f , 1.0f , 0.0f } ; // Vector3.up;
2019-02-21 18:45:19 +01:00
// TexCoords definition
2019-04-23 14:55:35 +02:00
Vector2 * texcoords = ( Vector2 * ) RL_MALLOC ( vertexCount * sizeof ( Vector2 ) ) ;
2017-09-18 00:59:22 +02:00
for ( int v = 0 ; v < resZ ; v + + )
{
for ( int u = 0 ; u < resX ; u + + )
{
texcoords [ u + v * resX ] = ( Vector2 ) { ( float ) u / ( resX - 1 ) , ( float ) v / ( resZ - 1 ) } ;
}
}
// Triangles definition (indices)
2017-10-19 14:14:18 +02:00
int numFaces = ( resX - 1 ) * ( resZ - 1 ) ;
2019-04-23 14:55:35 +02:00
int * triangles = ( int * ) RL_MALLOC ( numFaces * 6 * sizeof ( int ) ) ;
2017-09-18 00:59:22 +02:00
int t = 0 ;
2017-10-19 14:14:18 +02:00
for ( int face = 0 ; face < numFaces ; face + + )
2017-09-18 00:59:22 +02:00
{
// Retrieve lower left corner from face ind
2024-04-21 12:29:09 +02:00
int i = face + face / ( resX - 1 ) ;
2017-09-18 00:59:22 +02:00
triangles [ t + + ] = i + resX ;
triangles [ t + + ] = i + 1 ;
triangles [ t + + ] = i ;
2019-02-21 18:45:19 +01:00
triangles [ t + + ] = i + resX ;
2017-09-18 00:59:22 +02:00
triangles [ t + + ] = i + resX + 1 ;
triangles [ t + + ] = i + 1 ;
}
mesh . vertexCount = vertexCount ;
2017-10-19 14:14:18 +02:00
mesh . triangleCount = numFaces * 2 ;
2019-04-23 14:55:35 +02:00
mesh . vertices = ( float * ) RL_MALLOC ( mesh . vertexCount * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( mesh . vertexCount * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( mesh . vertexCount * 3 * sizeof ( float ) ) ;
mesh . indices = ( unsigned short * ) RL_MALLOC ( mesh . triangleCount * 3 * sizeof ( unsigned short ) ) ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// Mesh vertices position array
for ( int i = 0 ; i < mesh . vertexCount ; i + + )
{
mesh . vertices [ 3 * i ] = vertices [ i ] . x ;
mesh . vertices [ 3 * i + 1 ] = vertices [ i ] . y ;
mesh . vertices [ 3 * i + 2 ] = vertices [ i ] . z ;
}
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// Mesh texcoords array
for ( int i = 0 ; i < mesh . vertexCount ; i + + )
{
mesh . texcoords [ 2 * i ] = texcoords [ i ] . x ;
mesh . texcoords [ 2 * i + 1 ] = texcoords [ i ] . y ;
}
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// Mesh normals array
for ( int i = 0 ; i < mesh . vertexCount ; i + + )
{
mesh . normals [ 3 * i ] = normals [ i ] . x ;
mesh . normals [ 3 * i + 1 ] = normals [ i ] . y ;
mesh . normals [ 3 * i + 2 ] = normals [ i ] . z ;
}
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// Mesh indices array initialization
for ( int i = 0 ; i < mesh . triangleCount * 3 ; i + + ) mesh . indices [ i ] = triangles [ i ] ;
2019-02-21 18:45:19 +01:00
2019-04-23 14:55:35 +02:00
RL_FREE ( vertices ) ;
RL_FREE ( normals ) ;
RL_FREE ( texcoords ) ;
RL_FREE ( triangles ) ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
# else // Use par_shapes library to generate plane mesh
par_shapes_mesh * plane = par_shapes_create_plane ( resX , resZ ) ; // No normals/texcoords generated!!!
par_shapes_scale ( plane , width , length , 1.0f ) ;
par_shapes_rotate ( plane , - PI / 2.0f , ( float [ ] ) { 1 , 0 , 0 } ) ;
par_shapes_translate ( plane , - width / 2 , 0.0f , length / 2 ) ;
2019-02-21 18:45:19 +01:00
2019-04-23 14:55:35 +02:00
mesh . vertices = ( float * ) RL_MALLOC ( plane - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( plane - > ntriangles * 3 * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( plane - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
2017-09-18 00:59:22 +02:00
mesh . vertexCount = plane - > ntriangles * 3 ;
mesh . triangleCount = plane - > ntriangles ;
for ( int k = 0 ; k < mesh . vertexCount ; k + + )
{
mesh . vertices [ k * 3 ] = plane - > points [ plane - > triangles [ k ] * 3 ] ;
mesh . vertices [ k * 3 + 1 ] = plane - > points [ plane - > triangles [ k ] * 3 + 1 ] ;
mesh . vertices [ k * 3 + 2 ] = plane - > points [ plane - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
mesh . normals [ k * 3 ] = plane - > normals [ plane - > triangles [ k ] * 3 ] ;
mesh . normals [ k * 3 + 1 ] = plane - > normals [ plane - > triangles [ k ] * 3 + 1 ] ;
mesh . normals [ k * 3 + 2 ] = plane - > normals [ plane - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
mesh . texcoords [ k * 2 ] = plane - > tcoords [ plane - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = plane - > tcoords [ plane - > triangles [ k ] * 2 + 1 ] ;
}
par_shapes_free_mesh ( plane ) ;
# endif
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
UploadMesh ( & mesh , false ) ;
2017-09-18 00:59:22 +02:00
return mesh ;
}
2017-07-17 00:33:40 +02:00
// Generated cuboid mesh
Mesh GenMeshCube ( float width , float height , float length )
2016-05-10 18:24:28 +02:00
{
2017-07-17 00:33:40 +02:00
Mesh mesh = { 0 } ;
2017-07-20 12:26:25 +02:00
2017-09-18 00:59:22 +02:00
# define CUSTOM_MESH_GEN_CUBE
# if defined(CUSTOM_MESH_GEN_CUBE)
2017-07-17 00:33:40 +02:00
float vertices [ ] = {
- width / 2 , - height / 2 , length / 2 ,
width / 2 , - height / 2 , length / 2 ,
width / 2 , height / 2 , length / 2 ,
- width / 2 , height / 2 , length / 2 ,
- width / 2 , - height / 2 , - length / 2 ,
- width / 2 , height / 2 , - length / 2 ,
width / 2 , height / 2 , - length / 2 ,
width / 2 , - height / 2 , - length / 2 ,
- width / 2 , height / 2 , - length / 2 ,
- width / 2 , height / 2 , length / 2 ,
width / 2 , height / 2 , length / 2 ,
width / 2 , height / 2 , - length / 2 ,
- width / 2 , - height / 2 , - length / 2 ,
width / 2 , - height / 2 , - length / 2 ,
width / 2 , - height / 2 , length / 2 ,
- width / 2 , - height / 2 , length / 2 ,
width / 2 , - height / 2 , - length / 2 ,
width / 2 , height / 2 , - length / 2 ,
width / 2 , height / 2 , length / 2 ,
width / 2 , - height / 2 , length / 2 ,
- width / 2 , - height / 2 , - length / 2 ,
- width / 2 , - height / 2 , length / 2 ,
- width / 2 , height / 2 , length / 2 ,
- width / 2 , height / 2 , - length / 2
} ;
2019-02-21 18:45:19 +01:00
2017-07-17 00:33:40 +02:00
float texcoords [ ] = {
0.0f , 0.0f ,
1.0f , 0.0f ,
1.0f , 1.0f ,
0.0f , 1.0f ,
1.0f , 0.0f ,
1.0f , 1.0f ,
0.0f , 1.0f ,
0.0f , 0.0f ,
0.0f , 1.0f ,
0.0f , 0.0f ,
1.0f , 0.0f ,
1.0f , 1.0f ,
1.0f , 1.0f ,
0.0f , 1.0f ,
0.0f , 0.0f ,
1.0f , 0.0f ,
1.0f , 0.0f ,
1.0f , 1.0f ,
0.0f , 1.0f ,
0.0f , 0.0f ,
0.0f , 0.0f ,
1.0f , 0.0f ,
1.0f , 1.0f ,
0.0f , 1.0f
} ;
2019-02-21 18:45:19 +01:00
2017-07-17 00:33:40 +02:00
float normals [ ] = {
0.0f , 0.0f , 1.0f ,
0.0f , 0.0f , 1.0f ,
0.0f , 0.0f , 1.0f ,
0.0f , 0.0f , 1.0f ,
0.0f , 0.0f , - 1.0f ,
0.0f , 0.0f , - 1.0f ,
0.0f , 0.0f , - 1.0f ,
0.0f , 0.0f , - 1.0f ,
0.0f , 1.0f , 0.0f ,
0.0f , 1.0f , 0.0f ,
0.0f , 1.0f , 0.0f ,
0.0f , 1.0f , 0.0f ,
0.0f , - 1.0f , 0.0f ,
0.0f , - 1.0f , 0.0f ,
0.0f , - 1.0f , 0.0f ,
0.0f , - 1.0f , 0.0f ,
1.0f , 0.0f , 0.0f ,
1.0f , 0.0f , 0.0f ,
1.0f , 0.0f , 0.0f ,
1.0f , 0.0f , 0.0f ,
- 1.0f , 0.0f , 0.0f ,
- 1.0f , 0.0f , 0.0f ,
- 1.0f , 0.0f , 0.0f ,
- 1.0f , 0.0f , 0.0f
} ;
2019-04-23 14:55:35 +02:00
mesh . vertices = ( float * ) RL_MALLOC ( 24 * 3 * sizeof ( float ) ) ;
2017-07-17 00:33:40 +02:00
memcpy ( mesh . vertices , vertices , 24 * 3 * sizeof ( float ) ) ;
2019-02-21 18:45:19 +01:00
2019-04-23 14:55:35 +02:00
mesh . texcoords = ( float * ) RL_MALLOC ( 24 * 2 * sizeof ( float ) ) ;
2017-07-17 00:33:40 +02:00
memcpy ( mesh . texcoords , texcoords , 24 * 2 * sizeof ( float ) ) ;
2019-02-21 18:45:19 +01:00
2019-04-23 14:55:35 +02:00
mesh . normals = ( float * ) RL_MALLOC ( 24 * 3 * sizeof ( float ) ) ;
2017-07-17 00:33:40 +02:00
memcpy ( mesh . normals , normals , 24 * 3 * sizeof ( float ) ) ;
2019-02-21 18:45:19 +01:00
2019-04-23 14:55:35 +02:00
mesh . indices = ( unsigned short * ) RL_MALLOC ( 36 * sizeof ( unsigned short ) ) ;
2019-02-21 18:45:19 +01:00
2017-07-17 00:33:40 +02:00
int k = 0 ;
// Indices can be initialized right now
2021-12-17 20:55:38 +01:00
for ( int i = 0 ; i < 36 ; i + = 6 )
2017-07-17 00:33:40 +02:00
{
mesh . indices [ i ] = 4 * k ;
2021-12-17 20:55:38 +01:00
mesh . indices [ i + 1 ] = 4 * k + 1 ;
mesh . indices [ i + 2 ] = 4 * k + 2 ;
mesh . indices [ i + 3 ] = 4 * k ;
mesh . indices [ i + 4 ] = 4 * k + 2 ;
mesh . indices [ i + 5 ] = 4 * k + 3 ;
2017-07-17 00:33:40 +02:00
k + + ;
}
2019-02-21 18:45:19 +01:00
2017-07-17 00:33:40 +02:00
mesh . vertexCount = 24 ;
mesh . triangleCount = 12 ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
# else // Use par_shapes library to generate cube mesh
/*
// Platonic solids:
2024-12-03 19:14:14 +01:00
par_shapes_mesh * par_shapes_create_tetrahedron ( ) ; // 4 sides polyhedron (pyramid)
par_shapes_mesh * par_shapes_create_cube ( ) ; // 6 sides polyhedron (cube)
par_shapes_mesh * par_shapes_create_octahedron ( ) ; // 8 sides polyhedron (diamond)
par_shapes_mesh * par_shapes_create_dodecahedron ( ) ; // 12 sides polyhedron
par_shapes_mesh * par_shapes_create_icosahedron ( ) ; // 20 sides polyhedron
2017-09-18 00:59:22 +02:00
*/
// Platonic solid generation: cube (6 sides)
// NOTE: No normals/texcoords generated by default
par_shapes_mesh * cube = par_shapes_create_cube ( ) ;
cube - > tcoords = PAR_MALLOC ( float , 2 * cube - > npoints ) ;
2019-02-21 18:45:19 +01:00
for ( int i = 0 ; i < 2 * cube - > npoints ; i + + ) cube - > tcoords [ i ] = 0.0f ;
2017-09-02 17:42:22 +02:00
par_shapes_scale ( cube , width , height , length ) ;
2017-09-18 00:59:22 +02:00
par_shapes_translate ( cube , - width / 2 , 0.0f , - length / 2 ) ;
2017-09-02 17:42:22 +02:00
par_shapes_compute_normals ( cube ) ;
2019-02-21 18:45:19 +01:00
2019-04-23 14:55:35 +02:00
mesh . vertices = ( float * ) RL_MALLOC ( cube - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( cube - > ntriangles * 3 * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( cube - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
2017-09-02 17:42:22 +02:00
mesh . vertexCount = cube - > ntriangles * 3 ;
mesh . triangleCount = cube - > ntriangles ;
for ( int k = 0 ; k < mesh . vertexCount ; k + + )
{
mesh . vertices [ k * 3 ] = cube - > points [ cube - > triangles [ k ] * 3 ] ;
mesh . vertices [ k * 3 + 1 ] = cube - > points [ cube - > triangles [ k ] * 3 + 1 ] ;
mesh . vertices [ k * 3 + 2 ] = cube - > points [ cube - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2017-09-02 17:42:22 +02:00
mesh . normals [ k * 3 ] = cube - > normals [ cube - > triangles [ k ] * 3 ] ;
mesh . normals [ k * 3 + 1 ] = cube - > normals [ cube - > triangles [ k ] * 3 + 1 ] ;
mesh . normals [ k * 3 + 2 ] = cube - > normals [ cube - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
mesh . texcoords [ k * 2 ] = cube - > tcoords [ cube - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = cube - > tcoords [ cube - > triangles [ k ] * 2 + 1 ] ;
2017-09-02 17:42:22 +02:00
}
par_shapes_free_mesh ( cube ) ;
2017-09-18 00:59:22 +02:00
# endif
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
UploadMesh ( & mesh , false ) ;
2017-09-18 00:59:22 +02:00
return mesh ;
}
// Generate sphere mesh (standard sphere)
2021-04-02 15:56:35 +02:00
Mesh GenMeshSphere ( float radius , int rings , int slices )
2017-09-18 00:59:22 +02:00
{
Mesh mesh = { 0 } ;
2020-12-23 15:03:26 +01:00
2020-12-14 20:41:58 +01:00
if ( ( rings > = 3 ) & & ( slices > = 3 ) )
{
2024-11-01 22:35:35 +01:00
par_shapes_set_epsilon_degenerate_sphere ( 0.0 ) ;
2020-12-14 20:41:58 +01:00
par_shapes_mesh * sphere = par_shapes_create_parametric_sphere ( slices , rings ) ;
par_shapes_scale ( sphere , radius , radius , radius ) ;
// NOTE: Soft normals are computed internally
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . vertices = ( float * ) RL_MALLOC ( sphere - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( sphere - > ntriangles * 3 * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( sphere - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
mesh . vertexCount = sphere - > ntriangles * 3 ;
mesh . triangleCount = sphere - > ntriangles ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
for ( int k = 0 ; k < mesh . vertexCount ; k + + )
{
mesh . vertices [ k * 3 ] = sphere - > points [ sphere - > triangles [ k ] * 3 ] ;
mesh . vertices [ k * 3 + 1 ] = sphere - > points [ sphere - > triangles [ k ] * 3 + 1 ] ;
mesh . vertices [ k * 3 + 2 ] = sphere - > points [ sphere - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . normals [ k * 3 ] = sphere - > normals [ sphere - > triangles [ k ] * 3 ] ;
mesh . normals [ k * 3 + 1 ] = sphere - > normals [ sphere - > triangles [ k ] * 3 + 1 ] ;
mesh . normals [ k * 3 + 2 ] = sphere - > normals [ sphere - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . texcoords [ k * 2 ] = sphere - > tcoords [ sphere - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = sphere - > tcoords [ sphere - > triangles [ k ] * 2 + 1 ] ;
}
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
par_shapes_free_mesh ( sphere ) ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
UploadMesh ( & mesh , false ) ;
2020-12-14 20:41:58 +01:00
}
else TRACELOG ( LOG_WARNING , " MESH: Failed to generate mesh: sphere " ) ;
2017-07-17 00:33:40 +02:00
return mesh ;
2016-05-10 18:24:28 +02:00
}
2023-02-09 07:17:47 -05:00
// Generate hemisphere mesh (half sphere, no bottom cap)
2021-04-02 15:56:35 +02:00
Mesh GenMeshHemiSphere ( float radius , int rings , int slices )
2017-09-18 00:59:22 +02:00
{
Mesh mesh = { 0 } ;
2020-12-23 15:03:26 +01:00
2020-12-14 20:41:58 +01:00
if ( ( rings > = 3 ) & & ( slices > = 3 ) )
{
if ( radius < 0.0f ) radius = 0.0f ;
2020-12-23 15:03:26 +01:00
2020-12-14 20:41:58 +01:00
par_shapes_mesh * sphere = par_shapes_create_hemisphere ( slices , rings ) ;
par_shapes_scale ( sphere , radius , radius , radius ) ;
// NOTE: Soft normals are computed internally
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . vertices = ( float * ) RL_MALLOC ( sphere - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( sphere - > ntriangles * 3 * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( sphere - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
mesh . vertexCount = sphere - > ntriangles * 3 ;
mesh . triangleCount = sphere - > ntriangles ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
for ( int k = 0 ; k < mesh . vertexCount ; k + + )
{
mesh . vertices [ k * 3 ] = sphere - > points [ sphere - > triangles [ k ] * 3 ] ;
mesh . vertices [ k * 3 + 1 ] = sphere - > points [ sphere - > triangles [ k ] * 3 + 1 ] ;
mesh . vertices [ k * 3 + 2 ] = sphere - > points [ sphere - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . normals [ k * 3 ] = sphere - > normals [ sphere - > triangles [ k ] * 3 ] ;
mesh . normals [ k * 3 + 1 ] = sphere - > normals [ sphere - > triangles [ k ] * 3 + 1 ] ;
mesh . normals [ k * 3 + 2 ] = sphere - > normals [ sphere - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . texcoords [ k * 2 ] = sphere - > tcoords [ sphere - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = sphere - > tcoords [ sphere - > triangles [ k ] * 2 + 1 ] ;
}
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
par_shapes_free_mesh ( sphere ) ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
UploadMesh ( & mesh , false ) ;
2020-12-14 20:41:58 +01:00
}
else TRACELOG ( LOG_WARNING , " MESH: Failed to generate mesh: hemisphere " ) ;
2017-09-18 00:59:22 +02:00
return mesh ;
}
// Generate cylinder mesh
Mesh GenMeshCylinder ( float radius , float height , int slices )
{
Mesh mesh = { 0 } ;
2020-12-23 15:03:26 +01:00
2020-12-14 20:41:58 +01:00
if ( slices > = 3 )
2017-09-18 00:59:22 +02:00
{
2020-12-14 20:41:58 +01:00
// Instance a cylinder that sits on the Z=0 plane using the given tessellation
// levels across the UV domain. Think of "slices" like a number of pizza
2024-04-20 20:31:06 +02:00
// slices, and "stacks" like a number of stacked rings
2020-12-14 20:41:58 +01:00
// Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
par_shapes_mesh * cylinder = par_shapes_create_cylinder ( slices , 8 ) ;
par_shapes_scale ( cylinder , radius , radius , height ) ;
par_shapes_rotate ( cylinder , - PI / 2.0f , ( float [ ] ) { 1 , 0 , 0 } ) ;
// Generate an orientable disk shape (top cap)
par_shapes_mesh * capTop = par_shapes_create_disk ( radius , slices , ( float [ ] ) { 0 , 0 , 0 } , ( float [ ] ) { 0 , 0 , 1 } ) ;
capTop - > tcoords = PAR_MALLOC ( float , 2 * capTop - > npoints ) ;
for ( int i = 0 ; i < 2 * capTop - > npoints ; i + + ) capTop - > tcoords [ i ] = 0.0f ;
par_shapes_rotate ( capTop , - PI / 2.0f , ( float [ ] ) { 1 , 0 , 0 } ) ;
2021-12-19 13:40:55 +01:00
par_shapes_rotate ( capTop , 90 * DEG2RAD , ( float [ ] ) { 0 , 1 , 0 } ) ;
2020-12-14 20:41:58 +01:00
par_shapes_translate ( capTop , 0 , height , 0 ) ;
// Generate an orientable disk shape (bottom cap)
par_shapes_mesh * capBottom = par_shapes_create_disk ( radius , slices , ( float [ ] ) { 0 , 0 , 0 } , ( float [ ] ) { 0 , 0 , - 1 } ) ;
capBottom - > tcoords = PAR_MALLOC ( float , 2 * capBottom - > npoints ) ;
for ( int i = 0 ; i < 2 * capBottom - > npoints ; i + + ) capBottom - > tcoords [ i ] = 0.95f ;
par_shapes_rotate ( capBottom , PI / 2.0f , ( float [ ] ) { 1 , 0 , 0 } ) ;
2021-12-19 13:40:55 +01:00
par_shapes_rotate ( capBottom , - 90 * DEG2RAD , ( float [ ] ) { 0 , 1 , 0 } ) ;
2020-12-14 20:41:58 +01:00
par_shapes_merge_and_free ( cylinder , capTop ) ;
par_shapes_merge_and_free ( cylinder , capBottom ) ;
mesh . vertices = ( float * ) RL_MALLOC ( cylinder - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( cylinder - > ntriangles * 3 * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( cylinder - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . vertexCount = cylinder - > ntriangles * 3 ;
mesh . triangleCount = cylinder - > ntriangles ;
for ( int k = 0 ; k < mesh . vertexCount ; k + + )
{
mesh . vertices [ k * 3 ] = cylinder - > points [ cylinder - > triangles [ k ] * 3 ] ;
mesh . vertices [ k * 3 + 1 ] = cylinder - > points [ cylinder - > triangles [ k ] * 3 + 1 ] ;
mesh . vertices [ k * 3 + 2 ] = cylinder - > points [ cylinder - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . normals [ k * 3 ] = cylinder - > normals [ cylinder - > triangles [ k ] * 3 ] ;
mesh . normals [ k * 3 + 1 ] = cylinder - > normals [ cylinder - > triangles [ k ] * 3 + 1 ] ;
mesh . normals [ k * 3 + 2 ] = cylinder - > normals [ cylinder - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . texcoords [ k * 2 ] = cylinder - > tcoords [ cylinder - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = cylinder - > tcoords [ cylinder - > triangles [ k ] * 2 + 1 ] ;
}
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
par_shapes_free_mesh ( cylinder ) ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
UploadMesh ( & mesh , false ) ;
2020-12-14 20:41:58 +01:00
}
else TRACELOG ( LOG_WARNING , " MESH: Failed to generate mesh: cylinder " ) ;
2017-09-18 00:59:22 +02:00
return mesh ;
}
2021-08-08 11:29:24 +02:00
// Generate cone/pyramid mesh
Mesh GenMeshCone ( float radius , float height , int slices )
{
Mesh mesh = { 0 } ;
if ( slices > = 3 )
{
// Instance a cone that sits on the Z=0 plane using the given tessellation
// levels across the UV domain. Think of "slices" like a number of pizza
2024-04-20 20:31:06 +02:00
// slices, and "stacks" like a number of stacked rings
2021-08-08 11:29:24 +02:00
// Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
par_shapes_mesh * cone = par_shapes_create_cone ( slices , 8 ) ;
par_shapes_scale ( cone , radius , radius , height ) ;
par_shapes_rotate ( cone , - PI / 2.0f , ( float [ ] ) { 1 , 0 , 0 } ) ;
par_shapes_rotate ( cone , PI / 2.0f , ( float [ ] ) { 0 , 1 , 0 } ) ;
// Generate an orientable disk shape (bottom cap)
par_shapes_mesh * capBottom = par_shapes_create_disk ( radius , slices , ( float [ ] ) { 0 , 0 , 0 } , ( float [ ] ) { 0 , 0 , - 1 } ) ;
capBottom - > tcoords = PAR_MALLOC ( float , 2 * capBottom - > npoints ) ;
for ( int i = 0 ; i < 2 * capBottom - > npoints ; i + + ) capBottom - > tcoords [ i ] = 0.95f ;
par_shapes_rotate ( capBottom , PI / 2.0f , ( float [ ] ) { 1 , 0 , 0 } ) ;
par_shapes_merge_and_free ( cone , capBottom ) ;
mesh . vertices = ( float * ) RL_MALLOC ( cone - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( cone - > ntriangles * 3 * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( cone - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . vertexCount = cone - > ntriangles * 3 ;
mesh . triangleCount = cone - > ntriangles ;
for ( int k = 0 ; k < mesh . vertexCount ; k + + )
{
mesh . vertices [ k * 3 ] = cone - > points [ cone - > triangles [ k ] * 3 ] ;
mesh . vertices [ k * 3 + 1 ] = cone - > points [ cone - > triangles [ k ] * 3 + 1 ] ;
mesh . vertices [ k * 3 + 2 ] = cone - > points [ cone - > triangles [ k ] * 3 + 2 ] ;
mesh . normals [ k * 3 ] = cone - > normals [ cone - > triangles [ k ] * 3 ] ;
mesh . normals [ k * 3 + 1 ] = cone - > normals [ cone - > triangles [ k ] * 3 + 1 ] ;
mesh . normals [ k * 3 + 2 ] = cone - > normals [ cone - > triangles [ k ] * 3 + 2 ] ;
mesh . texcoords [ k * 2 ] = cone - > tcoords [ cone - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = cone - > tcoords [ cone - > triangles [ k ] * 2 + 1 ] ;
}
par_shapes_free_mesh ( cone ) ;
// Upload vertex data to GPU (static mesh)
UploadMesh ( & mesh , false ) ;
}
else TRACELOG ( LOG_WARNING , " MESH: Failed to generate mesh: cone " ) ;
return mesh ;
}
2017-09-18 00:59:22 +02:00
// Generate torus mesh
Mesh GenMeshTorus ( float radius , float size , int radSeg , int sides )
{
Mesh mesh = { 0 } ;
2020-12-23 15:03:26 +01:00
2020-12-14 20:41:58 +01:00
if ( ( sides > = 3 ) & & ( radSeg > = 3 ) )
{
if ( radius > 1.0f ) radius = 1.0f ;
else if ( radius < 0.1f ) radius = 0.1f ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
// Create a donut that sits on the Z=0 plane with the specified inner radius
// The outer radius can be controlled with par_shapes_scale
par_shapes_mesh * torus = par_shapes_create_torus ( radSeg , sides , radius ) ;
par_shapes_scale ( torus , size / 2 , size / 2 , size / 2 ) ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
mesh . vertices = ( float * ) RL_MALLOC ( torus - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( torus - > ntriangles * 3 * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( torus - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
mesh . vertexCount = torus - > ntriangles * 3 ;
mesh . triangleCount = torus - > ntriangles ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
for ( int k = 0 ; k < mesh . vertexCount ; k + + )
{
mesh . vertices [ k * 3 ] = torus - > points [ torus - > triangles [ k ] * 3 ] ;
mesh . vertices [ k * 3 + 1 ] = torus - > points [ torus - > triangles [ k ] * 3 + 1 ] ;
mesh . vertices [ k * 3 + 2 ] = torus - > points [ torus - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . normals [ k * 3 ] = torus - > normals [ torus - > triangles [ k ] * 3 ] ;
mesh . normals [ k * 3 + 1 ] = torus - > normals [ torus - > triangles [ k ] * 3 + 1 ] ;
mesh . normals [ k * 3 + 2 ] = torus - > normals [ torus - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . texcoords [ k * 2 ] = torus - > tcoords [ torus - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = torus - > tcoords [ torus - > triangles [ k ] * 2 + 1 ] ;
}
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
par_shapes_free_mesh ( torus ) ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
UploadMesh ( & mesh , false ) ;
2020-12-14 20:41:58 +01:00
}
else TRACELOG ( LOG_WARNING , " MESH: Failed to generate mesh: torus " ) ;
2017-09-18 00:59:22 +02:00
return mesh ;
}
// Generate trefoil knot mesh
Mesh GenMeshKnot ( float radius , float size , int radSeg , int sides )
{
Mesh mesh = { 0 } ;
2020-12-23 15:03:26 +01:00
2020-12-14 20:41:58 +01:00
if ( ( sides > = 3 ) & & ( radSeg > = 3 ) )
{
if ( radius > 3.0f ) radius = 3.0f ;
else if ( radius < 0.5f ) radius = 0.5f ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
par_shapes_mesh * knot = par_shapes_create_trefoil_knot ( radSeg , sides , radius ) ;
par_shapes_scale ( knot , size , size , size ) ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
mesh . vertices = ( float * ) RL_MALLOC ( knot - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( knot - > ntriangles * 3 * 2 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( knot - > ntriangles * 3 * 3 * sizeof ( float ) ) ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
mesh . vertexCount = knot - > ntriangles * 3 ;
mesh . triangleCount = knot - > ntriangles ;
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
for ( int k = 0 ; k < mesh . vertexCount ; k + + )
{
mesh . vertices [ k * 3 ] = knot - > points [ knot - > triangles [ k ] * 3 ] ;
mesh . vertices [ k * 3 + 1 ] = knot - > points [ knot - > triangles [ k ] * 3 + 1 ] ;
mesh . vertices [ k * 3 + 2 ] = knot - > points [ knot - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . normals [ k * 3 ] = knot - > normals [ knot - > triangles [ k ] * 3 ] ;
mesh . normals [ k * 3 + 1 ] = knot - > normals [ knot - > triangles [ k ] * 3 + 1 ] ;
mesh . normals [ k * 3 + 2 ] = knot - > normals [ knot - > triangles [ k ] * 3 + 2 ] ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
mesh . texcoords [ k * 2 ] = knot - > tcoords [ knot - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = knot - > tcoords [ knot - > triangles [ k ] * 2 + 1 ] ;
}
2017-09-18 00:59:22 +02:00
2020-12-14 20:41:58 +01:00
par_shapes_free_mesh ( knot ) ;
2019-02-21 18:45:19 +01:00
2020-12-14 20:41:58 +01:00
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
UploadMesh ( & mesh , false ) ;
2020-12-14 20:41:58 +01:00
}
else TRACELOG ( LOG_WARNING , " MESH: Failed to generate mesh: knot " ) ;
2017-09-18 00:59:22 +02:00
return mesh ;
}
2017-09-02 17:42:22 +02:00
2016-04-03 18:31:42 +02:00
// Generate a mesh from heightmap
2017-07-21 09:34:09 +02:00
// NOTE: Vertex data is uploaded to GPU
2017-07-17 00:33:40 +02:00
Mesh GenMeshHeightmap ( Image heightmap , Vector3 size )
2013-11-30 18:12:40 +01:00
{
2022-09-25 15:41:49 +02:00
# define GRAY_VALUE(c) ((float)(c.r + c.g + c.b) / 3.0f)
2016-08-16 11:09:55 +02:00
2016-05-08 15:24:02 +02:00
Mesh mesh = { 0 } ;
2014-04-04 20:11:57 +02:00
2013-11-30 18:12:40 +01:00
int mapX = heightmap . width ;
2014-03-16 20:59:02 +01:00
int mapZ = heightmap . height ;
2016-08-16 11:09:55 +02:00
2021-02-06 13:29:22 +01:00
Color * pixels = LoadImageColors ( heightmap ) ;
2014-09-03 16:51:28 +02:00
2013-11-30 18:12:40 +01:00
// NOTE: One vertex per pixel
2022-09-25 15:41:49 +02:00
mesh . triangleCount = ( mapX - 1 ) * ( mapZ - 1 ) * 2 ; // One quad every four pixels
2014-09-03 16:51:28 +02:00
2018-03-11 10:41:49 +01:00
mesh . vertexCount = mesh . triangleCount * 3 ;
2013-11-30 18:12:40 +01:00
2019-04-23 14:55:35 +02:00
mesh . vertices = ( float * ) RL_MALLOC ( mesh . vertexCount * 3 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( mesh . vertexCount * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( mesh . vertexCount * 2 * sizeof ( float ) ) ;
2016-05-08 15:24:02 +02:00
mesh . colors = NULL ;
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
int vCounter = 0 ; // Used to count vertices float by float
int tcCounter = 0 ; // Used to count texcoords float by float
int nCounter = 0 ; // Used to count normals float by float
2021-09-22 21:56:38 +02:00
2022-09-25 15:41:49 +02:00
Vector3 scaleFactor = { size . x / ( mapX - 1 ) , size . y / 255.0f , size . z / ( mapZ - 1 ) } ;
2013-11-30 18:12:40 +01:00
2021-05-14 14:01:42 +02:00
Vector3 vA = { 0 } ;
Vector3 vB = { 0 } ;
Vector3 vC = { 0 } ;
Vector3 vN = { 0 } ;
2020-03-24 13:27:49 +01:00
2016-05-21 18:22:15 +02:00
for ( int z = 0 ; z < mapZ - 1 ; z + + )
2014-03-16 20:59:02 +01:00
{
2016-05-21 18:22:15 +02:00
for ( int x = 0 ; x < mapX - 1 ; x + + )
2014-03-16 20:59:02 +01:00
{
// Fill vertices array with data
2014-01-28 21:21:29 +01:00
//----------------------------------------------------------
2014-09-03 16:51:28 +02:00
2014-01-28 21:21:29 +01:00
// one triangle - 3 vertex
2016-02-11 15:51:04 +01:00
mesh . vertices [ vCounter ] = ( float ) x * scaleFactor . x ;
2022-06-24 13:59:31 +05:00
mesh . vertices [ vCounter + 1 ] = GRAY_VALUE ( pixels [ x + z * mapX ] ) * scaleFactor . y ;
2016-02-11 15:51:04 +01:00
mesh . vertices [ vCounter + 2 ] = ( float ) z * scaleFactor . z ;
2014-09-03 16:51:28 +02:00
2016-02-11 15:51:04 +01:00
mesh . vertices [ vCounter + 3 ] = ( float ) x * scaleFactor . x ;
2022-06-24 13:59:31 +05:00
mesh . vertices [ vCounter + 4 ] = GRAY_VALUE ( pixels [ x + ( z + 1 ) * mapX ] ) * scaleFactor . y ;
2016-02-11 15:51:04 +01:00
mesh . vertices [ vCounter + 5 ] = ( float ) ( z + 1 ) * scaleFactor . z ;
2014-09-03 16:51:28 +02:00
2016-02-11 15:51:04 +01:00
mesh . vertices [ vCounter + 6 ] = ( float ) ( x + 1 ) * scaleFactor . x ;
2022-06-24 13:59:31 +05:00
mesh . vertices [ vCounter + 7 ] = GRAY_VALUE ( pixels [ ( x + 1 ) + z * mapX ] ) * scaleFactor . y ;
2016-02-11 15:51:04 +01:00
mesh . vertices [ vCounter + 8 ] = ( float ) z * scaleFactor . z ;
2014-09-03 16:51:28 +02:00
2022-09-25 15:41:49 +02:00
// Another triangle - 3 vertex
2016-01-18 13:36:18 +01:00
mesh . vertices [ vCounter + 9 ] = mesh . vertices [ vCounter + 6 ] ;
mesh . vertices [ vCounter + 10 ] = mesh . vertices [ vCounter + 7 ] ;
mesh . vertices [ vCounter + 11 ] = mesh . vertices [ vCounter + 8 ] ;
2014-09-03 16:51:28 +02:00
2016-01-18 13:36:18 +01:00
mesh . vertices [ vCounter + 12 ] = mesh . vertices [ vCounter + 3 ] ;
mesh . vertices [ vCounter + 13 ] = mesh . vertices [ vCounter + 4 ] ;
mesh . vertices [ vCounter + 14 ] = mesh . vertices [ vCounter + 5 ] ;
2014-09-03 16:51:28 +02:00
2016-02-11 15:51:04 +01:00
mesh . vertices [ vCounter + 15 ] = ( float ) ( x + 1 ) * scaleFactor . x ;
2022-06-24 13:59:31 +05:00
mesh . vertices [ vCounter + 16 ] = GRAY_VALUE ( pixels [ ( x + 1 ) + ( z + 1 ) * mapX ] ) * scaleFactor . y ;
2016-02-11 15:51:04 +01:00
mesh . vertices [ vCounter + 17 ] = ( float ) ( z + 1 ) * scaleFactor . z ;
2014-04-04 20:11:57 +02:00
vCounter + = 18 ; // 6 vertex, 18 floats
2014-09-03 16:51:28 +02:00
2014-01-28 21:21:29 +01:00
// Fill texcoords array with data
//--------------------------------------------------------------
2016-02-11 15:51:04 +01:00
mesh . texcoords [ tcCounter ] = ( float ) x / ( mapX - 1 ) ;
mesh . texcoords [ tcCounter + 1 ] = ( float ) z / ( mapZ - 1 ) ;
2014-09-03 16:51:28 +02:00
2016-02-11 15:51:04 +01:00
mesh . texcoords [ tcCounter + 2 ] = ( float ) x / ( mapX - 1 ) ;
mesh . texcoords [ tcCounter + 3 ] = ( float ) ( z + 1 ) / ( mapZ - 1 ) ;
2014-09-03 16:51:28 +02:00
2016-02-11 15:51:04 +01:00
mesh . texcoords [ tcCounter + 4 ] = ( float ) ( x + 1 ) / ( mapX - 1 ) ;
mesh . texcoords [ tcCounter + 5 ] = ( float ) z / ( mapZ - 1 ) ;
2014-09-03 16:51:28 +02:00
2016-01-18 13:36:18 +01:00
mesh . texcoords [ tcCounter + 6 ] = mesh . texcoords [ tcCounter + 4 ] ;
mesh . texcoords [ tcCounter + 7 ] = mesh . texcoords [ tcCounter + 5 ] ;
2014-09-03 16:51:28 +02:00
2016-01-18 13:36:18 +01:00
mesh . texcoords [ tcCounter + 8 ] = mesh . texcoords [ tcCounter + 2 ] ;
mesh . texcoords [ tcCounter + 9 ] = mesh . texcoords [ tcCounter + 3 ] ;
2014-09-03 16:51:28 +02:00
2016-02-11 15:51:04 +01:00
mesh . texcoords [ tcCounter + 10 ] = ( float ) ( x + 1 ) / ( mapX - 1 ) ;
mesh . texcoords [ tcCounter + 11 ] = ( float ) ( z + 1 ) / ( mapZ - 1 ) ;
2014-04-04 20:11:57 +02:00
tcCounter + = 12 ; // 6 texcoords, 12 floats
2014-09-03 16:51:28 +02:00
2014-01-28 21:21:29 +01:00
// Fill normals array with data
//--------------------------------------------------------------
2020-03-24 13:27:49 +01:00
for ( int i = 0 ; i < 18 ; i + = 9 )
2014-04-04 20:11:57 +02:00
{
2020-03-24 13:27:49 +01:00
vA . x = mesh . vertices [ nCounter + i ] ;
vA . y = mesh . vertices [ nCounter + i + 1 ] ;
vA . z = mesh . vertices [ nCounter + i + 2 ] ;
vB . x = mesh . vertices [ nCounter + i + 3 ] ;
vB . y = mesh . vertices [ nCounter + i + 4 ] ;
vB . z = mesh . vertices [ nCounter + i + 5 ] ;
2014-09-03 16:51:28 +02:00
2020-03-24 13:27:49 +01:00
vC . x = mesh . vertices [ nCounter + i + 6 ] ;
vC . y = mesh . vertices [ nCounter + i + 7 ] ;
vC . z = mesh . vertices [ nCounter + i + 8 ] ;
vN = Vector3Normalize ( Vector3CrossProduct ( Vector3Subtract ( vB , vA ) , Vector3Subtract ( vC , vA ) ) ) ;
mesh . normals [ nCounter + i ] = vN . x ;
mesh . normals [ nCounter + i + 1 ] = vN . y ;
mesh . normals [ nCounter + i + 2 ] = vN . z ;
mesh . normals [ nCounter + i + 3 ] = vN . x ;
mesh . normals [ nCounter + i + 4 ] = vN . y ;
mesh . normals [ nCounter + i + 5 ] = vN . z ;
mesh . normals [ nCounter + i + 6 ] = vN . x ;
mesh . normals [ nCounter + i + 7 ] = vN . y ;
mesh . normals [ nCounter + i + 8 ] = vN . z ;
}
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
nCounter + = 18 ; // 6 vertex, 18 floats
2014-03-16 20:59:02 +01:00
}
}
2016-08-16 11:09:55 +02:00
2021-02-06 13:29:22 +01:00
UnloadImageColors ( pixels ) ; // Unload pixels color data
2019-02-21 18:45:19 +01:00
2017-07-21 09:34:09 +02:00
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
UploadMesh ( & mesh , false ) ;
2014-09-03 16:51:28 +02:00
2016-03-05 16:17:54 +01:00
return mesh ;
2013-11-30 18:12:40 +01:00
}
2017-07-21 09:34:09 +02:00
// Generate a cubes mesh from pixel data
// NOTE: Vertex data is uploaded to GPU
2017-07-17 00:33:40 +02:00
Mesh GenMeshCubicmap ( Image cubicmap , Vector3 cubeSize )
2014-07-23 00:06:24 +02:00
{
2020-05-11 17:54:23 +02:00
# define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
2016-05-08 15:24:02 +02:00
Mesh mesh = { 0 } ;
2014-07-23 00:06:24 +02:00
2021-02-06 13:29:22 +01:00
Color * pixels = LoadImageColors ( cubicmap ) ;
2016-08-16 11:09:55 +02:00
2019-08-07 00:32:44 +02:00
// NOTE: Max possible number of triangles numCubes*(12 triangles by cube)
2024-04-21 12:29:09 +02:00
int maxTriangles = cubicmap . width * cubicmap . height * 12 ;
2014-07-23 00:06:24 +02:00
int vCounter = 0 ; // Used to count vertices
int tcCounter = 0 ; // Used to count texcoords
int nCounter = 0 ; // Used to count normals
2014-09-03 16:51:28 +02:00
2016-03-05 16:17:54 +01:00
float w = cubeSize . x ;
float h = cubeSize . z ;
float h2 = cubeSize . y ;
2014-09-03 16:51:28 +02:00
2019-04-23 14:55:35 +02:00
Vector3 * mapVertices = ( Vector3 * ) RL_MALLOC ( maxTriangles * 3 * sizeof ( Vector3 ) ) ;
Vector2 * mapTexcoords = ( Vector2 * ) RL_MALLOC ( maxTriangles * 3 * sizeof ( Vector2 ) ) ;
Vector3 * mapNormals = ( Vector3 * ) RL_MALLOC ( maxTriangles * 3 * sizeof ( Vector3 ) ) ;
2015-04-06 14:02:29 +02:00
2015-02-02 20:05:18 +01:00
// Define the 6 normals of the cube, we will combine them accordingly later...
Vector3 n1 = { 1.0f , 0.0f , 0.0f } ;
Vector3 n2 = { - 1.0f , 0.0f , 0.0f } ;
Vector3 n3 = { 0.0f , 1.0f , 0.0f } ;
Vector3 n4 = { 0.0f , - 1.0f , 0.0f } ;
2020-05-11 04:41:21 -05:00
Vector3 n5 = { 0.0f , 0.0f , - 1.0f } ;
Vector3 n6 = { 0.0f , 0.0f , 1.0f } ;
2015-02-02 20:05:18 +01:00
2015-02-09 18:29:32 +01:00
// NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6)
2015-02-02 20:05:18 +01:00
typedef struct RectangleF {
float x ;
float y ;
float width ;
float height ;
} RectangleF ;
2015-04-06 14:02:29 +02:00
2016-01-13 19:30:35 +01:00
RectangleF rightTexUV = { 0.0f , 0.0f , 0.5f , 0.5f } ;
RectangleF leftTexUV = { 0.5f , 0.0f , 0.5f , 0.5f } ;
RectangleF frontTexUV = { 0.0f , 0.0f , 0.5f , 0.5f } ;
RectangleF backTexUV = { 0.5f , 0.0f , 0.5f , 0.5f } ;
RectangleF topTexUV = { 0.0f , 0.5f , 0.5f , 0.5f } ;
RectangleF bottomTexUV = { 0.5f , 0.5f , 0.5f , 0.5f } ;
2015-04-06 14:02:29 +02:00
2023-04-29 15:03:19 +00:00
for ( int z = 0 ; z < cubicmap . height ; + + z )
2014-07-23 00:06:24 +02:00
{
2023-04-29 15:03:19 +00:00
for ( int x = 0 ; x < cubicmap . width ; + + x )
2014-09-03 16:51:28 +02:00
{
2014-07-23 00:06:24 +02:00
// Define the 8 vertex of the cube, we will combine them accordingly later...
2016-08-15 16:34:21 +02:00
Vector3 v1 = { w * ( x - 0.5f ) , h2 , h * ( z - 0.5f ) } ;
Vector3 v2 = { w * ( x - 0.5f ) , h2 , h * ( z + 0.5f ) } ;
Vector3 v3 = { w * ( x + 0.5f ) , h2 , h * ( z + 0.5f ) } ;
Vector3 v4 = { w * ( x + 0.5f ) , h2 , h * ( z - 0.5f ) } ;
Vector3 v5 = { w * ( x + 0.5f ) , 0 , h * ( z - 0.5f ) } ;
Vector3 v6 = { w * ( x - 0.5f ) , 0 , h * ( z - 0.5f ) } ;
Vector3 v7 = { w * ( x - 0.5f ) , 0 , h * ( z + 0.5f ) } ;
Vector3 v8 = { w * ( x + 0.5f ) , 0 , h * ( z + 0.5f ) } ;
2014-09-03 16:51:28 +02:00
2020-05-11 17:54:23 +02:00
// We check pixel color to be WHITE -> draw full cube
2021-02-06 13:29:22 +01:00
if ( COLOR_EQUAL ( pixels [ z * cubicmap . width + x ] , WHITE ) )
2014-07-23 00:06:24 +02:00
{
2020-05-11 17:54:23 +02:00
// Define triangles and checking collateral cubes
//------------------------------------------------
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
// Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
2020-05-11 17:54:23 +02:00
// WARNING: Not required for a WHITE cubes, created to allow seeing the map from outside
2014-07-23 00:06:24 +02:00
mapVertices [ vCounter ] = v1 ;
2014-09-03 16:51:28 +02:00
mapVertices [ vCounter + 1 ] = v2 ;
mapVertices [ vCounter + 2 ] = v3 ;
mapVertices [ vCounter + 3 ] = v1 ;
mapVertices [ vCounter + 4 ] = v3 ;
2014-07-23 00:06:24 +02:00
mapVertices [ vCounter + 5 ] = v4 ;
vCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter ] = n3 ;
2014-09-03 16:51:28 +02:00
mapNormals [ nCounter + 1 ] = n3 ;
mapNormals [ nCounter + 2 ] = n3 ;
mapNormals [ nCounter + 3 ] = n3 ;
mapNormals [ nCounter + 4 ] = n3 ;
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter + 5 ] = n3 ;
nCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2015-02-02 20:05:18 +01:00
mapTexcoords [ tcCounter ] = ( Vector2 ) { topTexUV . x , topTexUV . y } ;
mapTexcoords [ tcCounter + 1 ] = ( Vector2 ) { topTexUV . x , topTexUV . y + topTexUV . height } ;
mapTexcoords [ tcCounter + 2 ] = ( Vector2 ) { topTexUV . x + topTexUV . width , topTexUV . y + topTexUV . height } ;
mapTexcoords [ tcCounter + 3 ] = ( Vector2 ) { topTexUV . x , topTexUV . y } ;
mapTexcoords [ tcCounter + 4 ] = ( Vector2 ) { topTexUV . x + topTexUV . width , topTexUV . y + topTexUV . height } ;
mapTexcoords [ tcCounter + 5 ] = ( Vector2 ) { topTexUV . x + topTexUV . width , topTexUV . y } ;
2014-07-23 00:06:24 +02:00
tcCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
// Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
mapVertices [ vCounter ] = v6 ;
2014-09-03 16:51:28 +02:00
mapVertices [ vCounter + 1 ] = v8 ;
mapVertices [ vCounter + 2 ] = v7 ;
mapVertices [ vCounter + 3 ] = v6 ;
mapVertices [ vCounter + 4 ] = v5 ;
2014-07-23 00:06:24 +02:00
mapVertices [ vCounter + 5 ] = v8 ;
vCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter ] = n4 ;
2014-09-03 16:51:28 +02:00
mapNormals [ nCounter + 1 ] = n4 ;
mapNormals [ nCounter + 2 ] = n4 ;
mapNormals [ nCounter + 3 ] = n4 ;
mapNormals [ nCounter + 4 ] = n4 ;
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter + 5 ] = n4 ;
nCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2015-02-02 20:05:18 +01:00
mapTexcoords [ tcCounter ] = ( Vector2 ) { bottomTexUV . x + bottomTexUV . width , bottomTexUV . y } ;
mapTexcoords [ tcCounter + 1 ] = ( Vector2 ) { bottomTexUV . x , bottomTexUV . y + bottomTexUV . height } ;
mapTexcoords [ tcCounter + 2 ] = ( Vector2 ) { bottomTexUV . x + bottomTexUV . width , bottomTexUV . y + bottomTexUV . height } ;
mapTexcoords [ tcCounter + 3 ] = ( Vector2 ) { bottomTexUV . x + bottomTexUV . width , bottomTexUV . y } ;
mapTexcoords [ tcCounter + 4 ] = ( Vector2 ) { bottomTexUV . x , bottomTexUV . y } ;
mapTexcoords [ tcCounter + 5 ] = ( Vector2 ) { bottomTexUV . x , bottomTexUV . y + bottomTexUV . height } ;
2014-07-23 00:06:24 +02:00
tcCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2020-05-11 17:54:23 +02:00
// Checking cube on bottom of current cube
2021-02-06 13:29:22 +01:00
if ( ( ( z < cubicmap . height - 1 ) & & COLOR_EQUAL ( pixels [ ( z + 1 ) * cubicmap . width + x ] , BLACK ) ) | | ( z = = cubicmap . height - 1 ) )
2014-07-23 00:06:24 +02:00
{
// Define front triangles (2 tris, 6 vertex) --> v2 v7 v3, v3 v7 v8
// NOTE: Collateral occluded faces are not generated
mapVertices [ vCounter ] = v2 ;
2014-09-03 16:51:28 +02:00
mapVertices [ vCounter + 1 ] = v7 ;
mapVertices [ vCounter + 2 ] = v3 ;
mapVertices [ vCounter + 3 ] = v3 ;
mapVertices [ vCounter + 4 ] = v7 ;
2014-07-23 00:06:24 +02:00
mapVertices [ vCounter + 5 ] = v8 ;
vCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter ] = n6 ;
2014-09-03 16:51:28 +02:00
mapNormals [ nCounter + 1 ] = n6 ;
mapNormals [ nCounter + 2 ] = n6 ;
mapNormals [ nCounter + 3 ] = n6 ;
mapNormals [ nCounter + 4 ] = n6 ;
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter + 5 ] = n6 ;
nCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2015-02-02 20:05:18 +01:00
mapTexcoords [ tcCounter ] = ( Vector2 ) { frontTexUV . x , frontTexUV . y } ;
mapTexcoords [ tcCounter + 1 ] = ( Vector2 ) { frontTexUV . x , frontTexUV . y + frontTexUV . height } ;
mapTexcoords [ tcCounter + 2 ] = ( Vector2 ) { frontTexUV . x + frontTexUV . width , frontTexUV . y } ;
mapTexcoords [ tcCounter + 3 ] = ( Vector2 ) { frontTexUV . x + frontTexUV . width , frontTexUV . y } ;
mapTexcoords [ tcCounter + 4 ] = ( Vector2 ) { frontTexUV . x , frontTexUV . y + frontTexUV . height } ;
mapTexcoords [ tcCounter + 5 ] = ( Vector2 ) { frontTexUV . x + frontTexUV . width , frontTexUV . y + frontTexUV . height } ;
2014-07-23 00:06:24 +02:00
tcCounter + = 6 ;
}
2014-09-03 16:51:28 +02:00
2020-05-11 17:54:23 +02:00
// Checking cube on top of current cube
2021-02-06 13:29:22 +01:00
if ( ( ( z > 0 ) & & COLOR_EQUAL ( pixels [ ( z - 1 ) * cubicmap . width + x ] , BLACK ) ) | | ( z = = 0 ) )
2014-07-23 00:06:24 +02:00
{
// Define back triangles (2 tris, 6 vertex) --> v1 v5 v6, v1 v4 v5
// NOTE: Collateral occluded faces are not generated
mapVertices [ vCounter ] = v1 ;
2014-09-03 16:51:28 +02:00
mapVertices [ vCounter + 1 ] = v5 ;
mapVertices [ vCounter + 2 ] = v6 ;
mapVertices [ vCounter + 3 ] = v1 ;
mapVertices [ vCounter + 4 ] = v4 ;
2014-07-23 00:06:24 +02:00
mapVertices [ vCounter + 5 ] = v5 ;
vCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter ] = n5 ;
2014-09-03 16:51:28 +02:00
mapNormals [ nCounter + 1 ] = n5 ;
mapNormals [ nCounter + 2 ] = n5 ;
mapNormals [ nCounter + 3 ] = n5 ;
mapNormals [ nCounter + 4 ] = n5 ;
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter + 5 ] = n5 ;
nCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2015-02-02 20:05:18 +01:00
mapTexcoords [ tcCounter ] = ( Vector2 ) { backTexUV . x + backTexUV . width , backTexUV . y } ;
mapTexcoords [ tcCounter + 1 ] = ( Vector2 ) { backTexUV . x , backTexUV . y + backTexUV . height } ;
mapTexcoords [ tcCounter + 2 ] = ( Vector2 ) { backTexUV . x + backTexUV . width , backTexUV . y + backTexUV . height } ;
mapTexcoords [ tcCounter + 3 ] = ( Vector2 ) { backTexUV . x + backTexUV . width , backTexUV . y } ;
mapTexcoords [ tcCounter + 4 ] = ( Vector2 ) { backTexUV . x , backTexUV . y } ;
mapTexcoords [ tcCounter + 5 ] = ( Vector2 ) { backTexUV . x , backTexUV . y + backTexUV . height } ;
2014-07-23 00:06:24 +02:00
tcCounter + = 6 ;
}
2014-09-03 16:51:28 +02:00
2020-05-11 17:54:23 +02:00
// Checking cube on right of current cube
2021-02-06 13:29:22 +01:00
if ( ( ( x < cubicmap . width - 1 ) & & COLOR_EQUAL ( pixels [ z * cubicmap . width + ( x + 1 ) ] , BLACK ) ) | | ( x = = cubicmap . width - 1 ) )
2014-07-23 00:06:24 +02:00
{
// Define right triangles (2 tris, 6 vertex) --> v3 v8 v4, v4 v8 v5
// NOTE: Collateral occluded faces are not generated
mapVertices [ vCounter ] = v3 ;
2014-09-03 16:51:28 +02:00
mapVertices [ vCounter + 1 ] = v8 ;
mapVertices [ vCounter + 2 ] = v4 ;
mapVertices [ vCounter + 3 ] = v4 ;
mapVertices [ vCounter + 4 ] = v8 ;
2014-07-23 00:06:24 +02:00
mapVertices [ vCounter + 5 ] = v5 ;
vCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter ] = n1 ;
2014-09-03 16:51:28 +02:00
mapNormals [ nCounter + 1 ] = n1 ;
mapNormals [ nCounter + 2 ] = n1 ;
mapNormals [ nCounter + 3 ] = n1 ;
mapNormals [ nCounter + 4 ] = n1 ;
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter + 5 ] = n1 ;
nCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2015-02-02 20:05:18 +01:00
mapTexcoords [ tcCounter ] = ( Vector2 ) { rightTexUV . x , rightTexUV . y } ;
mapTexcoords [ tcCounter + 1 ] = ( Vector2 ) { rightTexUV . x , rightTexUV . y + rightTexUV . height } ;
mapTexcoords [ tcCounter + 2 ] = ( Vector2 ) { rightTexUV . x + rightTexUV . width , rightTexUV . y } ;
mapTexcoords [ tcCounter + 3 ] = ( Vector2 ) { rightTexUV . x + rightTexUV . width , rightTexUV . y } ;
mapTexcoords [ tcCounter + 4 ] = ( Vector2 ) { rightTexUV . x , rightTexUV . y + rightTexUV . height } ;
mapTexcoords [ tcCounter + 5 ] = ( Vector2 ) { rightTexUV . x + rightTexUV . width , rightTexUV . y + rightTexUV . height } ;
2014-07-23 00:06:24 +02:00
tcCounter + = 6 ;
}
2014-09-03 16:51:28 +02:00
2020-05-11 17:54:23 +02:00
// Checking cube on left of current cube
2021-02-06 13:29:22 +01:00
if ( ( ( x > 0 ) & & COLOR_EQUAL ( pixels [ z * cubicmap . width + ( x - 1 ) ] , BLACK ) ) | | ( x = = 0 ) )
2014-07-23 00:06:24 +02:00
{
// Define left triangles (2 tris, 6 vertex) --> v1 v7 v2, v1 v6 v7
// NOTE: Collateral occluded faces are not generated
mapVertices [ vCounter ] = v1 ;
2014-09-03 16:51:28 +02:00
mapVertices [ vCounter + 1 ] = v7 ;
mapVertices [ vCounter + 2 ] = v2 ;
mapVertices [ vCounter + 3 ] = v1 ;
mapVertices [ vCounter + 4 ] = v6 ;
2014-07-23 00:06:24 +02:00
mapVertices [ vCounter + 5 ] = v7 ;
vCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter ] = n2 ;
2014-09-03 16:51:28 +02:00
mapNormals [ nCounter + 1 ] = n2 ;
mapNormals [ nCounter + 2 ] = n2 ;
mapNormals [ nCounter + 3 ] = n2 ;
mapNormals [ nCounter + 4 ] = n2 ;
2014-07-23 00:06:24 +02:00
mapNormals [ nCounter + 5 ] = n2 ;
nCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2015-02-02 20:05:18 +01:00
mapTexcoords [ tcCounter ] = ( Vector2 ) { leftTexUV . x , leftTexUV . y } ;
mapTexcoords [ tcCounter + 1 ] = ( Vector2 ) { leftTexUV . x + leftTexUV . width , leftTexUV . y + leftTexUV . height } ;
mapTexcoords [ tcCounter + 2 ] = ( Vector2 ) { leftTexUV . x + leftTexUV . width , leftTexUV . y } ;
mapTexcoords [ tcCounter + 3 ] = ( Vector2 ) { leftTexUV . x , leftTexUV . y } ;
mapTexcoords [ tcCounter + 4 ] = ( Vector2 ) { leftTexUV . x , leftTexUV . y + leftTexUV . height } ;
mapTexcoords [ tcCounter + 5 ] = ( Vector2 ) { leftTexUV . x + leftTexUV . width , leftTexUV . y + leftTexUV . height } ;
2014-07-23 00:06:24 +02:00
tcCounter + = 6 ;
}
}
// We check pixel color to be BLACK, we will only draw floor and roof
2021-02-06 13:29:22 +01:00
else if ( COLOR_EQUAL ( pixels [ z * cubicmap . width + x ] , BLACK ) )
2014-07-23 00:06:24 +02:00
{
2015-02-02 20:05:18 +01:00
// Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
mapVertices [ vCounter ] = v1 ;
mapVertices [ vCounter + 1 ] = v3 ;
mapVertices [ vCounter + 2 ] = v2 ;
mapVertices [ vCounter + 3 ] = v1 ;
mapVertices [ vCounter + 4 ] = v4 ;
mapVertices [ vCounter + 5 ] = v3 ;
vCounter + = 6 ;
mapNormals [ nCounter ] = n4 ;
mapNormals [ nCounter + 1 ] = n4 ;
mapNormals [ nCounter + 2 ] = n4 ;
mapNormals [ nCounter + 3 ] = n4 ;
mapNormals [ nCounter + 4 ] = n4 ;
mapNormals [ nCounter + 5 ] = n4 ;
nCounter + = 6 ;
mapTexcoords [ tcCounter ] = ( Vector2 ) { topTexUV . x , topTexUV . y } ;
mapTexcoords [ tcCounter + 1 ] = ( Vector2 ) { topTexUV . x + topTexUV . width , topTexUV . y + topTexUV . height } ;
mapTexcoords [ tcCounter + 2 ] = ( Vector2 ) { topTexUV . x , topTexUV . y + topTexUV . height } ;
mapTexcoords [ tcCounter + 3 ] = ( Vector2 ) { topTexUV . x , topTexUV . y } ;
mapTexcoords [ tcCounter + 4 ] = ( Vector2 ) { topTexUV . x + topTexUV . width , topTexUV . y } ;
mapTexcoords [ tcCounter + 5 ] = ( Vector2 ) { topTexUV . x + topTexUV . width , topTexUV . y + topTexUV . height } ;
tcCounter + = 6 ;
2015-04-06 14:02:29 +02:00
2015-02-02 20:05:18 +01:00
// Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
mapVertices [ vCounter ] = v6 ;
mapVertices [ vCounter + 1 ] = v7 ;
mapVertices [ vCounter + 2 ] = v8 ;
mapVertices [ vCounter + 3 ] = v6 ;
mapVertices [ vCounter + 4 ] = v8 ;
mapVertices [ vCounter + 5 ] = v5 ;
vCounter + = 6 ;
2014-09-03 16:51:28 +02:00
2015-02-02 20:05:18 +01:00
mapNormals [ nCounter ] = n3 ;
mapNormals [ nCounter + 1 ] = n3 ;
mapNormals [ nCounter + 2 ] = n3 ;
mapNormals [ nCounter + 3 ] = n3 ;
mapNormals [ nCounter + 4 ] = n3 ;
mapNormals [ nCounter + 5 ] = n3 ;
nCounter + = 6 ;
mapTexcoords [ tcCounter ] = ( Vector2 ) { bottomTexUV . x + bottomTexUV . width , bottomTexUV . y } ;
mapTexcoords [ tcCounter + 1 ] = ( Vector2 ) { bottomTexUV . x + bottomTexUV . width , bottomTexUV . y + bottomTexUV . height } ;
mapTexcoords [ tcCounter + 2 ] = ( Vector2 ) { bottomTexUV . x , bottomTexUV . y + bottomTexUV . height } ;
mapTexcoords [ tcCounter + 3 ] = ( Vector2 ) { bottomTexUV . x + bottomTexUV . width , bottomTexUV . y } ;
mapTexcoords [ tcCounter + 4 ] = ( Vector2 ) { bottomTexUV . x , bottomTexUV . y + bottomTexUV . height } ;
mapTexcoords [ tcCounter + 5 ] = ( Vector2 ) { bottomTexUV . x , bottomTexUV . y } ;
tcCounter + = 6 ;
2014-07-23 00:06:24 +02:00
}
2014-09-03 16:51:28 +02:00
}
2014-07-23 00:06:24 +02:00
}
2023-02-09 07:17:47 -05:00
// Move data from mapVertices temp arrays to vertices float array
2016-01-18 13:36:18 +01:00
mesh . vertexCount = vCounter ;
2018-03-11 10:41:49 +01:00
mesh . triangleCount = vCounter / 3 ;
2014-09-03 16:51:28 +02:00
2019-04-23 14:55:35 +02:00
mesh . vertices = ( float * ) RL_MALLOC ( mesh . vertexCount * 3 * sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_MALLOC ( mesh . vertexCount * 3 * sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_MALLOC ( mesh . vertexCount * 2 * sizeof ( float ) ) ;
2016-05-08 15:24:02 +02:00
mesh . colors = NULL ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
int fCounter = 0 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
// Move vertices data
for ( int i = 0 ; i < vCounter ; i + + )
{
2016-01-18 13:36:18 +01:00
mesh . vertices [ fCounter ] = mapVertices [ i ] . x ;
mesh . vertices [ fCounter + 1 ] = mapVertices [ i ] . y ;
mesh . vertices [ fCounter + 2 ] = mapVertices [ i ] . z ;
2014-07-23 00:06:24 +02:00
fCounter + = 3 ;
}
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
fCounter = 0 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
// Move normals data
for ( int i = 0 ; i < nCounter ; i + + )
{
2016-01-18 13:36:18 +01:00
mesh . normals [ fCounter ] = mapNormals [ i ] . x ;
mesh . normals [ fCounter + 1 ] = mapNormals [ i ] . y ;
mesh . normals [ fCounter + 2 ] = mapNormals [ i ] . z ;
2014-07-23 00:06:24 +02:00
fCounter + = 3 ;
}
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
fCounter = 0 ;
2014-09-03 16:51:28 +02:00
2014-07-23 00:06:24 +02:00
// Move texcoords data
for ( int i = 0 ; i < tcCounter ; i + + )
{
2016-01-18 13:36:18 +01:00
mesh . texcoords [ fCounter ] = mapTexcoords [ i ] . x ;
mesh . texcoords [ fCounter + 1 ] = mapTexcoords [ i ] . y ;
2014-07-23 00:06:24 +02:00
fCounter + = 2 ;
}
2014-09-03 16:51:28 +02:00
2019-04-23 14:55:35 +02:00
RL_FREE ( mapVertices ) ;
RL_FREE ( mapNormals ) ;
RL_FREE ( mapTexcoords ) ;
2016-08-16 11:09:55 +02:00
2021-02-06 13:29:22 +01:00
UnloadImageColors ( pixels ) ; // Unload pixels color data
2019-02-21 18:45:19 +01:00
2017-07-21 09:34:09 +02:00
// Upload vertex data to GPU (static mesh)
2021-03-25 14:28:12 +01:00
UploadMesh ( & mesh , false ) ;
2016-08-16 11:09:55 +02:00
2016-03-05 16:17:54 +01:00
return mesh ;
2013-11-18 23:38:44 +01:00
}
2017-09-18 00:59:22 +02:00
# endif // SUPPORT_MESH_GENERATION
2013-11-18 23:38:44 +01:00
2019-04-05 13:15:56 +02:00
// Compute mesh bounding box limits
// NOTE: minVertex and maxVertex should be transformed by model transform matrix
2021-05-20 19:24:28 +02:00
BoundingBox GetMeshBoundingBox ( Mesh mesh )
2017-07-17 00:33:40 +02:00
{
2019-04-05 13:15:56 +02:00
// Get min and max vertex to construct bounds (AABB)
Vector3 minVertex = { 0 } ;
Vector3 maxVertex = { 0 } ;
2017-07-17 00:33:40 +02:00
2019-04-05 13:15:56 +02:00
if ( mesh . vertices ! = NULL )
2019-03-29 17:15:22 +01:00
{
2019-04-05 13:15:56 +02:00
minVertex = ( Vector3 ) { mesh . vertices [ 0 ] , mesh . vertices [ 1 ] , mesh . vertices [ 2 ] } ;
maxVertex = ( Vector3 ) { mesh . vertices [ 0 ] , mesh . vertices [ 1 ] , mesh . vertices [ 2 ] } ;
2019-04-04 13:50:52 +02:00
2019-04-05 13:15:56 +02:00
for ( int i = 1 ; i < mesh . vertexCount ; i + + )
{
minVertex = Vector3Min ( minVertex , ( Vector3 ) { mesh . vertices [ i * 3 ] , mesh . vertices [ i * 3 + 1 ] , mesh . vertices [ i * 3 + 2 ] } ) ;
maxVertex = Vector3Max ( maxVertex , ( Vector3 ) { mesh . vertices [ i * 3 ] , mesh . vertices [ i * 3 + 1 ] , mesh . vertices [ i * 3 + 2 ] } ) ;
}
2019-03-29 17:15:22 +01:00
}
2017-07-17 00:33:40 +02:00
2019-04-05 13:15:56 +02:00
// Create the bounding box
BoundingBox box = { 0 } ;
box . min = minVertex ;
box . max = maxVertex ;
2017-07-19 18:55:26 +02:00
2019-04-05 13:15:56 +02:00
return box ;
2017-07-17 00:33:40 +02:00
}
2019-04-05 13:15:56 +02:00
// Compute mesh tangents
// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates
2023-02-21 23:55:55 +01:00
// Implementation based on: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html
2021-06-17 00:04:24 +02:00
void GenMeshTangents ( Mesh * mesh )
2017-07-17 00:33:40 +02:00
{
2023-02-21 23:55:55 +01:00
if ( ( mesh - > vertices = = NULL ) | | ( mesh - > texcoords = = NULL ) )
{
TRACELOG ( LOG_WARNING , " MESH: Tangents generation requires texcoord vertex attribute data " ) ;
return ;
}
2021-07-23 18:16:08 +02:00
if ( mesh - > tangents = = NULL ) mesh - > tangents = ( float * ) RL_MALLOC ( mesh - > vertexCount * 4 * sizeof ( float ) ) ;
else
2021-07-16 15:14:13 -05:00
{
RL_FREE ( mesh - > tangents ) ;
2021-07-23 18:16:08 +02:00
mesh - > tangents = ( float * ) RL_MALLOC ( mesh - > vertexCount * 4 * sizeof ( float ) ) ;
2021-07-16 15:14:13 -05:00
}
2021-07-23 18:16:08 +02:00
Vector3 * tan1 = ( Vector3 * ) RL_MALLOC ( mesh - > vertexCount * sizeof ( Vector3 ) ) ;
Vector3 * tan2 = ( Vector3 * ) RL_MALLOC ( mesh - > vertexCount * sizeof ( Vector3 ) ) ;
2017-07-17 00:33:40 +02:00
2024-06-16 01:44:31 -07:00
if ( mesh - > vertexCount % 3 ! = 0 )
{
TRACELOG ( LOG_WARNING , " MESH: vertexCount expected to be a multiple of 3. Expect uninitialized values. " ) ;
}
for ( int i = 0 ; i < = mesh - > vertexCount - 3 ; i + = 3 )
2019-04-05 13:15:56 +02:00
{
// Get triangle vertices
Vector3 v1 = { mesh - > vertices [ ( i + 0 ) * 3 + 0 ] , mesh - > vertices [ ( i + 0 ) * 3 + 1 ] , mesh - > vertices [ ( i + 0 ) * 3 + 2 ] } ;
Vector3 v2 = { mesh - > vertices [ ( i + 1 ) * 3 + 0 ] , mesh - > vertices [ ( i + 1 ) * 3 + 1 ] , mesh - > vertices [ ( i + 1 ) * 3 + 2 ] } ;
Vector3 v3 = { mesh - > vertices [ ( i + 2 ) * 3 + 0 ] , mesh - > vertices [ ( i + 2 ) * 3 + 1 ] , mesh - > vertices [ ( i + 2 ) * 3 + 2 ] } ;
2017-07-17 00:33:40 +02:00
2019-04-05 13:15:56 +02:00
// Get triangle texcoords
Vector2 uv1 = { mesh - > texcoords [ ( i + 0 ) * 2 + 0 ] , mesh - > texcoords [ ( i + 0 ) * 2 + 1 ] } ;
Vector2 uv2 = { mesh - > texcoords [ ( i + 1 ) * 2 + 0 ] , mesh - > texcoords [ ( i + 1 ) * 2 + 1 ] } ;
Vector2 uv3 = { mesh - > texcoords [ ( i + 2 ) * 2 + 0 ] , mesh - > texcoords [ ( i + 2 ) * 2 + 1 ] } ;
float x1 = v2 . x - v1 . x ;
float y1 = v2 . y - v1 . y ;
float z1 = v2 . z - v1 . z ;
float x2 = v3 . x - v1 . x ;
float y2 = v3 . y - v1 . y ;
float z2 = v3 . z - v1 . z ;
float s1 = uv2 . x - uv1 . x ;
float t1 = uv2 . y - uv1 . y ;
float s2 = uv3 . x - uv1 . x ;
float t2 = uv3 . y - uv1 . y ;
float div = s1 * t2 - s2 * t1 ;
2021-07-23 18:16:08 +02:00
float r = ( div = = 0.0f ) ? 0.0f : 1.0f / div ;
2019-04-05 13:15:56 +02:00
Vector3 sdir = { ( t2 * x1 - t1 * x2 ) * r , ( t2 * y1 - t1 * y2 ) * r , ( t2 * z1 - t1 * z2 ) * r } ;
Vector3 tdir = { ( s1 * x2 - s2 * x1 ) * r , ( s1 * y2 - s2 * y1 ) * r , ( s1 * z2 - s2 * z1 ) * r } ;
tan1 [ i + 0 ] = sdir ;
tan1 [ i + 1 ] = sdir ;
tan1 [ i + 2 ] = sdir ;
tan2 [ i + 0 ] = tdir ;
tan2 [ i + 1 ] = tdir ;
tan2 [ i + 2 ] = tdir ;
}
// Compute tangents considering normals
2021-07-23 18:16:08 +02:00
for ( int i = 0 ; i < mesh - > vertexCount ; i + + )
2019-04-05 13:15:56 +02:00
{
Vector3 normal = { mesh - > normals [ i * 3 + 0 ] , mesh - > normals [ i * 3 + 1 ] , mesh - > normals [ i * 3 + 2 ] } ;
Vector3 tangent = tan1 [ i ] ;
// TODO: Review, not sure if tangent computation is right, just used reference proposed maths...
2021-07-23 18:16:08 +02:00
# if defined(COMPUTE_TANGENTS_METHOD_01)
2020-01-23 21:10:12 +01:00
Vector3 tmp = Vector3Subtract ( tangent , Vector3Scale ( normal , Vector3DotProduct ( normal , tangent ) ) ) ;
2019-04-05 13:15:56 +02:00
tmp = Vector3Normalize ( tmp ) ;
mesh - > tangents [ i * 4 + 0 ] = tmp . x ;
mesh - > tangents [ i * 4 + 1 ] = tmp . y ;
mesh - > tangents [ i * 4 + 2 ] = tmp . z ;
mesh - > tangents [ i * 4 + 3 ] = 1.0f ;
2021-07-23 18:16:08 +02:00
# else
2019-04-05 13:15:56 +02:00
Vector3OrthoNormalize ( & normal , & tangent ) ;
mesh - > tangents [ i * 4 + 0 ] = tangent . x ;
mesh - > tangents [ i * 4 + 1 ] = tangent . y ;
mesh - > tangents [ i * 4 + 2 ] = tangent . z ;
2021-07-23 18:16:08 +02:00
mesh - > tangents [ i * 4 + 3 ] = ( Vector3DotProduct ( Vector3CrossProduct ( normal , tangent ) , tan2 [ i ] ) < 0.0f ) ? - 1.0f : 1.0f ;
# endif
2019-04-05 13:15:56 +02:00
}
2019-04-23 14:55:35 +02:00
RL_FREE ( tan1 ) ;
RL_FREE ( tan2 ) ;
2019-09-13 12:29:52 +01:00
2021-07-23 18:16:08 +02:00
if ( mesh - > vboId ! = NULL )
2021-08-16 23:23:16 +02:00
{
2021-07-23 18:16:08 +02:00
if ( mesh - > vboId [ SHADER_LOC_VERTEX_TANGENT ] ! = 0 )
{
2023-03-21 00:43:22 +09:00
// Update existing vertex buffer
2021-07-23 18:16:08 +02:00
rlUpdateVertexBuffer ( mesh - > vboId [ SHADER_LOC_VERTEX_TANGENT ] , mesh - > tangents , mesh - > vertexCount * 4 * sizeof ( float ) , 0 ) ;
}
else
{
// Load a new tangent attributes buffer
2021-08-16 23:23:16 +02:00
mesh - > vboId [ SHADER_LOC_VERTEX_TANGENT ] = rlLoadVertexBuffer ( mesh - > tangents , mesh - > vertexCount * 4 * sizeof ( float ) , false ) ;
2021-07-23 18:16:08 +02:00
}
2021-08-16 23:23:16 +02:00
2021-07-23 18:16:08 +02:00
rlEnableVertexArray ( mesh - > vaoId ) ;
2024-04-23 20:22:29 +08:00
rlSetVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT , 4 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT ) ;
2021-07-23 18:16:08 +02:00
rlDisableVertexArray ( ) ;
}
2021-08-16 23:23:16 +02:00
2021-07-23 18:16:08 +02:00
TRACELOG ( LOG_INFO , " MESH: Tangents data computed and uploaded for provided mesh " ) ;
2017-07-17 00:33:40 +02:00
}
2014-04-19 16:36:49 +02:00
// Draw a model (with texture if set)
void DrawModel ( Model model , Vector3 position , float scale , Color tint )
2013-11-18 23:38:44 +01:00
{
2014-04-19 16:36:49 +02:00
Vector3 vScale = { scale , scale , scale } ;
2019-04-01 12:17:29 +02:00
Vector3 rotationAxis = { 0.0f , 1.0f , 0.0f } ;
2016-08-16 11:09:55 +02:00
2016-02-02 18:41:01 +01:00
DrawModelEx ( model , position , rotationAxis , 0.0f , vScale , tint ) ;
2013-11-18 23:38:44 +01:00
}
2014-04-19 16:36:49 +02:00
// Draw a model with extended parameters
2016-02-02 18:41:01 +01:00
void DrawModelEx ( Model model , Vector3 position , Vector3 rotationAxis , float rotationAngle , Vector3 scale , Color tint )
2014-04-19 16:36:49 +02:00
{
2016-05-07 18:07:15 +02:00
// Calculate transformation matrix from function parameters
// Get transform matrix (rotation -> scale -> translation)
Matrix matScale = MatrixScale ( scale . x , scale . y , scale . z ) ;
2017-07-21 17:46:44 +02:00
Matrix matRotation = MatrixRotate ( rotationAxis , rotationAngle * DEG2RAD ) ;
2016-05-07 18:07:15 +02:00
Matrix matTranslation = MatrixTranslate ( position . x , position . y , position . z ) ;
2019-02-21 18:45:19 +01:00
2017-05-04 17:42:24 +02:00
Matrix matTransform = MatrixMultiply ( MatrixMultiply ( matScale , matRotation ) , matTranslation ) ;
2016-08-16 11:09:55 +02:00
2016-05-07 18:07:15 +02:00
// Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform)
2017-05-04 17:42:24 +02:00
model . transform = MatrixMultiply ( model . transform , matTransform ) ;
2016-08-16 11:09:55 +02:00
2019-04-04 13:50:52 +02:00
for ( int i = 0 ; i < model . meshCount ; i + + )
2019-03-29 16:48:23 +01:00
{
2021-03-14 11:05:51 +01:00
Color color = model . materials [ model . meshMaterial [ i ] ] . maps [ MATERIAL_MAP_DIFFUSE ] . color ;
2020-02-03 19:26:28 +01:00
2019-10-21 17:55:37 +02:00
Color colorTint = WHITE ;
2024-05-29 13:16:19 +02:00
colorTint . r = ( unsigned char ) ( ( ( int ) color . r * ( int ) tint . r ) / 255 ) ;
colorTint . g = ( unsigned char ) ( ( ( int ) color . g * ( int ) tint . g ) / 255 ) ;
colorTint . b = ( unsigned char ) ( ( ( int ) color . b * ( int ) tint . b ) / 255 ) ;
colorTint . a = ( unsigned char ) ( ( ( int ) color . a * ( int ) tint . a ) / 255 ) ;
2020-02-03 19:26:28 +01:00
2021-03-14 11:05:51 +01:00
model . materials [ model . meshMaterial [ i ] ] . maps [ MATERIAL_MAP_DIFFUSE ] . color = colorTint ;
2021-03-25 14:28:12 +01:00
DrawMesh ( model . meshes [ i ] , model . materials [ model . meshMaterial [ i ] ] , model . transform ) ;
2021-03-14 11:05:51 +01:00
model . materials [ model . meshMaterial [ i ] ] . maps [ MATERIAL_MAP_DIFFUSE ] . color = color ;
2019-03-29 16:48:23 +01:00
}
2014-04-19 16:36:49 +02:00
}
// Draw a model wires (with texture if set)
2016-05-07 18:07:15 +02:00
void DrawModelWires ( Model model , Vector3 position , float scale , Color tint )
2013-11-18 23:38:44 +01:00
{
2016-05-18 13:22:14 +02:00
rlEnableWireMode ( ) ;
2016-08-16 11:09:55 +02:00
2016-05-18 13:22:14 +02:00
DrawModel ( model , position , scale , tint ) ;
2016-08-16 11:09:55 +02:00
2016-05-18 13:22:14 +02:00
rlDisableWireMode ( ) ;
2013-11-18 23:38:44 +01:00
}
2015-12-21 16:42:13 +01:00
// Draw a model wires (with texture if set) with extended parameters
2016-02-02 18:41:01 +01:00
void DrawModelWiresEx ( Model model , Vector3 position , Vector3 rotationAxis , float rotationAngle , Vector3 scale , Color tint )
2015-12-21 16:42:13 +01:00
{
2016-05-18 13:22:14 +02:00
rlEnableWireMode ( ) ;
2016-08-16 11:09:55 +02:00
2016-05-18 13:22:14 +02:00
DrawModelEx ( model , position , rotationAxis , rotationAngle , scale , tint ) ;
2016-08-16 11:09:55 +02:00
2016-05-18 13:22:14 +02:00
rlDisableWireMode ( ) ;
2015-12-21 16:42:13 +01:00
}
2024-08-24 12:42:38 -04:00
// Draw a model points
void DrawModelPoints ( Model model , Vector3 position , float scale , Color tint )
{
rlEnablePointMode ( ) ;
rlDisableBackfaceCulling ( ) ;
DrawModel ( model , position , scale , tint ) ;
rlEnableBackfaceCulling ( ) ;
rlDisableWireMode ( ) ;
}
// Draw a model points
void DrawModelPointsEx ( Model model , Vector3 position , Vector3 rotationAxis , float rotationAngle , Vector3 scale , Color tint )
{
rlEnablePointMode ( ) ;
rlDisableBackfaceCulling ( ) ;
DrawModelEx ( model , position , rotationAxis , rotationAngle , scale , tint ) ;
rlEnableBackfaceCulling ( ) ;
rlDisableWireMode ( ) ;
}
2013-11-18 23:38:44 +01:00
// Draw a billboard
2024-07-08 02:27:51 +08:00
void DrawBillboard ( Camera camera , Texture2D texture , Vector3 position , float scale , Color tint )
2013-11-18 23:38:44 +01:00
{
2020-12-19 19:27:31 +01:00
Rectangle source = { 0.0f , 0.0f , ( float ) texture . width , ( float ) texture . height } ;
2016-08-16 11:09:55 +02:00
2024-07-08 02:27:51 +08:00
DrawBillboardRec ( camera , texture , source , position , ( Vector2 ) { scale * fabsf ( ( float ) source . width / source . height ) , scale } , tint ) ;
2013-11-18 23:38:44 +01:00
}
// Draw a billboard (part of a texture defined by a rectangle)
2021-05-11 01:02:53 +02:00
void DrawBillboardRec ( Camera camera , Texture2D texture , Rectangle source , Vector3 position , Vector2 size , Color tint )
2013-11-18 23:38:44 +01:00
{
2021-09-21 10:22:30 -03:00
// NOTE: Billboard locked on axis-Y
Vector3 up = { 0.0f , 1.0f , 0.0f } ;
2021-09-22 21:56:38 +02:00
2024-07-08 02:27:51 +08:00
DrawBillboardPro ( camera , texture , source , position , up , size , Vector2Scale ( size , 0.5 ) , 0.0f , tint ) ;
2021-05-11 01:55:43 +03:00
}
2024-04-21 12:29:09 +02:00
// Draw a billboard with additional parameters
2021-09-21 10:22:30 -03:00
void DrawBillboardPro ( Camera camera , Texture2D texture , Rectangle source , Vector3 position , Vector3 up , Vector2 size , Vector2 origin , float rotation , Color tint )
2021-05-11 01:55:43 +03:00
{
2024-07-08 02:27:51 +08:00
// Compute the up vector and the right vector
2017-07-21 17:19:28 +02:00
Matrix matView = MatrixLookAt ( camera . position , camera . target , camera . up ) ;
Vector3 right = { matView . m0 , matView . m4 , matView . m8 } ;
2024-07-08 02:27:51 +08:00
right = Vector3Scale ( right , size . x ) ;
up = Vector3Scale ( up , size . y ) ;
// Flip the content of the billboard while maintaining the counterclockwise edge rendering order
if ( size . x < 0.0f )
{
source . x + = size . x ;
source . width * = - 1.0 ;
right = Vector3Negate ( right ) ;
origin . x * = - 1.0f ;
}
if ( size . y < 0.0f )
{
source . y + = size . y ;
source . height * = - 1.0 ;
up = Vector3Negate ( up ) ;
origin . y * = - 1.0f ;
}
// Draw the texture region described by source on the following rectangle in 3D space:
//
// size.x <--.
// 3 ^---------------------------+ 2 \ rotation
// | | /
// | |
// | origin.x position |
// up |.............. | size.y
// | . |
// | . origin.y |
// | . |
// 0 +---------------------------> 1
// right
Vector3 forward ;
if ( rotation ! = 0.0 ) forward = Vector3CrossProduct ( right , up ) ;
Vector3 origin3D = Vector3Add ( Vector3Scale ( Vector3Normalize ( right ) , origin . x ) , Vector3Scale ( Vector3Normalize ( up ) , origin . y ) ) ;
Vector3 points [ 4 ] ;
points [ 0 ] = Vector3Zero ( ) ;
points [ 1 ] = right ;
points [ 2 ] = Vector3Add ( up , right ) ;
points [ 3 ] = up ;
for ( int i = 0 ; i < 4 ; i + + )
{
points [ i ] = Vector3Subtract ( points [ i ] , origin3D ) ;
2024-12-03 19:14:14 +01:00
if ( rotation ! = 0.0 ) points [ i ] = Vector3RotateByAxisAngle ( points [ i ] , forward , rotation * DEG2RAD ) ;
2024-07-08 02:27:51 +08:00
points [ i ] = Vector3Add ( points [ i ] , position ) ;
}
Vector2 texcoords [ 4 ] ;
texcoords [ 0 ] = ( Vector2 ) { ( float ) source . x / texture . width , ( float ) ( source . y + source . height ) / texture . height } ;
texcoords [ 1 ] = ( Vector2 ) { ( float ) ( source . x + source . width ) / texture . width , ( float ) ( source . y + source . height ) / texture . height } ;
texcoords [ 2 ] = ( Vector2 ) { ( float ) ( source . x + source . width ) / texture . width , ( float ) source . y / texture . height } ;
texcoords [ 3 ] = ( Vector2 ) { ( float ) source . x / texture . width , ( float ) source . y / texture . height } ;
2021-06-13 17:08:30 +02:00
2021-03-25 14:28:12 +01:00
rlSetTexture ( texture . id ) ;
2014-03-25 12:40:35 +01:00
rlBegin ( RL_QUADS ) ;
2015-02-02 00:53:49 +01:00
2024-07-08 02:27:51 +08:00
rlColor4ub ( tint . r , tint . g , tint . b , tint . a ) ;
for ( int i = 0 ; i < 4 ; i + + )
2023-07-26 23:46:57 +08:00
{
2024-07-08 02:27:51 +08:00
rlTexCoord2f ( texcoords [ i ] . x , texcoords [ i ] . y ) ;
rlVertex3f ( points [ i ] . x , points [ i ] . y , points [ i ] . z ) ;
2023-07-26 23:46:57 +08:00
}
2014-09-03 16:51:28 +02:00
2014-03-25 12:40:35 +01:00
rlEnd ( ) ;
2021-06-13 17:08:30 +02:00
rlSetTexture ( 0 ) ;
2013-11-18 23:38:44 +01:00
}
2016-01-25 11:12:31 +01:00
// Draw a bounding box with wires
2016-03-01 20:26:01 +01:00
void DrawBoundingBox ( BoundingBox box , Color color )
2016-01-25 11:12:31 +01:00
{
2021-05-14 14:01:42 +02:00
Vector3 size = { 0 } ;
2016-08-16 11:09:55 +02:00
2020-02-28 12:54:39 +01:00
size . x = fabsf ( box . max . x - box . min . x ) ;
size . y = fabsf ( box . max . y - box . min . y ) ;
size . z = fabsf ( box . max . z - box . min . z ) ;
2016-08-16 11:09:55 +02:00
2016-01-25 11:12:31 +01:00
Vector3 center = { box . min . x + size . x / 2.0f , box . min . y + size . y / 2.0f , box . min . z + size . z / 2.0f } ;
2016-08-16 11:09:55 +02:00
2016-03-01 20:26:01 +01:00
DrawCubeWires ( center , size . x , size . y , size . z , color ) ;
2016-01-25 11:12:31 +01:00
}
2021-06-10 17:49:55 +02:00
// Check collision between two spheres
2020-12-19 20:05:32 +01:00
bool CheckCollisionSpheres ( Vector3 center1 , float radius1 , Vector3 center2 , float radius2 )
2015-02-09 18:35:25 +01:00
{
2019-05-09 16:09:49 +02:00
bool collision = false ;
2019-09-13 12:29:52 +01:00
2019-05-09 16:09:49 +02:00
// Simple way to check for collision, just checking distance between two points
// Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution
/*
2020-12-19 20:05:32 +01:00
float dx = center1 . x - center2 . x ; // X distance between centers
float dy = center1 . y - center2 . y ; // Y distance between centers
float dz = center1 . z - center2 . z ; // Z distance between centers
2019-05-09 16:09:49 +02:00
2019-08-08 08:57:21 +01:00
float distance = sqrtf ( dx * dx + dy * dy + dz * dz ) ; // Distance between centers
2019-05-09 16:09:49 +02:00
2020-12-19 20:05:32 +01:00
if ( distance < = ( radius1 + radius2 ) ) collision = true ;
2019-05-09 16:09:49 +02:00
*/
2019-09-13 12:29:52 +01:00
2019-05-09 16:09:49 +02:00
// Check for distances squared to avoid sqrtf()
2020-12-19 20:05:32 +01:00
if ( Vector3DotProduct ( Vector3Subtract ( center2 , center1 ) , Vector3Subtract ( center2 , center1 ) ) < = ( radius1 + radius2 ) * ( radius1 + radius2 ) ) collision = true ;
2019-09-13 12:29:52 +01:00
2019-05-09 16:09:49 +02:00
return collision ;
2015-02-09 18:35:25 +01:00
}
2021-06-10 17:49:55 +02:00
// Check collision between two boxes
2015-07-13 18:19:29 +02:00
// NOTE: Boxes are defined by two points minimum and maximum
2016-03-01 20:54:02 +01:00
bool CheckCollisionBoxes ( BoundingBox box1 , BoundingBox box2 )
2015-02-09 18:35:25 +01:00
{
bool collision = true ;
2015-04-06 14:02:29 +02:00
2016-03-01 20:54:02 +01:00
if ( ( box1 . max . x > = box2 . min . x ) & & ( box1 . min . x < = box2 . max . x ) )
2015-02-09 18:35:25 +01:00
{
2016-03-01 20:54:02 +01:00
if ( ( box1 . max . y < box2 . min . y ) | | ( box1 . min . y > box2 . max . y ) ) collision = false ;
if ( ( box1 . max . z < box2 . min . z ) | | ( box1 . min . z > box2 . max . z ) ) collision = false ;
2015-02-09 18:35:25 +01:00
}
else collision = false ;
return collision ;
}
2021-06-10 17:49:55 +02:00
// Check collision between box and sphere
2019-08-07 00:32:44 +02:00
bool CheckCollisionBoxSphere ( BoundingBox box , Vector3 center , float radius )
2015-02-09 18:35:25 +01:00
{
bool collision = false ;
2015-04-06 14:02:29 +02:00
2016-03-01 20:54:02 +01:00
float dmin = 0 ;
2015-02-09 18:35:25 +01:00
2019-08-07 00:32:44 +02:00
if ( center . x < box . min . x ) dmin + = powf ( center . x - box . min . x , 2 ) ;
else if ( center . x > box . max . x ) dmin + = powf ( center . x - box . max . x , 2 ) ;
2015-02-09 18:35:25 +01:00
2019-08-07 00:32:44 +02:00
if ( center . y < box . min . y ) dmin + = powf ( center . y - box . min . y , 2 ) ;
else if ( center . y > box . max . y ) dmin + = powf ( center . y - box . max . y , 2 ) ;
2015-02-09 18:35:25 +01:00
2019-08-07 00:32:44 +02:00
if ( center . z < box . min . z ) dmin + = powf ( center . z - box . min . z , 2 ) ;
else if ( center . z > box . max . z ) dmin + = powf ( center . z - box . max . z , 2 ) ;
2015-02-09 18:35:25 +01:00
2019-08-07 00:32:44 +02:00
if ( dmin < = ( radius * radius ) ) collision = true ;
2015-02-09 18:35:25 +01:00
return collision ;
}
2021-05-31 11:41:56 +02:00
// Get collision info between ray and sphere
RayCollision GetRayCollisionSphere ( Ray ray , Vector3 center , float radius )
2016-01-19 20:27:41 +01:00
{
2021-05-31 11:41:56 +02:00
RayCollision collision = { 0 } ;
2016-08-16 11:09:55 +02:00
2019-08-07 00:32:44 +02:00
Vector3 raySpherePos = Vector3Subtract ( center , ray . position ) ;
2017-07-22 22:15:50 +02:00
float vector = Vector3DotProduct ( raySpherePos , ray . direction ) ;
2021-06-03 20:15:27 +02:00
float distance = Vector3Length ( raySpherePos ) ;
2021-06-03 20:25:28 +02:00
float d = radius * radius - ( distance * distance - vector * vector ) ;
2016-08-16 11:09:55 +02:00
2021-06-03 20:15:27 +02:00
collision . hit = d > = 0.0f ;
2016-08-16 11:09:55 +02:00
2018-04-02 15:16:45 +02:00
// Check if ray origin is inside the sphere to calculate the correct collision point
2021-06-13 17:08:30 +02:00
if ( distance < radius )
2021-06-03 20:25:28 +02:00
{
2021-06-03 20:15:27 +02:00
collision . distance = vector + sqrtf ( d ) ;
2019-02-21 18:45:19 +01:00
2021-06-03 20:15:27 +02:00
// Calculate collision point
collision . point = Vector3Add ( ray . position , Vector3Scale ( ray . direction , collision . distance ) ) ;
2016-08-16 11:09:55 +02:00
2021-06-03 20:15:27 +02:00
// Calculate collision normal (pointing outwards)
collision . normal = Vector3Negate ( Vector3Normalize ( Vector3Subtract ( collision . point , center ) ) ) ;
2021-06-13 17:08:30 +02:00
}
else
2021-06-03 20:25:28 +02:00
{
2021-06-03 20:15:27 +02:00
collision . distance = vector - sqrtf ( d ) ;
// Calculate collision point
collision . point = Vector3Add ( ray . position , Vector3Scale ( ray . direction , collision . distance ) ) ;
// Calculate collision normal (pointing inwards)
collision . normal = Vector3Normalize ( Vector3Subtract ( collision . point , center ) ) ;
}
2021-06-13 17:08:30 +02:00
2016-01-19 20:27:41 +01:00
return collision ;
}
2021-05-31 11:41:56 +02:00
// Get collision info between ray and box
RayCollision GetRayCollisionBox ( Ray ray , BoundingBox box )
2016-01-07 16:18:24 +01:00
{
2021-05-31 11:41:56 +02:00
RayCollision collision = { 0 } ;
2016-08-16 11:09:55 +02:00
2021-06-03 20:15:27 +02:00
// Note: If ray.position is inside the box, the distance is negative (as if the ray was reversed)
2024-04-20 20:31:06 +02:00
// Reversing ray.direction will give use the correct result
2021-06-03 20:25:28 +02:00
bool insideBox = ( ray . position . x > box . min . x ) & & ( ray . position . x < box . max . x ) & &
( ray . position . y > box . min . y ) & & ( ray . position . y < box . max . y ) & &
( ray . position . z > box . min . z ) & & ( ray . position . z < box . max . z ) ;
2021-06-03 20:15:27 +02:00
2021-06-03 20:25:28 +02:00
if ( insideBox ) ray . direction = Vector3Negate ( ray . direction ) ;
2021-06-03 20:15:27 +02:00
float t [ 11 ] = { 0 } ;
2021-06-03 20:25:28 +02:00
t [ 8 ] = 1.0f / ray . direction . x ;
t [ 9 ] = 1.0f / ray . direction . y ;
t [ 10 ] = 1.0f / ray . direction . z ;
2021-06-03 20:15:27 +02:00
2021-06-03 20:25:28 +02:00
t [ 0 ] = ( box . min . x - ray . position . x ) * t [ 8 ] ;
t [ 1 ] = ( box . max . x - ray . position . x ) * t [ 8 ] ;
t [ 2 ] = ( box . min . y - ray . position . y ) * t [ 9 ] ;
t [ 3 ] = ( box . max . y - ray . position . y ) * t [ 9 ] ;
t [ 4 ] = ( box . min . z - ray . position . z ) * t [ 10 ] ;
t [ 5 ] = ( box . max . z - ray . position . z ) * t [ 10 ] ;
2016-12-05 01:14:18 +01:00
t [ 6 ] = ( float ) fmax ( fmax ( fmin ( t [ 0 ] , t [ 1 ] ) , fmin ( t [ 2 ] , t [ 3 ] ) ) , fmin ( t [ 4 ] , t [ 5 ] ) ) ;
t [ 7 ] = ( float ) fmin ( fmin ( fmax ( t [ 0 ] , t [ 1 ] ) , fmax ( t [ 2 ] , t [ 3 ] ) ) , fmax ( t [ 4 ] , t [ 5 ] ) ) ;
2016-08-16 11:09:55 +02:00
2021-06-03 20:25:28 +02:00
collision . hit = ! ( ( t [ 7 ] < 0 ) | | ( t [ 6 ] > t [ 7 ] ) ) ;
2021-06-03 20:15:27 +02:00
collision . distance = t [ 6 ] ;
collision . point = Vector3Add ( ray . position , Vector3Scale ( ray . direction , collision . distance ) ) ;
// Get box center point
collision . normal = Vector3Lerp ( box . min , box . max , 0.5f ) ;
// Get vector center point->hit point
collision . normal = Vector3Subtract ( collision . point , collision . normal ) ;
// Scale vector to unit cube
2021-06-03 20:25:28 +02:00
// NOTE: We use an additional .01 to fix numerical errors
2021-06-03 20:15:27 +02:00
collision . normal = Vector3Scale ( collision . normal , 2.01f ) ;
collision . normal = Vector3Divide ( collision . normal , Vector3Subtract ( box . max , box . min ) ) ;
2023-02-09 07:17:47 -05:00
// The relevant elements of the vector are now slightly larger than 1.0f (or smaller than -1.0f)
2021-08-08 13:02:51 +02:00
// and the others are somewhere between -1.0 and 1.0 casting to int is exactly our wanted normal!
collision . normal . x = ( float ) ( ( int ) collision . normal . x ) ;
collision . normal . y = ( float ) ( ( int ) collision . normal . y ) ;
collision . normal . z = ( float ) ( ( int ) collision . normal . z ) ;
2021-06-03 20:15:27 +02:00
collision . normal = Vector3Normalize ( collision . normal ) ;
2021-06-03 20:25:28 +02:00
if ( insideBox )
{
2021-06-03 20:15:27 +02:00
// Reset ray.direction
ray . direction = Vector3Negate ( ray . direction ) ;
// Fix result
collision . distance * = - 1.0f ;
collision . normal = Vector3Negate ( collision . normal ) ;
}
2016-08-16 11:09:55 +02:00
2016-01-07 16:18:24 +01:00
return collision ;
}
2021-05-31 11:41:56 +02:00
2020-12-14 10:26:32 -08:00
// Get collision info between ray and mesh
2021-05-31 11:41:56 +02:00
RayCollision GetRayCollisionMesh ( Ray ray , Mesh mesh , Matrix transform )
2020-12-14 10:26:32 -08:00
{
2021-05-31 11:41:56 +02:00
RayCollision collision = { 0 } ;
2020-12-14 10:26:32 -08:00
2020-12-24 23:07:52 +01:00
// Check if mesh vertex data on CPU for testing
if ( mesh . vertices ! = NULL )
{
2021-03-27 18:24:39 +01:00
int triangleCount = mesh . triangleCount ;
2020-12-24 23:07:52 +01:00
// Test against all triangles in mesh
for ( int i = 0 ; i < triangleCount ; i + + )
{
Vector3 a , b , c ;
2024-12-03 19:14:14 +01:00
Vector3 * vertdata = ( Vector3 * ) mesh . vertices ;
2020-12-24 23:07:52 +01:00
if ( mesh . indices )
{
2021-03-31 17:55:46 +02:00
a = vertdata [ mesh . indices [ i * 3 + 0 ] ] ;
b = vertdata [ mesh . indices [ i * 3 + 1 ] ] ;
c = vertdata [ mesh . indices [ i * 3 + 2 ] ] ;
2020-12-24 23:07:52 +01:00
}
else
{
2021-03-31 17:55:46 +02:00
a = vertdata [ i * 3 + 0 ] ;
b = vertdata [ i * 3 + 1 ] ;
c = vertdata [ i * 3 + 2 ] ;
2020-12-24 23:07:52 +01:00
}
a = Vector3Transform ( a , transform ) ;
b = Vector3Transform ( b , transform ) ;
c = Vector3Transform ( c , transform ) ;
2021-05-31 11:41:56 +02:00
RayCollision triHitInfo = GetRayCollisionTriangle ( ray , a , b , c ) ;
2020-12-24 23:07:52 +01:00
if ( triHitInfo . hit )
{
// Save the closest hit triangle
2021-05-31 11:41:56 +02:00
if ( ( ! collision . hit ) | | ( collision . distance > triHitInfo . distance ) ) collision = triHitInfo ;
2020-12-24 23:07:52 +01:00
}
}
}
2021-06-13 17:08:30 +02:00
2021-05-31 11:41:56 +02:00
return collision ;
2020-12-14 10:26:32 -08:00
}
2016-01-07 16:18:24 +01:00
2017-01-05 19:33:05 +01:00
// Get collision info between ray and triangle
2021-06-03 20:15:27 +02:00
// NOTE: The points are expected to be in counter-clockwise winding
2017-01-05 19:33:05 +01:00
// NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
2021-05-31 11:41:56 +02:00
RayCollision GetRayCollisionTriangle ( Ray ray , Vector3 p1 , Vector3 p2 , Vector3 p3 )
2017-01-05 19:33:05 +01:00
{
2022-03-20 12:15:51 +01:00
# define EPSILON 0.000001f // A small number
2017-01-05 19:33:05 +01:00
2021-05-31 11:41:56 +02:00
RayCollision collision = { 0 } ;
Vector3 edge1 = { 0 } ;
Vector3 edge2 = { 0 } ;
2017-01-05 19:33:05 +01:00
Vector3 p , q , tv ;
float det , invDet , u , v , t ;
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
// Find vectors for two edges sharing V1
2017-07-22 22:15:50 +02:00
edge1 = Vector3Subtract ( p2 , p1 ) ;
edge2 = Vector3Subtract ( p3 , p1 ) ;
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
// Begin calculating determinant - also used to calculate u parameter
2017-07-22 22:15:50 +02:00
p = Vector3CrossProduct ( ray . direction , edge2 ) ;
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
// If determinant is near zero, ray lies in plane of triangle or ray is parallel to plane of triangle
2017-07-22 22:15:50 +02:00
det = Vector3DotProduct ( edge1 , p ) ;
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
// Avoid culling!
2021-05-31 11:41:56 +02:00
if ( ( det > - EPSILON ) & & ( det < EPSILON ) ) return collision ;
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
invDet = 1.0f / det ;
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
// Calculate distance from V1 to ray origin
2017-07-22 22:15:50 +02:00
tv = Vector3Subtract ( ray . position , p1 ) ;
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
// Calculate u parameter and test bound
2017-07-22 22:15:50 +02:00
u = Vector3DotProduct ( tv , p ) * invDet ;
2017-01-28 23:02:30 +01:00
2023-02-09 07:17:47 -05:00
// The intersection lies outside the triangle
2021-05-31 11:41:56 +02:00
if ( ( u < 0.0f ) | | ( u > 1.0f ) ) return collision ;
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
// Prepare to test v parameter
2017-07-22 22:15:50 +02:00
q = Vector3CrossProduct ( tv , edge1 ) ;
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
// Calculate V parameter and test bound
2017-07-22 22:15:50 +02:00
v = Vector3DotProduct ( ray . direction , q ) * invDet ;
2017-01-28 23:02:30 +01:00
2023-02-09 07:17:47 -05:00
// The intersection lies outside the triangle
2021-05-31 11:41:56 +02:00
if ( ( v < 0.0f ) | | ( ( u + v ) > 1.0f ) ) return collision ;
2017-01-28 23:02:30 +01:00
2017-07-22 22:15:50 +02:00
t = Vector3DotProduct ( edge2 , q ) * invDet ;
2017-01-28 23:02:30 +01:00
if ( t > EPSILON )
{
2017-01-05 19:33:05 +01:00
// Ray hit, get hit point and normal
2021-05-31 11:41:56 +02:00
collision . hit = true ;
collision . distance = t ;
collision . normal = Vector3Normalize ( Vector3CrossProduct ( edge1 , edge2 ) ) ;
collision . point = Vector3Add ( ray . position , Vector3Scale ( ray . direction , t ) ) ;
2017-01-05 19:33:05 +01:00
}
2017-01-28 23:02:30 +01:00
2021-05-31 11:41:56 +02:00
return collision ;
2017-01-05 19:33:05 +01:00
}
2021-06-03 20:15:27 +02:00
// Get collision info between ray and quad
// NOTE: The points are expected to be in counter-clockwise winding
2021-06-03 20:25:28 +02:00
RayCollision GetRayCollisionQuad ( Ray ray , Vector3 p1 , Vector3 p2 , Vector3 p3 , Vector3 p4 )
{
2021-05-31 11:41:56 +02:00
RayCollision collision = { 0 } ;
2017-01-05 19:33:05 +01:00
2021-06-03 20:15:27 +02:00
collision = GetRayCollisionTriangle ( ray , p1 , p2 , p4 ) ;
2017-01-28 23:02:30 +01:00
2021-06-03 20:15:27 +02:00
if ( ! collision . hit ) collision = GetRayCollisionTriangle ( ray , p2 , p3 , p4 ) ;
2017-01-28 23:02:30 +01:00
2021-05-31 11:41:56 +02:00
return collision ;
2017-01-05 19:33:05 +01:00
}
2023-01-02 20:46:33 +01:00
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
# if defined(SUPPORT_FILEFORMAT_IQM) || defined(SUPPORT_FILEFORMAT_GLTF)
// Build pose from parent joints
// NOTE: Required for animations loading (required by IQM and GLTF)
2023-01-02 14:23:48 -05:00
static void BuildPoseFromParentJoints ( BoneInfo * bones , int boneCount , Transform * transforms )
{
for ( int i = 0 ; i < boneCount ; i + + )
{
if ( bones [ i ] . parent > = 0 )
{
if ( bones [ i ] . parent > i )
{
TRACELOG ( LOG_WARNING , " Assumes bones are toplogically sorted, but bone %d has parent %d. Skipping. " , i , bones [ i ] . parent ) ;
continue ;
}
transforms [ i ] . rotation = QuaternionMultiply ( transforms [ bones [ i ] . parent ] . rotation , transforms [ i ] . rotation ) ;
transforms [ i ] . translation = Vector3RotateByQuaternion ( transforms [ i ] . translation , transforms [ bones [ i ] . parent ] . rotation ) ;
transforms [ i ] . translation = Vector3Add ( transforms [ i ] . translation , transforms [ bones [ i ] . parent ] . translation ) ;
transforms [ i ] . scale = Vector3Multiply ( transforms [ i ] . scale , transforms [ bones [ i ] . parent ] . scale ) ;
}
}
}
2023-01-02 20:46:33 +01:00
# endif
2023-01-02 14:23:48 -05:00
2017-03-26 22:49:01 +02:00
# if defined(SUPPORT_FILEFORMAT_OBJ)
2014-04-19 16:36:49 +02:00
// Load OBJ mesh data
2021-09-01 21:29:20 +02:00
//
// Keep the following information in mind when reading this
// - A mesh is created for every material present in the obj file
// - the model.meshCount is therefore the materialCount returned from tinyobj
// - the mesh is automatically triangulated by tinyobj
2019-03-29 16:48:23 +01:00
static Model LoadOBJ ( const char * fileName )
2014-04-19 16:36:49 +02:00
{
2024-08-25 09:49:52 -07:00
tinyobj_attrib_t objAttributes = { 0 } ;
2024-12-01 23:10:59 +01:00
tinyobj_shape_t * objShapes = NULL ;
2024-08-25 09:49:52 -07:00
unsigned int objShapeCount = 0 ;
2024-12-01 23:10:59 +01:00
tinyobj_material_t * objMaterials = NULL ;
2024-08-25 09:49:52 -07:00
unsigned int objMaterialCount = 0 ;
2019-03-29 16:48:23 +01:00
Model model = { 0 } ;
2024-08-25 09:49:52 -07:00
model . transform = MatrixIdentity ( ) ;
2019-04-01 12:17:29 +02:00
2024-12-01 23:10:59 +01:00
char * fileText = LoadFileText ( fileName ) ;
2019-04-01 12:17:29 +02:00
2024-08-25 09:49:52 -07:00
if ( fileText = = NULL )
{
2024-12-01 23:10:59 +01:00
TRACELOG ( LOG_ERROR , " MODEL: [%s] Unable to read obj file " , fileName ) ;
2024-08-25 09:49:52 -07:00
return model ;
}
char currentDir [ 1024 ] = { 0 } ;
strcpy ( currentDir , GetWorkingDirectory ( ) ) ; // Save current working directory
2024-12-01 23:10:59 +01:00
const char * workingDir = GetDirectoryPath ( fileName ) ; // Switch to OBJ directory for material path correctness
if ( CHDIR ( workingDir ) ! = 0 ) TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to change working directory " , workingDir ) ;
2014-09-03 16:51:28 +02:00
2024-08-25 09:49:52 -07:00
unsigned int dataSize = ( unsigned int ) strlen ( fileText ) ;
2024-12-03 19:14:14 +01:00
2024-08-25 09:49:52 -07:00
unsigned int flags = TINYOBJ_FLAG_TRIANGULATE ;
int ret = tinyobj_parse_obj ( & objAttributes , & objShapes , & objShapeCount , & objMaterials , & objMaterialCount , fileText , dataSize , flags ) ;
if ( ret ! = TINYOBJ_SUCCESS )
2019-03-29 17:28:10 +01:00
{
2024-12-01 23:10:59 +01:00
TRACELOG ( LOG_ERROR , " MODEL: Unable to read obj data %s " , fileName ) ;
2024-08-25 09:49:52 -07:00
return model ;
}
UnloadFileText ( fileText ) ;
unsigned int faceVertIndex = 0 ;
unsigned int nextShape = 1 ;
int lastMaterial = - 1 ;
unsigned int meshIndex = 0 ;
2023-10-31 20:15:45 +01:00
2024-12-01 23:10:59 +01:00
// Count meshes
2024-08-25 09:49:52 -07:00
unsigned int nextShapeEnd = objAttributes . num_face_num_verts ;
2024-12-01 23:10:59 +01:00
// See how many verts till the next shape
2024-08-25 09:49:52 -07:00
if ( objShapeCount > 1 ) nextShapeEnd = objShapes [ nextShape ] . face_offset ;
2024-12-01 23:10:59 +01:00
// Walk all the faces
2024-08-25 09:49:52 -07:00
for ( unsigned int faceId = 0 ; faceId < objAttributes . num_faces ; faceId + + )
{
2024-11-15 16:40:14 +01:00
if ( faceId > = nextShapeEnd )
2021-04-09 10:57:14 -07:00
{
2024-12-01 23:10:59 +01:00
// Try to find the last vert in the next shape
2024-08-25 09:49:52 -07:00
nextShape + + ;
if ( nextShape < objShapeCount ) nextShapeEnd = objShapes [ nextShape ] . face_offset ;
2024-12-01 23:10:59 +01:00
else nextShapeEnd = objAttributes . num_face_num_verts ; // This is actually the total number of face verts in the file, not faces
2024-08-25 09:49:52 -07:00
meshIndex + + ;
2021-04-09 10:57:14 -07:00
}
2024-12-01 23:10:59 +01:00
else if ( ( lastMaterial ! = - 1 ) & & ( objAttributes . material_ids [ faceId ] ! = lastMaterial ) )
2024-08-25 09:49:52 -07:00
{
2024-12-01 23:10:59 +01:00
meshIndex + + ; // If this is a new material, we need to allocate a new mesh
2024-08-25 09:49:52 -07:00
}
lastMaterial = objAttributes . material_ids [ faceId ] ;
faceVertIndex + = objAttributes . face_num_verts [ faceId ] ;
}
2024-12-01 23:10:59 +01:00
// Allocate the base meshes and materials
2024-08-25 09:49:52 -07:00
model . meshCount = meshIndex + 1 ;
2024-12-01 23:10:59 +01:00
model . meshes = ( Mesh * ) MemAlloc ( sizeof ( Mesh ) * model . meshCount ) ;
2024-08-25 09:49:52 -07:00
if ( objMaterialCount > 0 )
{
model . materialCount = objMaterialCount ;
2024-12-01 23:10:59 +01:00
model . materials = ( Material * ) MemAlloc ( sizeof ( Material ) * objMaterialCount ) ;
2024-08-25 09:49:52 -07:00
}
2024-12-01 23:10:59 +01:00
else // We must allocate at least one material
2024-08-25 09:49:52 -07:00
{
model . materialCount = 1 ;
2024-12-01 23:10:59 +01:00
model . materials = ( Material * ) MemAlloc ( sizeof ( Material ) * 1 ) ;
2024-08-25 09:49:52 -07:00
}
2024-12-01 23:10:59 +01:00
model . meshMaterial = ( int * ) MemAlloc ( sizeof ( int ) * model . meshCount ) ;
2019-03-29 17:28:10 +01:00
2024-12-01 23:10:59 +01:00
// See how many verts are in each mesh
unsigned int * localMeshVertexCounts = ( unsigned int * ) MemAlloc ( sizeof ( unsigned int ) * model . meshCount ) ;
2019-04-04 13:50:52 +02:00
2024-08-25 09:49:52 -07:00
faceVertIndex = 0 ;
nextShapeEnd = objAttributes . num_face_num_verts ;
lastMaterial = - 1 ;
meshIndex = 0 ;
unsigned int localMeshVertexCount = 0 ;
2019-04-04 13:50:52 +02:00
2024-08-25 09:49:52 -07:00
nextShape = 1 ;
2024-12-01 23:10:59 +01:00
if ( objShapeCount > 1 ) nextShapeEnd = objShapes [ nextShape ] . face_offset ;
2020-11-03 23:47:33 +01:00
2024-12-01 23:10:59 +01:00
// Walk all the faces
2024-08-25 09:49:52 -07:00
for ( unsigned int faceId = 0 ; faceId < objAttributes . num_faces ; faceId + + )
{
2024-12-01 23:10:59 +01:00
bool newMesh = false ; // Do we need a new mesh?
2024-11-15 16:40:14 +01:00
if ( faceId > = nextShapeEnd )
2020-11-17 00:30:12 +01:00
{
2024-12-01 23:10:59 +01:00
// Try to find the last vert in the next shape
2024-08-25 09:49:52 -07:00
nextShape + + ;
if ( nextShape < objShapeCount ) nextShapeEnd = objShapes [ nextShape ] . face_offset ;
else nextShapeEnd = objAttributes . num_face_num_verts ; // this is actually the total number of face verts in the file, not faces
newMesh = true ;
2019-08-27 11:16:43 +02:00
}
2024-12-01 23:10:59 +01:00
else if ( ( lastMaterial ! = - 1 ) & & ( objAttributes . material_ids [ faceId ] ! = lastMaterial ) )
2024-08-21 08:11:59 -07:00
{
2024-08-25 09:49:52 -07:00
newMesh = true ;
2024-08-21 08:11:59 -07:00
}
2019-09-13 12:29:52 +01:00
2024-08-25 09:49:52 -07:00
lastMaterial = objAttributes . material_ids [ faceId ] ;
2020-11-03 23:47:33 +01:00
2024-08-25 09:49:52 -07:00
if ( newMesh )
2021-09-09 18:14:15 +02:00
{
2024-08-25 09:49:52 -07:00
localMeshVertexCounts [ meshIndex ] = localMeshVertexCount ;
2020-11-03 23:47:33 +01:00
2024-08-25 09:49:52 -07:00
localMeshVertexCount = 0 ;
meshIndex + + ;
}
2020-11-03 23:47:33 +01:00
2024-08-25 09:49:52 -07:00
faceVertIndex + = objAttributes . face_num_verts [ faceId ] ;
localMeshVertexCount + = objAttributes . face_num_verts [ faceId ] ;
}
2024-12-03 19:14:14 +01:00
2024-08-25 09:49:52 -07:00
localMeshVertexCounts [ meshIndex ] = localMeshVertexCount ;
2020-11-03 23:47:33 +01:00
2024-08-25 09:49:52 -07:00
for ( int i = 0 ; i < model . meshCount ; i + + )
{
2024-12-01 23:10:59 +01:00
// Allocate the buffers for each mesh
2024-08-25 09:49:52 -07:00
unsigned int vertexCount = localMeshVertexCounts [ i ] ;
2020-11-03 23:47:33 +01:00
2024-08-25 09:49:52 -07:00
model . meshes [ i ] . vertexCount = vertexCount ;
2024-12-03 19:14:14 +01:00
model . meshes [ i ] . triangleCount = vertexCount / 3 ;
2020-11-03 23:47:33 +01:00
2024-12-01 23:10:59 +01:00
model . meshes [ i ] . vertices = ( float * ) MemAlloc ( sizeof ( float ) * vertexCount * 3 ) ;
model . meshes [ i ] . normals = ( float * ) MemAlloc ( sizeof ( float ) * vertexCount * 3 ) ;
model . meshes [ i ] . texcoords = ( float * ) MemAlloc ( sizeof ( float ) * vertexCount * 2 ) ;
model . meshes [ i ] . colors = ( unsigned char * ) MemAlloc ( sizeof ( unsigned char ) * vertexCount * 4 ) ;
2024-08-25 09:49:52 -07:00
}
MemFree ( localMeshVertexCounts ) ;
localMeshVertexCounts = NULL ;
2024-12-01 23:10:59 +01:00
// Fill meshes
2024-08-25 09:49:52 -07:00
faceVertIndex = 0 ;
nextShapeEnd = objAttributes . num_face_num_verts ;
2024-12-01 23:10:59 +01:00
// See how many verts till the next shape
2024-08-25 09:49:52 -07:00
nextShape = 1 ;
if ( objShapeCount > 1 ) nextShapeEnd = objShapes [ nextShape ] . face_offset ;
lastMaterial = - 1 ;
meshIndex = 0 ;
localMeshVertexCount = 0 ;
2024-12-01 23:10:59 +01:00
// Walk all the faces
2024-08-25 09:49:52 -07:00
for ( unsigned int faceId = 0 ; faceId < objAttributes . num_faces ; faceId + + )
{
2024-12-01 23:10:59 +01:00
bool newMesh = false ; // Do we need a new mesh?
2024-11-15 16:40:14 +01:00
if ( faceId > = nextShapeEnd )
2024-08-25 09:49:52 -07:00
{
2024-12-01 23:10:59 +01:00
// Try to find the last vert in the next shape
2024-08-25 09:49:52 -07:00
nextShape + + ;
if ( nextShape < objShapeCount ) nextShapeEnd = objShapes [ nextShape ] . face_offset ;
2024-12-01 23:10:59 +01:00
else nextShapeEnd = objAttributes . num_face_num_verts ; // This is actually the total number of face verts in the file, not faces
2024-08-25 09:49:52 -07:00
newMesh = true ;
2014-04-19 16:36:49 +02:00
}
2024-12-03 19:14:14 +01:00
2024-12-01 23:10:59 +01:00
// If this is a new material, we need to allocate a new mesh
2024-08-25 09:49:52 -07:00
if ( lastMaterial ! = - 1 & & objAttributes . material_ids [ faceId ] ! = lastMaterial ) newMesh = true ;
2024-09-20 17:32:01 +02:00
lastMaterial = objAttributes . material_ids [ faceId ] ;
2019-03-29 17:15:22 +01:00
2024-08-25 09:49:52 -07:00
if ( newMesh )
{
localMeshVertexCount = 0 ;
meshIndex + + ;
}
2019-03-29 17:15:22 +01:00
2024-08-25 09:49:52 -07:00
int matId = 0 ;
2024-12-01 23:10:59 +01:00
if ( ( lastMaterial > = 0 ) & & ( lastMaterial < ( int ) objMaterialCount ) ) matId = lastMaterial ;
2020-11-03 23:47:33 +01:00
2024-08-25 09:49:52 -07:00
model . meshMaterial [ meshIndex ] = matId ;
2021-06-13 17:08:30 +02:00
2024-08-25 09:49:52 -07:00
for ( int f = 0 ; f < objAttributes . face_num_verts [ faceId ] ; f + + )
2021-04-09 10:57:14 -07:00
{
2024-08-25 09:49:52 -07:00
int vertIndex = objAttributes . faces [ faceVertIndex ] . v_idx ;
int normalIndex = objAttributes . faces [ faceVertIndex ] . vn_idx ;
int texcordIndex = objAttributes . faces [ faceVertIndex ] . vt_idx ;
2024-12-01 23:10:59 +01:00
for ( int i = 0 ; i < 3 ; i + + ) model . meshes [ meshIndex ] . vertices [ localMeshVertexCount * 3 + i ] = objAttributes . vertices [ vertIndex * 3 + i ] ;
2024-08-25 09:49:52 -07:00
2024-12-01 23:10:59 +01:00
for ( int i = 0 ; i < 3 ; i + + ) model . meshes [ meshIndex ] . normals [ localMeshVertexCount * 3 + i ] = objAttributes . normals [ normalIndex * 3 + i ] ;
2024-08-25 09:49:52 -07:00
2024-12-01 23:10:59 +01:00
for ( int i = 0 ; i < 2 ; i + + ) model . meshes [ meshIndex ] . texcoords [ localMeshVertexCount * 2 + i ] = objAttributes . texcoords [ texcordIndex * 2 + i ] ;
2024-08-25 09:49:52 -07:00
2024-12-01 23:10:59 +01:00
model . meshes [ meshIndex ] . texcoords [ localMeshVertexCount * 2 + 1 ] = 1.0f - model . meshes [ meshIndex ] . texcoords [ localMeshVertexCount * 2 + 1 ] ;
2024-08-25 09:49:52 -07:00
2024-12-01 23:10:59 +01:00
for ( int i = 0 ; i < 4 ; i + + ) model . meshes [ meshIndex ] . colors [ localMeshVertexCount * 4 + i ] = 255 ;
2024-08-25 09:49:52 -07:00
faceVertIndex + + ;
localMeshVertexCount + + ;
2021-04-09 10:57:14 -07:00
}
2014-04-19 16:36:49 +02:00
}
2014-09-03 16:51:28 +02:00
2024-08-25 09:49:52 -07:00
if ( objMaterialCount > 0 ) ProcessMaterialsOBJ ( model . materials , objMaterials , objMaterialCount ) ;
else model . materials [ 0 ] = LoadMaterialDefault ( ) ; // Set default material for the mesh
tinyobj_attrib_free ( & objAttributes ) ;
tinyobj_shapes_free ( objShapes , objShapeCount ) ;
tinyobj_materials_free ( objMaterials , objMaterialCount ) ;
2024-12-01 23:10:59 +01:00
for ( int i = 0 ; i < model . meshCount ; i + + ) UploadMesh ( model . meshes + i , true ) ;
2024-08-25 09:49:52 -07:00
// Restore current working directory
if ( CHDIR ( currentDir ) ! = 0 )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to change working directory " , currentDir ) ;
}
2019-03-29 16:48:23 +01:00
return model ;
2014-12-31 18:03:32 +01:00
}
2017-03-26 22:49:01 +02:00
# endif
2016-05-07 18:07:15 +02:00
2019-04-04 13:33:54 +02:00
# if defined(SUPPORT_FILEFORMAT_IQM)
2018-09-05 10:59:05 +02:00
// Load IQM mesh data
2019-03-29 16:48:23 +01:00
static Model LoadIQM ( const char * fileName )
2018-09-05 10:59:05 +02:00
{
2020-05-23 19:24:15 +02:00
# define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
# define IQM_VERSION 2 // only IQM version 2 supported
2019-04-04 13:33:54 +02:00
# define BONE_NAME_LENGTH 32 // BoneInfo name string length
# define MESH_NAME_LENGTH 32 // Mesh name string length
2020-05-08 20:27:10 +02:00
# define MATERIAL_NAME_LENGTH 32 // Material name string length
2020-11-03 23:47:33 +01:00
2023-09-02 12:05:34 +02:00
int dataSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
2020-05-23 19:24:15 +02:00
unsigned char * fileDataPtr = fileData ;
2019-04-04 13:33:54 +02:00
// IQM file structs
//-----------------------------------------------------------------------------------
typedef struct IQMHeader {
char magic [ 16 ] ;
unsigned int version ;
2023-09-02 12:05:34 +02:00
unsigned int dataSize ;
2019-04-04 13:33:54 +02:00
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 IQMMesh {
unsigned int name ;
unsigned int material ;
unsigned int first_vertex , num_vertexes ;
unsigned int first_triangle , num_triangles ;
} IQMMesh ;
typedef struct IQMTriangle {
unsigned int vertex [ 3 ] ;
} IQMTriangle ;
2019-09-13 12:29:52 +01:00
2019-04-04 13:33:54 +02:00
typedef struct IQMJoint {
unsigned int name ;
int parent ;
float translate [ 3 ] , rotate [ 4 ] , scale [ 3 ] ;
} IQMJoint ;
2019-09-13 12:29:52 +01:00
2019-04-23 14:55:35 +02:00
typedef struct IQMVertexArray {
unsigned int type ;
unsigned int flags ;
unsigned int format ;
unsigned int size ;
unsigned int offset ;
} IQMVertexArray ;
// NOTE: Below IQM structures are not used but listed for reference
/*
typedef struct IQMAdjacency {
unsigned int triangle [ 3 ] ;
} IQMAdjacency ;
2019-04-04 13:33:54 +02:00
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 ;
typedef struct IQMBounds {
float bbmin [ 3 ] , bbmax [ 3 ] ;
float xyradius , radius ;
} IQMBounds ;
2019-04-23 14:55:35 +02:00
*/
2019-04-04 13:33:54 +02:00
//-----------------------------------------------------------------------------------
// IQM vertex data types
2020-01-24 18:23:36 +01:00
enum {
2019-04-04 13:33:54 +02:00
IQM_POSITION = 0 ,
IQM_TEXCOORD = 1 ,
IQM_NORMAL = 2 ,
IQM_TANGENT = 3 , // NOTE: Tangents unused by default
IQM_BLENDINDEXES = 4 ,
IQM_BLENDWEIGHTS = 5 ,
2021-05-26 21:23:13 +03:00
IQM_COLOR = 6 ,
2019-04-04 13:33:54 +02:00
IQM_CUSTOM = 0x10 // NOTE: Custom vertex values unused by default
2020-01-24 18:23:36 +01:00
} ;
2019-04-04 13:33:54 +02:00
2019-03-29 16:48:23 +01:00
Model model = { 0 } ;
2019-02-21 18:45:19 +01:00
2020-05-23 19:24:15 +02:00
IQMMesh * imesh = NULL ;
IQMTriangle * tri = NULL ;
IQMVertexArray * va = NULL ;
IQMJoint * ijoint = NULL ;
2019-04-04 13:33:54 +02:00
float * vertex = NULL ;
float * normal = NULL ;
float * text = NULL ;
char * blendi = NULL ;
unsigned char * blendw = NULL ;
2021-05-26 21:23:13 +03:00
unsigned char * color = NULL ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
// In case file can not be read, return an empty model
if ( fileDataPtr = = NULL ) return model ;
2019-04-04 13:33:54 +02:00
2024-07-08 22:54:19 +02:00
const char * basePath = GetDirectoryPath ( fileName ) ;
2024-06-03 03:03:33 -04:00
2020-05-23 19:24:15 +02:00
// Read IQM header
IQMHeader * iqmHeader = ( IQMHeader * ) fileDataPtr ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
if ( memcmp ( iqmHeader - > magic , IQM_MAGIC , sizeof ( IQM_MAGIC ) ) ! = 0 )
2019-04-04 13:33:54 +02:00
{
2020-03-27 17:43:51 +01:00
TRACELOG ( LOG_WARNING , " MODEL: [%s] IQM file is not a valid model " , fileName ) ;
2019-04-04 13:33:54 +02:00
return model ;
}
2020-05-23 19:24:15 +02:00
if ( iqmHeader - > version ! = IQM_VERSION )
2019-04-04 13:33:54 +02:00
{
2020-05-23 19:24:15 +02:00
TRACELOG ( LOG_WARNING , " MODEL: [%s] IQM file version not supported (%i) " , fileName , iqmHeader - > version ) ;
2019-04-04 13:33:54 +02:00
return model ;
}
2020-11-03 23:47:33 +01:00
2020-05-23 19:24:15 +02:00
//fileDataPtr += sizeof(IQMHeader); // Move file data pointer
2019-04-04 13:33:54 +02:00
// Meshes data processing
2021-05-22 16:54:04 +02:00
imesh = RL_MALLOC ( iqmHeader - > num_meshes * sizeof ( IQMMesh ) ) ;
2020-05-23 19:24:15 +02:00
//fseek(iqmFile, iqmHeader->ofs_meshes, SEEK_SET);
//fread(imesh, sizeof(IQMMesh)*iqmHeader->num_meshes, 1, iqmFile);
memcpy ( imesh , fileDataPtr + iqmHeader - > ofs_meshes , iqmHeader - > num_meshes * sizeof ( IQMMesh ) ) ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
model . meshCount = iqmHeader - > num_meshes ;
2019-05-22 20:27:19 +02:00
model . meshes = RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
2019-04-04 13:50:52 +02:00
2020-05-08 20:27:10 +02:00
model . materialCount = model . meshCount ;
model . materials = ( Material * ) RL_CALLOC ( model . materialCount , sizeof ( Material ) ) ;
model . meshMaterial = ( int * ) RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2019-08-27 11:16:43 +02:00
char name [ MESH_NAME_LENGTH ] = { 0 } ;
2020-05-08 20:27:10 +02:00
char material [ MATERIAL_NAME_LENGTH ] = { 0 } ;
2019-04-04 13:33:54 +02:00
2019-04-09 13:23:51 +02:00
for ( int i = 0 ; i < model . meshCount ; i + + )
2019-04-04 13:33:54 +02:00
{
2020-05-23 19:24:15 +02:00
//fseek(iqmFile, iqmHeader->ofs_text + imesh[i].name, SEEK_SET);
2023-02-25 01:07:52 +01:00
//fread(name, sizeof(char), MESH_NAME_LENGTH, iqmFile);
2020-05-23 19:24:15 +02:00
memcpy ( name , fileDataPtr + iqmHeader - > ofs_text + imesh [ i ] . name , MESH_NAME_LENGTH * sizeof ( char ) ) ;
2020-05-08 20:27:10 +02:00
2020-05-23 19:24:15 +02:00
//fseek(iqmFile, iqmHeader->ofs_text + imesh[i].material, SEEK_SET);
2023-02-25 01:07:52 +01:00
//fread(material, sizeof(char), MATERIAL_NAME_LENGTH, iqmFile);
2020-05-23 19:24:15 +02:00
memcpy ( material , fileDataPtr + iqmHeader - > ofs_text + imesh [ i ] . material , MATERIAL_NAME_LENGTH * sizeof ( char ) ) ;
2020-05-08 20:27:10 +02:00
model . materials [ i ] = LoadMaterialDefault ( ) ;
2024-06-03 03:03:33 -04:00
model . materials [ i ] . maps [ MATERIAL_MAP_ALBEDO ] . texture = LoadTexture ( TextFormat ( " %s/%s " , basePath , material ) ) ;
2020-05-08 20:27:10 +02:00
2024-06-23 21:14:03 +02:00
model . meshMaterial [ i ] = i ;
2024-06-30 11:07:38 +02:00
2020-05-08 20:27:10 +02:00
TRACELOG ( LOG_DEBUG , " MODEL: [%s] mesh name (%s), material (%s) " , fileName , name , material ) ;
2019-04-04 13:33:54 +02:00
model . meshes [ i ] . vertexCount = imesh [ i ] . num_vertexes ;
2019-04-04 13:50:52 +02:00
2019-08-27 11:16:43 +02:00
model . meshes [ i ] . vertices = RL_CALLOC ( model . meshes [ i ] . vertexCount * 3 , sizeof ( float ) ) ; // Default vertex positions
model . meshes [ i ] . normals = RL_CALLOC ( model . meshes [ i ] . vertexCount * 3 , sizeof ( float ) ) ; // Default vertex normals
model . meshes [ i ] . texcoords = RL_CALLOC ( model . meshes [ i ] . vertexCount * 2 , sizeof ( float ) ) ; // Default vertex texcoords
2019-04-04 13:50:52 +02:00
2021-10-23 13:59:20 +02:00
model . meshes [ i ] . boneIds = RL_CALLOC ( model . meshes [ i ] . vertexCount * 4 , sizeof ( unsigned char ) ) ; // Up-to 4 bones supported!
model . meshes [ i ] . boneWeights = RL_CALLOC ( model . meshes [ i ] . vertexCount * 4 , sizeof ( float ) ) ; // Up-to 4 bones supported!
2019-04-04 13:50:52 +02:00
2019-04-04 13:33:54 +02:00
model . meshes [ i ] . triangleCount = imesh [ i ] . num_triangles ;
2019-08-27 11:16:43 +02:00
model . meshes [ i ] . indices = RL_CALLOC ( model . meshes [ i ] . triangleCount * 3 , sizeof ( unsigned short ) ) ;
2019-04-04 13:50:52 +02:00
2023-08-09 18:21:14 -04:00
// Animated vertex data, what we actually process for rendering
2019-04-04 13:33:54 +02:00
// NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning)
2019-08-27 11:16:43 +02:00
model . meshes [ i ] . animVertices = RL_CALLOC ( model . meshes [ i ] . vertexCount * 3 , sizeof ( float ) ) ;
model . meshes [ i ] . animNormals = RL_CALLOC ( model . meshes [ i ] . vertexCount * 3 , sizeof ( float ) ) ;
2019-04-04 13:33:54 +02:00
}
// Triangles data processing
2020-05-23 19:24:15 +02:00
tri = RL_MALLOC ( iqmHeader - > num_triangles * sizeof ( IQMTriangle ) ) ;
//fseek(iqmFile, iqmHeader->ofs_triangles, SEEK_SET);
2023-02-25 01:07:52 +01:00
//fread(tri, sizeof(IQMTriangle), iqmHeader->num_triangles, iqmFile);
2020-05-23 19:24:15 +02:00
memcpy ( tri , fileDataPtr + iqmHeader - > ofs_triangles , iqmHeader - > num_triangles * sizeof ( IQMTriangle ) ) ;
2019-04-04 13:33:54 +02:00
2019-04-09 13:23:51 +02:00
for ( int m = 0 ; m < model . meshCount ; m + + )
2019-04-04 13:33:54 +02:00
{
int tcounter = 0 ;
2020-05-06 19:12:09 +02:00
for ( unsigned int i = imesh [ m ] . first_triangle ; i < ( imesh [ m ] . first_triangle + imesh [ m ] . num_triangles ) ; i + + )
2019-04-04 13:33:54 +02:00
{
2020-07-17 18:22:09 +02:00
// IQM triangles indexes are stored in counter-clockwise, but raylib processes the index in linear order,
2020-11-03 23:47:33 +01:00
// expecting they point to the counter-clockwise vertex triangle, so we need to reverse triangle indexes
2020-07-17 18:22:09 +02:00
// NOTE: raylib renders vertex data in counter-clockwise order (standard convention) by default
2019-04-09 13:23:51 +02:00
model . meshes [ m ] . indices [ tcounter + 2 ] = tri [ i ] . vertex [ 0 ] - imesh [ m ] . first_vertex ;
model . meshes [ m ] . indices [ tcounter + 1 ] = tri [ i ] . vertex [ 1 ] - imesh [ m ] . first_vertex ;
2019-04-04 13:33:54 +02:00
model . meshes [ m ] . indices [ tcounter ] = tri [ i ] . vertex [ 2 ] - imesh [ m ] . first_vertex ;
tcounter + = 3 ;
}
}
// Vertex arrays data processing
2020-05-23 19:24:15 +02:00
va = RL_MALLOC ( iqmHeader - > num_vertexarrays * sizeof ( IQMVertexArray ) ) ;
//fseek(iqmFile, iqmHeader->ofs_vertexarrays, SEEK_SET);
2023-02-25 01:07:52 +01:00
//fread(va, sizeof(IQMVertexArray), iqmHeader->num_vertexarrays, iqmFile);
2020-05-23 19:24:15 +02:00
memcpy ( va , fileDataPtr + iqmHeader - > ofs_vertexarrays , iqmHeader - > num_vertexarrays * sizeof ( IQMVertexArray ) ) ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
for ( unsigned int i = 0 ; i < iqmHeader - > num_vertexarrays ; i + + )
2019-04-04 13:33:54 +02:00
{
switch ( va [ i ] . type )
{
case IQM_POSITION :
{
2020-05-23 19:24:15 +02:00
vertex = RL_MALLOC ( iqmHeader - > num_vertexes * 3 * sizeof ( float ) ) ;
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(vertex, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
memcpy ( vertex , fileDataPtr + va [ i ] . offset , iqmHeader - > num_vertexes * 3 * sizeof ( float ) ) ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
for ( unsigned int m = 0 ; m < iqmHeader - > num_meshes ; m + + )
2019-04-04 13:33:54 +02:00
{
int vCounter = 0 ;
2020-05-06 19:12:09 +02:00
for ( unsigned int i = imesh [ m ] . first_vertex * 3 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 3 ; i + + )
2019-04-04 13:33:54 +02:00
{
model . meshes [ m ] . vertices [ vCounter ] = vertex [ i ] ;
model . meshes [ m ] . animVertices [ vCounter ] = vertex [ i ] ;
vCounter + + ;
}
}
} break ;
case IQM_NORMAL :
{
2020-05-23 19:24:15 +02:00
normal = RL_MALLOC ( iqmHeader - > num_vertexes * 3 * sizeof ( float ) ) ;
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(normal, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
memcpy ( normal , fileDataPtr + va [ i ] . offset , iqmHeader - > num_vertexes * 3 * sizeof ( float ) ) ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
for ( unsigned int m = 0 ; m < iqmHeader - > num_meshes ; m + + )
2019-04-04 13:33:54 +02:00
{
int vCounter = 0 ;
2020-05-06 19:12:09 +02:00
for ( unsigned int i = imesh [ m ] . first_vertex * 3 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 3 ; i + + )
2019-04-04 13:33:54 +02:00
{
model . meshes [ m ] . normals [ vCounter ] = normal [ i ] ;
model . meshes [ m ] . animNormals [ vCounter ] = normal [ i ] ;
vCounter + + ;
}
}
} break ;
case IQM_TEXCOORD :
{
2020-05-23 19:24:15 +02:00
text = RL_MALLOC ( iqmHeader - > num_vertexes * 2 * sizeof ( float ) ) ;
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(text, iqmHeader->num_vertexes*2*sizeof(float), 1, iqmFile);
memcpy ( text , fileDataPtr + va [ i ] . offset , iqmHeader - > num_vertexes * 2 * sizeof ( float ) ) ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
for ( unsigned int m = 0 ; m < iqmHeader - > num_meshes ; m + + )
2019-04-04 13:33:54 +02:00
{
int vCounter = 0 ;
2020-05-06 19:12:09 +02:00
for ( unsigned int i = imesh [ m ] . first_vertex * 2 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 2 ; i + + )
2019-04-04 13:33:54 +02:00
{
model . meshes [ m ] . texcoords [ vCounter ] = text [ i ] ;
vCounter + + ;
}
}
} break ;
case IQM_BLENDINDEXES :
{
2020-05-23 19:24:15 +02:00
blendi = RL_MALLOC ( iqmHeader - > num_vertexes * 4 * sizeof ( char ) ) ;
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(blendi, iqmHeader->num_vertexes*4*sizeof(char), 1, iqmFile);
memcpy ( blendi , fileDataPtr + va [ i ] . offset , iqmHeader - > num_vertexes * 4 * sizeof ( char ) ) ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
for ( unsigned int m = 0 ; m < iqmHeader - > num_meshes ; m + + )
2019-04-04 13:33:54 +02:00
{
int boneCounter = 0 ;
2020-05-06 19:12:09 +02:00
for ( unsigned int i = imesh [ m ] . first_vertex * 4 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 4 ; i + + )
2019-04-04 13:33:54 +02:00
{
model . meshes [ m ] . boneIds [ boneCounter ] = blendi [ i ] ;
boneCounter + + ;
}
}
} break ;
case IQM_BLENDWEIGHTS :
{
2020-05-23 19:24:15 +02:00
blendw = RL_MALLOC ( iqmHeader - > num_vertexes * 4 * sizeof ( unsigned char ) ) ;
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
memcpy ( blendw , fileDataPtr + va [ i ] . offset , iqmHeader - > num_vertexes * 4 * sizeof ( unsigned char ) ) ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
for ( unsigned int m = 0 ; m < iqmHeader - > num_meshes ; m + + )
2019-04-04 13:33:54 +02:00
{
int boneCounter = 0 ;
2020-05-06 19:12:09 +02:00
for ( unsigned int i = imesh [ m ] . first_vertex * 4 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 4 ; i + + )
2019-04-04 13:33:54 +02:00
{
model . meshes [ m ] . boneWeights [ boneCounter ] = blendw [ i ] / 255.0f ;
boneCounter + + ;
}
}
} break ;
2021-05-26 21:23:13 +03:00
case IQM_COLOR :
{
color = RL_MALLOC ( iqmHeader - > num_vertexes * 4 * sizeof ( unsigned char ) ) ;
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
memcpy ( color , fileDataPtr + va [ i ] . offset , iqmHeader - > num_vertexes * 4 * sizeof ( unsigned char ) ) ;
2021-06-13 17:08:30 +02:00
2021-05-26 21:23:13 +03:00
for ( unsigned int m = 0 ; m < iqmHeader - > num_meshes ; m + + )
{
model . meshes [ m ] . colors = RL_CALLOC ( model . meshes [ m ] . vertexCount * 4 , sizeof ( unsigned char ) ) ;
2021-06-13 17:08:30 +02:00
2021-05-26 21:23:13 +03:00
int vCounter = 0 ;
for ( unsigned int i = imesh [ m ] . first_vertex * 4 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 4 ; i + + )
{
model . meshes [ m ] . colors [ vCounter ] = color [ i ] ;
vCounter + + ;
}
}
} break ;
2019-04-04 13:33:54 +02:00
}
}
// Bones (joints) data processing
2020-05-23 19:24:15 +02:00
ijoint = RL_MALLOC ( iqmHeader - > num_joints * sizeof ( IQMJoint ) ) ;
//fseek(iqmFile, iqmHeader->ofs_joints, SEEK_SET);
2023-02-25 01:07:52 +01:00
//fread(ijoint, sizeof(IQMJoint), iqmHeader->num_joints, iqmFile);
2020-05-23 19:24:15 +02:00
memcpy ( ijoint , fileDataPtr + iqmHeader - > ofs_joints , iqmHeader - > num_joints * sizeof ( IQMJoint ) ) ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
model . boneCount = iqmHeader - > num_joints ;
model . bones = RL_MALLOC ( iqmHeader - > num_joints * sizeof ( BoneInfo ) ) ;
model . bindPose = RL_MALLOC ( iqmHeader - > num_joints * sizeof ( Transform ) ) ;
2019-04-04 13:33:54 +02:00
2020-05-23 19:24:15 +02:00
for ( unsigned int i = 0 ; i < iqmHeader - > num_joints ; i + + )
2019-04-04 13:33:54 +02:00
{
// Bones
model . bones [ i ] . parent = ijoint [ i ] . parent ;
2020-05-23 19:24:15 +02:00
//fseek(iqmFile, iqmHeader->ofs_text + ijoint[i].name, SEEK_SET);
2023-02-25 01:07:52 +01:00
//fread(model.bones[i].name, sizeof(char), BONE_NAME_LENGTH, iqmFile);
2020-05-23 19:24:15 +02:00
memcpy ( model . bones [ i ] . name , fileDataPtr + iqmHeader - > ofs_text + ijoint [ i ] . name , BONE_NAME_LENGTH * sizeof ( char ) ) ;
2019-04-04 13:33:54 +02:00
// Bind pose (base pose)
model . bindPose [ i ] . translation . x = ijoint [ i ] . translate [ 0 ] ;
model . bindPose [ i ] . translation . y = ijoint [ i ] . translate [ 1 ] ;
model . bindPose [ i ] . translation . z = ijoint [ i ] . translate [ 2 ] ;
model . bindPose [ i ] . rotation . x = ijoint [ i ] . rotate [ 0 ] ;
model . bindPose [ i ] . rotation . y = ijoint [ i ] . rotate [ 1 ] ;
model . bindPose [ i ] . rotation . z = ijoint [ i ] . rotate [ 2 ] ;
model . bindPose [ i ] . rotation . w = ijoint [ i ] . rotate [ 3 ] ;
model . bindPose [ i ] . scale . x = ijoint [ i ] . scale [ 0 ] ;
model . bindPose [ i ] . scale . y = ijoint [ i ] . scale [ 1 ] ;
model . bindPose [ i ] . scale . z = ijoint [ i ] . scale [ 2 ] ;
}
2023-01-02 14:23:48 -05:00
BuildPoseFromParentJoints ( model . bones , model . boneCount , model . bindPose ) ;
2024-10-21 17:00:52 +02:00
2024-09-20 11:30:37 -04:00
for ( int i = 0 ; i < model . meshCount ; i + + )
{
model . meshes [ i ] . boneCount = model . boneCount ;
model . meshes [ i ] . boneMatrices = RL_CALLOC ( model . meshes [ i ] . boneCount , sizeof ( Matrix ) ) ;
2024-10-21 17:00:52 +02:00
2024-09-20 11:30:37 -04:00
for ( int j = 0 ; j < model . meshes [ i ] . boneCount ; j + + )
{
model . meshes [ i ] . boneMatrices [ j ] = MatrixIdentity ( ) ;
}
}
2019-04-04 13:33:54 +02:00
2024-04-20 13:53:13 +02:00
UnloadFileData ( fileData ) ;
2020-11-03 23:47:33 +01:00
2019-04-23 14:55:35 +02:00
RL_FREE ( imesh ) ;
RL_FREE ( tri ) ;
RL_FREE ( va ) ;
RL_FREE ( vertex ) ;
RL_FREE ( normal ) ;
RL_FREE ( text ) ;
RL_FREE ( blendi ) ;
RL_FREE ( blendw ) ;
RL_FREE ( ijoint ) ;
2022-09-01 10:14:45 +02:00
RL_FREE ( color ) ;
2019-02-21 18:45:19 +01:00
2019-03-29 16:48:23 +01:00
return model ;
2019-02-21 18:45:19 +01:00
}
2021-01-28 12:29:06 +02:00
// Load IQM animation data
2023-09-02 12:05:34 +02:00
static ModelAnimation * LoadModelAnimationsIQM ( const char * fileName , int * animCount )
2021-01-28 12:29:06 +02:00
{
2021-10-12 21:01:49 +02:00
# define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
# define IQM_VERSION 2 // only IQM version 2 supported
2021-03-19 19:43:44 +01:00
2023-09-02 12:05:34 +02:00
int dataSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
2021-01-28 12:29:06 +02:00
unsigned char * fileDataPtr = fileData ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
typedef struct IQMHeader {
char magic [ 16 ] ;
unsigned int version ;
2023-09-02 12:05:34 +02:00
unsigned int dataSize ;
2021-01-28 12:29:06 +02:00
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 ;
2021-03-19 19:43:44 +01:00
2023-01-23 13:55:02 -05:00
typedef struct IQMJoint {
unsigned int name ;
int parent ;
float translate [ 3 ] , rotate [ 4 ] , scale [ 3 ] ;
} IQMJoint ;
2021-01-28 12:29:06 +02:00
typedef struct IQMPose {
int parent ;
unsigned int mask ;
float channeloffset [ 10 ] ;
float channelscale [ 10 ] ;
} IQMPose ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
typedef struct IQMAnim {
unsigned int name ;
unsigned int first_frame , num_frames ;
float framerate ;
unsigned int flags ;
} IQMAnim ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
// In case file can not be read, return an empty model
if ( fileDataPtr = = NULL ) return NULL ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
// Read IQM header
IQMHeader * iqmHeader = ( IQMHeader * ) fileDataPtr ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
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 ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( iqmHeader - > version ! = IQM_VERSION )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] IQM file version not supported (%i) " , fileName , iqmHeader - > version ) ;
return NULL ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
// Get bones data
IQMPose * poses = RL_MALLOC ( iqmHeader - > num_poses * sizeof ( IQMPose ) ) ;
//fseek(iqmFile, iqmHeader->ofs_poses, SEEK_SET);
2023-02-25 01:07:52 +01:00
//fread(poses, sizeof(IQMPose), iqmHeader->num_poses, iqmFile);
2021-01-28 12:29:06 +02:00
memcpy ( poses , fileDataPtr + iqmHeader - > ofs_poses , iqmHeader - > num_poses * sizeof ( IQMPose ) ) ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
// Get animations data
* animCount = iqmHeader - > num_anims ;
IQMAnim * anim = RL_MALLOC ( iqmHeader - > num_anims * sizeof ( IQMAnim ) ) ;
//fseek(iqmFile, iqmHeader->ofs_anims, SEEK_SET);
2023-02-25 01:07:52 +01:00
//fread(anim, sizeof(IQMAnim), iqmHeader->num_anims, iqmFile);
2021-01-28 12:29:06 +02:00
memcpy ( anim , fileDataPtr + iqmHeader - > ofs_anims , iqmHeader - > num_anims * sizeof ( IQMAnim ) ) ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
ModelAnimation * animations = RL_MALLOC ( iqmHeader - > num_anims * sizeof ( ModelAnimation ) ) ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
// frameposes
unsigned short * framedata = RL_MALLOC ( iqmHeader - > num_frames * iqmHeader - > num_framechannels * sizeof ( unsigned short ) ) ;
//fseek(iqmFile, iqmHeader->ofs_frames, SEEK_SET);
2023-02-25 01:07:52 +01:00
//fread(framedata, sizeof(unsigned short), iqmHeader->num_frames*iqmHeader->num_framechannels, iqmFile);
2021-01-28 12:29:06 +02:00
memcpy ( framedata , fileDataPtr + iqmHeader - > ofs_frames , iqmHeader - > num_frames * iqmHeader - > num_framechannels * sizeof ( unsigned short ) ) ;
2021-03-19 19:43:44 +01:00
2023-01-23 13:55:02 -05:00
// joints
IQMJoint * joints = RL_MALLOC ( iqmHeader - > num_joints * sizeof ( IQMJoint ) ) ;
memcpy ( joints , fileDataPtr + iqmHeader - > ofs_joints , iqmHeader - > num_joints * sizeof ( IQMJoint ) ) ;
2021-01-28 12:29:06 +02:00
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 * ) ) ;
2024-06-30 11:07:38 +02:00
memcpy ( animations [ a ] . name , fileDataPtr + iqmHeader - > ofs_text + anim [ a ] . name , 32 ) ; // I don't like this 32 here
2024-06-02 05:51:25 -04:00
TraceLog ( LOG_INFO , " IQM Anim %s " , animations [ a ] . name ) ;
2023-04-22 21:17:53 +02:00
// animations[a].framerate = anim.framerate; // TODO: Use animation framerate data?
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
for ( unsigned int j = 0 ; j < iqmHeader - > num_poses ; j + + )
{
2023-01-23 13:55:02 -05:00
// If animations and skeleton are in the same file, copy bone names to anim
if ( iqmHeader - > num_joints > 0 )
memcpy ( animations [ a ] . bones [ j ] . name , fileDataPtr + iqmHeader - > ofs_text + joints [ j ] . name , BONE_NAME_LENGTH * sizeof ( char ) ) ;
else
strcpy ( animations [ a ] . bones [ j ] . name , " ANIMJOINTNAME " ) ; // default bone name otherwise
2021-01-28 12:29:06 +02:00
animations [ a ] . bones [ j ] . parent = poses [ j ] . parent ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
for ( unsigned int j = 0 ; j < anim [ a ] . num_frames ; j + + ) animations [ a ] . framePoses [ j ] = RL_MALLOC ( iqmHeader - > num_poses * sizeof ( Transform ) ) ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
int dcounter = anim [ a ] . first_frame * iqmHeader - > num_framechannels ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
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 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x01 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 0 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . translation . y = poses [ i ] . channeloffset [ 1 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x02 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 1 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . translation . z = poses [ i ] . channeloffset [ 2 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x04 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 2 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation . x = poses [ i ] . channeloffset [ 3 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x08 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 3 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation . y = poses [ i ] . channeloffset [ 4 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x10 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 4 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation . z = poses [ i ] . channeloffset [ 5 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x20 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 5 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation . w = poses [ i ] . channeloffset [ 6 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x40 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . w + = framedata [ dcounter ] * poses [ i ] . channelscale [ 6 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . scale . x = poses [ i ] . channeloffset [ 7 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x80 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 7 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . scale . y = poses [ i ] . channeloffset [ 8 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x100 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 8 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . scale . z = poses [ i ] . channeloffset [ 9 ] ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( poses [ i ] . mask & 0x200 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 9 ] ;
dcounter + + ;
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation = QuaternionNormalize ( animations [ a ] . framePoses [ frame ] [ i ] . rotation ) ;
}
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
// 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 ) ;
}
}
}
}
2021-03-19 19:43:44 +01:00
2024-04-20 13:53:13 +02:00
UnloadFileData ( fileData ) ;
2021-03-19 19:43:44 +01:00
2023-01-23 13:55:02 -05:00
RL_FREE ( joints ) ;
2021-01-28 12:29:06 +02:00
RL_FREE ( framedata ) ;
RL_FREE ( poses ) ;
RL_FREE ( anim ) ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
return animations ;
}
2018-09-05 10:59:05 +02:00
# endif
# if defined(SUPPORT_FILEFORMAT_GLTF)
2023-12-19 02:38:57 -07:00
// Load file data callback for cgltf
static cgltf_result LoadFileGLTFCallback ( const struct cgltf_memory_options * memoryOptions , const struct cgltf_file_options * fileOptions , const char * path , cgltf_size * size , void * * data )
{
int filesize ;
unsigned char * filedata = LoadFileData ( path , & filesize ) ;
if ( filedata = = NULL ) return cgltf_result_io_error ;
* size = filesize ;
* data = filedata ;
return cgltf_result_success ;
}
// Release file data callback for cgltf
2024-04-19 21:13:52 +02:00
static void ReleaseFileGLTFCallback ( const struct cgltf_memory_options * memoryOptions , const struct cgltf_file_options * fileOptions , void * data )
{
2023-12-19 02:38:57 -07:00
UnloadFileData ( data ) ;
}
2021-10-22 21:27:11 +02:00
// Load image from different glTF provided methods (uri, path, buffer_view)
static Image LoadImageFromCgltfImage ( cgltf_image * cgltfImage , const char * texPath )
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
Image image = { 0 } ;
2021-10-22 14:53:11 +02:00
2024-12-02 07:35:54 -05:00
if ( cgltfImage = = NULL ) return image ;
2023-02-09 07:17:47 -05:00
if ( cgltfImage - > uri ! = NULL ) // Check if image data is provided as an uri (base64 or path)
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
if ( ( strlen ( cgltfImage - > uri ) > 5 ) & &
( cgltfImage - > uri [ 0 ] = = ' d ' ) & &
( cgltfImage - > uri [ 1 ] = = ' a ' ) & &
( cgltfImage - > uri [ 2 ] = = ' t ' ) & &
( cgltfImage - > uri [ 3 ] = = ' a ' ) & &
( cgltfImage - > uri [ 4 ] = = ' : ' ) ) // Check if image is provided as base64 text data
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
// Data URI Format: data:<mediatype>;base64,<data>
2021-10-22 14:53:11 +02:00
// Find the comma
int i = 0 ;
2021-10-22 21:27:11 +02:00
while ( ( cgltfImage - > uri [ i ] ! = ' , ' ) & & ( cgltfImage - > uri [ i ] ! = 0 ) ) i + + ;
2021-10-22 14:53:11 +02:00
2021-10-22 21:27:11 +02:00
if ( cgltfImage - > uri [ i ] = = 0 ) TRACELOG ( LOG_WARNING , " IMAGE: glTF data URI is not a valid image " ) ;
2021-10-22 14:53:11 +02:00
else
{
2021-10-25 01:18:42 -07:00
int base64Size = ( int ) strlen ( cgltfImage - > uri + i + 1 ) ;
2024-06-28 09:56:27 +02:00
while ( cgltfImage - > uri [ i + base64Size ] = = ' = ' ) base64Size - - ; // Ignore optional paddings
int numberOfEncodedBits = base64Size * 6 - ( base64Size * 6 ) % 8 ; // Encoded bits minus extra bits, so it becomes a multiple of 8 bits
int outSize = numberOfEncodedBits / 8 ; // Actual encoded bytes
2021-10-26 22:26:28 +02:00
void * data = NULL ;
2021-10-22 14:53:11 +02:00
cgltf_options options = { 0 } ;
2023-12-23 14:07:45 +01:00
options . file . read = LoadFileGLTFCallback ;
options . file . release = ReleaseFileGLTFCallback ;
2021-10-22 21:27:11 +02:00
cgltf_result result = cgltf_load_buffer_base64 ( & options , outSize , cgltfImage - > uri + i + 1 , & data ) ;
2022-02-18 20:30:46 +01:00
2021-10-22 14:53:11 +02:00
if ( result = = cgltf_result_success )
{
2021-10-26 22:26:28 +02:00
image = LoadImageFromMemory ( " .png " , ( unsigned char * ) data , outSize ) ;
2023-09-02 12:58:47 +02:00
RL_FREE ( data ) ;
2021-10-22 14:53:11 +02:00
}
}
}
2021-10-22 21:27:11 +02:00
else // Check if image is provided as image path
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
image = LoadImage ( TextFormat ( " %s/%s " , texPath , cgltfImage - > uri ) ) ;
2021-10-22 14:53:11 +02:00
}
}
2024-12-01 16:46:26 -05:00
else if ( cgltfImage - > buffer_view ! = NULL & & cgltfImage - > buffer_view - > buffer - > data ! = NULL ) // Check if image is provided as data buffer
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
unsigned char * data = RL_MALLOC ( cgltfImage - > buffer_view - > size ) ;
int offset = ( int ) cgltfImage - > buffer_view - > offset ;
int stride = ( int ) cgltfImage - > buffer_view - > stride ? ( int ) cgltfImage - > buffer_view - > stride : 1 ;
2021-10-22 14:53:11 +02:00
2021-10-22 21:27:11 +02:00
// Copy buffer data to memory for loading
for ( unsigned int i = 0 ; i < cgltfImage - > buffer_view - > size ; i + + )
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
data [ i ] = ( ( unsigned char * ) cgltfImage - > buffer_view - > buffer - > data ) [ offset ] ;
offset + = stride ;
2021-10-22 14:53:11 +02:00
}
2021-10-23 11:42:02 +02:00
// Check mime_type for image: (cgltfImage->mime_type == "image/png")
// NOTE: Detected that some models define mime_type as "image\\/png"
2022-02-18 20:30:46 +01:00
if ( ( strcmp ( cgltfImage - > mime_type , " image \\ /png " ) = = 0 ) | |
2021-10-23 11:42:02 +02:00
( strcmp ( cgltfImage - > mime_type , " image/png " ) = = 0 ) ) image = LoadImageFromMemory ( " .png " , data , ( int ) cgltfImage - > buffer_view - > size ) ;
else if ( ( strcmp ( cgltfImage - > mime_type , " image \\ /jpeg " ) = = 0 ) | |
( strcmp ( cgltfImage - > mime_type , " image/jpeg " ) = = 0 ) ) image = LoadImageFromMemory ( " .jpg " , data , ( int ) cgltfImage - > buffer_view - > size ) ;
2021-10-22 21:27:11 +02:00
else TRACELOG ( LOG_WARNING , " MODEL: glTF image data MIME type not recognized " , TextFormat ( " %s/%s " , texPath , cgltfImage - > uri ) ) ;
2022-02-18 20:30:46 +01:00
2021-10-22 14:53:11 +02:00
RL_FREE ( data ) ;
}
2021-10-22 21:27:11 +02:00
return image ;
2021-10-22 14:53:11 +02:00
}
2023-01-02 20:46:33 +01:00
// Load bone info from GLTF skin data
static BoneInfo * LoadBoneInfoGLTF ( cgltf_skin skin , int * boneCount )
2023-01-02 14:23:48 -05:00
{
2023-01-20 07:05:19 -08:00
* boneCount = ( int ) skin . joints_count ;
2023-01-02 14:23:48 -05:00
BoneInfo * bones = RL_MALLOC ( skin . joints_count * sizeof ( BoneInfo ) ) ;
for ( unsigned int i = 0 ; i < skin . joints_count ; i + + )
{
cgltf_node node = * skin . joints [ i ] ;
2024-06-24 08:29:10 +01:00
if ( node . name ! = NULL )
{
strncpy ( bones [ i ] . name , node . name , sizeof ( bones [ i ] . name ) ) ;
bones [ i ] . name [ sizeof ( bones [ i ] . name ) - 1 ] = ' \0 ' ;
}
2023-01-02 14:23:48 -05:00
2023-01-02 20:46:33 +01:00
// Find parent bone index
2024-06-28 09:53:29 +02:00
int parentIndex = - 1 ;
2023-02-05 16:30:23 +01:00
2023-01-02 14:23:48 -05:00
for ( unsigned int j = 0 ; j < skin . joints_count ; j + + )
{
if ( skin . joints [ j ] = = node . parent )
{
2024-06-28 09:53:29 +02:00
parentIndex = ( int ) j ;
2023-01-02 14:23:48 -05:00
break ;
}
}
bones [ i ] . parent = parentIndex ;
}
return bones ;
}
2021-10-22 21:27:11 +02:00
// Load glTF file into model struct, .gltf and .glb supported
2021-06-26 14:22:00 +03:00
static Model LoadGLTF ( const char * fileName )
{
2021-10-22 21:27:11 +02:00
/*********************************************************************************************
2021-03-19 19:43:44 +01:00
2021-10-22 14:53:11 +02:00
Function implemented by Wilhem Barbier ( @ wbrbr ) , with modifications by Tyler Bezera ( @ gamerfiend )
2024-06-22 19:42:11 +02:00
Transform handling implemented by Paul Melis ( @ paulmelis ) .
2021-10-22 21:27:11 +02:00
Reviewed by Ramon Santamaria ( @ raysan5 )
2021-03-19 19:43:44 +01:00
2021-10-22 21:27:11 +02:00
FEATURES :
2021-06-26 14:22:00 +03:00
- Supports . gltf and . glb files
- Supports embedded ( base64 ) or external textures
2021-10-22 21:27:11 +02:00
- Supports PBR metallic / roughness flow , loads material textures , values and colors
PBR specular / glossiness flow and extended texture flows not supported
- Supports multiple meshes per model ( every primitives is loaded as a separate mesh )
2023-01-02 20:46:33 +01:00
- Supports basic animations
2024-06-30 11:07:38 +02:00
- Transforms , including parent - child relations , are applied on the mesh data , but the
2024-06-22 19:42:11 +02:00
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 .
2021-10-22 21:27:11 +02:00
RESTRICTIONS :
- Only triangle meshes supported
2023-08-09 18:21:14 -04:00
- Vertex attribute types and formats supported :
2021-10-22 21:27:11 +02:00
> Vertices ( position ) : vec3 : float
> Normals : vec3 : float
> Texcoords : vec2 : float
> Colors : vec4 : u8 , u16 , f32 ( normalized )
> Indices : u16 , u32 ( truncated to u16 )
2024-06-22 19:42:11 +02:00
- Scenes defined in the glTF file are ignored . All nodes in the file
are used .
2021-10-22 21:27:11 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Macro to simplify attributes loading code
2024-10-21 17:00:52 +02:00
# define LOAD_ATTRIBUTE(accesor, numComp, srcType, dstPtr) LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, srcType)
2024-10-10 12:53:02 -04:00
# define LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, dstType) \
2021-10-22 14:53:11 +02:00
{ \
int n = 0 ; \
2024-10-10 12:53:02 -04:00
srcType * buffer = ( srcType * ) accesor - > buffer_view - > buffer - > data + accesor - > buffer_view - > offset / sizeof ( srcType ) + accesor - > offset / sizeof ( srcType ) ; \
2021-10-22 21:27:11 +02:00
for ( unsigned int k = 0 ; k < accesor - > count ; k + + ) \
2021-10-22 14:53:11 +02:00
{ \
2021-10-22 21:27:11 +02:00
for ( int l = 0 ; l < numComp ; l + + ) \
2021-10-22 14:53:11 +02:00
{ \
2024-10-10 12:53:02 -04:00
dstPtr [ numComp * k + l ] = ( dstType ) buffer [ n + l ] ; \
2021-10-22 14:53:11 +02:00
} \
2024-10-10 12:53:02 -04:00
n + = ( int ) ( accesor - > stride / sizeof ( srcType ) ) ; \
2021-10-22 14:53:11 +02:00
} \
}
2021-06-26 14:22:00 +03:00
Model model = { 0 } ;
// glTF file loading
2023-09-02 12:05:34 +02:00
int dataSize = 0 ;
2021-10-22 14:53:11 +02:00
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
2021-06-26 14:22:00 +03:00
if ( fileData = = NULL ) return model ;
2021-03-19 19:43:44 +01:00
2021-06-26 14:22:00 +03:00
// glTF data loading
2021-10-22 14:53:11 +02:00
cgltf_options options = { 0 } ;
2023-12-19 02:38:57 -07:00
options . file . read = LoadFileGLTFCallback ;
options . file . release = ReleaseFileGLTFCallback ;
2021-10-22 14:53:11 +02:00
cgltf_data * data = NULL ;
cgltf_result result = cgltf_parse ( & options , fileData , dataSize , & data ) ;
2022-02-18 20:30:46 +01:00
2021-06-26 14:22:00 +03:00
if ( result = = cgltf_result_success )
{
2021-10-22 21:27:11 +02:00
if ( data - > file_type = = cgltf_file_type_glb ) TRACELOG ( LOG_INFO , " MODEL: [%s] Model basic data (glb) loaded successfully " , fileName ) ;
else if ( data - > file_type = = cgltf_file_type_gltf ) TRACELOG ( LOG_INFO , " MODEL: [%s] Model basic data (glTF) loaded successfully " , fileName ) ;
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Model format not recognized " , fileName ) ;
TRACELOG ( LOG_INFO , " > Meshes count: %i " , data - > meshes_count ) ;
2021-10-23 11:42:02 +02:00
TRACELOG ( LOG_INFO , " > Materials count: %i (+1 default) " , data - > materials_count ) ;
2021-10-22 21:27:11 +02:00
TRACELOG ( LOG_DEBUG , " > Buffers count: %i " , data - > buffers_count ) ;
TRACELOG ( LOG_DEBUG , " > Images count: %i " , data - > images_count ) ;
TRACELOG ( LOG_DEBUG , " > Textures count: %i " , data - > textures_count ) ;
// Force reading data buffers (fills buffer_view->buffer->data)
2023-04-22 21:17:53 +02:00
// NOTE: If an uri is defined to base64 data or external path, it's automatically loaded
2021-10-22 14:53:11 +02:00
result = cgltf_load_buffers ( & options , data , fileName ) ;
2021-06-26 14:22:00 +03:00
if ( result ! = cgltf_result_success ) TRACELOG ( LOG_INFO , " MODEL: [%s] Failed to load mesh/material buffers " , fileName ) ;
2021-06-30 16:39:07 +02:00
2021-10-22 14:53:11 +02:00
int primitivesCount = 0 ;
2024-06-22 19:42:11 +02:00
// 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 ) ;
2021-06-30 16:39:07 +02:00
2021-10-22 21:27:11 +02:00
// Load our model data: meshes and materials
2021-10-22 14:53:11 +02:00
model . meshCount = primitivesCount ;
2021-06-26 14:22:00 +03:00
model . meshes = RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
2021-10-22 21:27:11 +02:00
// NOTE: We keep an extra slot for default material, in case some mesh requires it
2022-02-18 20:30:46 +01:00
model . materialCount = ( int ) data - > materials_count + 1 ;
2021-10-22 21:27:11 +02:00
model . materials = RL_CALLOC ( model . materialCount , sizeof ( Material ) ) ;
model . materials [ 0 ] = LoadMaterialDefault ( ) ; // Load default material (index: 0)
2021-06-30 16:39:07 +02:00
2021-10-22 21:27:11 +02:00
// Load mesh-material indices, by default all meshes are mapped to material index: 0
model . meshMaterial = RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2021-10-22 14:53:11 +02:00
2021-10-22 21:27:11 +02:00
// Load materials data
2021-10-23 14:00:05 +02:00
//----------------------------------------------------------------------------------------------------
2021-10-22 21:27:11 +02:00
for ( unsigned int i = 0 , j = 1 ; i < data - > materials_count ; i + + , j + + )
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
model . materials [ j ] = LoadMaterialDefault ( ) ;
2021-10-22 14:53:11 +02:00
const char * texPath = GetDirectoryPath ( fileName ) ;
2021-10-22 21:27:11 +02:00
// Check glTF material flow: PBR metallic/roughness flow
// NOTE: Alternatively, materials can follow PBR specular/glossiness flow
2021-10-22 14:53:11 +02:00
if ( data - > materials [ i ] . has_pbr_metallic_roughness )
{
2021-10-22 21:27:11 +02:00
// Load base color texture (albedo)
2021-10-22 14:53:11 +02:00
if ( data - > materials [ i ] . pbr_metallic_roughness . base_color_texture . texture )
{
2021-10-22 21:27:11 +02:00
Image imAlbedo = LoadImageFromCgltfImage ( data - > materials [ i ] . pbr_metallic_roughness . base_color_texture . texture - > image , texPath ) ;
if ( imAlbedo . data ! = NULL )
{
model . materials [ j ] . maps [ MATERIAL_MAP_ALBEDO ] . texture = LoadTextureFromImage ( imAlbedo ) ;
UnloadImage ( imAlbedo ) ;
}
}
2022-01-21 19:52:07 +08:00
// Load base color factor (tint)
model . materials [ j ] . maps [ MATERIAL_MAP_ALBEDO ] . color . r = ( unsigned char ) ( data - > materials [ i ] . pbr_metallic_roughness . base_color_factor [ 0 ] * 255 ) ;
model . materials [ j ] . maps [ MATERIAL_MAP_ALBEDO ] . color . g = ( unsigned char ) ( data - > materials [ i ] . pbr_metallic_roughness . base_color_factor [ 1 ] * 255 ) ;
model . materials [ j ] . maps [ MATERIAL_MAP_ALBEDO ] . color . b = ( unsigned char ) ( data - > materials [ i ] . pbr_metallic_roughness . base_color_factor [ 2 ] * 255 ) ;
model . materials [ j ] . maps [ MATERIAL_MAP_ALBEDO ] . color . a = ( unsigned char ) ( data - > materials [ i ] . pbr_metallic_roughness . base_color_factor [ 3 ] * 255 ) ;
2021-10-22 14:53:11 +02:00
2021-10-22 21:27:11 +02:00
// Load metallic/roughness texture
2021-10-22 14:53:11 +02:00
if ( data - > materials [ i ] . pbr_metallic_roughness . metallic_roughness_texture . texture )
{
2021-10-22 21:27:11 +02:00
Image imMetallicRoughness = LoadImageFromCgltfImage ( data - > materials [ i ] . pbr_metallic_roughness . metallic_roughness_texture . texture - > image , texPath ) ;
if ( imMetallicRoughness . data ! = NULL )
{
model . materials [ j ] . maps [ MATERIAL_MAP_ROUGHNESS ] . texture = LoadTextureFromImage ( imMetallicRoughness ) ;
UnloadImage ( imMetallicRoughness ) ;
}
2022-02-18 20:30:46 +01:00
// Load metallic/roughness material properties
2021-10-22 14:53:11 +02:00
float roughness = data - > materials [ i ] . pbr_metallic_roughness . roughness_factor ;
2021-10-22 21:27:11 +02:00
model . materials [ j ] . maps [ MATERIAL_MAP_ROUGHNESS ] . value = roughness ;
2021-10-22 14:53:11 +02:00
float metallic = data - > materials [ i ] . pbr_metallic_roughness . metallic_factor ;
2021-10-22 21:27:11 +02:00
model . materials [ j ] . maps [ MATERIAL_MAP_METALNESS ] . value = metallic ;
2021-10-22 14:53:11 +02:00
}
2021-10-22 21:27:11 +02:00
// Load normal texture
2021-10-22 14:53:11 +02:00
if ( data - > materials [ i ] . normal_texture . texture )
{
2021-10-22 21:27:11 +02:00
Image imNormal = LoadImageFromCgltfImage ( data - > materials [ i ] . normal_texture . texture - > image , texPath ) ;
if ( imNormal . data ! = NULL )
{
model . materials [ j ] . maps [ MATERIAL_MAP_NORMAL ] . texture = LoadTextureFromImage ( imNormal ) ;
UnloadImage ( imNormal ) ;
}
2021-10-22 14:53:11 +02:00
}
2021-10-22 21:27:11 +02:00
// Load ambient occlusion texture
2021-10-22 14:53:11 +02:00
if ( data - > materials [ i ] . occlusion_texture . texture )
{
2021-10-22 21:27:11 +02:00
Image imOcclusion = LoadImageFromCgltfImage ( data - > materials [ i ] . occlusion_texture . texture - > image , texPath ) ;
if ( imOcclusion . data ! = NULL )
{
model . materials [ j ] . maps [ MATERIAL_MAP_OCCLUSION ] . texture = LoadTextureFromImage ( imOcclusion ) ;
UnloadImage ( imOcclusion ) ;
}
2021-10-22 14:53:11 +02:00
}
2021-10-22 21:27:11 +02:00
// Load emissive texture
2021-10-22 14:53:11 +02:00
if ( data - > materials [ i ] . emissive_texture . texture )
{
2021-10-22 21:27:11 +02:00
Image imEmissive = LoadImageFromCgltfImage ( data - > materials [ i ] . emissive_texture . texture - > image , texPath ) ;
if ( imEmissive . data ! = NULL )
{
model . materials [ j ] . maps [ MATERIAL_MAP_EMISSION ] . texture = LoadTextureFromImage ( imEmissive ) ;
UnloadImage ( imEmissive ) ;
}
// Load emissive color factor
model . materials [ j ] . maps [ MATERIAL_MAP_EMISSION ] . color . r = ( unsigned char ) ( data - > materials [ i ] . emissive_factor [ 0 ] * 255 ) ;
model . materials [ j ] . maps [ MATERIAL_MAP_EMISSION ] . color . g = ( unsigned char ) ( data - > materials [ i ] . emissive_factor [ 1 ] * 255 ) ;
model . materials [ j ] . maps [ MATERIAL_MAP_EMISSION ] . color . b = ( unsigned char ) ( data - > materials [ i ] . emissive_factor [ 2 ] * 255 ) ;
model . materials [ j ] . maps [ MATERIAL_MAP_EMISSION ] . color . a = 255 ;
2021-10-22 14:53:11 +02:00
}
}
2021-06-30 16:39:07 +02:00
2022-02-18 20:30:46 +01:00
// Other possible materials not supported by raylib pipeline:
2021-10-22 21:27:11 +02:00
// has_clearcoat, has_transmission, has_volume, has_ior, has specular, has_sheen
}
2021-10-22 14:53:11 +02:00
2024-06-22 19:42:11 +02:00
// 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).
2024-06-30 11:07:38 +02:00
// Any glTF mesh linked from more than one Node (i.e. instancing)
2024-06-22 19:42:11 +02:00
// 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.
2021-10-23 14:00:05 +02:00
//----------------------------------------------------------------------------------------------------
2024-06-22 19:42:11 +02:00
int meshIndex = 0 ;
for ( unsigned int i = 0 ; i < data - > nodes_count ; i + + )
2021-06-26 14:22:00 +03:00
{
2024-06-22 19:42:11 +02:00
cgltf_node * node = & ( data - > nodes [ i ] ) ;
2021-10-23 14:00:05 +02:00
2024-06-22 19:42:11 +02:00
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 + + )
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
// NOTE: We only support primitives defined by triangles
// Other alternatives: points, lines, line_strip, triangle_strip
2024-06-22 19:42:11 +02:00
if ( mesh - > primitives [ p ] . type ! = cgltf_primitive_type_triangles ) continue ;
2021-10-22 21:27:11 +02:00
// 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!
2024-06-22 19:42:11 +02:00
for ( unsigned int j = 0 ; j < mesh - > primitives [ p ] . attributes_count ; j + + )
2021-10-22 14:53:11 +02:00
{
2023-02-09 07:17:47 -05:00
// Check the different attributes for every primitive
2024-06-22 19:42:11 +02:00
if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_position ) // POSITION, vec3, float
2021-10-22 21:27:11 +02:00
{
2024-06-22 19:42:11 +02:00
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-10-22 21:27:11 +02:00
2024-04-20 20:31:06 +02:00
// WARNING: SPECS: POSITION accessor MUST have its min and max properties defined
2021-10-23 14:00:05 +02:00
2024-04-20 22:01:59 +02:00
if ( ( attribute - > type = = cgltf_type_vec3 ) & & ( attribute - > component_type = = cgltf_component_type_r_32f ) )
2021-10-22 21:27:11 +02:00
{
2022-02-18 20:30:46 +01:00
// Init raylib mesh vertices to copy glTF attribute data
2021-10-23 11:42:02 +02:00
model . meshes [ meshIndex ] . vertexCount = ( int ) attribute - > count ;
model . meshes [ meshIndex ] . vertices = RL_MALLOC ( attribute - > count * 3 * sizeof ( float ) ) ;
2021-10-22 21:27:11 +02:00
// Load 3 components of float data type into mesh.vertices
2021-10-23 11:42:02 +02:00
LOAD_ATTRIBUTE ( attribute , 3 , float , model . meshes [ meshIndex ] . vertices )
2024-06-22 19:42:11 +02:00
// Transform the vertices
float * vertices = model . meshes [ meshIndex ] . vertices ;
2024-07-07 21:02:20 +02:00
for ( unsigned int k = 0 ; k < attribute - > count ; k + + )
2024-06-22 19:42:11 +02:00
{
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 ;
}
2021-10-22 21:27:11 +02:00
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Vertices attribute data format not supported, use vec3 float " , fileName ) ;
}
2024-06-22 19:42:11 +02:00
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_normal ) // NORMAL, vec3, float
2021-10-22 14:53:11 +02:00
{
2024-06-22 19:42:11 +02:00
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-10-22 14:53:11 +02:00
2024-04-20 22:01:59 +02:00
if ( ( attribute - > type = = cgltf_type_vec3 ) & & ( attribute - > component_type = = cgltf_component_type_r_32f ) )
2021-10-22 21:27:11 +02:00
{
// Init raylib mesh normals to copy glTF attribute data
2021-10-23 11:42:02 +02:00
model . meshes [ meshIndex ] . normals = RL_MALLOC ( attribute - > count * 3 * sizeof ( float ) ) ;
2021-10-22 21:27:11 +02:00
// Load 3 components of float data type into mesh.normals
2021-10-23 11:42:02 +02:00
LOAD_ATTRIBUTE ( attribute , 3 , float , model . meshes [ meshIndex ] . normals )
2024-06-22 19:42:11 +02:00
// Transform the normals
float * normals = model . meshes [ meshIndex ] . normals ;
2024-07-07 21:02:20 +02:00
for ( unsigned int k = 0 ; k < attribute - > count ; k + + )
2024-06-22 19:42:11 +02:00
{
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 ;
}
2021-10-22 21:27:11 +02:00
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Normal attribute data format not supported, use vec3 float " , fileName ) ;
2021-10-22 14:53:11 +02:00
}
2024-06-22 19:42:11 +02:00
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_tangent ) // TANGENT, vec3, float
2021-10-23 14:00:05 +02:00
{
2024-06-22 19:42:11 +02:00
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-10-23 14:00:05 +02:00
2024-04-20 22:01:59 +02:00
if ( ( attribute - > type = = cgltf_type_vec4 ) & & ( attribute - > component_type = = cgltf_component_type_r_32f ) )
2021-10-23 14:00:05 +02:00
{
2021-10-23 14:17:26 +02:00
// Init raylib mesh tangent to copy glTF attribute data
2021-10-23 14:00:05 +02:00
model . meshes [ meshIndex ] . tangents = RL_MALLOC ( attribute - > count * 4 * sizeof ( float ) ) ;
2021-10-23 14:17:26 +02:00
// Load 4 components of float data type into mesh.tangents
2021-10-23 14:00:05 +02:00
LOAD_ATTRIBUTE ( attribute , 4 , float , model . meshes [ meshIndex ] . tangents )
2024-06-22 19:42:11 +02:00
// Transform the tangents
float * tangents = model . meshes [ meshIndex ] . tangents ;
2024-07-07 21:02:20 +02:00
for ( unsigned int k = 0 ; k < attribute - > count ; k + + )
2024-06-22 19:42:11 +02:00
{
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 ;
}
2021-10-23 14:00:05 +02:00
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Tangent attribute data format not supported, use vec4 float " , fileName ) ;
}
2024-06-22 19:42:11 +02:00
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_texcoord ) // TEXCOORD_n, vec2, float/u8n/u16n
2021-10-22 14:53:11 +02:00
{
2024-04-20 23:23:30 +02:00
// Support up to 2 texture coordinates attributes
float * texcoordPtr = NULL ;
2021-10-23 14:00:05 +02:00
2024-06-22 19:42:11 +02:00
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-10-22 14:53:11 +02:00
2024-04-20 22:01:59 +02:00
if ( attribute - > type = = cgltf_type_vec2 )
2021-10-22 21:27:11 +02:00
{
2024-04-20 22:01:59 +02:00
if ( attribute - > component_type = = cgltf_component_type_r_32f ) // vec2, float
{
// Init raylib mesh texcoords to copy glTF attribute data
2024-04-24 17:02:03 +02:00
texcoordPtr = ( float * ) RL_MALLOC ( attribute - > count * 2 * sizeof ( float ) ) ;
2024-04-20 22:01:59 +02:00
// Load 3 components of float data type into mesh.texcoords
2024-04-20 23:23:30 +02:00
LOAD_ATTRIBUTE ( attribute , 2 , float , texcoordPtr )
2024-04-20 22:01:59 +02:00
}
else if ( attribute - > component_type = = cgltf_component_type_r_8u ) // vec2, u8n
{
// Init raylib mesh texcoords to copy glTF attribute data
2024-04-24 17:02:03 +02:00
texcoordPtr = ( float * ) RL_MALLOC ( attribute - > count * 2 * sizeof ( float ) ) ;
2024-04-20 22:01:59 +02:00
// Load data into a temp buffer to be converted to raylib data type
2024-04-24 17:02:03 +02:00
unsigned char * temp = ( unsigned char * ) RL_MALLOC ( attribute - > count * 2 * sizeof ( unsigned char ) ) ;
2024-04-20 22:01:59 +02:00
LOAD_ATTRIBUTE ( attribute , 2 , unsigned char , temp ) ;
// Convert data to raylib texcoord data type (float)
2024-04-20 23:23:30 +02:00
for ( unsigned int t = 0 ; t < attribute - > count * 2 ; t + + ) texcoordPtr [ t ] = ( float ) temp [ t ] / 255.0f ;
2024-04-20 22:01:59 +02:00
RL_FREE ( temp ) ;
}
else if ( attribute - > component_type = = cgltf_component_type_r_16u ) // vec2, u16n
{
// Init raylib mesh texcoords to copy glTF attribute data
2024-04-24 17:02:03 +02:00
texcoordPtr = ( float * ) RL_MALLOC ( attribute - > count * 2 * sizeof ( float ) ) ;
2024-04-20 22:01:59 +02:00
// Load data into a temp buffer to be converted to raylib data type
2024-04-24 17:02:03 +02:00
unsigned short * temp = ( unsigned short * ) RL_MALLOC ( attribute - > count * 2 * sizeof ( unsigned short ) ) ;
2024-04-20 22:01:59 +02:00
LOAD_ATTRIBUTE ( attribute , 2 , unsigned short , temp ) ;
// Convert data to raylib texcoord data type (float)
2024-04-20 23:23:30 +02:00
for ( unsigned int t = 0 ; t < attribute - > count * 2 ; t + + ) texcoordPtr [ t ] = ( float ) temp [ t ] / 65535.0f ;
2021-10-22 21:27:11 +02:00
2024-04-20 22:01:59 +02:00
RL_FREE ( temp ) ;
}
2024-04-20 23:23:30 +02:00
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Texcoords attribute data format not supported " , fileName ) ;
2021-10-22 21:27:11 +02:00
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Texcoords attribute data format not supported, use vec2 float " , fileName ) ;
2024-06-30 11:07:38 +02:00
2024-06-22 19:42:11 +02:00
int index = mesh - > primitives [ p ] . attributes [ j ] . index ;
2024-04-24 17:02:03 +02:00
if ( index = = 0 ) model . meshes [ meshIndex ] . texcoords = texcoordPtr ;
else if ( index = = 1 ) model . meshes [ meshIndex ] . texcoords2 = texcoordPtr ;
else
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] No more than 2 texture coordinates attributes supported " , fileName ) ;
if ( texcoordPtr ! = NULL ) RL_FREE ( texcoordPtr ) ;
}
2021-10-22 14:53:11 +02:00
}
2024-06-22 19:42:11 +02:00
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_color ) // COLOR_n, vec3/vec4, float/u8n/u16n
2021-10-22 14:53:11 +02:00
{
2024-06-22 19:42:11 +02:00
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-10-22 21:27:11 +02:00
2024-04-20 20:31:06 +02:00
// WARNING: SPECS: All components of each COLOR_n accessor element MUST be clamped to [0.0, 1.0] range
2021-10-23 14:00:05 +02:00
2024-04-20 22:01:59 +02:00
if ( attribute - > type = = cgltf_type_vec3 ) // RGB
2021-10-22 21:27:11 +02:00
{
2024-04-20 22:01:59 +02:00
if ( attribute - > component_type = = cgltf_component_type_r_8u )
{
// Init raylib mesh color to copy glTF attribute data
model . meshes [ meshIndex ] . colors = RL_MALLOC ( attribute - > count * 4 * sizeof ( unsigned char ) ) ;
2021-10-22 14:53:11 +02:00
2024-04-20 22:01:59 +02:00
// Load data into a temp buffer to be converted to raylib data type
unsigned char * temp = RL_MALLOC ( attribute - > count * 3 * sizeof ( unsigned char ) ) ;
LOAD_ATTRIBUTE ( attribute , 3 , unsigned char , temp ) ;
2021-10-22 21:27:11 +02:00
2024-04-20 22:01:59 +02:00
// Convert data to raylib color data type (4 bytes)
for ( unsigned int c = 0 , k = 0 ; c < ( attribute - > count * 4 - 3 ) ; c + = 4 , k + = 3 )
{
model . meshes [ meshIndex ] . colors [ c ] = temp [ k ] ;
model . meshes [ meshIndex ] . colors [ c + 1 ] = temp [ k + 1 ] ;
model . meshes [ meshIndex ] . colors [ c + 2 ] = temp [ k + 2 ] ;
model . meshes [ meshIndex ] . colors [ c + 3 ] = 255 ;
}
RL_FREE ( temp ) ;
}
else if ( attribute - > component_type = = cgltf_component_type_r_16u )
{
// Init raylib mesh color to copy glTF attribute data
model . meshes [ meshIndex ] . colors = RL_MALLOC ( attribute - > count * 4 * sizeof ( unsigned char ) ) ;
// Load data into a temp buffer to be converted to raylib data type
unsigned short * temp = RL_MALLOC ( attribute - > count * 3 * sizeof ( unsigned short ) ) ;
LOAD_ATTRIBUTE ( attribute , 3 , unsigned short , temp ) ;
2021-10-22 21:27:11 +02:00
2024-04-20 22:01:59 +02:00
// Convert data to raylib color data type (4 bytes)
for ( unsigned int c = 0 , k = 0 ; c < ( attribute - > count * 4 - 3 ) ; c + = 4 , k + = 3 )
{
model . meshes [ meshIndex ] . colors [ c ] = ( unsigned char ) ( ( ( float ) temp [ k ] / 65535.0f ) * 255.0f ) ;
model . meshes [ meshIndex ] . colors [ c + 1 ] = ( unsigned char ) ( ( ( float ) temp [ k + 1 ] / 65535.0f ) * 255.0f ) ;
model . meshes [ meshIndex ] . colors [ c + 2 ] = ( unsigned char ) ( ( ( float ) temp [ k + 2 ] / 65535.0f ) * 255.0f ) ;
model . meshes [ meshIndex ] . colors [ c + 3 ] = 255 ;
}
RL_FREE ( temp ) ;
}
else if ( attribute - > component_type = = cgltf_component_type_r_32f )
{
// Init raylib mesh color to copy glTF attribute data
model . meshes [ meshIndex ] . colors = RL_MALLOC ( attribute - > count * 4 * sizeof ( unsigned char ) ) ;
// Load data into a temp buffer to be converted to raylib data type
float * temp = RL_MALLOC ( attribute - > count * 3 * sizeof ( float ) ) ;
LOAD_ATTRIBUTE ( attribute , 3 , float , temp ) ;
// Convert data to raylib color data type (4 bytes)
for ( unsigned int c = 0 , k = 0 ; c < ( attribute - > count * 4 - 3 ) ; c + = 4 , k + = 3 )
{
model . meshes [ meshIndex ] . colors [ c ] = ( unsigned char ) ( temp [ k ] * 255.0f ) ;
model . meshes [ meshIndex ] . colors [ c + 1 ] = ( unsigned char ) ( temp [ k + 1 ] * 255.0f ) ;
model . meshes [ meshIndex ] . colors [ c + 2 ] = ( unsigned char ) ( temp [ k + 2 ] * 255.0f ) ;
model . meshes [ meshIndex ] . colors [ c + 3 ] = 255 ;
}
2021-10-22 21:27:11 +02:00
2024-04-20 22:01:59 +02:00
RL_FREE ( temp ) ;
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Color attribute data format not supported " , fileName ) ;
2021-10-22 14:53:11 +02:00
}
2024-04-20 22:01:59 +02:00
else if ( attribute - > type = = cgltf_type_vec4 ) // RGBA
2021-10-22 14:53:11 +02:00
{
2024-04-20 22:01:59 +02:00
if ( attribute - > component_type = = cgltf_component_type_r_8u )
{
// Init raylib mesh color to copy glTF attribute data
model . meshes [ meshIndex ] . colors = RL_MALLOC ( attribute - > count * 4 * sizeof ( unsigned char ) ) ;
// Load 4 components of unsigned char data type into mesh.colors
LOAD_ATTRIBUTE ( attribute , 4 , unsigned char , model . meshes [ meshIndex ] . colors )
}
else if ( attribute - > component_type = = cgltf_component_type_r_16u )
{
// Init raylib mesh color to copy glTF attribute data
model . meshes [ meshIndex ] . colors = RL_MALLOC ( attribute - > count * 4 * sizeof ( unsigned char ) ) ;
// Load data into a temp buffer to be converted to raylib data type
unsigned short * temp = RL_MALLOC ( attribute - > count * 4 * sizeof ( unsigned short ) ) ;
LOAD_ATTRIBUTE ( attribute , 4 , unsigned short , temp ) ;
// Convert data to raylib color data type (4 bytes)
for ( unsigned int c = 0 ; c < attribute - > count * 4 ; c + + ) model . meshes [ meshIndex ] . colors [ c ] = ( unsigned char ) ( ( ( float ) temp [ c ] / 65535.0f ) * 255.0f ) ;
RL_FREE ( temp ) ;
}
else if ( attribute - > component_type = = cgltf_component_type_r_32f )
{
// Init raylib mesh color to copy glTF attribute data
model . meshes [ meshIndex ] . colors = RL_MALLOC ( attribute - > count * 4 * sizeof ( unsigned char ) ) ;
2021-10-22 21:27:11 +02:00
2024-04-20 22:01:59 +02:00
// Load data into a temp buffer to be converted to raylib data type
float * temp = RL_MALLOC ( attribute - > count * 4 * sizeof ( float ) ) ;
LOAD_ATTRIBUTE ( attribute , 4 , float , temp ) ;
2021-10-22 21:27:11 +02:00
2024-04-20 22:01:59 +02:00
// Convert data to raylib color data type (4 bytes), we expect the color data normalized
for ( unsigned int c = 0 ; c < attribute - > count * 4 ; c + + ) model . meshes [ meshIndex ] . colors [ c ] = ( unsigned char ) ( temp [ c ] * 255.0f ) ;
2021-10-22 21:27:11 +02:00
2024-04-20 22:01:59 +02:00
RL_FREE ( temp ) ;
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Color attribute data format not supported " , fileName ) ;
2021-10-22 14:53:11 +02:00
}
2021-10-22 21:27:11 +02:00
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Color attribute data format not supported " , fileName ) ;
2021-10-22 14:53:11 +02:00
}
2021-10-22 21:27:11 +02:00
2021-10-23 14:00:05 +02:00
// NOTE: Attributes related to animations are processed separately
2021-10-22 14:53:11 +02:00
}
2021-10-22 21:27:11 +02:00
// Load primitive indices data (if provided)
2024-12-03 12:35:24 -05:00
if ( ( mesh - > primitives [ p ] . indices ! = NULL ) & & ( mesh - > primitives [ p ] . indices - > buffer_view ! = NULL ) )
2021-10-22 14:53:11 +02:00
{
2024-06-22 19:42:11 +02:00
cgltf_accessor * attribute = mesh - > primitives [ p ] . indices ;
2021-10-23 14:00:05 +02:00
2021-10-23 11:42:02 +02:00
model . meshes [ meshIndex ] . triangleCount = ( int ) attribute - > count / 3 ;
2021-10-22 21:27:11 +02:00
if ( attribute - > component_type = = cgltf_component_type_r_16u )
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
// Init raylib mesh indices to copy glTF attribute data
2021-10-23 11:42:02 +02:00
model . meshes [ meshIndex ] . indices = RL_MALLOC ( attribute - > count * sizeof ( unsigned short ) ) ;
2021-10-22 21:27:11 +02:00
// Load unsigned short data type into mesh.indices
2021-10-23 11:42:02 +02:00
LOAD_ATTRIBUTE ( attribute , 1 , unsigned short , model . meshes [ meshIndex ] . indices )
2021-10-22 14:53:11 +02:00
}
2024-10-10 12:53:02 -04:00
else if ( attribute - > component_type = = cgltf_component_type_r_8u )
{
// Init raylib mesh indices to copy glTF attribute data
2024-12-03 19:14:14 +01:00
model . meshes [ meshIndex ] . indices = RL_MALLOC ( attribute - > count * sizeof ( unsigned short ) ) ;
2024-10-10 12:53:02 -04:00
LOAD_ATTRIBUTE_CAST ( attribute , 1 , unsigned char , model . meshes [ meshIndex ] . indices , unsigned short )
}
2021-10-22 21:27:11 +02:00
else if ( attribute - > component_type = = cgltf_component_type_r_32u )
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
// Init raylib mesh indices to copy glTF attribute data
2021-10-23 11:42:02 +02:00
model . meshes [ meshIndex ] . indices = RL_MALLOC ( attribute - > count * sizeof ( unsigned short ) ) ;
2024-10-10 12:53:02 -04:00
LOAD_ATTRIBUTE_CAST ( attribute , 1 , unsigned int , model . meshes [ meshIndex ] . indices , unsigned short ) ;
2021-10-22 21:27:11 +02:00
TRACELOG ( LOG_WARNING , " MODEL: [%s] Indices data converted from u32 to u16, possible loss of data " , fileName ) ;
2021-10-22 14:53:11 +02:00
}
2024-10-21 17:00:52 +02:00
else
2024-10-10 12:53:02 -04:00
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Indices data format not supported, use u16 " , fileName ) ;
}
2021-10-22 14:53:11 +02:00
}
2021-10-23 11:42:02 +02:00
else model . meshes [ meshIndex ] . triangleCount = model . meshes [ meshIndex ] . vertexCount / 3 ; // Unindexed mesh
2021-10-22 14:53:11 +02:00
2021-10-22 21:27:11 +02:00
// Assign to the primitive mesh the corresponding material index
// NOTE: If no material defined, mesh uses the already assigned default material (index: 0)
2022-06-07 10:04:24 +02:00
for ( unsigned int m = 0 ; m < data - > materials_count ; m + + )
2021-10-22 14:53:11 +02:00
{
2021-10-22 21:27:11 +02:00
// The primitive actually keeps the pointer to the corresponding material,
// raylib instead assigns to the mesh the by its index, as loaded in model.materials array
2023-02-09 07:17:47 -05:00
// To get the index, we check if material pointers match, and we assign the corresponding index,
2021-10-22 21:27:11 +02:00
// skipping index 0, the default material
2024-06-22 19:42:11 +02:00
if ( & data - > materials [ m ] = = mesh - > primitives [ p ] . material )
2021-10-23 11:42:02 +02:00
{
model . meshMaterial [ meshIndex ] = m + 1 ;
break ;
}
2021-10-22 14:53:11 +02:00
}
2021-10-23 11:42:02 +02:00
meshIndex + + ; // Move to next mesh
2021-10-22 14:53:11 +02:00
}
2019-04-09 13:23:51 +02:00
}
2021-03-19 19:43:44 +01:00
2023-01-02 14:23:48 -05:00
// Load glTF meshes animation data
2021-12-02 20:48:12 +01:00
// REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skins
// REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skinned-mesh-attributes
2023-01-02 20:46:33 +01:00
//
// LIMITATIONS:
// - Only supports 1 armature per file, and skips loading it if there are multiple armatures
// - Only supports linear interpolation (default method in Blender when checked "Always Sample Animations" when exporting a GLTF file)
// - Only supports translation/rotation/scale animation channel.path, weights not considered (i.e. morph targets)
2021-12-02 20:48:12 +01:00
//----------------------------------------------------------------------------------------------------
2024-10-10 12:53:02 -04:00
if ( data - > skins_count > 0 )
2023-01-02 14:23:48 -05:00
{
cgltf_skin skin = data - > skins [ 0 ] ;
2023-01-02 20:46:33 +01:00
model . bones = LoadBoneInfoGLTF ( skin , & model . boneCount ) ;
2023-01-02 14:23:48 -05:00
model . bindPose = RL_MALLOC ( model . boneCount * sizeof ( Transform ) ) ;
2023-01-20 07:05:19 -08:00
for ( int i = 0 ; i < model . boneCount ; i + + )
2023-01-02 14:23:48 -05:00
{
2024-12-03 19:14:14 +01:00
cgltf_node * node = skin . joints [ i ] ;
2024-06-28 09:53:29 +02:00
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 ]
} ;
MatrixDecompose ( worldMatrix , & ( model . bindPose [ i ] . translation ) , & ( model . bindPose [ i ] . rotation ) , & ( model . bindPose [ i ] . scale ) ) ;
2023-01-02 14:23:48 -05:00
}
}
2024-10-10 12:53:02 -04:00
if ( data - > skins_count > 1 )
2023-01-02 14:23:48 -05:00
{
2024-10-10 12:53:02 -04:00
TRACELOG ( LOG_WARNING , " MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i " , fileName , data - > skins_count ) ;
2023-01-02 14:23:48 -05:00
}
2024-06-22 19:42:11 +02:00
meshIndex = 0 ;
for ( unsigned int i = 0 ; i < data - > nodes_count ; i + + )
2021-12-02 20:48:12 +01:00
{
2024-06-22 19:42:11 +02:00
cgltf_node * node = & ( data - > nodes [ i ] ) ;
cgltf_mesh * mesh = node - > mesh ;
if ( ! mesh )
continue ;
for ( unsigned int p = 0 ; p < mesh - > primitives_count ; p + + )
2021-12-02 20:48:12 +01:00
{
// NOTE: We only support primitives defined by triangles
2024-06-22 19:42:11 +02:00
if ( mesh - > primitives [ p ] . type ! = cgltf_primitive_type_triangles ) continue ;
2021-12-02 20:48:12 +01:00
2024-06-22 19:42:11 +02:00
for ( unsigned int j = 0 ; j < mesh - > primitives [ p ] . attributes_count ; j + + )
2021-12-02 20:48:12 +01:00
{
// NOTE: JOINTS_1 + WEIGHT_1 will be used for +4 joints influencing a vertex -> Not supported by raylib
2024-06-22 19:42:11 +02:00
if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_joints ) // JOINTS_n (vec4: 4 bones max per vertex / u8, u16)
2021-12-02 20:48:12 +01:00
{
2024-06-22 19:42:11 +02:00
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-12-02 20:48:12 +01:00
2024-04-20 21:22:49 +02:00
// 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
2024-02-29 18:50:44 +01:00
2024-04-20 21:22:49 +02:00
// WARNING: raylib only supports model.meshes[].boneIds as u8 (unsigned char),
// if data is provided in any other format, it is converted to supported format but
// it could imply data loss (a warning message is issued in that case)
2024-02-29 18:50:44 +01:00
2024-04-20 22:01:59 +02:00
if ( attribute - > type = = cgltf_type_vec4 )
2023-11-18 20:07:30 +01:00
{
2024-04-20 22:01:59 +02:00
if ( attribute - > component_type = = cgltf_component_type_r_8u )
{
// Init raylib mesh boneIds to copy glTF attribute data
model . meshes [ meshIndex ] . boneIds = RL_CALLOC ( model . meshes [ meshIndex ] . vertexCount * 4 , sizeof ( unsigned char ) ) ;
2024-04-21 12:29:09 +02:00
2024-04-20 22:01:59 +02:00
// Load attribute: vec4, u8 (unsigned char)
LOAD_ATTRIBUTE ( attribute , 4 , unsigned char , model . meshes [ meshIndex ] . boneIds )
}
else if ( attribute - > component_type = = cgltf_component_type_r_16u )
2024-04-20 21:22:49 +02:00
{
2024-04-20 22:01:59 +02:00
// Init raylib mesh boneIds to copy glTF attribute data
model . meshes [ meshIndex ] . boneIds = RL_CALLOC ( model . meshes [ meshIndex ] . vertexCount * 4 , sizeof ( unsigned char ) ) ;
// Load data into a temp buffer to be converted to raylib data type
unsigned short * temp = RL_CALLOC ( model . meshes [ meshIndex ] . vertexCount * 4 , sizeof ( unsigned short ) ) ;
LOAD_ATTRIBUTE ( attribute , 4 , unsigned short , temp ) ;
// Convert data to raylib color data type (4 bytes)
bool boneIdOverflowWarning = false ;
for ( int b = 0 ; b < model . meshes [ meshIndex ] . vertexCount * 4 ; b + + )
2024-04-20 21:22:49 +02:00
{
2024-04-20 22:01:59 +02:00
if ( ( temp [ b ] > 255 ) & & ! boneIdOverflowWarning )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Joint attribute data format (u16) overflow " , fileName ) ;
boneIdOverflowWarning = true ;
}
// Despite the possible overflow, we convert data to unsigned char
model . meshes [ meshIndex ] . boneIds [ b ] = ( unsigned char ) temp [ b ] ;
2024-04-20 21:22:49 +02:00
}
2024-04-20 22:01:59 +02:00
RL_FREE ( temp ) ;
2024-04-20 21:22:49 +02:00
}
2024-04-20 22:01:59 +02:00
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Joint attribute data format not supported " , fileName ) ;
2023-11-18 20:07:30 +01:00
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Joint attribute data format not supported " , fileName ) ;
2021-12-02 20:48:12 +01:00
}
2024-06-22 19:42:11 +02:00
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_weights ) // WEIGHTS_n (vec4, u8n/u16n/f32)
2021-12-02 20:48:12 +01:00
{
2024-06-22 19:42:11 +02:00
cgltf_accessor * attribute = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-12-02 20:48:12 +01:00
2024-04-20 22:01:59 +02:00
if ( attribute - > type = = cgltf_type_vec4 )
2021-12-02 20:48:12 +01:00
{
2024-04-20 22:01:59 +02:00
// TODO: Support component types: u8, u16?
if ( attribute - > component_type = = cgltf_component_type_r_8u )
{
// Init raylib mesh bone weight to copy glTF attribute data
model . meshes [ meshIndex ] . boneWeights = RL_CALLOC ( model . meshes [ meshIndex ] . vertexCount * 4 , sizeof ( float ) ) ;
// Load data into a temp buffer to be converted to raylib data type
unsigned char * temp = RL_MALLOC ( attribute - > count * 4 * sizeof ( unsigned char ) ) ;
LOAD_ATTRIBUTE ( attribute , 4 , unsigned char , temp ) ;
// Convert data to raylib bone weight data type (4 bytes)
for ( unsigned int b = 0 ; b < attribute - > count * 4 ; b + + ) model . meshes [ meshIndex ] . boneWeights [ b ] = ( float ) temp [ b ] / 255.0f ;
RL_FREE ( temp ) ;
}
else if ( attribute - > component_type = = cgltf_component_type_r_16u )
{
// Init raylib mesh bone weight to copy glTF attribute data
model . meshes [ meshIndex ] . boneWeights = RL_CALLOC ( model . meshes [ meshIndex ] . vertexCount * 4 , sizeof ( float ) ) ;
// Load data into a temp buffer to be converted to raylib data type
unsigned short * temp = RL_MALLOC ( attribute - > count * 4 * sizeof ( unsigned short ) ) ;
LOAD_ATTRIBUTE ( attribute , 4 , unsigned short , temp ) ;
// Convert data to raylib bone weight data type
for ( unsigned int b = 0 ; b < attribute - > count * 4 ; b + + ) model . meshes [ meshIndex ] . boneWeights [ b ] = ( float ) temp [ b ] / 65535.0f ;
RL_FREE ( temp ) ;
}
else if ( attribute - > component_type = = cgltf_component_type_r_32f )
{
// Init raylib mesh bone weight to copy glTF attribute data
model . meshes [ meshIndex ] . boneWeights = RL_CALLOC ( model . meshes [ meshIndex ] . vertexCount * 4 , sizeof ( float ) ) ;
// Load 4 components of float data type into mesh.boneWeights
// for cgltf_attribute_type_weights we have:
// - data.meshes[0] (256 vertices)
// - 256 values, provided as cgltf_type_vec4 of float (4 byte per joint, stride 16)
LOAD_ATTRIBUTE ( attribute , 4 , float , model . meshes [ meshIndex ] . boneWeights )
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Joint weight attribute data format not supported, use vec4 float " , fileName ) ;
2021-12-02 20:48:12 +01:00
}
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Joint weight attribute data format not supported, use vec4 float " , fileName ) ;
}
}
2023-01-02 14:23:48 -05:00
// Animated vertex data
model . meshes [ meshIndex ] . animVertices = RL_CALLOC ( model . meshes [ meshIndex ] . vertexCount * 3 , sizeof ( float ) ) ;
memcpy ( model . meshes [ meshIndex ] . animVertices , model . meshes [ meshIndex ] . vertices , model . meshes [ meshIndex ] . vertexCount * 3 * sizeof ( float ) ) ;
model . meshes [ meshIndex ] . animNormals = RL_CALLOC ( model . meshes [ meshIndex ] . vertexCount * 3 , sizeof ( float ) ) ;
2024-04-19 21:13:52 +02:00
if ( model . meshes [ meshIndex ] . normals ! = NULL )
{
2023-06-10 16:15:24 -04:00
memcpy ( model . meshes [ meshIndex ] . animNormals , model . meshes [ meshIndex ] . normals , model . meshes [ meshIndex ] . vertexCount * 3 * sizeof ( float ) ) ;
}
2024-10-21 17:00:52 +02:00
2024-09-20 11:30:37 -04:00
// Bone Transform Matrices
model . meshes [ meshIndex ] . boneCount = model . boneCount ;
model . meshes [ meshIndex ] . boneMatrices = RL_CALLOC ( model . meshes [ meshIndex ] . boneCount , sizeof ( Matrix ) ) ;
2024-10-21 17:00:52 +02:00
2024-09-20 11:30:37 -04:00
for ( int j = 0 ; j < model . meshes [ meshIndex ] . boneCount ; j + + )
{
model . meshes [ meshIndex ] . boneMatrices [ j ] = MatrixIdentity ( ) ;
}
2023-01-02 14:23:48 -05:00
2021-12-02 20:48:12 +01:00
meshIndex + + ; // Move to next mesh
}
2023-01-02 14:23:48 -05:00
2021-12-02 20:48:12 +01:00
}
2023-01-02 14:23:48 -05:00
2021-10-22 21:27:11 +02:00
// Free all cgltf loaded data
2021-10-22 14:53:11 +02:00
cgltf_free ( data ) ;
2018-09-14 13:00:48 +02:00
}
2020-03-27 17:43:51 +01:00
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to load glTF data " , fileName ) ;
2018-09-05 10:59:05 +02:00
2021-10-22 21:27:11 +02:00
// WARNING: cgltf requires the file pointer available while reading data
UnloadFileData ( fileData ) ;
2019-05-14 14:06:17 +02:00
2019-03-29 16:48:23 +01:00
return model ;
2018-09-05 10:59:05 +02:00
}
2023-01-02 14:23:48 -05:00
2024-04-20 20:31:06 +02:00
// Get interpolated pose for bone sampler at a specific time. Returns true on success
2024-04-15 17:08:09 -04:00
static bool GetPoseAtTimeGLTF ( cgltf_interpolation_type interpolationType , cgltf_accessor * input , cgltf_accessor * output , float time , void * data )
2023-01-02 14:23:48 -05:00
{
2024-04-15 17:08:09 -04:00
if ( interpolationType > = cgltf_interpolation_type_max_enum ) return false ;
2023-01-02 20:46:33 +01:00
// Input and output should have the same count
2023-01-02 14:23:48 -05:00
float tstart = 0.0f ;
float tend = 0.0f ;
2023-01-02 20:46:33 +01:00
int keyframe = 0 ; // Defaults to first pose
2023-02-05 16:30:23 +01:00
2023-09-22 11:58:24 +02:00
for ( int i = 0 ; i < ( int ) input - > count - 1 ; i + + )
2023-01-02 14:23:48 -05:00
{
cgltf_bool r1 = cgltf_accessor_read_float ( input , i , & tstart , 1 ) ;
if ( ! r1 ) return false ;
2023-02-05 16:30:23 +01:00
2023-01-02 20:46:33 +01:00
cgltf_bool r2 = cgltf_accessor_read_float ( input , i + 1 , & tend , 1 ) ;
2023-01-02 14:23:48 -05:00
if ( ! r2 ) return false ;
if ( ( tstart < = time ) & & ( time < tend ) )
{
keyframe = i ;
break ;
}
}
2024-04-19 21:13:52 +02:00
2024-06-28 09:53:29 +02:00
// Constant animation, no need to interpolate
2024-06-30 09:50:12 +02:00
if ( FloatEquals ( tend , tstart ) ) return true ;
2024-06-28 09:53:29 +02:00
2024-05-01 18:12:57 +02:00
float duration = fmaxf ( ( tend - tstart ) , EPSILON ) ;
2024-04-17 02:10:48 -04:00
float t = ( time - tstart ) / duration ;
2023-01-02 14:23:48 -05:00
t = ( t < 0.0f ) ? 0.0f : t ;
t = ( t > 1.0f ) ? 1.0f : t ;
if ( output - > component_type ! = cgltf_component_type_r_32f ) return false ;
if ( output - > type = = cgltf_type_vec3 )
{
2024-04-15 17:08:09 -04:00
switch ( interpolationType )
{
case cgltf_interpolation_type_step :
{
float tmp [ 3 ] = { 0.0f } ;
cgltf_accessor_read_float ( output , keyframe , tmp , 3 ) ;
Vector3 v1 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] } ;
Vector3 * r = data ;
* r = v1 ;
} break ;
case cgltf_interpolation_type_linear :
{
float tmp [ 3 ] = { 0.0f } ;
cgltf_accessor_read_float ( output , keyframe , tmp , 3 ) ;
Vector3 v1 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] } ;
cgltf_accessor_read_float ( output , keyframe + 1 , tmp , 3 ) ;
Vector3 v2 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] } ;
Vector3 * r = data ;
2024-04-19 21:13:52 +02:00
2024-04-15 17:08:09 -04:00
* r = Vector3Lerp ( v1 , v2 , t ) ;
} break ;
case cgltf_interpolation_type_cubic_spline :
{
float tmp [ 3 ] = { 0.0f } ;
cgltf_accessor_read_float ( output , 3 * keyframe + 1 , tmp , 3 ) ;
Vector3 v1 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] } ;
cgltf_accessor_read_float ( output , 3 * keyframe + 2 , tmp , 3 ) ;
Vector3 tangent1 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] } ;
cgltf_accessor_read_float ( output , 3 * ( keyframe + 1 ) + 1 , tmp , 3 ) ;
2024-04-17 02:10:48 -04:00
Vector3 v2 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] } ;
cgltf_accessor_read_float ( output , 3 * ( keyframe + 1 ) , tmp , 3 ) ;
2024-04-15 17:08:09 -04:00
Vector3 tangent2 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] } ;
Vector3 * r = data ;
* r = Vector3CubicHermite ( v1 , tangent1 , v2 , tangent2 , t ) ;
} break ;
2024-04-19 21:13:52 +02:00
default : break ;
2024-04-15 17:08:09 -04:00
}
2023-01-02 14:23:48 -05:00
}
else if ( output - > type = = cgltf_type_vec4 )
{
2023-08-09 18:21:14 -04:00
// Only v4 is for rotations, so we know it's a quaternion
2024-04-15 17:08:09 -04:00
switch ( interpolationType )
{
case cgltf_interpolation_type_step :
{
float tmp [ 4 ] = { 0.0f } ;
cgltf_accessor_read_float ( output , keyframe , tmp , 4 ) ;
Vector4 v1 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] , tmp [ 3 ] } ;
Vector4 * r = data ;
* r = v1 ;
} break ;
case cgltf_interpolation_type_linear :
{
float tmp [ 4 ] = { 0.0f } ;
cgltf_accessor_read_float ( output , keyframe , tmp , 4 ) ;
Vector4 v1 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] , tmp [ 3 ] } ;
cgltf_accessor_read_float ( output , keyframe + 1 , tmp , 4 ) ;
Vector4 v2 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] , tmp [ 3 ] } ;
Vector4 * r = data ;
2024-04-19 21:13:52 +02:00
2024-04-15 17:08:09 -04:00
* r = QuaternionSlerp ( v1 , v2 , t ) ;
} break ;
case cgltf_interpolation_type_cubic_spline :
{
float tmp [ 4 ] = { 0.0f } ;
cgltf_accessor_read_float ( output , 3 * keyframe + 1 , tmp , 4 ) ;
Vector4 v1 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] , tmp [ 3 ] } ;
cgltf_accessor_read_float ( output , 3 * keyframe + 2 , tmp , 4 ) ;
2024-08-16 19:07:23 -04:00
Vector4 outTangent1 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] , 0.0f } ;
2024-04-15 17:08:09 -04:00
cgltf_accessor_read_float ( output , 3 * ( keyframe + 1 ) + 1 , tmp , 4 ) ;
2024-04-17 02:10:48 -04:00
Vector4 v2 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] , tmp [ 3 ] } ;
cgltf_accessor_read_float ( output , 3 * ( keyframe + 1 ) , tmp , 4 ) ;
2024-08-16 19:07:23 -04:00
Vector4 inTangent2 = { tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] , 0.0f } ;
2024-04-15 17:08:09 -04:00
Vector4 * r = data ;
2024-04-17 02:10:48 -04:00
v1 = QuaternionNormalize ( v1 ) ;
v2 = QuaternionNormalize ( v2 ) ;
if ( Vector4DotProduct ( v1 , v2 ) < 0.0f )
{
v2 = Vector4Negate ( v2 ) ;
}
2024-04-19 21:13:52 +02:00
2024-04-17 02:10:48 -04:00
outTangent1 = Vector4Scale ( outTangent1 , duration ) ;
inTangent2 = Vector4Scale ( inTangent2 , duration ) ;
* r = QuaternionCubicHermiteSpline ( v1 , outTangent1 , v2 , inTangent2 , t ) ;
2024-04-15 17:08:09 -04:00
} break ;
2024-04-19 21:13:52 +02:00
default : break ;
2024-04-15 17:08:09 -04:00
}
2023-01-02 14:23:48 -05:00
}
2023-02-05 16:30:23 +01:00
2023-01-02 14:23:48 -05:00
return true ;
}
2023-01-02 20:46:33 +01:00
# define GLTF_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms)
2023-09-02 12:05:34 +02:00
static ModelAnimation * LoadModelAnimationsGLTF ( const char * fileName , int * animCount )
2023-01-02 14:23:48 -05:00
{
// glTF file loading
2023-09-02 12:05:34 +02:00
int dataSize = 0 ;
2023-01-02 14:23:48 -05:00
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
ModelAnimation * animations = NULL ;
2023-02-05 16:30:23 +01:00
2023-01-02 14:23:48 -05:00
// glTF data loading
cgltf_options options = { 0 } ;
2023-12-19 02:38:57 -07:00
options . file . read = LoadFileGLTFCallback ;
options . file . release = ReleaseFileGLTFCallback ;
2023-01-02 14:23:48 -05:00
cgltf_data * data = NULL ;
cgltf_result result = cgltf_parse ( & options , fileData , dataSize , & data ) ;
2023-02-05 16:30:23 +01:00
2023-01-02 14:23:48 -05:00
if ( result ! = cgltf_result_success )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to load glTF data " , fileName ) ;
* animCount = 0 ;
return NULL ;
}
result = cgltf_load_buffers ( & options , data , fileName ) ;
if ( result ! = cgltf_result_success ) TRACELOG ( LOG_INFO , " MODEL: [%s] Failed to load animation buffers " , fileName ) ;
if ( result = = cgltf_result_success )
{
2024-10-10 12:53:02 -04:00
if ( data - > skins_count > 0 )
2023-01-02 14:23:48 -05:00
{
cgltf_skin skin = data - > skins [ 0 ] ;
2023-01-20 07:05:19 -08:00
* animCount = ( int ) data - > animations_count ;
2023-01-02 14:23:48 -05:00
animations = RL_MALLOC ( data - > animations_count * sizeof ( ModelAnimation ) ) ;
2023-02-05 16:30:23 +01:00
2023-01-02 14:23:48 -05:00
for ( unsigned int i = 0 ; i < data - > animations_count ; i + + )
{
2023-01-02 20:46:33 +01:00
animations [ i ] . bones = LoadBoneInfoGLTF ( skin , & animations [ i ] . boneCount ) ;
2023-01-02 14:23:48 -05:00
cgltf_animation animData = data - > animations [ i ] ;
struct Channels {
cgltf_animation_channel * translate ;
cgltf_animation_channel * rotate ;
cgltf_animation_channel * scale ;
2024-04-15 17:08:09 -04:00
cgltf_interpolation_type interpolationType ;
2023-01-02 14:23:48 -05:00
} ;
struct Channels * boneChannels = RL_CALLOC ( animations [ i ] . boneCount , sizeof ( struct Channels ) ) ;
float animDuration = 0.0f ;
2023-02-05 16:30:23 +01:00
2023-01-02 14:23:48 -05:00
for ( unsigned int j = 0 ; j < animData . channels_count ; j + + )
{
cgltf_animation_channel channel = animData . channels [ j ] ;
int boneIndex = - 1 ;
2023-02-05 16:30:23 +01:00
2023-01-02 14:23:48 -05:00
for ( unsigned int k = 0 ; k < skin . joints_count ; k + + )
{
if ( animData . channels [ j ] . target_node = = skin . joints [ k ] )
{
boneIndex = k ;
break ;
}
}
if ( boneIndex = = - 1 )
{
2023-01-02 20:46:33 +01:00
// Animation channel for a node not in the armature
2023-01-02 14:23:48 -05:00
continue ;
}
2024-04-15 17:08:09 -04:00
boneChannels [ boneIndex ] . interpolationType = animData . channels [ j ] . sampler - > interpolation ;
if ( animData . channels [ j ] . sampler - > interpolation ! = cgltf_interpolation_type_max_enum )
2023-01-02 14:23:48 -05:00
{
if ( channel . target_path = = cgltf_animation_path_type_translation )
{
boneChannels [ boneIndex ] . translate = & animData . channels [ j ] ;
}
else if ( channel . target_path = = cgltf_animation_path_type_rotation )
{
boneChannels [ boneIndex ] . rotate = & animData . channels [ j ] ;
}
else if ( channel . target_path = = cgltf_animation_path_type_scale )
{
boneChannels [ boneIndex ] . scale = & animData . channels [ j ] ;
}
else
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Unsupported target_path on channel %d's sampler for animation %d. Skipping. " , fileName , j , i ) ;
}
2023-02-05 16:30:23 +01:00
}
2024-04-15 17:08:09 -04:00
else TRACELOG ( LOG_WARNING , " MODEL: [%s] Invalid interpolation curve encountered for GLTF animation. " , fileName ) ;
2023-01-02 14:23:48 -05:00
float t = 0.0f ;
cgltf_bool r = cgltf_accessor_read_float ( channel . sampler - > input , channel . sampler - > input - > count - 1 , & t , 1 ) ;
2023-02-05 16:30:23 +01:00
2023-01-02 14:23:48 -05:00
if ( ! r )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to load input time " , fileName ) ;
continue ;
}
animDuration = ( t > animDuration ) ? t : animDuration ;
}
2024-06-09 13:16:18 +02:00
if ( animData . name ! = NULL )
{
strncpy ( animations [ i ] . name , animData . name , sizeof ( animations [ i ] . name ) ) ;
animations [ i ] . name [ sizeof ( animations [ i ] . name ) - 1 ] = ' \0 ' ;
}
2023-06-17 16:48:18 +02:00
2024-02-19 18:51:04 +01:00
animations [ i ] . frameCount = ( int ) ( animDuration * 1000.0f / GLTF_ANIMDELAY ) + 1 ;
2023-01-02 14:23:48 -05:00
animations [ i ] . framePoses = RL_MALLOC ( animations [ i ] . frameCount * sizeof ( Transform * ) ) ;
2023-01-20 07:05:19 -08:00
for ( int j = 0 ; j < animations [ i ] . frameCount ; j + + )
2023-01-02 14:23:48 -05:00
{
animations [ i ] . framePoses [ j ] = RL_MALLOC ( animations [ i ] . boneCount * sizeof ( Transform ) ) ;
float time = ( ( float ) j * GLTF_ANIMDELAY ) / 1000.0f ;
2023-02-05 16:30:23 +01:00
2023-01-20 07:05:19 -08:00
for ( int k = 0 ; k < animations [ i ] . boneCount ; k + + )
2023-01-02 14:23:48 -05:00
{
2024-06-28 09:53:29 +02:00
Vector3 translation = { skin . joints [ k ] - > translation [ 0 ] , skin . joints [ k ] - > translation [ 1 ] , skin . joints [ k ] - > translation [ 2 ] } ;
Quaternion rotation = { skin . joints [ k ] - > rotation [ 0 ] , skin . joints [ k ] - > rotation [ 1 ] , skin . joints [ k ] - > rotation [ 2 ] , skin . joints [ k ] - > rotation [ 3 ] } ;
Vector3 scale = { skin . joints [ k ] - > scale [ 0 ] , skin . joints [ k ] - > scale [ 1 ] , skin . joints [ k ] - > scale [ 2 ] } ;
2023-02-05 16:30:23 +01:00
2023-01-02 14:23:48 -05:00
if ( boneChannels [ k ] . translate )
{
2024-04-15 17:08:09 -04:00
if ( ! GetPoseAtTimeGLTF ( boneChannels [ k ] . interpolationType , boneChannels [ k ] . translate - > sampler - > input , boneChannels [ k ] . translate - > sampler - > output , time , & translation ) )
2023-01-02 14:23:48 -05:00
{
TRACELOG ( LOG_INFO , " MODEL: [%s] Failed to load translate pose data for bone %s " , fileName , animations [ i ] . bones [ k ] . name ) ;
}
}
if ( boneChannels [ k ] . rotate )
{
2024-04-15 17:08:09 -04:00
if ( ! GetPoseAtTimeGLTF ( boneChannels [ k ] . interpolationType , boneChannels [ k ] . rotate - > sampler - > input , boneChannels [ k ] . rotate - > sampler - > output , time , & rotation ) )
2023-01-02 14:23:48 -05:00
{
TRACELOG ( LOG_INFO , " MODEL: [%s] Failed to load rotate pose data for bone %s " , fileName , animations [ i ] . bones [ k ] . name ) ;
}
}
if ( boneChannels [ k ] . scale )
{
2024-04-15 17:08:09 -04:00
if ( ! GetPoseAtTimeGLTF ( boneChannels [ k ] . interpolationType , boneChannels [ k ] . scale - > sampler - > input , boneChannels [ k ] . scale - > sampler - > output , time , & scale ) )
2023-01-02 14:23:48 -05:00
{
TRACELOG ( LOG_INFO , " MODEL: [%s] Failed to load scale pose data for bone %s " , fileName , animations [ i ] . bones [ k ] . name ) ;
}
}
animations [ i ] . framePoses [ j ] [ k ] = ( Transform ) {
. translation = translation ,
. rotation = rotation ,
2023-01-02 20:46:33 +01:00
. scale = scale
} ;
2023-01-02 14:23:48 -05:00
}
BuildPoseFromParentJoints ( animations [ i ] . bones , animations [ i ] . boneCount , animations [ i ] . framePoses [ j ] ) ;
}
2024-06-09 13:16:18 +02:00
TRACELOG ( LOG_INFO , " MODEL: [%s] Loaded animation: %s (%d frames, %fs) " , fileName , ( animData . name ! = NULL ) ? animData . name : " NULL " , animations [ i ] . frameCount , animDuration ) ;
2023-01-02 14:23:48 -05:00
RL_FREE ( boneChannels ) ;
}
2023-02-05 16:30:23 +01:00
}
2024-10-21 17:00:52 +02:00
2024-10-10 12:53:02 -04:00
if ( data - > skins_count > 1 )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] expected exactly one skin to load animation data from, but found %i " , fileName , data - > skins_count ) ;
}
2023-01-02 14:23:48 -05:00
cgltf_free ( data ) ;
}
2023-05-15 03:24:37 -06:00
UnloadFileData ( fileData ) ;
2023-01-02 14:23:48 -05:00
return animations ;
}
2018-09-05 10:59:05 +02:00
# endif
2021-09-04 19:55:09 +02:00
# if defined(SUPPORT_FILEFORMAT_VOX)
2021-09-10 15:24:01 +02:00
// Load VOX (MagicaVoxel) mesh data
2021-09-05 20:39:34 +02:00
static Model LoadVOX ( const char * fileName )
2021-09-04 19:55:09 +02:00
{
Model model = { 0 } ;
2021-10-13 20:28:58 +02:00
2021-09-04 19:55:09 +02:00
int nbvertices = 0 ;
int meshescount = 0 ;
2023-10-09 11:07:02 +02:00
2021-10-13 20:28:58 +02:00
// Read vox file into buffer
2023-09-02 12:05:34 +02:00
int dataSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
2023-10-09 11:07:02 +02:00
2021-09-10 15:24:01 +02:00
if ( fileData = = 0 )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to load VOX file " , fileName ) ;
return model ;
}
2021-10-13 20:28:58 +02:00
// Read and build voxarray description
2021-09-05 20:39:34 +02:00
VoxArray3D voxarray = { 0 } ;
2023-09-02 12:05:34 +02:00
int ret = Vox_LoadFromMemory ( fileData , dataSize , & voxarray ) ;
2021-09-04 19:55:09 +02:00
if ( ret ! = VOX_SUCCESS )
{
2021-09-10 15:24:01 +02:00
// Error
UnloadFileData ( fileData ) ;
2021-09-04 19:55:09 +02:00
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to load VOX data " , fileName ) ;
return model ;
}
else
{
2021-09-10 15:24:01 +02:00
// Success: Compute meshes count
2021-09-04 19:55:09 +02:00
nbvertices = voxarray . vertices . used ;
2021-09-05 20:39:34 +02:00
meshescount = 1 + ( nbvertices / 65536 ) ;
2021-09-04 19:55:09 +02:00
TRACELOG ( LOG_INFO , " MODEL: [%s] VOX data loaded successfully : %i vertices/%i meshes " , fileName , nbvertices , meshescount ) ;
}
2021-09-05 20:39:34 +02:00
// Build models from meshes
2021-09-04 19:55:09 +02:00
model . transform = MatrixIdentity ( ) ;
model . meshCount = meshescount ;
2021-10-13 20:28:58 +02:00
model . meshes = ( Mesh * ) RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
2021-09-04 19:55:09 +02:00
2021-10-13 20:28:58 +02:00
model . meshMaterial = ( int * ) RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2021-09-04 19:55:09 +02:00
model . materialCount = 1 ;
2021-10-13 20:28:58 +02:00
model . materials = ( Material * ) RL_CALLOC ( model . materialCount , sizeof ( Material ) ) ;
2021-09-04 19:55:09 +02:00
model . materials [ 0 ] = LoadMaterialDefault ( ) ;
2021-09-05 20:39:34 +02:00
// Init model meshes
2021-09-04 19:55:09 +02:00
int verticesRemain = voxarray . vertices . used ;
2021-09-05 20:39:34 +02:00
int verticesMax = 65532 ; // 5461 voxels x 12 vertices per voxel -> 65532 (must be inf 65536)
2021-09-04 19:55:09 +02:00
2021-10-03 07:11:20 -03:00
// 6*4 = 12 vertices per voxel
2021-10-13 20:28:58 +02:00
Vector3 * pvertices = ( Vector3 * ) voxarray . vertices . array ;
2024-02-29 18:50:44 +01:00
Vector3 * pnormals = ( Vector3 * ) voxarray . normals . array ;
2021-10-13 20:28:58 +02:00
Color * pcolors = ( Color * ) voxarray . colors . array ;
2021-10-06 01:20:46 +02:00
2021-09-27 13:55:45 +02:00
unsigned short * pindices = voxarray . indices . array ; // 5461*6*6 = 196596 indices max per mesh
2021-09-04 19:55:09 +02:00
2021-09-05 20:39:34 +02:00
int size = 0 ;
2021-09-04 19:55:09 +02:00
2021-10-13 20:28:58 +02:00
for ( int i = 0 ; i < meshescount ; i + + )
2021-09-04 19:55:09 +02:00
{
2021-10-13 20:28:58 +02:00
Mesh * pmesh = & model . meshes [ i ] ;
2021-09-04 19:55:09 +02:00
memset ( pmesh , 0 , sizeof ( Mesh ) ) ;
2021-09-05 20:39:34 +02:00
// Copy vertices
2021-09-04 19:55:09 +02:00
pmesh - > vertexCount = ( int ) fmin ( verticesMax , verticesRemain ) ;
2021-09-05 20:39:34 +02:00
size = pmesh - > vertexCount * sizeof ( float ) * 3 ;
2024-02-29 18:50:44 +01:00
pmesh - > vertices = ( float * ) RL_MALLOC ( size ) ;
2021-09-04 19:55:09 +02:00
memcpy ( pmesh - > vertices , pvertices , size ) ;
2024-02-29 18:28:51 +01:00
// Copy normals
2024-02-29 18:50:44 +01:00
pmesh - > normals = ( float * ) RL_MALLOC ( size ) ;
2024-02-29 18:28:51 +01:00
memcpy ( pmesh - > normals , pnormals , size ) ;
2021-09-22 21:56:38 +02:00
// Copy indices
2021-10-13 20:28:58 +02:00
size = voxarray . indices . used * sizeof ( unsigned short ) ;
2024-03-07 12:33:45 +01:00
pmesh - > indices = ( unsigned short * ) RL_MALLOC ( size ) ;
2021-09-04 19:55:09 +02:00
memcpy ( pmesh - > indices , pindices , size ) ;
2021-09-05 20:39:34 +02:00
pmesh - > triangleCount = ( pmesh - > vertexCount / 4 ) * 2 ;
2021-09-04 19:55:09 +02:00
2021-09-05 20:39:34 +02:00
// Copy colors
size = pmesh - > vertexCount * sizeof ( Color ) ;
2021-10-13 20:28:58 +02:00
pmesh - > colors = RL_MALLOC ( size ) ;
2021-09-04 19:55:09 +02:00
memcpy ( pmesh - > colors , pcolors , size ) ;
// First material index
2021-10-13 20:28:58 +02:00
model . meshMaterial [ i ] = 0 ;
2021-09-04 19:55:09 +02:00
verticesRemain - = verticesMax ;
pvertices + = verticesMax ;
2024-02-29 18:28:51 +01:00
pnormals + = verticesMax ;
2021-09-04 19:55:09 +02:00
pcolors + = verticesMax ;
}
2021-10-13 20:28:58 +02:00
// Free buffers
2021-09-04 19:55:09 +02:00
Vox_FreeArrays ( & voxarray ) ;
2021-09-10 15:24:01 +02:00
UnloadFileData ( fileData ) ;
2021-09-04 19:55:09 +02:00
return model ;
}
2021-09-09 18:14:15 +02:00
# endif
2021-12-04 19:56:02 +01:00
2022-08-20 14:01:54 +02:00
# if defined(SUPPORT_FILEFORMAT_M3D)
// Hook LoadFileData()/UnloadFileData() calls to M3D loaders
2023-09-15 17:04:07 +02:00
unsigned char * m3d_loaderhook ( char * fn , unsigned int * len ) { return LoadFileData ( ( const char * ) fn , ( int * ) len ) ; }
2022-08-20 14:01:54 +02:00
void m3d_freehook ( void * data ) { UnloadFileData ( ( unsigned char * ) data ) ; }
// Load M3D mesh data
static Model LoadM3D ( const char * fileName )
{
Model model = { 0 } ;
m3d_t * m3d = NULL ;
m3dp_t * prop = NULL ;
2023-08-10 22:45:25 +02:00
int i , j , k , l , n , mi = - 2 , vcolor = 0 ;
2023-10-09 11:07:02 +02:00
2023-09-02 12:05:34 +02:00
int dataSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
2022-08-20 14:01:54 +02:00
if ( fileData ! = NULL )
{
m3d = m3d_load ( fileData , m3d_loaderhook , m3d_freehook , NULL ) ;
2022-08-26 10:04:38 +02:00
if ( ! m3d | | M3D_ERR_ISFATAL ( m3d - > errcode ) )
2022-08-20 14:01:54 +02:00
{
2023-10-08 18:10:05 +02:00
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to load M3D data, error code %d " , fileName , m3d ? m3d - > errcode : - 2 ) ;
2022-08-26 10:04:38 +02:00
if ( m3d ) m3d_free ( m3d ) ;
UnloadFileData ( fileData ) ;
2022-08-20 14:01:54 +02:00
return model ;
2022-08-22 11:11:05 +02:00
}
2022-08-20 14:01:54 +02:00
else TRACELOG ( LOG_INFO , " MODEL: [%s] M3D data loaded successfully: %i faces/%i materials " , fileName , m3d - > numface , m3d - > nummaterial ) ;
2022-08-26 10:04:38 +02:00
// no face? this is probably just a material library
if ( ! m3d - > numface )
{
m3d_free ( m3d ) ;
UnloadFileData ( fileData ) ;
return model ;
}
2022-08-20 14:01:54 +02:00
if ( m3d - > nummaterial > 0 )
{
model . meshCount = model . materialCount = m3d - > nummaterial ;
TRACELOG ( LOG_INFO , " MODEL: model has %i material meshes " , model . materialCount ) ;
2022-08-22 11:11:05 +02:00
}
2022-08-20 14:01:54 +02:00
else
{
2023-08-10 22:45:25 +02:00
model . meshCount = 1 ; model . materialCount = 0 ;
2022-08-20 14:01:54 +02:00
TRACELOG ( LOG_INFO , " MODEL: No materials, putting all meshes in a default material " ) ;
}
2023-08-10 22:45:25 +02:00
// We always need a default material, so we add +1
model . materialCount + + ;
2024-04-20 20:31:06 +02:00
// Faces must be in non-decreasing materialid order. Verify that quickly, sorting them otherwise
2023-10-09 09:53:52 +02:00
// WARNING: Sorting is not needed, valid M3D model files should already be sorted
// Just keeping the sorting function for reference (Check PR #3363 #3385)
/*
2023-10-09 00:49:58 -07:00
for ( i = 1 ; i < m3d - > numface ; i + + )
2023-10-07 21:07:50 +02:00
{
2023-10-09 09:53:52 +02:00
if ( m3d - > face [ i - 1 ] . materialid < = m3d - > face [ i ] . materialid ) continue ;
2023-10-09 00:49:58 -07:00
2024-04-20 20:31:06 +02:00
// face[i-1] > face[i]. slide face[i] lower
2023-10-09 00:49:58 -07:00
m3df_t slider = m3d - > face [ i ] ;
j = i - 1 ;
do
2024-04-20 20:31:06 +02:00
{ // face[j] > slider, face[j+1] is svailable vacant gap
2023-10-09 00:49:58 -07:00
m3d - > face [ j + 1 ] = m3d - > face [ j ] ;
j = j - 1 ;
}
while ( j > = 0 & & m3d - > face [ j ] . materialid > slider . materialid ) ;
m3d - > face [ j + 1 ] = slider ;
2023-10-07 21:07:50 +02:00
}
2023-10-09 09:53:52 +02:00
*/
2023-10-08 03:02:05 +08:00
2022-08-20 14:01:54 +02:00
model . meshes = ( Mesh * ) RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
model . meshMaterial = ( int * ) RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2022-08-26 10:04:38 +02:00
model . materials = ( Material * ) RL_CALLOC ( model . materialCount + 1 , sizeof ( Material ) ) ;
2022-08-22 11:11:05 +02:00
2022-08-20 14:01:54 +02:00
// Map no material to index 0 with default shader, everything else materialid + 1
model . materials [ 0 ] = LoadMaterialDefault ( ) ;
2023-01-20 07:05:19 -08:00
for ( i = l = 0 , k = - 1 ; i < ( int ) m3d - > numface ; i + + , l + + )
2022-08-20 14:01:54 +02:00
{
// Materials are grouped together
if ( mi ! = m3d - > face [ i ] . materialid )
{
2023-02-09 07:17:47 -05:00
// there should be only one material switch per material kind, but be bulletproof for non-optimal model files
2022-08-26 10:04:38 +02:00
if ( k + 1 > = model . meshCount )
{
model . meshCount + + ;
model . meshes = ( Mesh * ) RL_REALLOC ( model . meshes , model . meshCount * sizeof ( Mesh ) ) ;
memset ( & model . meshes [ model . meshCount - 1 ] , 0 , sizeof ( Mesh ) ) ;
model . meshMaterial = ( int * ) RL_REALLOC ( model . meshMaterial , model . meshCount * sizeof ( int ) ) ;
}
2022-08-20 14:01:54 +02:00
k + + ;
mi = m3d - > face [ i ] . materialid ;
2022-08-22 11:11:05 +02:00
2023-08-10 22:45:25 +02:00
// Only allocate colors VertexBuffer if there's a color vertex in the model for this material batch
// if all colors are fully transparent black for all verteces of this materal, then we assume no vertex colors
for ( j = i , l = vcolor = 0 ; ( j < ( int ) m3d - > numface ) & & ( mi = = m3d - > face [ j ] . materialid ) ; j + + , l + + )
{
if ( ! m3d - > vertex [ m3d - > face [ j ] . vertex [ 0 ] ] . color | |
! m3d - > vertex [ m3d - > face [ j ] . vertex [ 1 ] ] . color | |
! m3d - > vertex [ m3d - > face [ j ] . vertex [ 2 ] ] . color ) vcolor = 1 ;
}
2022-08-22 11:11:05 +02:00
2022-08-20 14:01:54 +02:00
model . meshes [ k ] . vertexCount = l * 3 ;
model . meshes [ k ] . triangleCount = l ;
model . meshes [ k ] . vertices = ( float * ) RL_CALLOC ( model . meshes [ k ] . vertexCount * 3 , sizeof ( float ) ) ;
model . meshes [ k ] . texcoords = ( float * ) RL_CALLOC ( model . meshes [ k ] . vertexCount * 2 , sizeof ( float ) ) ;
model . meshes [ k ] . normals = ( float * ) RL_CALLOC ( model . meshes [ k ] . vertexCount * 3 , sizeof ( float ) ) ;
2023-02-05 16:30:23 +01:00
2023-08-10 22:45:25 +02:00
// If no map is provided, or we have colors defined, we allocate storage for vertex colors
// M3D specs only consider vertex colors if no material is provided, however raylib uses both and mixes the colors
if ( ( mi = = M3D_UNDEF ) | | vcolor ) model . meshes [ k ] . colors = RL_CALLOC ( model . meshes [ k ] . vertexCount * 4 , sizeof ( unsigned char ) ) ;
2024-04-02 09:38:06 +02:00
2024-03-07 12:10:26 +01:00
// If no map is provided and we allocated vertex colors, set them to white
if ( ( mi = = M3D_UNDEF ) & & ( model . meshes [ k ] . colors ! = NULL ) )
{
for ( int c = 0 ; c < model . meshes [ k ] . vertexCount * 4 ; c + + ) model . meshes [ k ] . colors [ c ] = 255 ;
}
2023-01-30 17:05:12 +01:00
2022-08-26 10:04:38 +02:00
if ( m3d - > numbone & & m3d - > numskin )
{
model . meshes [ k ] . boneIds = ( unsigned char * ) RL_CALLOC ( model . meshes [ k ] . vertexCount * 4 , sizeof ( unsigned char ) ) ;
model . meshes [ k ] . boneWeights = ( float * ) RL_CALLOC ( model . meshes [ k ] . vertexCount * 4 , sizeof ( float ) ) ;
model . meshes [ k ] . animVertices = ( float * ) RL_CALLOC ( model . meshes [ k ] . vertexCount * 3 , sizeof ( float ) ) ;
model . meshes [ k ] . animNormals = ( float * ) RL_CALLOC ( model . meshes [ k ] . vertexCount * 3 , sizeof ( float ) ) ;
}
2023-02-05 16:30:23 +01:00
2022-08-20 14:01:54 +02:00
model . meshMaterial [ k ] = mi + 1 ;
l = 0 ;
}
2022-08-22 11:11:05 +02:00
2022-08-20 14:01:54 +02:00
// Process meshes per material, add triangles
2023-01-30 17:05:12 +01:00
model . meshes [ k ] . vertices [ l * 9 + 0 ] = m3d - > vertex [ m3d - > face [ i ] . vertex [ 0 ] ] . x * m3d - > scale ;
model . meshes [ k ] . vertices [ l * 9 + 1 ] = m3d - > vertex [ m3d - > face [ i ] . vertex [ 0 ] ] . y * m3d - > scale ;
model . meshes [ k ] . vertices [ l * 9 + 2 ] = m3d - > vertex [ m3d - > face [ i ] . vertex [ 0 ] ] . z * m3d - > scale ;
model . meshes [ k ] . vertices [ l * 9 + 3 ] = m3d - > vertex [ m3d - > face [ i ] . vertex [ 1 ] ] . x * m3d - > scale ;
model . meshes [ k ] . vertices [ l * 9 + 4 ] = m3d - > vertex [ m3d - > face [ i ] . vertex [ 1 ] ] . y * m3d - > scale ;
model . meshes [ k ] . vertices [ l * 9 + 5 ] = m3d - > vertex [ m3d - > face [ i ] . vertex [ 1 ] ] . z * m3d - > scale ;
model . meshes [ k ] . vertices [ l * 9 + 6 ] = m3d - > vertex [ m3d - > face [ i ] . vertex [ 2 ] ] . x * m3d - > scale ;
model . meshes [ k ] . vertices [ l * 9 + 7 ] = m3d - > vertex [ m3d - > face [ i ] . vertex [ 2 ] ] . y * m3d - > scale ;
model . meshes [ k ] . vertices [ l * 9 + 8 ] = m3d - > vertex [ m3d - > face [ i ] . vertex [ 2 ] ] . z * m3d - > scale ;
2023-02-05 16:30:23 +01:00
2023-10-07 21:07:50 +02:00
// Without vertex color (full transparency), we use the default color
2023-01-30 17:05:12 +01:00
if ( model . meshes [ k ] . colors ! = NULL )
2022-08-26 10:04:38 +02:00
{
if ( m3d - > vertex [ m3d - > face [ i ] . vertex [ 0 ] ] . color & 0xFF000000 )
2023-01-30 17:05:12 +01:00
memcpy ( & model . meshes [ k ] . colors [ l * 12 + 0 ] , & m3d - > vertex [ m3d - > face [ i ] . vertex [ 0 ] ] . color , 4 ) ;
2022-08-26 10:04:38 +02:00
if ( m3d - > vertex [ m3d - > face [ i ] . vertex [ 1 ] ] . color & 0xFF000000 )
2023-01-30 17:05:12 +01:00
memcpy ( & model . meshes [ k ] . colors [ l * 12 + 4 ] , & m3d - > vertex [ m3d - > face [ i ] . vertex [ 1 ] ] . color , 4 ) ;
2022-08-26 10:04:38 +02:00
if ( m3d - > vertex [ m3d - > face [ i ] . vertex [ 2 ] ] . color & 0xFF000000 )
2023-01-30 17:05:12 +01:00
memcpy ( & model . meshes [ k ] . colors [ l * 12 + 8 ] , & m3d - > vertex [ m3d - > face [ i ] . vertex [ 2 ] ] . color , 4 ) ;
2022-08-26 10:04:38 +02:00
}
2022-08-20 14:01:54 +02:00
if ( m3d - > face [ i ] . texcoord [ 0 ] ! = M3D_UNDEF )
{
2023-01-02 20:46:33 +01:00
model . meshes [ k ] . texcoords [ l * 6 + 0 ] = m3d - > tmap [ m3d - > face [ i ] . texcoord [ 0 ] ] . u ;
2023-01-20 07:05:19 -08:00
model . meshes [ k ] . texcoords [ l * 6 + 1 ] = 1.0f - m3d - > tmap [ m3d - > face [ i ] . texcoord [ 0 ] ] . v ;
2023-01-02 20:46:33 +01:00
model . meshes [ k ] . texcoords [ l * 6 + 2 ] = m3d - > tmap [ m3d - > face [ i ] . texcoord [ 1 ] ] . u ;
2023-01-20 07:05:19 -08:00
model . meshes [ k ] . texcoords [ l * 6 + 3 ] = 1.0f - m3d - > tmap [ m3d - > face [ i ] . texcoord [ 1 ] ] . v ;
2023-01-02 20:46:33 +01:00
model . meshes [ k ] . texcoords [ l * 6 + 4 ] = m3d - > tmap [ m3d - > face [ i ] . texcoord [ 2 ] ] . u ;
2023-01-20 07:05:19 -08:00
model . meshes [ k ] . texcoords [ l * 6 + 5 ] = 1.0f - m3d - > tmap [ m3d - > face [ i ] . texcoord [ 2 ] ] . v ;
2022-08-20 14:01:54 +02:00
}
2022-08-22 11:11:05 +02:00
2022-08-20 14:01:54 +02:00
if ( m3d - > face [ i ] . normal [ 0 ] ! = M3D_UNDEF )
{
2023-01-02 20:46:33 +01:00
model . meshes [ k ] . normals [ l * 9 + 0 ] = m3d - > vertex [ m3d - > face [ i ] . normal [ 0 ] ] . x ;
model . meshes [ k ] . normals [ l * 9 + 1 ] = m3d - > vertex [ m3d - > face [ i ] . normal [ 0 ] ] . y ;
model . meshes [ k ] . normals [ l * 9 + 2 ] = m3d - > vertex [ m3d - > face [ i ] . normal [ 0 ] ] . z ;
model . meshes [ k ] . normals [ l * 9 + 3 ] = m3d - > vertex [ m3d - > face [ i ] . normal [ 1 ] ] . x ;
model . meshes [ k ] . normals [ l * 9 + 4 ] = m3d - > vertex [ m3d - > face [ i ] . normal [ 1 ] ] . y ;
model . meshes [ k ] . normals [ l * 9 + 5 ] = m3d - > vertex [ m3d - > face [ i ] . normal [ 1 ] ] . z ;
model . meshes [ k ] . normals [ l * 9 + 6 ] = m3d - > vertex [ m3d - > face [ i ] . normal [ 2 ] ] . x ;
model . meshes [ k ] . normals [ l * 9 + 7 ] = m3d - > vertex [ m3d - > face [ i ] . normal [ 2 ] ] . y ;
model . meshes [ k ] . normals [ l * 9 + 8 ] = m3d - > vertex [ m3d - > face [ i ] . normal [ 2 ] ] . z ;
2022-08-20 14:01:54 +02:00
}
2022-08-26 10:04:38 +02:00
// Add skin (vertex / bone weight pairs)
2022-09-01 20:46:06 +02:00
if ( m3d - > numbone & & m3d - > numskin )
{
for ( n = 0 ; n < 3 ; n + + )
{
2022-08-26 10:04:38 +02:00
int skinid = m3d - > vertex [ m3d - > face [ i ] . vertex [ n ] ] . skinid ;
2022-09-01 20:46:06 +02:00
// Check if there is a skin for this mesh, should be, just failsafe
2023-09-22 11:58:24 +02:00
if ( ( skinid ! = M3D_UNDEF ) & & ( skinid < ( int ) m3d - > numskin ) )
2022-08-26 10:04:38 +02:00
{
for ( j = 0 ; j < 4 ; j + + )
{
2022-09-01 20:46:06 +02:00
model . meshes [ k ] . boneIds [ l * 12 + n * 4 + j ] = m3d - > skin [ skinid ] . boneid [ j ] ;
model . meshes [ k ] . boneWeights [ l * 12 + n * 4 + j ] = m3d - > skin [ skinid ] . weight [ j ] ;
2022-08-26 10:04:38 +02:00
}
}
2022-09-01 20:46:06 +02:00
else
{
// raylib does not handle boneless meshes with skeletal animations, so
// we put all vertices without a bone into a special "no bone" bone
2023-01-02 20:46:33 +01:00
model . meshes [ k ] . boneIds [ l * 12 + n * 4 ] = m3d - > numbone ;
model . meshes [ k ] . boneWeights [ l * 12 + n * 4 ] = 1.0f ;
2022-09-01 20:46:06 +02:00
}
2022-08-26 10:04:38 +02:00
}
}
2022-08-20 14:01:54 +02:00
}
2022-08-26 10:04:38 +02:00
// Load materials
2023-01-20 07:05:19 -08:00
for ( i = 0 ; i < ( int ) m3d - > nummaterial ; i + + )
2022-08-20 14:01:54 +02:00
{
model . materials [ i + 1 ] = LoadMaterialDefault ( ) ;
2022-08-22 11:11:05 +02:00
2022-08-20 14:01:54 +02:00
for ( j = 0 ; j < m3d - > material [ i ] . numprop ; j + + )
{
prop = & m3d - > material [ i ] . prop [ j ] ;
2022-08-22 11:11:05 +02:00
2022-08-20 14:01:54 +02:00
switch ( prop - > type )
{
case m3dp_Kd :
{
memcpy ( & model . materials [ i + 1 ] . maps [ MATERIAL_MAP_DIFFUSE ] . color , & prop - > value . color , 4 ) ;
model . materials [ i + 1 ] . maps [ MATERIAL_MAP_DIFFUSE ] . value = 0.0f ;
} break ;
case m3dp_Ks :
{
memcpy ( & model . materials [ i + 1 ] . maps [ MATERIAL_MAP_SPECULAR ] . color , & prop - > value . color , 4 ) ;
} break ;
2022-08-22 11:11:05 +02:00
case m3dp_Ns :
{
model . materials [ i + 1 ] . maps [ MATERIAL_MAP_SPECULAR ] . value = prop - > value . fnum ;
} break ;
2022-08-20 14:01:54 +02:00
case m3dp_Ke :
{
memcpy ( & model . materials [ i + 1 ] . maps [ MATERIAL_MAP_EMISSION ] . color , & prop - > value . color , 4 ) ;
model . materials [ i + 1 ] . maps [ MATERIAL_MAP_EMISSION ] . value = 0.0f ;
} break ;
case m3dp_Pm :
{
model . materials [ i + 1 ] . maps [ MATERIAL_MAP_METALNESS ] . value = prop - > value . fnum ;
} break ;
case m3dp_Pr :
{
model . materials [ i + 1 ] . maps [ MATERIAL_MAP_ROUGHNESS ] . value = prop - > value . fnum ;
} break ;
case m3dp_Ps :
{
model . materials [ i + 1 ] . maps [ MATERIAL_MAP_NORMAL ] . color = WHITE ;
model . materials [ i + 1 ] . maps [ MATERIAL_MAP_NORMAL ] . value = prop - > value . fnum ;
} break ;
2022-08-22 11:11:05 +02:00
default :
{
if ( prop - > type > = 128 )
{
Image image = { 0 } ;
image . data = m3d - > texture [ prop - > value . textureid ] . d ;
2022-08-26 10:04:38 +02:00
image . width = m3d - > texture [ prop - > value . textureid ] . w ;
2022-08-22 11:11:05 +02:00
image . height = m3d - > texture [ prop - > value . textureid ] . h ;
image . mipmaps = 1 ;
2022-08-26 10:04:38 +02:00
image . format = ( m3d - > texture [ prop - > value . textureid ] . f = = 4 ) ? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 :
( ( m3d - > texture [ prop - > value . textureid ] . f = = 3 ) ? PIXELFORMAT_UNCOMPRESSED_R8G8B8 :
2022-08-22 11:11:05 +02:00
( ( m3d - > texture [ prop - > value . textureid ] . f = = 2 ) ? PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA : PIXELFORMAT_UNCOMPRESSED_GRAYSCALE ) ) ;
switch ( prop - > type )
{
case m3dp_map_Kd : model . materials [ i + 1 ] . maps [ MATERIAL_MAP_DIFFUSE ] . texture = LoadTextureFromImage ( image ) ; break ;
case m3dp_map_Ks : model . materials [ i + 1 ] . maps [ MATERIAL_MAP_SPECULAR ] . texture = LoadTextureFromImage ( image ) ; break ;
case m3dp_map_Ke : model . materials [ i + 1 ] . maps [ MATERIAL_MAP_EMISSION ] . texture = LoadTextureFromImage ( image ) ; break ;
case m3dp_map_Km : model . materials [ i + 1 ] . maps [ MATERIAL_MAP_NORMAL ] . texture = LoadTextureFromImage ( image ) ; break ;
2022-08-26 10:04:38 +02:00
case m3dp_map_Ka : model . materials [ i + 1 ] . maps [ MATERIAL_MAP_OCCLUSION ] . texture = LoadTextureFromImage ( image ) ; break ;
case m3dp_map_Pm : model . materials [ i + 1 ] . maps [ MATERIAL_MAP_ROUGHNESS ] . texture = LoadTextureFromImage ( image ) ; break ;
2022-08-22 11:11:05 +02:00
default : break ;
}
}
} break ;
2022-08-20 14:01:54 +02:00
}
}
}
2022-08-26 10:04:38 +02:00
// Load bones
2022-09-01 20:46:06 +02:00
if ( m3d - > numbone )
2022-08-26 10:04:38 +02:00
{
2022-09-01 20:46:06 +02:00
model . boneCount = m3d - > numbone + 1 ;
model . bones = RL_CALLOC ( model . boneCount , sizeof ( BoneInfo ) ) ;
model . bindPose = RL_CALLOC ( model . boneCount , sizeof ( Transform ) ) ;
2023-01-20 07:05:19 -08:00
for ( i = 0 ; i < ( int ) m3d - > numbone ; i + + )
2022-08-26 10:04:38 +02:00
{
model . bones [ i ] . parent = m3d - > bone [ i ] . parent ;
strncpy ( model . bones [ i ] . name , m3d - > bone [ i ] . name , sizeof ( model . bones [ i ] . name ) ) ;
2022-09-04 18:49:54 +02:00
model . bindPose [ i ] . translation . x = m3d - > vertex [ m3d - > bone [ i ] . pos ] . x * m3d - > scale ;
model . bindPose [ i ] . translation . y = m3d - > vertex [ m3d - > bone [ i ] . pos ] . y * m3d - > scale ;
model . bindPose [ i ] . translation . z = m3d - > vertex [ m3d - > bone [ i ] . pos ] . z * m3d - > scale ;
2022-08-26 10:04:38 +02:00
model . bindPose [ i ] . rotation . x = m3d - > vertex [ m3d - > bone [ i ] . ori ] . x ;
model . bindPose [ i ] . rotation . y = m3d - > vertex [ m3d - > bone [ i ] . ori ] . y ;
model . bindPose [ i ] . rotation . z = m3d - > vertex [ m3d - > bone [ i ] . ori ] . z ;
model . bindPose [ i ] . rotation . w = m3d - > vertex [ m3d - > bone [ i ] . ori ] . w ;
2023-02-05 16:30:23 +01:00
2023-04-22 21:17:53 +02:00
// TODO: If the orientation quaternion is not normalized, then that's encoding scaling
2022-08-26 10:04:38 +02:00
model . bindPose [ i ] . rotation = QuaternionNormalize ( model . bindPose [ i ] . rotation ) ;
model . bindPose [ i ] . scale . x = model . bindPose [ i ] . scale . y = model . bindPose [ i ] . scale . z = 1.0f ;
2022-09-01 10:27:16 +02:00
// Child bones are stored in parent bone relative space, convert that into model space
if ( model . bones [ i ] . parent > = 0 )
{
model . bindPose [ i ] . rotation = QuaternionMultiply ( model . bindPose [ model . bones [ i ] . parent ] . rotation , model . bindPose [ i ] . rotation ) ;
model . bindPose [ i ] . translation = Vector3RotateByQuaternion ( model . bindPose [ i ] . translation , model . bindPose [ model . bones [ i ] . parent ] . rotation ) ;
model . bindPose [ i ] . translation = Vector3Add ( model . bindPose [ i ] . translation , model . bindPose [ model . bones [ i ] . parent ] . translation ) ;
model . bindPose [ i ] . scale = Vector3Multiply ( model . bindPose [ i ] . scale , model . bindPose [ model . bones [ i ] . parent ] . scale ) ;
}
2022-08-26 10:04:38 +02:00
}
2022-09-01 20:46:06 +02:00
// Add a special "no bone" bone
model . bones [ i ] . parent = - 1 ;
strcpy ( model . bones [ i ] . name , " NO BONE " ) ;
model . bindPose [ i ] . translation . x = 0.0f ;
model . bindPose [ i ] . translation . y = 0.0f ;
model . bindPose [ i ] . translation . z = 0.0f ;
model . bindPose [ i ] . rotation . x = 0.0f ;
model . bindPose [ i ] . rotation . y = 0.0f ;
model . bindPose [ i ] . rotation . z = 0.0f ;
model . bindPose [ i ] . rotation . w = 1.0f ;
model . bindPose [ i ] . scale . x = model . bindPose [ i ] . scale . y = model . bindPose [ i ] . scale . z = 1.0f ;
2022-08-26 10:04:38 +02:00
}
// Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets
2024-04-20 20:31:06 +02:00
// called, but not before, however DrawMesh uses these if they exist (so not good if they are left empty)
2022-08-26 10:04:38 +02:00
if ( m3d - > numbone & & m3d - > numskin )
{
2024-04-20 13:53:13 +02:00
for ( i = 0 ; i < model . meshCount ; i + + )
2022-08-26 10:04:38 +02:00
{
memcpy ( model . meshes [ i ] . animVertices , model . meshes [ i ] . vertices , model . meshes [ i ] . vertexCount * 3 * sizeof ( float ) ) ;
memcpy ( model . meshes [ i ] . animNormals , model . meshes [ i ] . normals , model . meshes [ i ] . vertexCount * 3 * sizeof ( float ) ) ;
2024-10-21 17:00:52 +02:00
2024-09-20 11:30:37 -04:00
model . meshes [ i ] . boneCount = model . boneCount ;
model . meshes [ i ] . boneMatrices = RL_CALLOC ( model . meshes [ i ] . boneCount , sizeof ( Matrix ) ) ;
for ( j = 0 ; j < model . meshes [ i ] . boneCount ; j + + )
{
model . meshes [ i ] . boneMatrices [ j ] = MatrixIdentity ( ) ;
}
2022-08-26 10:04:38 +02:00
}
}
2022-08-20 14:01:54 +02:00
m3d_free ( m3d ) ;
UnloadFileData ( fileData ) ;
}
return model ;
}
2022-08-26 10:04:38 +02:00
2023-01-02 20:46:33 +01:00
# define M3D_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms)
2022-08-26 10:04:38 +02:00
// Load M3D animation data
2023-09-02 12:05:34 +02:00
static ModelAnimation * LoadModelAnimationsM3D ( const char * fileName , int * animCount )
2022-08-26 10:04:38 +02:00
{
ModelAnimation * animations = NULL ;
2023-10-09 11:07:02 +02:00
2023-09-02 12:05:34 +02:00
m3d_t * m3d = NULL ;
2023-01-02 20:46:33 +01:00
int i = 0 , j = 0 ;
2022-08-26 10:04:38 +02:00
* animCount = 0 ;
2023-09-02 12:05:34 +02:00
int dataSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
2022-08-26 10:04:38 +02:00
if ( fileData ! = NULL )
{
m3d = m3d_load ( fileData , m3d_loaderhook , m3d_freehook , NULL ) ;
if ( ! m3d | | M3D_ERR_ISFATAL ( m3d - > errcode ) )
{
2023-10-08 18:10:05 +02:00
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to load M3D data, error code %d " , fileName , m3d ? m3d - > errcode : - 2 ) ;
2022-08-26 10:04:38 +02:00
UnloadFileData ( fileData ) ;
return NULL ;
}
else TRACELOG ( LOG_INFO , " MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins " , fileName ,
m3d - > numaction , m3d - > numbone , m3d - > numskin ) ;
2023-01-02 20:46:33 +01:00
// No animation or bone+skin?
2022-08-26 10:04:38 +02:00
if ( ! m3d - > numaction | | ! m3d - > numbone | | ! m3d - > numskin )
{
m3d_free ( m3d ) ;
UnloadFileData ( fileData ) ;
return NULL ;
}
animations = RL_MALLOC ( m3d - > numaction * sizeof ( ModelAnimation ) ) ;
* animCount = m3d - > numaction ;
for ( unsigned int a = 0 ; a < m3d - > numaction ; a + + )
{
2024-04-21 12:29:09 +02:00
animations [ a ] . frameCount = m3d - > action [ a ] . durationmsec / M3D_ANIMDELAY ;
2022-09-01 20:46:06 +02:00
animations [ a ] . boneCount = m3d - > numbone + 1 ;
animations [ a ] . bones = RL_MALLOC ( ( m3d - > numbone + 1 ) * sizeof ( BoneInfo ) ) ;
2022-08-26 10:04:38 +02:00
animations [ a ] . framePoses = RL_MALLOC ( animations [ a ] . frameCount * sizeof ( Transform * ) ) ;
2024-02-29 18:50:44 +01:00
strncpy ( animations [ a ] . name , m3d - > action [ a ] . name , sizeof ( animations [ a ] . name ) ) ;
animations [ a ] . name [ sizeof ( animations [ a ] . name ) - 1 ] = ' \0 ' ;
2024-04-02 09:38:06 +02:00
2022-08-26 10:04:38 +02:00
TRACELOG ( LOG_INFO , " MODEL: [%s] animation #%i: %i msec, %i frames " , fileName , a , m3d - > action [ a ] . durationmsec , animations [ a ] . frameCount ) ;
2023-01-20 07:05:19 -08:00
for ( i = 0 ; i < ( int ) m3d - > numbone ; i + + )
2022-08-26 10:04:38 +02:00
{
animations [ a ] . bones [ i ] . parent = m3d - > bone [ i ] . parent ;
strncpy ( animations [ a ] . bones [ i ] . name , m3d - > bone [ i ] . name , sizeof ( animations [ a ] . bones [ i ] . name ) ) ;
}
2022-09-01 20:46:06 +02:00
// A special, never transformed "no bone" bone, used for boneless vertices
animations [ a ] . bones [ i ] . parent = - 1 ;
strcpy ( animations [ a ] . bones [ i ] . name , " NO BONE " ) ;
2022-08-26 10:04:38 +02:00
// M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at
// regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones
for ( i = 0 ; i < animations [ a ] . frameCount ; i + + )
{
2022-09-01 20:46:06 +02:00
animations [ a ] . framePoses [ i ] = RL_MALLOC ( ( m3d - > numbone + 1 ) * sizeof ( Transform ) ) ;
2022-08-26 10:04:38 +02:00
2023-01-02 20:46:33 +01:00
m3db_t * pose = m3d_pose ( m3d , a , i * M3D_ANIMDELAY ) ;
2023-02-05 16:30:23 +01:00
2022-08-26 10:04:38 +02:00
if ( pose ! = NULL )
{
2023-01-20 07:05:19 -08:00
for ( j = 0 ; j < ( int ) m3d - > numbone ; j + + )
2022-08-26 10:04:38 +02:00
{
2022-09-04 18:49:54 +02:00
animations [ a ] . framePoses [ i ] [ j ] . translation . x = m3d - > vertex [ pose [ j ] . pos ] . x * m3d - > scale ;
animations [ a ] . framePoses [ i ] [ j ] . translation . y = m3d - > vertex [ pose [ j ] . pos ] . y * m3d - > scale ;
animations [ a ] . framePoses [ i ] [ j ] . translation . z = m3d - > vertex [ pose [ j ] . pos ] . z * m3d - > scale ;
2022-08-26 10:04:38 +02:00
animations [ a ] . framePoses [ i ] [ j ] . rotation . x = m3d - > vertex [ pose [ j ] . ori ] . x ;
animations [ a ] . framePoses [ i ] [ j ] . rotation . y = m3d - > vertex [ pose [ j ] . ori ] . y ;
animations [ a ] . framePoses [ i ] [ j ] . rotation . z = m3d - > vertex [ pose [ j ] . ori ] . z ;
animations [ a ] . framePoses [ i ] [ j ] . rotation . w = m3d - > vertex [ pose [ j ] . ori ] . w ;
animations [ a ] . framePoses [ i ] [ j ] . rotation = QuaternionNormalize ( animations [ a ] . framePoses [ i ] [ j ] . rotation ) ;
animations [ a ] . framePoses [ i ] [ j ] . scale . x = animations [ a ] . framePoses [ i ] [ j ] . scale . y = animations [ a ] . framePoses [ i ] [ j ] . scale . z = 1.0f ;
2022-09-01 10:27:16 +02:00
2022-09-01 20:46:06 +02:00
// Child bones are stored in parent bone relative space, convert that into model space
2022-09-01 10:27:16 +02:00
if ( animations [ a ] . bones [ j ] . parent > = 0 )
{
animations [ a ] . framePoses [ i ] [ j ] . rotation = QuaternionMultiply ( animations [ a ] . framePoses [ i ] [ animations [ a ] . bones [ j ] . parent ] . rotation , animations [ a ] . framePoses [ i ] [ j ] . rotation ) ;
animations [ a ] . framePoses [ i ] [ j ] . translation = Vector3RotateByQuaternion ( animations [ a ] . framePoses [ i ] [ j ] . translation , animations [ a ] . framePoses [ i ] [ animations [ a ] . bones [ j ] . parent ] . rotation ) ;
animations [ a ] . framePoses [ i ] [ j ] . translation = Vector3Add ( animations [ a ] . framePoses [ i ] [ j ] . translation , animations [ a ] . framePoses [ i ] [ animations [ a ] . bones [ j ] . parent ] . translation ) ;
animations [ a ] . framePoses [ i ] [ j ] . scale = Vector3Multiply ( animations [ a ] . framePoses [ i ] [ j ] . scale , animations [ a ] . framePoses [ i ] [ animations [ a ] . bones [ j ] . parent ] . scale ) ;
}
2022-08-26 10:04:38 +02:00
}
2022-09-01 20:46:06 +02:00
// Default transform for the "no bone" bone
animations [ a ] . framePoses [ i ] [ j ] . translation . x = 0.0f ;
animations [ a ] . framePoses [ i ] [ j ] . translation . y = 0.0f ;
animations [ a ] . framePoses [ i ] [ j ] . translation . z = 0.0f ;
animations [ a ] . framePoses [ i ] [ j ] . rotation . x = 0.0f ;
animations [ a ] . framePoses [ i ] [ j ] . rotation . y = 0.0f ;
animations [ a ] . framePoses [ i ] [ j ] . rotation . z = 0.0f ;
animations [ a ] . framePoses [ i ] [ j ] . rotation . w = 1.0f ;
animations [ a ] . framePoses [ i ] [ j ] . scale . x = animations [ a ] . framePoses [ i ] [ j ] . scale . y = animations [ a ] . framePoses [ i ] [ j ] . scale . z = 1.0f ;
2022-08-26 10:04:38 +02:00
RL_FREE ( pose ) ;
}
}
}
m3d_free ( m3d ) ;
UnloadFileData ( fileData ) ;
}
return animations ;
}
2022-08-20 14:01:54 +02:00
# endif
2021-12-04 19:56:02 +01:00
# endif // SUPPORT_MODULE_RMODELS