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 :
2013-11-18 23:38:44 +01:00
*
2017-03-26 22:49:01 +02:00
* # define SUPPORT_FILEFORMAT_OBJ
2017-02-16 00:50:02 +01:00
* # define SUPPORT_FILEFORMAT_MTL
2018-09-05 10:59:05 +02:00
* # define SUPPORT_FILEFORMAT_IQM
* # define SUPPORT_FILEFORMAT_GLTF
2021-09-04 19:55:09 +02:00
* # define SUPPORT_FILEFORMAT_VOX
2021-09-22 21:56:38 +02:00
*
2018-09-05 10:59:05 +02:00
* Selected desired fileformats to be supported for model data loading .
2017-02-16 00:50:02 +01:00
*
2017-09-18 00:59:22 +02:00
* # 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-02-16 00:50:02 +01:00
*
* LICENSE : zlib / libpng
2016-11-16 18:46:13 +01:00
*
2021-01-02 18:15:13 +01:00
* Copyright ( c ) 2013 - 2021 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-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()
2020-02-28 12:54:39 +01:00
# include <stdlib.h> // Required for: malloc(), free()
2020-05-23 19:48:40 +02:00
# include <string.h> // Required for: memcmp(), strlen()
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-09-05 20:39:34 +02:00
# include "external/vox_loader.h" // vox file format loading (MagikaVoxel)
2021-09-04 19:55:09 +02:00
# endif
2018-09-05 10:59:05 +02:00
# if defined(SUPPORT_MESH_GENERATION)
2020-03-17 13:40:07 +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)))
# define PAR_FREE RL_FREE
2020-03-24 13:27:49 +01:00
2018-09-05 10:59:05 +02:00
# define PAR_SHAPES_IMPLEMENTATION
# include "external/par_shapes.h" // Shapes 3d parametric generation
# 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
# define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh
# 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
2021-09-22 07:04:10 -04:00
static ModelAnimation * LoadIQMModelAnimations ( const char * fileName , unsigned 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
2021-09-22 07:04:10 -04:00
static ModelAnimation * LoadGLTFModelAnimations ( const char * fileName , unsigned int * animCount ) ; // Load GLTF animation data
2021-06-10 18:00:44 +02:00
static void LoadGLTFMaterial ( Model * model , const char * fileName , const cgltf_data * data ) ;
2021-09-23 21:06:51 +02:00
static void LoadGLTFMesh ( cgltf_data * data , cgltf_node * node , Model * outModel , Matrix currentTransform , int * primitiveIndex , const char * fileName ) ;
2021-08-28 12:01:16 +02:00
static void LoadGLTFNode ( cgltf_data * data , cgltf_node * node , Model * outModel , Matrix currentTransform , int * primitiveIndex , const char * fileName ) ;
2021-06-10 18:00:44 +02:00
static void InitGLTFBones ( Model * model , const cgltf_data * data ) ;
2021-09-23 21:06:51 +02:00
static void BindGLTFPrimitiveToBones ( Model * model , cgltf_node * node , const cgltf_data * data , int primitiveIndex ) ;
2021-08-28 12:01:16 +02:00
static void GetGLTFPrimitiveCount ( cgltf_node * node , int * outCount ) ;
static bool ReadGLTFValue ( cgltf_accessor * acc , unsigned int index , void * variable ) ;
static void * ReadGLTFValuesAs ( cgltf_accessor * acc , cgltf_component_type type , bool adjustOnDownCasting ) ;
2021-09-23 21:06:51 +02:00
static Matrix GetNodeTransformationMatrix ( cgltf_node * node , Matrix current ) ;
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
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
{
2021-04-13 00:05:21 +02:00
// WARNING: Be careful with internal buffer vertex alignment
// when using RL_LINES or RL_TRIANGLES, data is aligned to fit
// lines-triangles-quads in the same indexed buffers!!!
2021-04-13 01:08:39 +02:00
rlCheckRenderBatchLimit ( 8 ) ;
2021-03-18 13:41:58 +01:00
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 )
{
2021-04-13 01:08:39 +02:00
rlCheckRenderBatchLimit ( 8 ) ;
2020-02-03 19:26:28 +01:00
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
{
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( 2 * 36 ) ;
2019-02-21 18:45:19 +01:00
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 )
{
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( 3 ) ;
2020-05-07 12:46:45 +02:00
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
2021-09-01 23:09:30 +02:00
void DrawTriangleStrip3D ( Vector3 * points , int pointCount , Color color )
2020-05-07 12:46:45 +02:00
{
2021-09-01 23:09:30 +02:00
if ( pointCount > = 3 )
2020-05-07 12:46:45 +02:00
{
2021-09-01 23:09:30 +02:00
rlCheckRenderBatchLimit ( 3 * ( pointCount - 2 ) ) ;
2020-05-07 12:46:45 +02:00
rlBegin ( RL_TRIANGLES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2021-09-01 23:09:30 +02:00
for ( int i = 2 ; i < pointCount ; i + + )
2020-05-07 12:46:45 +02:00
{
if ( ( i % 2 ) = = 0 )
{
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 ) ;
}
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 ( ) ;
}
}
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
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( 36 ) ;
2013-11-23 13:30:54 +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
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
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
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
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
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
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
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( 36 ) ;
2014-09-03 16:51:28 +02: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 ) ;
2014-04-04 20:11:57 +02:00
// Front Face -----------------------------------------------------
// Bottom Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Left Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Top Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Right Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Back Face ------------------------------------------------------
// Bottom Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Left Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Top Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Right Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Top Face -------------------------------------------------------
// Left Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Right Line
2015-12-21 16:42:13 +01: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
// Bottom Face ---------------------------------------------------
// Left Line
2015-12-21 16:42:13 +01: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
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
// Right Line
2015-12-21 16:42:13 +01: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 ) ;
}
2014-04-04 20:11:57 +02:00
// Draw cube
// NOTE: Cube position is the center position
2015-12-21 16:42:13 +01:00
void DrawCubeTexture ( Texture2D texture , Vector3 position , float width , float height , float length , Color color )
2014-04-04 20:11:57 +02:00
{
float x = position . x ;
float y = position . y ;
float z = position . z ;
2019-09-13 12:29:52 +01:00
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( 36 ) ;
2014-04-04 20:11:57 +02:00
2021-03-25 14:28:12 +01:00
rlSetTexture ( texture . id ) ;
2014-09-03 16:51:28 +02:00
//rlPushMatrix();
2017-08-04 18:34:51 +02:00
// NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
2017-07-21 17:19:28 +02:00
//rlTranslatef(2.0f, 0.0f, 0.0f);
2017-08-04 18:34:51 +02:00
//rlRotatef(45, 0, 1, 0);
//rlScalef(2.0f, 2.0f, 2.0f);
2014-09-03 16:51:28 +02:00
2014-03-25 12:40:35 +01:00
rlBegin ( RL_QUADS ) ;
2014-09-03 16:51:28 +02:00
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
2013-11-23 13:30:54 +01:00
// Front Face
2014-03-25 12:40:35 +01:00
rlNormal3f ( 0.0f , 0.0f , 1.0f ) ; // Normal Pointing Towards Viewer
2017-08-04 18:34:51 +02:00
rlTexCoord2f ( 0.0f , 0.0f ) ; rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Left Of The Texture and Quad
rlTexCoord2f ( 1.0f , 0.0f ) ; rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Right Of The Texture and Quad
rlTexCoord2f ( 1.0f , 1.0f ) ; rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Top Right Of The Texture and Quad
rlTexCoord2f ( 0.0f , 1.0f ) ; rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Top Left Of The Texture and Quad
2013-11-23 13:30:54 +01:00
// Back Face
2017-08-04 18:34:51 +02:00
rlNormal3f ( 0.0f , 0.0f , - 1.0f ) ; // Normal Pointing Away From Viewer
rlTexCoord2f ( 1.0f , 0.0f ) ; rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Right Of The Texture and Quad
rlTexCoord2f ( 1.0f , 1.0f ) ; rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top Right Of The Texture and Quad
rlTexCoord2f ( 0.0f , 1.0f ) ; rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ; // Top Left Of The Texture and Quad
rlTexCoord2f ( 0.0f , 0.0f ) ; rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Left Of The Texture and Quad
2013-11-23 13:30:54 +01:00
// Top Face
2016-08-31 10:27:29 +02:00
rlNormal3f ( 0.0f , 1.0f , 0.0f ) ; // Normal Pointing Up
2017-08-04 18:34:51 +02:00
rlTexCoord2f ( 0.0f , 1.0f ) ; rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top Left Of The Texture and Quad
rlTexCoord2f ( 0.0f , 0.0f ) ; rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Bottom Left Of The Texture and Quad
rlTexCoord2f ( 1.0f , 0.0f ) ; rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Bottom Right Of The Texture and Quad
rlTexCoord2f ( 1.0f , 1.0f ) ; rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ; // Top Right Of The Texture and Quad
2013-11-23 13:30:54 +01:00
// Bottom Face
2017-08-04 18:34:51 +02:00
rlNormal3f ( 0.0f , - 1.0f , 0.0f ) ; // Normal Pointing Down
rlTexCoord2f ( 1.0f , 1.0f ) ; rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Top Right Of The Texture and Quad
rlTexCoord2f ( 0.0f , 1.0f ) ; rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Top Left Of The Texture and Quad
rlTexCoord2f ( 0.0f , 0.0f ) ; rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Left Of The Texture and Quad
rlTexCoord2f ( 1.0f , 0.0f ) ; rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Right Of The Texture and Quad
2013-11-23 13:30:54 +01:00
// Right face
2016-08-31 10:27:29 +02:00
rlNormal3f ( 1.0f , 0.0f , 0.0f ) ; // Normal Pointing Right
2017-08-04 18:34:51 +02:00
rlTexCoord2f ( 1.0f , 0.0f ) ; rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Right Of The Texture and Quad
rlTexCoord2f ( 1.0f , 1.0f ) ; rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ; // Top Right Of The Texture and Quad
rlTexCoord2f ( 0.0f , 1.0f ) ; rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ; // Top Left Of The Texture and Quad
rlTexCoord2f ( 0.0f , 0.0f ) ; rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Left Of The Texture and Quad
2013-11-23 13:30:54 +01:00
// Left Face
2017-08-04 18:34:51 +02:00
rlNormal3f ( - 1.0f , 0.0f , 0.0f ) ; // Normal Pointing Left
rlTexCoord2f ( 0.0f , 0.0f ) ; rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ; // Bottom Left Of The Texture and Quad
rlTexCoord2f ( 1.0f , 0.0f ) ; rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ; // Bottom Right Of The Texture and Quad
rlTexCoord2f ( 1.0f , 1.0f ) ; rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ; // Top Right Of The Texture and Quad
rlTexCoord2f ( 0.0f , 1.0f ) ; rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ; // Top Left Of The Texture and Quad
2014-03-25 12:40:35 +01:00
rlEnd ( ) ;
2014-04-19 16:36:49 +02:00
//rlPopMatrix();
2014-09-03 16:51:28 +02:00
2021-03-25 14:28:12 +01:00
rlSetTexture ( 0 ) ;
2013-11-18 23:38:44 +01:00
}
2021-09-22 21:56:38 +02:00
void DrawCubeTextureRec ( Texture2D texture , Rectangle source , Vector3 position , float width , float height , float length , Color color )
{
float x = position . x ;
float y = position . y ;
float z = position . z ;
float texture_width = ( float ) texture . width ;
float texture_height = ( float ) texture . height ;
rlCheckRenderBatchLimit ( 36 ) ;
rlSetTexture ( texture . id ) ;
rlBegin ( RL_QUADS ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
// Front Face
{
// Normal Pointing Towards Viewer
rlNormal3f ( 0.0f , 0.0f , 1.0f ) ;
// Bottom Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ;
// Bottom Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ;
// Top Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , source . y / texture_height ) ;
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ;
// Top Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , source . y / texture_height ) ;
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ;
}
// Back Face
{
// Normal Pointing Away From Viewer
rlNormal3f ( 0.0f , 0.0f , - 1.0f ) ;
// Bottom Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ;
// Top Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , source . y / texture_height ) ;
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ;
// Top Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , source . y / texture_height ) ;
rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ;
// Bottom Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ;
}
// Top Face
{
// Normal Pointing Up
rlNormal3f ( 0.0f , 1.0f , 0.0f ) ;
// Top Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , source . y / texture_height ) ;
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ;
// Bottom Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ;
// Bottom Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ;
// Top Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , source . y / texture_height ) ;
rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ;
}
// Bottom Face
{
// Normal Pointing Down
rlNormal3f ( 0.0f , - 1.0f , 0.0f ) ;
// Top Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , source . y / texture_height ) ;
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ;
// Top Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , source . y / texture_height ) ;
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ;
// Bottom Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ;
// Bottom Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ;
}
// Right face
{
// Normal Pointing Right
rlNormal3f ( 1.0f , 0.0f , 0.0f ) ;
// Bottom Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x + width / 2 , y - height / 2 , z - length / 2 ) ;
// Top Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , source . y / texture_height ) ;
rlVertex3f ( x + width / 2 , y + height / 2 , z - length / 2 ) ;
// Top Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , source . y / texture_height ) ;
rlVertex3f ( x + width / 2 , y + height / 2 , z + length / 2 ) ;
// Bottom Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x + width / 2 , y - height / 2 , z + length / 2 ) ;
}
// Left Face
{
// Normal Pointing Left
rlNormal3f ( - 1.0f , 0.0f , 0.0f ) ;
// Bottom Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x - width / 2 , y - height / 2 , z - length / 2 ) ;
// Bottom Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , ( source . y + source . height ) / texture_height ) ;
rlVertex3f ( x - width / 2 , y - height / 2 , z + length / 2 ) ;
// Top Right Of The Texture and Quad
rlTexCoord2f ( ( source . x + source . width ) / texture_width , source . y / texture_height ) ;
rlVertex3f ( x - width / 2 , y + height / 2 , z + length / 2 ) ;
// Top Left Of The Texture and Quad
rlTexCoord2f ( source . x / texture_width , source . y / texture_height ) ;
rlVertex3f ( x - width / 2 , y + height / 2 , z - length / 2 ) ;
}
rlEnd ( ) ;
rlSetTexture ( 0 ) ;
}
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 )
{
2019-04-14 22:29:14 +02:00
int numVertex = ( rings + 2 ) * slices * 6 ;
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( numVertex ) ;
2019-09-13 12:29:52 +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 -> 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
2016-05-21 18:22:15 +02:00
for ( int i = 0 ; i < ( rings + 2 ) ; i + + )
2013-11-23 13:30:54 +01:00
{
2016-05-21 18:22:15 +02:00
for ( int j = 0 ; j < slices ; j + + )
2013-11-23 13:30:54 +01: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 / 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 ) ) ) ;
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
{
2019-04-14 22:29:14 +02:00
int numVertex = ( rings + 2 ) * slices * 6 ;
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( numVertex ) ;
2019-09-13 12:29:52 +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
2019-04-14 22:29:14 +02:00
int numVertex = sides * 6 ;
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( numVertex ) ;
2014-09-03 16:51:28 +02:00
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 -------------------------------------------------------------------------------------
2016-05-21 18:22:15 +02:00
for ( int i = 0 ; i < 360 ; i + = 360 / sides )
2014-04-04 20:11:57 +02:00
{
2016-10-17 00:03:38 +02:00
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusBottom , 0 , cosf ( DEG2RAD * i ) * radiusBottom ) ; //Bottom Left
2021-05-14 14:01:42 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusBottom ) ; //Bottom Right
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusTop ) ; //Top Right
2014-09-03 16:51:28 +02:00
2016-10-17 00:03:38 +02:00
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusTop , height , cosf ( DEG2RAD * i ) * radiusTop ) ; //Top Left
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusBottom , 0 , cosf ( DEG2RAD * i ) * radiusBottom ) ; //Bottom Left
2021-05-14 14:01:42 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * 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 --------------------------------------------------------------------------------------
2016-05-21 18:22:15 +02:00
for ( int i = 0 ; i < 360 ; i + = 360 / sides )
2014-04-04 20:11:57 +02:00
{
rlVertex3f ( 0 , height , 0 ) ;
2016-10-17 00:03:38 +02:00
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusTop , height , cosf ( DEG2RAD * i ) * radiusTop ) ;
2021-05-14 14:01:42 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * 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 -------------------------------------------------------------------------------------
2016-05-21 18:22:15 +02:00
for ( int i = 0 ; i < 360 ; i + = 360 / sides )
2014-04-04 20:11:57 +02:00
{
rlVertex3f ( 0 , height , 0 ) ;
2016-10-17 00:03:38 +02:00
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusBottom , 0 , cosf ( DEG2RAD * i ) * radiusBottom ) ;
2021-05-14 14:01:42 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * 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 -----------------------------------------------------------------------------------------
2016-05-21 18:22:15 +02:00
for ( int i = 0 ; i < 360 ; i + = 360 / sides )
2014-03-25 12:40:35 +01:00
{
2014-04-04 20:11:57 +02:00
rlVertex3f ( 0 , 0 , 0 ) ;
2021-05-14 14:01:42 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusBottom ) ;
2016-10-17 00:03:38 +02:00
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusBottom , 0 , cosf ( DEG2RAD * i ) * radiusBottom ) ;
2014-03-25 12:40:35 +01:00
}
2014-09-03 16:51:28 +02:00
rlEnd ( ) ;
2014-04-04 20:11:57 +02:00
rlPopMatrix ( ) ;
}
// 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
2019-04-14 22:29:14 +02:00
int numVertex = sides * 8 ;
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( numVertex ) ;
2014-09-03 16:51:28 +02:00
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
2016-05-21 18:22:15 +02:00
for ( int i = 0 ; i < 360 ; i + = 360 / sides )
2014-03-25 12:40:35 +01:00
{
2016-10-17 00:03:38 +02:00
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusBottom , 0 , cosf ( DEG2RAD * i ) * radiusBottom ) ;
2021-05-14 14:01:42 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusBottom ) ;
2014-09-03 16:51:28 +02:00
2021-05-14 14:01:42 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusBottom ) ;
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusTop ) ;
2014-09-03 16:51:28 +02:00
2021-05-14 14:01:42 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360.0f / sides ) ) * radiusTop ) ;
2016-10-17 00:03:38 +02:00
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusTop , height , cosf ( DEG2RAD * i ) * radiusTop ) ;
2014-09-03 16:51:28 +02:00
2016-10-17 00:03:38 +02:00
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusTop , height , cosf ( DEG2RAD * i ) * radiusTop ) ;
rlVertex3f ( sinf ( DEG2RAD * i ) * radiusBottom , 0 , cosf ( DEG2RAD * i ) * 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
}
// 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
{
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( 4 ) ;
2019-09-13 12:29:52 +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
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( ( slices + 2 ) * 4 ) ;
2019-04-14 22:29:14 +02: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 ) ;
rlColor3f ( 0.5f , 0.5f , 0.5f ) ;
rlColor3f ( 0.5f , 0.5f , 0.5f ) ;
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 ) ;
rlColor3f ( 0.75f , 0.75f , 0.75f ) ;
rlColor3f ( 0.75f , 0.75f , 0.75f ) ;
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)
2020-09-13 16:40:34 +02:00
if ( IsFileExtension ( fileName , " .gltf;.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
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
2019-04-04 13:50:52 +02:00
if ( model . meshCount = = 0 )
2019-03-29 20:22:30 +01:00
{
model . meshCount = 1 ;
2019-04-23 14:55:35 +02:00
model . meshes = ( Mesh * ) RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
2020-01-17 23:06:46 +01:00
# if defined(SUPPORT_MESH_GENERATION)
2020-03-27 17:43:51 +01:00
TRACELOG ( LOG_WARNING , " MESH: [%s] Failed to load mesh data, default to cube mesh " , fileName ) ;
2019-03-29 20:22:30 +01:00
model . meshes [ 0 ] = GenMeshCube ( 1.0f , 1.0f , 1.0f ) ;
2020-01-17 23:06:46 +01:00
# else
2020-03-27 17:43:51 +01:00
TRACELOG ( LOG_WARNING , " MESH: [%s] Failed to load mesh data " , fileName ) ;
2020-01-17 23:06:46 +01:00
# endif
2019-03-29 20:22:30 +01:00
}
2019-04-04 13:50:52 +02:00
else
2019-04-04 13:33:54 +02:00
{
// Upload vertex data to GPU (static mesh)
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
}
2019-03-29 20:22:30 +01:00
if ( model . materialCount = = 0 )
{
2020-03-27 17:43:51 +01:00
TRACELOG ( LOG_WARNING , " MATERIAL: [%s] Failed to load 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
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,
2021-03-18 13:57:53 +01:00
// we don't unload the material but just free it's 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 " ) ;
}
// Unload model (but not meshes) from memory (RAM and/or VRAM)
void UnloadModelKeepMeshes ( Model model )
{
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,
2021-03-18 13:57:53 +01:00
// we don't unload the material but just free it's 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 ) ;
2019-04-04 13:50:52 +02:00
2020-12-01 23:44:10 +01:00
// Unload arrays
2019-04-23 14:55:35 +02:00
RL_FREE ( model . meshes ) ;
RL_FREE ( model . materials ) ;
RL_FREE ( model . meshMaterial ) ;
2019-04-04 13:50:52 +02:00
2019-04-04 13:33:54 +02:00
// Unload animation data
2019-04-23 14:55:35 +02:00
RL_FREE ( model . bones ) ;
RL_FREE ( model . bindPose ) ;
2016-08-16 11:09:55 +02:00
2020-12-01 23:44:10 +01:00
TRACELOG ( LOG_INFO , " MODEL: Unloaded model (but not meshes) from RAM and VRAM " ) ;
2017-07-17 00:33:40 +02:00
}
2016-08-16 11:09:55 +02:00
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 ;
}
}
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
2021-04-20 12:16:16 +02:00
mesh - > vboId [ 0 ] = 0 ; // Vertex buffer: positions
mesh - > vboId [ 1 ] = 0 ; // Vertex buffer: texcoords
mesh - > vboId [ 2 ] = 0 ; // Vertex buffer: normals
mesh - > vboId [ 3 ] = 0 ; // Vertex buffer: colors
mesh - > vboId [ 4 ] = 0 ; // Vertex buffer: tangents
mesh - > vboId [ 5 ] = 0 ; // Vertex buffer: texcoords2
mesh - > vboId [ 6 ] = 0 ; // Vertex buffer: indices
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 ) ;
// NOTE: Attributes must be uploaded considering default locations points
2021-04-01 20:24:33 +02:00
2021-03-25 14:28:12 +01:00
// Enable vertex attributes: position (shader-location = 0)
2021-06-26 13:45:15 +02:00
void * vertices = mesh - > animVertices ! = NULL ? mesh - > animVertices : mesh - > vertices ;
2021-06-26 14:22:00 +03:00
mesh - > vboId [ 0 ] = rlLoadVertexBuffer ( vertices , mesh - > vertexCount * 3 * sizeof ( float ) , dynamic ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( 0 , 3 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( 0 ) ;
// Enable vertex attributes: texcoords (shader-location = 1)
mesh - > vboId [ 1 ] = rlLoadVertexBuffer ( mesh - > texcoords , mesh - > vertexCount * 2 * sizeof ( float ) , dynamic ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( 1 , 2 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( 1 ) ;
if ( mesh - > normals ! = NULL )
{
// Enable vertex attributes: normals (shader-location = 2)
2021-07-13 05:18:52 -05:00
void * normals = mesh - > animNormals ! = NULL ? mesh - > animNormals : mesh - > normals ;
2021-06-26 14:22:00 +03:00
mesh - > vboId [ 2 ] = rlLoadVertexBuffer ( normals , mesh - > vertexCount * 3 * sizeof ( float ) , dynamic ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( 2 , 3 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( 2 ) ;
}
else
{
// Default color vertex attribute set to WHITE
float value [ 3 ] = { 1.0f , 1.0f , 1.0f } ;
rlSetVertexAttributeDefault ( 2 , value , SHADER_ATTRIB_VEC3 , 3 ) ;
rlDisableVertexAttribute ( 2 ) ;
}
if ( mesh - > colors ! = NULL )
{
// Enable vertex attribute: color (shader-location = 3)
mesh - > vboId [ 3 ] = rlLoadVertexBuffer ( mesh - > colors , mesh - > vertexCount * 4 * sizeof ( unsigned char ) , dynamic ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( 3 , 4 , RL_UNSIGNED_BYTE , 1 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( 3 ) ;
}
else
{
// Default color vertex attribute set to WHITE
float value [ 4 ] = { 1.0f , 1.0f , 1.0f , 1.0f } ;
rlSetVertexAttributeDefault ( 3 , value , SHADER_ATTRIB_VEC4 , 4 ) ;
rlDisableVertexAttribute ( 3 ) ;
}
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)
mesh - > vboId [ 4 ] = rlLoadVertexBuffer ( mesh - > tangents , mesh - > vertexCount * 4 * sizeof ( float ) , dynamic ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( 4 , 4 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( 4 ) ;
}
else
{
// Default tangents vertex attribute
float value [ 4 ] = { 0.0f , 0.0f , 0.0f , 0.0f } ;
rlSetVertexAttributeDefault ( 4 , value , SHADER_ATTRIB_VEC4 , 4 ) ;
rlDisableVertexAttribute ( 4 ) ;
}
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)
mesh - > vboId [ 5 ] = rlLoadVertexBuffer ( mesh - > texcoords2 , mesh - > vertexCount * 2 * sizeof ( float ) , dynamic ) ;
2021-03-25 20:35:48 +01:00
rlSetVertexAttribute ( 5 , 2 , RL_FLOAT , 0 , 0 , 0 ) ;
2021-03-25 14:28:12 +01:00
rlEnableVertexAttribute ( 5 ) ;
}
else
{
// Default texcoord2 vertex attribute
float value [ 2 ] = { 0.0f , 0.0f } ;
rlSetVertexAttributeDefault ( 5 , value , SHADER_ATTRIB_VEC2 , 2 ) ;
rlDisableVertexAttribute ( 5 ) ;
}
if ( mesh - > indices ! = NULL )
{
mesh - > vboId [ 6 ] = rlLoadVertexBufferElement ( mesh - > indices , mesh - > triangleCount * 3 * sizeof ( unsigned short ) , dynamic ) ;
}
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
void UpdateMeshBuffer ( Mesh mesh , int index , void * data , int dataSize , int offset )
{
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 ] = {
( 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 ,
2021-04-01 20:24:33 +02:00
( float ) material . maps [ SHADER_LOC_COLOR_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,
// just in case stereo render is required and we need to modify them
// 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
// Model transformation matrix is send 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 ] , transform ) ;
// 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 ( ) ) ;
// 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 ) ) ) ;
//-----------------------------------------------------
// 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 ) ;
}
}
// 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)
rlEnableVertexBuffer ( mesh . vboId [ 0 ] ) ;
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)
rlEnableVertexBuffer ( mesh . vboId [ 1 ] ) ;
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)
rlEnableVertexBuffer ( mesh . vboId [ 2 ] ) ;
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 )
{
if ( mesh . vboId [ 3 ] ! = 0 )
{
rlEnableVertexBuffer ( mesh . vboId [ 3 ] ) ;
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] , 4 , RL_UNSIGNED_BYTE , 1 , 0 , 0 ) ;
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 } ;
rlSetVertexAttributeDefault ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] , value , SHADER_ATTRIB_VEC2 , 4 ) ;
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
{
2021-08-27 12:13:44 +02:00
rlEnableVertexBuffer ( mesh . vboId [ 4 ] ) ;
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 )
{
rlEnableVertexBuffer ( mesh . vboId [ 5 ] ) ;
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD02 ] , 2 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_VERTEX_TEXCOORD02 ] ) ;
}
2021-04-01 20:24:33 +02:00
2021-08-27 12:13:44 +02:00
if ( mesh . indices ! = NULL ) rlEnableVertexBufferElement ( mesh . vboId [ 6 ] ) ;
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 ) ;
}
// Unbind all binded texture maps
for ( int i = 0 ; i < MAX_MATERIAL_MAPS ; i + + )
{
// 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 ( ) ;
}
// 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
void DrawMeshInstanced ( Mesh mesh , Material material , Matrix * transforms , int instances )
{
# 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,
// just in case stereo render is required and we need to modify them
// 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 ) ;
// This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData().
// 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 ) ;
// Instances transformation matrices are send to shader attribute location: SHADER_LOC_MATRIX_MODEL
for ( unsigned int i = 0 ; i < 4 ; i + + )
{
rlEnableVertexAttribute ( material . shader . locs [ SHADER_LOC_MATRIX_MODEL ] + i ) ;
rlSetVertexAttribute ( material . shader . locs [ SHADER_LOC_MATRIX_MODEL ] + i , 4 , RL_FLOAT , 0 , sizeof ( Matrix ) , ( void * ) ( i * sizeof ( Vector4 ) ) ) ;
rlSetVertexAttributeDivisor ( material . shader . locs [ SHADER_LOC_MATRIX_MODEL ] + 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 ) ) ) ;
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)
rlEnableVertexBuffer ( mesh . vboId [ 0 ] ) ;
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)
rlEnableVertexBuffer ( mesh . vboId [ 1 ] ) ;
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)
rlEnableVertexBuffer ( mesh . vboId [ 2 ] ) ;
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 )
{
if ( mesh . vboId [ 3 ] ! = 0 )
{
rlEnableVertexBuffer ( mesh . vboId [ 3 ] ) ;
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 } ;
rlSetVertexAttributeDefault ( material . shader . locs [ SHADER_LOC_VERTEX_COLOR ] , value , SHADER_ATTRIB_VEC2 , 4 ) ;
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 )
{
rlEnableVertexBuffer ( mesh . vboId [ 4 ] ) ;
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 )
{
rlEnableVertexBuffer ( mesh . vboId [ 5 ] ) ;
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 ] ) ;
}
if ( mesh . indices ! = NULL ) rlEnableVertexBufferElement ( mesh . vboId [ 6 ] ) ;
}
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
}
// Unbind all binded texture maps
for ( int i = 0 ; i < MAX_MATERIAL_MAPS ; i + + )
{
// Select current shader texture slot
rlActiveTextureSlot ( i ) ;
2021-04-01 20:24:33 +02:00
2021-03-25 14:28:12 +01:00
// Disable 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 ) ) rlDisableTextureCubemap ( ) ;
else rlDisableTexture ( ) ;
}
// 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 ) ;
for ( int i = 0 ; i < MAX_MESH_VERTEX_BUFFERS ; i + + ) rlUnloadVertexBuffer ( mesh . vboId [ i ] ) ;
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 ) ;
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...
2020-11-29 23:14:11 -08:00
int dataSize = mesh . vertexCount / 3 * ( int ) strlen ( " v 0000.00f 0000.00f 0000.00f " ) +
mesh . vertexCount / 2 * ( int ) strlen ( " vt 0.000f 0.00f " ) +
mesh . vertexCount / 3 * ( int ) strlen ( " vn 0.000f 0.00f 0.00f " ) +
mesh . triangleCount / 3 * ( int ) strlen ( " f 00000/00000/00000 00000/00000/00000 00000/00000/00000 " ) ;
2020-05-23 19:48:40 +02:00
// NOTE: Text data buffer size is estimated considering mesh data size
char * txtData = ( char * ) RL_CALLOC ( dataSize + 2000 , sizeof ( char ) ) ;
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 " ) ;
byteCount + = sprintf ( txtData + byteCount , " # // Copyright (c) 2018 Ramon Santamaria (@raysan5) // \n " ) ;
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 )
{
2021-09-01 23:09:30 +02:00
byteCount + = sprintf ( txtData + byteCount , " v %.2f %.2f %.2f \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 )
{
2021-09-01 23:09:30 +02:00
byteCount + = sprintf ( txtData + byteCount , " vt %.3f %.3f \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 )
{
2021-09-01 23:09:30 +02:00
byteCount + = sprintf ( txtData + byteCount , " vn %.3f %.3f %.3f \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
2018-09-17 16:56:02 +02:00
for ( int i = 0 ; i < mesh . triangleCount ; i + = 3 )
{
2021-09-01 23:09:30 +02:00
byteCount + = sprintf ( txtData + byteCount , " f %i/%i/%i %i/%i/%i %i/%i/%i \n " , i , i , i , i + 1 , i + 1 , i + 1 , i + 2 , i + 2 , i + 2 ) ;
2018-09-17 16:56:02 +02:00
}
2019-02-21 18:45:19 +01:00
2021-09-01 23:09:30 +02:00
byteCount + = sprintf ( txtData + byteCount , " \n " ) ;
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
}
2021-03-25 14:28:12 +01: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
// TODO: Process materials to return
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
// Set materials shader to default (DIFFUSE, SPECULAR, NORMAL)
2020-12-31 13:28:53 +01:00
if ( materials ! = NULL )
{
2021-08-16 23:23:16 +02:00
for ( unsigned int i = 0 ; i < count ; i + + )
2021-07-29 21:57:50 +02:00
{
materials [ i ] . shader . id = rlGetShaderIdDefault ( ) ;
materials [ i ] . shader . locs = rlGetShaderLocsDefault ( ) ;
}
2020-12-31 13:28:53 +01:00
}
2019-04-05 13:15:56 +02:00
* 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 ;
}
// 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)
for ( int i = 0 ; i < MAX_MATERIAL_MAPS ; i + + )
{
2021-07-29 21:57:50 +02:00
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
2021-09-22 07:04:10 -04:00
ModelAnimation * LoadModelAnimations ( const char * fileName , unsigned 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)
if ( IsFileExtension ( fileName , " .iqm " ) ) animations = LoadIQMModelAnimations ( fileName , animCount ) ;
# endif
# if defined(SUPPORT_FILEFORMAT_GLTF)
if ( IsFileExtension ( fileName , " .gltf;.glb " ) ) animations = LoadGLTFModelAnimations ( fileName , animCount ) ;
# endif
2021-03-19 19:43:44 +01:00
2019-04-05 13:15:56 +02:00
return animations ;
}
// 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 )
{
2019-08-27 12:23:30 +02:00
if ( ( anim . frameCount > 0 ) & & ( anim . bones ! = NULL ) & & ( anim . framePoses ! = NULL ) )
2019-04-05 13:15:56 +02:00
{
2019-08-27 12:23:30 +02:00
if ( frame > = anim . frameCount ) frame = frame % anim . frameCount ;
2019-04-05 13:15:56 +02:00
2019-08-27 12:23:30 +02:00
for ( int m = 0 ; m < model . meshCount ; m + + )
{
2021-09-23 21:06:51 +02:00
Mesh mesh = model . meshes [ m ] ;
if ( mesh . boneIds = = NULL | | mesh . boneWeights = = NULL )
{
TRACELOG ( LOG_WARNING , " MODEL: UpdateModelAnimation Mesh %i has no connection to bones " , m ) ;
continue ;
}
bool updated = false ; // set to true when anim vertex information is updated
2019-08-27 12:23:30 +02:00
Vector3 animVertex = { 0 } ;
Vector3 animNormal = { 0 } ;
2019-04-05 13:15:56 +02:00
2019-08-27 12:23:30 +02:00
Vector3 inTranslation = { 0 } ;
Quaternion inRotation = { 0 } ;
2021-09-23 21:06:51 +02:00
// Vector3 inScale = { 0 };
2019-04-05 13:15:56 +02:00
2019-08-27 12:23:30 +02:00
Vector3 outTranslation = { 0 } ;
Quaternion outRotation = { 0 } ;
Vector3 outScale = { 0 } ;
2019-04-05 13:15:56 +02:00
2019-08-27 12:23:30 +02:00
int boneId = 0 ;
2021-09-23 21:06:51 +02:00
int boneCounter = 0 ;
2021-04-19 09:26:40 -07:00
float boneWeight = 0.0 ;
2019-04-05 13:15:56 +02:00
2021-09-23 21:06:51 +02:00
const int vValues = mesh . vertexCount * 3 ;
for ( int vCounter = 0 ; vCounter < vValues ; vCounter + = 3 )
2019-08-27 12:23:30 +02:00
{
2021-09-23 21:06:51 +02:00
mesh . animVertices [ vCounter ] = 0 ;
mesh . animVertices [ vCounter + 1 ] = 0 ;
mesh . animVertices [ vCounter + 2 ] = 0 ;
2021-04-19 09:26:40 -07:00
2021-09-23 21:06:51 +02:00
if ( mesh . animNormals ! = NULL )
{
mesh . animNormals [ vCounter ] = 0 ;
mesh . animNormals [ vCounter + 1 ] = 0 ;
mesh . animNormals [ vCounter + 2 ] = 0 ;
}
2021-04-19 09:26:40 -07:00
2021-09-23 21:06:51 +02:00
// Iterates over 4 bones per vertex
for ( int j = 0 ; j < 4 ; j + + , boneCounter + + )
2021-03-14 13:09:31 +02:00
{
2021-09-23 21:06:51 +02:00
boneWeight = mesh . boneWeights [ boneCounter ] ;
// early stop when no transformation will be applied
if ( boneWeight = = 0.0f )
{
continue ;
}
boneId = mesh . boneIds [ boneCounter ] ;
int boneIdParent = model . bones [ boneId ] . parent ;
2021-04-19 09:26:40 -07:00
inTranslation = model . bindPose [ boneId ] . translation ;
inRotation = model . bindPose [ boneId ] . rotation ;
2021-09-23 21:06:51 +02:00
// inScale = model.bindPose[boneId].scale;
2021-04-19 09:26:40 -07:00
outTranslation = anim . framePoses [ frame ] [ boneId ] . translation ;
outRotation = anim . framePoses [ frame ] [ boneId ] . rotation ;
outScale = anim . framePoses [ frame ] [ boneId ] . scale ;
// Vertices processing
// NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position)
2021-09-23 21:06:51 +02:00
animVertex = ( Vector3 ) { mesh . vertices [ vCounter ] , mesh . vertices [ vCounter + 1 ] , mesh . vertices [ vCounter + 2 ] } ;
2021-04-19 09:26:40 -07:00
animVertex = Vector3Multiply ( animVertex , outScale ) ;
animVertex = Vector3Subtract ( animVertex , inTranslation ) ;
animVertex = Vector3RotateByQuaternion ( animVertex , QuaternionMultiply ( outRotation , QuaternionInvert ( inRotation ) ) ) ;
animVertex = Vector3Add ( animVertex , outTranslation ) ;
2021-09-23 21:06:51 +02:00
// animVertex = Vector3Transform(animVertex, model.transform);
mesh . animVertices [ vCounter ] + = animVertex . x * boneWeight ;
mesh . animVertices [ vCounter + 1 ] + = animVertex . y * boneWeight ;
mesh . animVertices [ vCounter + 2 ] + = animVertex . z * boneWeight ;
updated = true ;
2021-04-19 09:26:40 -07:00
// Normals processing
// NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
2021-09-23 21:06:51 +02:00
if ( mesh . normals ! = NULL )
2021-04-19 09:26:40 -07:00
{
2021-09-23 21:06:51 +02:00
animNormal = ( Vector3 ) { mesh . normals [ vCounter ] , mesh . normals [ vCounter + 1 ] , mesh . normals [ vCounter + 2 ] } ;
2021-04-19 09:26:40 -07:00
animNormal = Vector3RotateByQuaternion ( animNormal , QuaternionMultiply ( outRotation , QuaternionInvert ( inRotation ) ) ) ;
2021-09-23 21:06:51 +02:00
mesh . animNormals [ vCounter ] + = animNormal . x * boneWeight ;
mesh . animNormals [ vCounter + 1 ] + = animNormal . y * boneWeight ;
mesh . animNormals [ vCounter + 2 ] + = animNormal . z * boneWeight ;
2021-04-19 09:26:40 -07:00
}
2021-03-14 13:09:31 +02:00
}
2019-08-27 12:23:30 +02:00
}
// Upload new vertex data to GPU for model drawing
2021-09-23 21:06:51 +02:00
// Only update data when values changed.
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
}
2019-08-27 12:23:30 +02:00
}
2019-04-05 13:15:56 +02:00
}
}
2021-03-14 20:30:18 +02:00
// Unload animation array data
void UnloadModelAnimations ( ModelAnimation * animations , unsigned int count )
{
2021-03-22 12:45:46 -07:00
for ( unsigned int i = 0 ; i < count ; 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
2020-06-05 18:13:31 +01:00
if ( sides < 3 ) return mesh ;
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 ;
for ( int v = 0 ; v < vertexCount ; 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 } ;
2021-03-31 17:55:46 +02: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
int i = face % ( resX - 1 ) + ( face / ( resZ - 1 ) * resX ) ;
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
for ( int i = 0 ; i < 36 ; i + = 6 )
{
mesh . indices [ i ] = 4 * k ;
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 ;
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:
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 (dyamond)
par_shapes_mesh * par_shapes_create_dodecahedron ( ) ; // 12 sides polyhedron
par_shapes_mesh * par_shapes_create_icosahedron ( ) ; // 20 sides polyhedron
*/
// 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 ) )
{
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
}
2017-09-18 00:59:22 +02:00
// Generate hemi-sphere 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
// slices, and "stacks" like a number of stacked rings.
// 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 } ) ;
par_shapes_rotate ( cylinder , PI / 2.0f , ( float [ ] ) { 0 , 1 , 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 } ) ;
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 } ) ;
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
// slices, and "stacks" like a number of stacked rings.
// 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
{
2016-02-11 15:51:04 +01:00
# define GRAY_VALUE(c) ((c.r+c.g+c.b) / 3)
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
2018-03-11 10:41:49 +01: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
2014-01-28 21:21:29 +01:00
int trisCounter = 0 ;
2014-09-03 16:51:28 +02:00
2016-02-11 15:51:04 +01:00
Vector3 scaleFactor = { size . x / mapX , size . y / 255.0f , size . z / mapZ } ;
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 ;
mesh . vertices [ vCounter + 1 ] = ( float ) GRAY_VALUE ( pixels [ x + z * mapX ] ) * scaleFactor . y ;
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 ;
mesh . vertices [ vCounter + 4 ] = ( float ) GRAY_VALUE ( pixels [ x + ( z + 1 ) * mapX ] ) * scaleFactor . y ;
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 ;
mesh . vertices [ vCounter + 7 ] = ( float ) GRAY_VALUE ( pixels [ ( x + 1 ) + z * mapX ] ) * scaleFactor . y ;
mesh . vertices [ vCounter + 8 ] = ( float ) z * scaleFactor . z ;
2014-09-03 16:51:28 +02:00
2014-01-28 21:21:29 +01: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 ;
mesh . vertices [ vCounter + 16 ] = ( float ) GRAY_VALUE ( pixels [ ( x + 1 ) + ( z + 1 ) * mapX ] ) * scaleFactor . y ;
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-01-28 21:21:29 +01:00
trisCounter + = 2 ;
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
2016-08-12 21:42:17 +03:00
int mapWidth = cubicmap . width ;
int mapHeight = cubicmap . height ;
2014-09-03 16:51:28 +02:00
2019-08-07 00:32:44 +02:00
// NOTE: Max possible number of triangles numCubes*(12 triangles by cube)
2015-05-04 23:46:31 +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
2016-08-12 21:42:17 +03:00
for ( int z = 0 ; z < mapHeight ; + + z )
2014-07-23 00:06:24 +02:00
{
2016-08-12 21:42:17 +03:00
for ( int x = 0 ; x < mapWidth ; + + 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
}
// Move data from mapVertices temp arays 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
// Implementation base don: 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
{
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
2019-04-05 13:15:56 +02:00
for ( int i = 0 ; i < mesh - > vertexCount ; i + = 3 )
{
// 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 )
{
// Upate existing vertex buffer
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 ) ;
rlSetVertexAttribute ( 4 , 4 , RL_FLOAT , 0 , 0 , 0 ) ;
rlEnableVertexAttribute ( 4 ) ;
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
}
2019-04-05 13:15:56 +02:00
// Compute mesh binormals (aka bitangent)
2021-06-17 00:04:24 +02:00
void GenMeshBinormals ( Mesh * mesh )
2017-07-17 00:33:40 +02:00
{
2019-04-05 13:15:56 +02:00
for ( int i = 0 ; i < mesh - > vertexCount ; i + + )
2017-07-17 00:33:40 +02:00
{
2020-01-24 18:23:36 +01:00
//Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] };
//Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] };
//Vector3 binormal = Vector3Scale(Vector3CrossProduct(normal, tangent), mesh->tangents[i*4 + 3]);
2020-02-03 19:26:28 +01:00
2019-04-05 13:15:56 +02:00
// TODO: Register computed binormal in mesh->binormal?
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 ;
2020-05-06 19:12:09 +02:00
colorTint . r = ( unsigned char ) ( ( ( ( float ) color . r / 255.0 ) * ( ( float ) tint . r / 255.0 ) ) * 255.0f ) ;
colorTint . g = ( unsigned char ) ( ( ( ( float ) color . g / 255.0 ) * ( ( float ) tint . g / 255.0 ) ) * 255.0f ) ;
colorTint . b = ( unsigned char ) ( ( ( ( float ) color . b / 255.0 ) * ( ( float ) tint . b / 255.0 ) ) * 255.0f ) ;
colorTint . a = ( unsigned char ) ( ( ( ( float ) color . a / 255.0 ) * ( ( float ) tint . a / 255.0 ) ) * 255.0f ) ;
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
}
2013-11-18 23:38:44 +01:00
// Draw a billboard
2021-05-11 01:02:53 +02:00
void DrawBillboard ( Camera camera , Texture2D texture , Vector3 position , float size , 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
2021-05-11 01:02:53 +02:00
DrawBillboardRec ( camera , texture , source , position , ( Vector2 ) { size , size } , 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
2021-09-21 10:22:30 -03:00
DrawBillboardPro ( camera , texture , source , position , up , size , Vector2Zero ( ) , 0.0f , tint ) ;
2021-05-11 01:55:43 +03:00
}
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
{
// NOTE: Billboard size will maintain source rectangle aspect ratio, size will represent billboard width
Vector2 sizeRatio = { size . y , size . x * ( float ) source . height / source . width } ;
2021-06-13 17:08:30 +02:00
2017-07-21 17:19:28 +02:00
Matrix matView = MatrixLookAt ( camera . position , camera . target , camera . up ) ;
2014-09-03 16:51:28 +02:00
2017-07-21 17:19:28 +02:00
Vector3 right = { matView . m0 , matView . m4 , matView . m8 } ;
//Vector3 up = { matView.m1, matView.m5, matView.m9 };
2016-08-16 11:09:55 +02:00
2021-06-08 21:01:17 +02:00
Vector3 rightScaled = Vector3Scale ( right , sizeRatio . x / 2 ) ;
Vector3 upScaled = Vector3Scale ( up , sizeRatio . y / 2 ) ;
2021-06-13 17:08:30 +02:00
2021-05-11 01:55:43 +03:00
Vector3 p1 = Vector3Add ( rightScaled , upScaled ) ;
Vector3 p2 = Vector3Subtract ( rightScaled , upScaled ) ;
Vector3 topLeft = Vector3Scale ( p2 , - 1 ) ;
Vector3 topRight = p1 ;
Vector3 bottomRight = p2 ;
Vector3 bottomLeft = Vector3Scale ( p1 , - 1 ) ;
2021-06-13 17:08:30 +02:00
2021-06-08 21:01:17 +02:00
if ( rotation ! = 0.0f )
2021-06-13 17:08:30 +02:00
{
2021-06-08 21:01:17 +02:00
float sinRotation = sinf ( rotation * DEG2RAD ) ;
float cosRotation = cosf ( rotation * DEG2RAD ) ;
2021-06-13 17:08:30 +02:00
2021-06-08 21:01:17 +02:00
// NOTE: (-1, 1) is the range where origin.x, origin.y is inside the texture
float rotateAboutX = sizeRatio . x * origin . x / 2 ;
float rotateAboutY = sizeRatio . y * origin . y / 2 ;
2021-06-13 17:08:30 +02:00
2021-06-08 21:01:17 +02:00
float xtvalue , ytvalue ;
float rotatedX , rotatedY ;
2021-06-13 17:08:30 +02:00
2021-06-08 21:01:17 +02:00
xtvalue = Vector3DotProduct ( right , topLeft ) - rotateAboutX ; // Project points to x and y coordinates on the billboard plane
ytvalue = Vector3DotProduct ( up , topLeft ) - rotateAboutY ;
2021-06-13 17:08:30 +02:00
rotatedX = xtvalue * cosRotation - ytvalue * sinRotation + rotateAboutX ; // Rotate about the point origin
2021-06-08 21:01:17 +02:00
rotatedY = xtvalue * sinRotation + ytvalue * cosRotation + rotateAboutY ;
topLeft = Vector3Add ( Vector3Scale ( up , rotatedY ) , Vector3Scale ( right , rotatedX ) ) ; // Translate back to cartesian coordinates
2021-06-13 17:08:30 +02:00
2021-06-08 21:01:17 +02:00
xtvalue = Vector3DotProduct ( right , topRight ) - rotateAboutX ;
ytvalue = Vector3DotProduct ( up , topRight ) - rotateAboutY ;
rotatedX = xtvalue * cosRotation - ytvalue * sinRotation + rotateAboutX ;
rotatedY = xtvalue * sinRotation + ytvalue * cosRotation + rotateAboutY ;
topRight = Vector3Add ( Vector3Scale ( up , rotatedY ) , Vector3Scale ( right , rotatedX ) ) ;
xtvalue = Vector3DotProduct ( right , bottomRight ) - rotateAboutX ;
ytvalue = Vector3DotProduct ( up , bottomRight ) - rotateAboutY ;
rotatedX = xtvalue * cosRotation - ytvalue * sinRotation + rotateAboutX ;
rotatedY = xtvalue * sinRotation + ytvalue * cosRotation + rotateAboutY ;
bottomRight = Vector3Add ( Vector3Scale ( up , rotatedY ) , Vector3Scale ( right , rotatedX ) ) ;
2021-06-13 17:08:30 +02:00
2021-06-08 21:01:17 +02:00
xtvalue = Vector3DotProduct ( right , bottomLeft ) - rotateAboutX ;
ytvalue = Vector3DotProduct ( up , bottomLeft ) - rotateAboutY ;
rotatedX = xtvalue * cosRotation - ytvalue * sinRotation + rotateAboutX ;
rotatedY = xtvalue * sinRotation + ytvalue * cosRotation + rotateAboutY ;
bottomLeft = Vector3Add ( Vector3Scale ( up , rotatedY ) , Vector3Scale ( right , rotatedX ) ) ;
}
2021-06-13 17:08:30 +02:00
2021-05-11 01:02:53 +02:00
// Translate points to the draw center (position)
2021-06-08 21:01:17 +02:00
topLeft = Vector3Add ( topLeft , position ) ;
topRight = Vector3Add ( topRight , position ) ;
bottomRight = Vector3Add ( bottomRight , position ) ;
bottomLeft = Vector3Add ( bottomLeft , position ) ;
2021-06-13 17:08:30 +02:00
2021-03-21 01:29:31 +01:00
rlCheckRenderBatchLimit ( 4 ) ;
2019-07-22 00:12:51 +02:00
2021-03-25 14:28:12 +01:00
rlSetTexture ( texture . id ) ;
2014-09-03 16:51:28 +02:00
2014-03-25 12:40:35 +01:00
rlBegin ( RL_QUADS ) ;
rlColor4ub ( tint . r , tint . g , tint . b , tint . a ) ;
2014-09-03 16:51:28 +02:00
2014-03-16 20:59:02 +01:00
// Bottom-left corner for texture and quad
2020-12-19 19:27:31 +01:00
rlTexCoord2f ( ( float ) source . x / texture . width , ( float ) source . y / texture . height ) ;
2021-05-11 01:55:43 +03:00
rlVertex3f ( topLeft . x , topLeft . y , topLeft . z ) ;
2015-02-02 00:53:49 +01:00
2014-11-24 17:23:05 +01:00
// Top-left corner for texture and quad
2020-12-19 19:27:31 +01:00
rlTexCoord2f ( ( float ) source . x / texture . width , ( float ) ( source . y + source . height ) / texture . height ) ;
2021-05-11 01:55:43 +03:00
rlVertex3f ( bottomLeft . x , bottomLeft . y , bottomLeft . z ) ;
2015-02-02 00:53:49 +01:00
2014-03-16 20:59:02 +01:00
// Top-right corner for texture and quad
2020-12-19 19:27:31 +01:00
rlTexCoord2f ( ( float ) ( source . x + source . width ) / texture . width , ( float ) ( source . y + source . height ) / texture . height ) ;
2021-05-11 01:55:43 +03:00
rlVertex3f ( bottomRight . x , bottomRight . y , bottomRight . z ) ;
2014-09-03 16:51:28 +02:00
2014-11-24 17:23:05 +01:00
// Bottom-right corner for texture and quad
2020-12-19 19:27:31 +01:00
rlTexCoord2f ( ( float ) ( source . x + source . width ) / texture . width , ( float ) source . y / texture . height ) ;
2021-05-11 01:55:43 +03:00
rlVertex3f ( topRight . x , topRight . y , topRight . z ) ;
2014-03-25 12:40:35 +01:00
rlEnd ( ) ;
2014-09-03 16:51:28 +02:00
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)
// 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 ) ) ;
2021-06-03 20:25:28 +02:00
// The relevant elemets 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 ;
Vector3 * vertdata = ( Vector3 * ) mesh . vertices ;
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
2018-04-06 12:04:09 -04:00
// Get collision info between ray and model
2021-05-31 11:41:56 +02:00
RayCollision GetRayCollisionModel ( Ray ray , Model model )
2018-04-06 12:04:09 -04:00
{
2021-05-31 11:41:56 +02:00
RayCollision collision = { 0 } ;
2018-04-06 12:04:09 -04:00
2019-08-07 00:32:44 +02:00
for ( int m = 0 ; m < model . meshCount ; m + + )
2018-04-06 12:04:09 -04:00
{
2021-05-31 11:41:56 +02:00
RayCollision meshHitInfo = GetRayCollisionMesh ( ray , model . meshes [ m ] , model . transform ) ;
2018-04-06 12:04:09 -04:00
2020-12-14 10:26:32 -08:00
if ( meshHitInfo . hit )
{
// Save the closest hit mesh
2021-05-31 11:41:56 +02:00
if ( ( ! collision . hit ) | | ( collision . distance > meshHitInfo . distance ) ) collision = meshHitInfo ;
2017-01-05 19:33:05 +01:00
}
}
2021-05-31 11:41:56 +02:00
return collision ;
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
{
# define EPSILON 0.000001 // A small number
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
2017-01-05 19:33:05 +01:00
// The intersection lies outside of 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
2017-01-05 19:33:05 +01:00
// The intersection lies outside of 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
}
2015-02-09 18:35:25 +01:00
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
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
{
2019-03-29 16:48:23 +01:00
Model model = { 0 } ;
2019-04-01 12:17:29 +02:00
2020-10-08 19:31:59 +01:00
tinyobj_attrib_t attrib = { 0 } ;
2019-03-29 17:15:22 +01:00
tinyobj_shape_t * meshes = NULL ;
2019-03-29 17:28:10 +01:00
unsigned int meshCount = 0 ;
2019-04-01 12:17:29 +02:00
2019-03-29 17:15:22 +01:00
tinyobj_material_t * materials = NULL ;
2019-03-29 17:28:10 +01:00
unsigned int materialCount = 0 ;
2014-09-03 16:51:28 +02:00
2021-05-22 16:54:04 +02:00
char * fileText = LoadFileText ( fileName ) ;
2019-04-01 12:17:29 +02:00
2021-05-22 16:54:04 +02:00
if ( fileText ! = NULL )
2019-03-29 17:28:10 +01:00
{
2021-05-22 16:54:04 +02:00
unsigned int dataSize = ( unsigned int ) strlen ( fileText ) ;
2020-03-24 18:42:57 +01:00
char currentDir [ 1024 ] = { 0 } ;
strcpy ( currentDir , GetWorkingDirectory ( ) ) ;
2021-04-09 10:57:14 -07:00
const char * workingDir = GetDirectoryPath ( fileName ) ;
if ( CHDIR ( workingDir ) ! = 0 )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to change working directory " , workingDir ) ;
}
2019-03-29 17:28:10 +01:00
2019-03-29 17:15:22 +01:00
unsigned int flags = TINYOBJ_FLAG_TRIANGULATE ;
2021-05-22 16:54:04 +02:00
int ret = tinyobj_parse_obj ( & attrib , & meshes , & meshCount , & materials , & materialCount , fileText , dataSize , flags ) ;
2019-04-04 13:50:52 +02:00
2020-03-27 17:43:51 +01:00
if ( ret ! = TINYOBJ_SUCCESS ) TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to load OBJ data " , fileName ) ;
2021-06-03 20:25:28 +02:00
else TRACELOG ( LOG_INFO , " MODEL: [%s] OBJ data loaded successfully: %i meshes/%i materials " , fileName , meshCount , materialCount ) ;
2019-04-04 13:50:52 +02:00
2021-08-16 23:23:16 +02:00
model . meshCount = materialCount ;
2020-11-03 23:47:33 +01:00
2019-04-01 00:16:56 +02:00
// Init model materials array
2019-08-27 11:16:43 +02:00
if ( materialCount > 0 )
{
model . materialCount = materialCount ;
model . materials = ( Material * ) RL_CALLOC ( model . materialCount , sizeof ( Material ) ) ;
2020-10-08 19:31:59 +01:00
TraceLog ( LOG_INFO , " MODEL: model has %i material meshes " , materialCount ) ;
2020-12-23 15:03:26 +01:00
}
2020-11-17 00:30:12 +01:00
else
{
2020-11-03 23:47:33 +01:00
model . meshCount = 1 ;
TraceLog ( LOG_INFO , " MODEL: No materials, putting all meshes in a default material " ) ;
2019-08-27 11:16:43 +02:00
}
2019-09-13 12:29:52 +01:00
2020-10-08 19:31:59 +01:00
model . meshes = ( Mesh * ) RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
2019-04-23 14:55:35 +02:00
model . meshMaterial = ( int * ) RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2020-11-03 23:47:33 +01:00
2021-07-07 21:41:45 +02:00
// Count the faces for each material
2021-09-09 18:14:15 +02:00
int * matFaces = RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2020-11-03 23:47:33 +01:00
2021-09-09 18:14:15 +02:00
// iff no materials are present use all faces on one mesh
if ( materialCount > 0 )
{
for ( int fi = 0 ; fi < attrib . num_faces ; fi + + )
{
//tinyobj_vertex_index_t face = attrib.faces[fi];
int idx = attrib . material_ids [ fi ] ;
matFaces [ idx ] + + ;
}
}
else
2021-09-01 23:09:30 +02:00
{
2021-09-09 18:14:15 +02:00
matFaces [ 0 ] = attrib . num_faces ;
2020-10-08 19:31:59 +01:00
}
2021-09-01 21:29:20 +02:00
2020-10-08 19:31:59 +01:00
//--------------------------------------
2021-07-07 21:41:45 +02:00
// Create the material meshes
2020-11-03 23:47:33 +01:00
2021-07-07 21:41:45 +02:00
// Running counts/indexes for each material mesh as we are
2020-11-03 23:47:33 +01:00
// building them at the same time
2020-11-17 00:30:12 +01:00
int * vCount = RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
int * vtCount = RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
int * vnCount = RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
int * faceCount = RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2020-11-03 23:47:33 +01:00
2021-07-07 21:41:45 +02:00
// Allocate space for each of the material meshes
2020-11-17 00:30:12 +01:00
for ( int mi = 0 ; mi < model . meshCount ; mi + + )
2020-10-08 19:31:59 +01:00
{
2021-03-31 17:55:46 +02:00
model . meshes [ mi ] . vertexCount = matFaces [ mi ] * 3 ;
2020-10-08 19:31:59 +01:00
model . meshes [ mi ] . triangleCount = matFaces [ mi ] ;
model . meshes [ mi ] . vertices = ( float * ) RL_CALLOC ( model . meshes [ mi ] . vertexCount * 3 , sizeof ( float ) ) ;
model . meshes [ mi ] . texcoords = ( float * ) RL_CALLOC ( model . meshes [ mi ] . vertexCount * 2 , sizeof ( float ) ) ;
model . meshes [ mi ] . normals = ( float * ) RL_CALLOC ( model . meshes [ mi ] . vertexCount * 3 , sizeof ( float ) ) ;
model . meshMaterial [ mi ] = mi ;
}
2020-11-03 23:47:33 +01:00
2021-07-07 21:41:45 +02:00
// Scan through the combined sub meshes and pick out each material mesh
2020-11-03 23:47:33 +01:00
for ( unsigned int af = 0 ; af < attrib . num_faces ; af + + )
2014-07-23 00:06:24 +02:00
{
2020-10-08 19:31:59 +01:00
int mm = attrib . material_ids [ af ] ; // mesh material for this face
if ( mm = = - 1 ) { mm = 0 ; } // no material object..
2020-12-23 15:03:26 +01:00
2020-10-08 19:31:59 +01:00
// Get indices for the face
2021-03-31 17:55:46 +02:00
tinyobj_vertex_index_t idx0 = attrib . faces [ 3 * af + 0 ] ;
tinyobj_vertex_index_t idx1 = attrib . faces [ 3 * af + 1 ] ;
tinyobj_vertex_index_t idx2 = attrib . faces [ 3 * af + 2 ] ;
2020-11-03 23:47:33 +01:00
2020-10-08 19:31:59 +01:00
// Fill vertices buffer (float) using vertex index of the face
for ( int v = 0 ; v < 3 ; v + + ) { model . meshes [ mm ] . vertices [ vCount [ mm ] + v ] = attrib . vertices [ idx0 . v_idx * 3 + v ] ; } vCount [ mm ] + = 3 ;
for ( int v = 0 ; v < 3 ; v + + ) { model . meshes [ mm ] . vertices [ vCount [ mm ] + v ] = attrib . vertices [ idx1 . v_idx * 3 + v ] ; } vCount [ mm ] + = 3 ;
2020-11-03 23:47:33 +01:00
for ( int v = 0 ; v < 3 ; v + + ) { model . meshes [ mm ] . vertices [ vCount [ mm ] + v ] = attrib . vertices [ idx2 . v_idx * 3 + v ] ; } vCount [ mm ] + = 3 ;
2020-10-08 19:31:59 +01:00
if ( attrib . num_texcoords > 0 )
2019-04-01 00:16:56 +02:00
{
2020-10-08 19:31:59 +01:00
// Fill texcoords buffer (float) using vertex index of the face
2020-11-03 23:47:33 +01:00
// NOTE: Y-coordinate must be flipped upside-down to account for
2020-10-08 19:31:59 +01:00
// raylib's upside down textures...
model . meshes [ mm ] . texcoords [ vtCount [ mm ] + 0 ] = attrib . texcoords [ idx0 . vt_idx * 2 + 0 ] ;
model . meshes [ mm ] . texcoords [ vtCount [ mm ] + 1 ] = 1.0f - attrib . texcoords [ idx0 . vt_idx * 2 + 1 ] ; vtCount [ mm ] + = 2 ;
model . meshes [ mm ] . texcoords [ vtCount [ mm ] + 0 ] = attrib . texcoords [ idx1 . vt_idx * 2 + 0 ] ;
model . meshes [ mm ] . texcoords [ vtCount [ mm ] + 1 ] = 1.0f - attrib . texcoords [ idx1 . vt_idx * 2 + 1 ] ; vtCount [ mm ] + = 2 ;
model . meshes [ mm ] . texcoords [ vtCount [ mm ] + 0 ] = attrib . texcoords [ idx2 . vt_idx * 2 + 0 ] ;
model . meshes [ mm ] . texcoords [ vtCount [ mm ] + 1 ] = 1.0f - attrib . texcoords [ idx2 . vt_idx * 2 + 1 ] ; vtCount [ mm ] + = 2 ;
2020-11-03 23:47:33 +01:00
}
2020-10-08 19:31:59 +01:00
if ( attrib . num_normals > 0 )
{
// Fill normals buffer (float) using vertex index of the face
for ( int v = 0 ; v < 3 ; v + + ) { model . meshes [ mm ] . normals [ vnCount [ mm ] + v ] = attrib . normals [ idx0 . vn_idx * 3 + v ] ; } vnCount [ mm ] + = 3 ;
for ( int v = 0 ; v < 3 ; v + + ) { model . meshes [ mm ] . normals [ vnCount [ mm ] + v ] = attrib . normals [ idx1 . vn_idx * 3 + v ] ; } vnCount [ mm ] + = 3 ;
for ( int v = 0 ; v < 3 ; v + + ) { model . meshes [ mm ] . normals [ vnCount [ mm ] + v ] = attrib . normals [ idx2 . vn_idx * 3 + v ] ; } vnCount [ mm ] + = 3 ;
2020-11-03 23:47:33 +01:00
}
2014-04-19 16:36:49 +02:00
}
2019-03-29 17:15:22 +01:00
2019-04-01 00:16:56 +02:00
// Init model materials
2020-05-06 19:12:09 +02:00
for ( unsigned int m = 0 ; m < materialCount ; m + + )
2020-11-03 23:47:33 +01:00
{
2019-04-01 12:41:32 +02:00
// Init material to default
2021-03-14 11:05:51 +01:00
// NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE
2019-04-01 12:41:32 +02:00
model . materials [ m ] = LoadMaterialDefault ( ) ;
2019-04-04 13:50:52 +02:00
2021-07-29 21:57:50 +02:00
// Get default texture, in case no texture is defined
// NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
2021-08-16 23:23:16 +02:00
model . materials [ m ] . maps [ MATERIAL_MAP_DIFFUSE ] . texture = ( Texture2D ) { rlGetTextureIdDefault ( ) , 1 , 1 , 1 , PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 } ;
2019-09-13 12:29:52 +01:00
2021-03-14 11:05:51 +01:00
if ( materials [ m ] . diffuse_texname ! = NULL ) model . materials [ m ] . maps [ MATERIAL_MAP_DIFFUSE ] . texture = LoadTexture ( materials [ m ] . diffuse_texname ) ; //char *diffuse_texname; // map_Kd
2020-11-03 23:47:33 +01:00
2021-03-14 11:05:51 +01:00
model . materials [ m ] . maps [ MATERIAL_MAP_DIFFUSE ] . color = ( Color ) { ( unsigned char ) ( materials [ m ] . diffuse [ 0 ] * 255.0f ) , ( unsigned char ) ( materials [ m ] . diffuse [ 1 ] * 255.0f ) , ( unsigned char ) ( materials [ m ] . diffuse [ 2 ] * 255.0f ) , 255 } ; //float diffuse[3];
model . materials [ m ] . maps [ MATERIAL_MAP_DIFFUSE ] . value = 0.0f ;
2019-04-04 13:50:52 +02:00
2021-03-14 11:05:51 +01:00
if ( materials [ m ] . specular_texname ! = NULL ) model . materials [ m ] . maps [ MATERIAL_MAP_SPECULAR ] . texture = LoadTexture ( materials [ m ] . specular_texname ) ; //char *specular_texname; // map_Ks
model . materials [ m ] . maps [ MATERIAL_MAP_SPECULAR ] . color = ( Color ) { ( unsigned char ) ( materials [ m ] . specular [ 0 ] * 255.0f ) , ( unsigned char ) ( materials [ m ] . specular [ 1 ] * 255.0f ) , ( unsigned char ) ( materials [ m ] . specular [ 2 ] * 255.0f ) , 255 } ; //float specular[3];
model . materials [ m ] . maps [ MATERIAL_MAP_SPECULAR ] . value = 0.0f ;
2019-04-04 13:50:52 +02:00
2021-03-14 11:05:51 +01:00
if ( materials [ m ] . bump_texname ! = NULL ) model . materials [ m ] . maps [ MATERIAL_MAP_NORMAL ] . texture = LoadTexture ( materials [ m ] . bump_texname ) ; //char *bump_texname; // map_bump, bump
model . materials [ m ] . maps [ MATERIAL_MAP_NORMAL ] . color = WHITE ;
model . materials [ m ] . maps [ MATERIAL_MAP_NORMAL ] . value = materials [ m ] . shininess ;
2019-04-04 13:50:52 +02:00
2021-03-14 11:05:51 +01:00
model . materials [ m ] . maps [ MATERIAL_MAP_EMISSION ] . color = ( Color ) { ( unsigned char ) ( materials [ m ] . emission [ 0 ] * 255.0f ) , ( unsigned char ) ( materials [ m ] . emission [ 1 ] * 255.0f ) , ( unsigned char ) ( materials [ m ] . emission [ 2 ] * 255.0f ) , 255 } ; //float emission[3];
2019-04-01 12:41:32 +02:00
2021-03-14 11:05:51 +01:00
if ( materials [ m ] . displacement_texname ! = NULL ) model . materials [ m ] . maps [ MATERIAL_MAP_HEIGHT ] . texture = LoadTexture ( materials [ m ] . displacement_texname ) ; //char *displacement_texname; // disp
2019-04-01 00:16:56 +02:00
}
2019-03-29 17:15:22 +01:00
tinyobj_attrib_free ( & attrib ) ;
tinyobj_shapes_free ( meshes , meshCount ) ;
tinyobj_materials_free ( materials , materialCount ) ;
2020-11-03 23:47:33 +01:00
2021-05-22 16:54:04 +02:00
UnloadFileText ( fileText ) ;
2021-06-13 17:08:30 +02:00
2020-12-01 23:23:30 +01:00
RL_FREE ( matFaces ) ;
2020-10-08 19:31:59 +01:00
RL_FREE ( vCount ) ;
RL_FREE ( vtCount ) ;
RL_FREE ( vnCount ) ;
RL_FREE ( faceCount ) ;
2020-03-24 18:42:57 +01:00
2021-04-09 10:57:14 -07:00
if ( CHDIR ( currentDir ) ! = 0 )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to change working directory " , currentDir ) ;
}
2014-04-19 16:36:49 +02:00
}
2014-09-03 16:51:28 +02:00
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
2020-05-23 19:24:15 +02:00
unsigned int fileSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & fileSize ) ;
unsigned char * fileDataPtr = fileData ;
2019-04-04 13:33:54 +02:00
// IQM file structs
//-----------------------------------------------------------------------------------
typedef struct IQMHeader {
char magic [ 16 ] ;
unsigned int version ;
unsigned int filesize ;
unsigned int flags ;
unsigned int num_text , ofs_text ;
unsigned int num_meshes , ofs_meshes ;
unsigned int num_vertexarrays , num_vertexes , ofs_vertexarrays ;
unsigned int num_triangles , ofs_triangles , ofs_adjacency ;
unsigned int num_joints , ofs_joints ;
unsigned int num_poses , ofs_poses ;
unsigned int num_anims , ofs_anims ;
unsigned int num_frames , num_framechannels , ofs_frames , ofs_bounds ;
unsigned int num_comment , ofs_comment ;
unsigned int num_extensions , ofs_extensions ;
} IQMHeader ;
typedef struct 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
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);
//fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile);
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);
//fread(material, sizeof(char)*MATERIAL_NAME_LENGTH, 1, iqmFile);
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 ( ) ;
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
2019-08-27 11:16:43 +02:00
model . meshes [ i ] . boneIds = RL_CALLOC ( model . meshes [ i ] . vertexCount * 4 , sizeof ( float ) ) ; // 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
2019-04-04 13:33:54 +02:00
// Animated verted data, what we actually process for rendering
// 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);
//fread(tri, iqmHeader->num_triangles*sizeof(IQMTriangle), 1, iqmFile);
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);
//fread(va, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray), 1, iqmFile);
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);
//fread(ijoint, iqmHeader->num_joints*sizeof(IQMJoint), 1, iqmFile);
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);
//fread(model.bones[i].name, BONE_NAME_LENGTH*sizeof(char), 1, iqmFile);
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 ] ;
}
// Build bind pose from parent joints
for ( int i = 0 ; i < model . boneCount ; i + + )
{
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 ) ;
2020-01-23 21:10:12 +01:00
model . bindPose [ i ] . scale = Vector3Multiply ( model . bindPose [ i ] . scale , model . bindPose [ model . bones [ i ] . parent ] . scale ) ;
2019-04-04 13:33:54 +02:00
}
}
2020-05-23 19:24:15 +02:00
RL_FREE ( 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 ) ;
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
2021-09-22 07:04:10 -04:00
static ModelAnimation * LoadIQMModelAnimations ( const char * fileName , unsigned int * animCount )
2021-01-28 12:29:06 +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
2021-01-28 12:29:06 +02:00
unsigned int fileSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & fileSize ) ;
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 ;
unsigned int filesize ;
unsigned int flags ;
unsigned int num_text , ofs_text ;
unsigned int num_meshes , ofs_meshes ;
unsigned int num_vertexarrays , num_vertexes , ofs_vertexarrays ;
unsigned int num_triangles , ofs_triangles , ofs_adjacency ;
unsigned int num_joints , ofs_joints ;
unsigned int num_poses , ofs_poses ;
unsigned int num_anims , ofs_anims ;
unsigned int num_frames , num_framechannels , ofs_frames , ofs_bounds ;
unsigned int num_comment , ofs_comment ;
unsigned int num_extensions , ofs_extensions ;
} IQMHeader ;
2021-03-19 19:43:44 +01:00
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);
//fread(poses, iqmHeader->num_poses*sizeof(IQMPose), 1, iqmFile);
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);
//fread(anim, iqmHeader->num_anims*sizeof(IQMAnim), 1, iqmFile);
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);
//fread(framedata, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short), 1, iqmFile);
memcpy ( framedata , fileDataPtr + iqmHeader - > ofs_frames , iqmHeader - > num_frames * iqmHeader - > num_framechannels * sizeof ( unsigned short ) ) ;
2021-03-19 19:43:44 +01:00
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 * ) ) ;
2021-02-02 11:49:42 +02:00
// animations[a].framerate = anim.framerate; // TODO: Use framerate?
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 + + )
{
strcpy ( animations [ a ] . bones [ j ] . name , " ANIMJOINTNAME " ) ;
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
2021-01-28 12:29:06 +02:00
RL_FREE ( fileData ) ;
2021-03-19 19:43:44 +01:00
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)
2019-05-14 14:06:17 +02:00
2019-05-20 11:13:38 +02:00
static const unsigned char base64Table [ ] = {
2019-05-14 14:06:17 +02:00
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 62 , 0 , 0 , 0 , 63 , 52 , 53 ,
54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 1 , 2 , 3 , 4 ,
5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 ,
15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 ,
25 , 0 , 0 , 0 , 0 , 0 , 0 , 26 , 27 , 28 ,
29 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 ,
39 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 , 48 ,
49 , 50 , 51
} ;
2019-05-20 11:13:38 +02:00
static int GetSizeBase64 ( char * input )
2019-05-14 14:06:17 +02:00
{
int size = 0 ;
2019-09-13 12:29:52 +01:00
2019-05-14 14:06:17 +02:00
for ( int i = 0 ; input [ 4 * i ] ! = 0 ; i + + )
{
2019-05-20 11:13:38 +02:00
if ( input [ 4 * i + 3 ] = = ' = ' )
2019-05-14 14:06:17 +02:00
{
2019-05-20 11:13:38 +02:00
if ( input [ 4 * i + 2 ] = = ' = ' ) size + = 1 ;
else size + = 2 ;
2019-05-14 14:06:17 +02:00
}
else size + = 3 ;
}
2019-09-13 12:29:52 +01:00
2019-05-14 14:06:17 +02:00
return size ;
}
2019-05-20 11:13:38 +02:00
static unsigned char * DecodeBase64 ( char * input , int * size )
2019-05-14 14:06:17 +02:00
{
2019-07-12 14:33:06 +02:00
* size = GetSizeBase64 ( input ) ;
2019-05-14 14:06:17 +02:00
2019-05-20 11:13:38 +02:00
unsigned char * buf = ( unsigned char * ) RL_MALLOC ( * size ) ;
2019-05-14 14:06:17 +02:00
for ( int i = 0 ; i < * size / 3 ; i + + )
{
2019-05-20 11:13:38 +02:00
unsigned char a = base64Table [ ( int ) input [ 4 * i ] ] ;
unsigned char b = base64Table [ ( int ) input [ 4 * i + 1 ] ] ;
unsigned char c = base64Table [ ( int ) input [ 4 * i + 2 ] ] ;
unsigned char d = base64Table [ ( int ) input [ 4 * i + 3 ] ] ;
2019-05-14 14:06:17 +02:00
buf [ 3 * i ] = ( a < < 2 ) | ( b > > 4 ) ;
2019-05-20 11:13:38 +02:00
buf [ 3 * i + 1 ] = ( b < < 4 ) | ( c > > 2 ) ;
buf [ 3 * i + 2 ] = ( c < < 6 ) | d ;
2019-05-14 14:06:17 +02:00
}
2019-05-20 11:13:38 +02:00
if ( * size % 3 = = 1 )
2019-05-14 14:06:17 +02:00
{
int n = * size / 3 ;
2019-05-20 11:13:38 +02:00
unsigned char a = base64Table [ ( int ) input [ 4 * n ] ] ;
unsigned char b = base64Table [ ( int ) input [ 4 * n + 1 ] ] ;
buf [ * size - 1 ] = ( a < < 2 ) | ( b > > 4 ) ;
2019-05-14 14:06:17 +02:00
}
2019-05-20 11:13:38 +02:00
else if ( * size % 3 = = 2 )
2019-05-14 14:06:17 +02:00
{
2019-05-20 11:13:38 +02:00
int n = * size / 3 ;
unsigned char a = base64Table [ ( int ) input [ 4 * n ] ] ;
unsigned char b = base64Table [ ( int ) input [ 4 * n + 1 ] ] ;
unsigned char c = base64Table [ ( int ) input [ 4 * n + 2 ] ] ;
buf [ * size - 2 ] = ( a < < 2 ) | ( b > > 4 ) ;
buf [ * size - 1 ] = ( b < < 4 ) | ( c > > 2 ) ;
2019-05-14 14:06:17 +02:00
}
return buf ;
}
2019-10-21 17:55:37 +02:00
// Load texture from cgltf_image
2020-02-22 01:17:30 -08:00
static Image LoadImageFromCgltfImage ( cgltf_image * image , const char * texPath , Color tint )
2019-10-21 08:38:23 -07:00
{
2020-02-22 01:17:30 -08:00
Image rimage = { 0 } ;
2019-10-21 08:38:23 -07:00
if ( image - > uri )
{
if ( ( strlen ( image - > uri ) > 5 ) & &
( image - > uri [ 0 ] = = ' d ' ) & &
( image - > uri [ 1 ] = = ' a ' ) & &
( image - > uri [ 2 ] = = ' t ' ) & &
( image - > uri [ 3 ] = = ' a ' ) & &
( image - > uri [ 4 ] = = ' : ' ) )
{
// Data URI
// Format: data:<mediatype>;base64,<data>
// Find the comma
int i = 0 ;
2019-10-21 17:55:37 +02:00
while ( ( image - > uri [ i ] ! = ' , ' ) & & ( image - > uri [ i ] ! = 0 ) ) i + + ;
2019-10-21 08:38:23 -07:00
2020-03-27 17:43:51 +01:00
if ( image - > uri [ i ] = = 0 ) TRACELOG ( LOG_WARNING , " IMAGE: glTF data URI is not a valid image " ) ;
2019-10-21 08:38:23 -07:00
else
{
2020-04-10 19:26:36 +02:00
int size = 0 ;
2019-10-21 08:38:23 -07:00
unsigned char * data = DecodeBase64 ( image - > uri + i + 1 , & size ) ;
2021-09-22 12:19:25 +02:00
rimage = LoadImageFromMemory ( " .png " , data , size ) ;
2020-05-23 19:24:15 +02:00
RL_FREE ( data ) ;
2019-10-21 08:38:23 -07:00
2019-10-21 17:55:37 +02:00
// TODO: Tint shouldn't be applied here!
2019-10-21 08:38:23 -07:00
ImageColorTint ( & rimage , tint ) ;
}
}
else
{
2020-02-22 01:17:30 -08:00
rimage = LoadImage ( TextFormat ( " %s/%s " , texPath , image - > uri ) ) ;
2020-02-03 19:26:28 +01:00
2019-10-21 17:55:37 +02:00
// TODO: Tint shouldn't be applied here!
2019-10-21 08:38:23 -07:00
ImageColorTint ( & rimage , tint ) ;
}
}
else if ( image - > buffer_view )
{
unsigned char * data = RL_MALLOC ( image - > buffer_view - > size ) ;
2020-05-06 19:12:09 +02:00
int n = ( int ) image - > buffer_view - > offset ;
int stride = ( int ) image - > buffer_view - > stride ? ( int ) image - > buffer_view - > stride : 1 ;
2019-10-21 08:38:23 -07:00
2020-05-06 19:12:09 +02:00
for ( unsigned int i = 0 ; i < image - > buffer_view - > size ; i + + )
2019-10-21 08:38:23 -07:00
{
data [ i ] = ( ( unsigned char * ) image - > buffer_view - > buffer - > data ) [ n ] ;
n + = stride ;
}
2021-09-22 13:02:44 +02:00
rimage = LoadImageFromMemory ( " .png " , data , ( int ) image - > buffer_view - > size ) ;
2020-05-23 19:24:15 +02:00
RL_FREE ( data ) ;
2019-10-21 08:38:23 -07:00
2019-10-21 17:55:37 +02:00
// TODO: Tint shouldn't be applied here!
2019-10-21 08:38:23 -07:00
ImageColorTint ( & rimage , tint ) ;
}
2020-04-10 19:26:36 +02:00
else rimage = GenImageColor ( 1 , 1 , tint ) ;
2019-10-21 08:38:23 -07:00
2020-02-22 01:17:30 -08:00
return rimage ;
2019-10-21 08:38:23 -07:00
}
2021-06-26 13:45:15 +02:00
//
static bool ReadGLTFValue ( cgltf_accessor * acc , unsigned int index , void * variable )
2021-03-14 13:09:31 +02:00
{
2021-06-26 14:22:00 +03:00
unsigned int typeElements = 0 ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
switch ( acc - > type )
{
2021-06-26 14:22:00 +03:00
case cgltf_type_scalar : typeElements = 1 ; break ;
case cgltf_type_vec2 : typeElements = 2 ; break ;
case cgltf_type_vec3 : typeElements = 3 ; break ;
case cgltf_type_vec4 :
case cgltf_type_mat2 : typeElements = 4 ; break ;
case cgltf_type_mat3 : typeElements = 9 ; break ;
case cgltf_type_mat4 : typeElements = 16 ; break ;
case cgltf_type_invalid : typeElements = 0 ; break ;
2021-06-26 13:45:15 +02:00
default : break ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
unsigned int typeSize = 0 ;
2021-06-30 16:39:07 +02:00
switch ( acc - > component_type )
2021-06-26 13:45:15 +02:00
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8u :
case cgltf_component_type_r_8 : typeSize = 1 ; break ;
case cgltf_component_type_r_16u :
case cgltf_component_type_r_16 : typeSize = 2 ; break ;
case cgltf_component_type_r_32f :
case cgltf_component_type_r_32u : typeSize = 4 ; break ;
case cgltf_component_type_invalid : typeSize = 0 ; break ;
2021-06-26 13:45:15 +02:00
default : break ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
unsigned int singleElementSize = typeSize * typeElements ;
2021-06-30 16:39:07 +02:00
2021-03-14 13:09:31 +02:00
if ( acc - > count = = 2 )
{
2021-03-31 17:55:46 +02:00
if ( index > 1 ) return false ;
2021-03-19 19:43:44 +01:00
2021-06-26 14:22:00 +03:00
memcpy ( variable , index = = 0 ? acc - > min : acc - > max , singleElementSize ) ;
2021-03-14 13:09:31 +02:00
return true ;
}
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
memset ( variable , 0 , singleElementSize ) ;
2021-03-19 19:43:44 +01:00
2021-03-31 17:55:46 +02:00
if ( acc - > buffer_view = = NULL | | acc - > buffer_view - > buffer = = NULL | | acc - > buffer_view - > buffer - > data = = NULL ) return false ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
if ( ! acc - > buffer_view - > stride )
2021-06-26 14:22:00 +03:00
{
2021-06-26 13:45:15 +02:00
void * readPosition = ( ( char * ) acc - > buffer_view - > buffer - > data ) + ( index * singleElementSize ) + acc - > buffer_view - > offset + acc - > offset ;
2021-06-26 14:22:00 +03:00
memcpy ( variable , readPosition , singleElementSize ) ;
}
else
{
2021-06-26 13:45:15 +02:00
void * readPosition = ( ( char * ) acc - > buffer_view - > buffer - > data ) + ( index * acc - > buffer_view - > stride ) + acc - > buffer_view - > offset + acc - > offset ;
2021-06-26 14:22:00 +03:00
memcpy ( variable , readPosition , singleElementSize ) ;
}
2021-06-30 16:39:07 +02:00
2021-03-14 13:09:31 +02:00
return true ;
}
2021-06-26 13:45:15 +02:00
static void * ReadGLTFValuesAs ( cgltf_accessor * acc , cgltf_component_type type , bool adjustOnDownCasting )
2018-09-05 10:59:05 +02:00
{
2021-06-26 14:22:00 +03:00
unsigned int count = acc - > count ;
unsigned int typeSize = 0 ;
2021-06-26 13:45:15 +02:00
switch ( type )
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8u :
case cgltf_component_type_r_8 : typeSize = 1 ; break ;
case cgltf_component_type_r_16u :
case cgltf_component_type_r_16 : typeSize = 2 ; break ;
case cgltf_component_type_r_32f :
case cgltf_component_type_r_32u : typeSize = 4 ; break ;
case cgltf_component_type_invalid : typeSize = 0 ; break ;
2021-06-26 13:45:15 +02:00
default : break ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
unsigned int typeElements = 0 ;
2021-06-26 13:45:15 +02:00
switch ( acc - > type )
{
2021-06-26 14:22:00 +03:00
case cgltf_type_scalar : typeElements = 1 ; break ;
case cgltf_type_vec2 : typeElements = 2 ; break ;
case cgltf_type_vec3 : typeElements = 3 ; break ;
case cgltf_type_vec4 :
case cgltf_type_mat2 : typeElements = 4 ; break ;
case cgltf_type_mat3 : typeElements = 9 ; break ;
case cgltf_type_mat4 : typeElements = 16 ; break ;
case cgltf_type_invalid : typeElements = 0 ; break ;
2021-06-26 13:45:15 +02:00
default : break ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
if ( acc - > component_type = = type )
2018-09-14 13:00:48 +02:00
{
2021-06-26 13:45:15 +02:00
void * array = RL_MALLOC ( count * typeElements * typeSize ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
for ( unsigned int i = 0 ; i < count ; i + + ) ReadGLTFValue ( acc , i , ( char * ) array + i * typeElements * typeSize ) ;
2021-06-26 14:22:00 +03:00
return array ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
}
else
{
2021-06-26 14:22:00 +03:00
unsigned int accTypeSize = 0 ;
2021-06-26 13:45:15 +02:00
switch ( acc - > component_type )
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8u :
case cgltf_component_type_r_8 : accTypeSize = 1 ; break ;
case cgltf_component_type_r_16u :
case cgltf_component_type_r_16 : accTypeSize = 2 ; break ;
case cgltf_component_type_r_32f :
case cgltf_component_type_r_32u : accTypeSize = 4 ; break ;
case cgltf_component_type_invalid : accTypeSize = 0 ; break ;
2021-06-26 13:45:15 +02:00
default : break ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
void * array = RL_MALLOC ( count * typeElements * typeSize ) ;
void * additionalArray = RL_MALLOC ( count * typeElements * accTypeSize ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
for ( unsigned int i = 0 ; i < count ; i + + )
2019-04-09 13:23:51 +02:00
{
2021-06-26 13:45:15 +02:00
ReadGLTFValue ( acc , i , ( char * ) additionalArray + i * typeElements * accTypeSize ) ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
switch ( acc - > component_type )
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8u :
2019-04-09 13:23:51 +02:00
{
2021-06-26 13:45:15 +02:00
unsigned char * typedAdditionalArray = ( unsigned char * ) additionalArray ;
switch ( type )
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8 :
2019-05-14 14:06:17 +02:00
{
2021-06-26 13:45:15 +02:00
char * typedArray = ( char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2021-03-14 13:09:31 +02:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( char ) ( typedAdditionalArray [ i ] / ( UCHAR_MAX / CHAR_MAX ) ) ;
else typedArray [ i ] = ( char ) typedAdditionalArray [ i ] ;
2021-03-14 13:09:31 +02:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16u :
{
2021-06-26 13:45:15 +02:00
unsigned short * typedArray = ( unsigned short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned short ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16 :
2019-05-14 14:06:17 +02:00
{
2021-06-26 13:45:15 +02:00
short * typedArray = ( short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( short ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32f :
{
2021-06-26 13:45:15 +02:00
float * typedArray = ( float * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( float ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32u :
{
2021-06-26 13:45:15 +02:00
unsigned int * typedArray = ( unsigned int * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned int ) typedAdditionalArray [ i ] ;
} break ;
default :
{
2021-06-26 14:22:00 +03:00
RL_FREE ( array ) ;
RL_FREE ( additionalArray ) ;
return NULL ;
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8 :
{
2021-06-26 13:45:15 +02:00
char * typedAdditionalArray = ( char * ) additionalArray ;
switch ( type )
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8u :
{
2021-06-26 13:45:15 +02:00
unsigned char * typedArray = ( unsigned char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned char ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16u :
{
2021-06-26 13:45:15 +02:00
unsigned short * typedArray = ( unsigned short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned short ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16 :
{
2021-06-26 13:45:15 +02:00
short * typedArray = ( short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( short ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32f :
{
2021-06-26 13:45:15 +02:00
float * typedArray = ( float * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( float ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32u :
{
2021-06-26 13:45:15 +02:00
unsigned int * typedArray = ( unsigned int * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned int ) typedAdditionalArray [ i ] ;
} break ;
2021-06-30 16:39:07 +02:00
default :
2021-06-26 13:45:15 +02:00
{
2021-06-26 14:22:00 +03:00
RL_FREE ( array ) ;
RL_FREE ( additionalArray ) ;
return NULL ;
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16u :
{
2021-06-26 13:45:15 +02:00
unsigned short * typedAdditionalArray = ( unsigned short * ) additionalArray ;
switch ( type )
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8u :
{
2021-06-26 13:45:15 +02:00
unsigned char * typedArray = ( unsigned char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2021-06-26 14:22:00 +03:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( unsigned char ) ( typedAdditionalArray [ i ] / ( USHRT_MAX / UCHAR_MAX ) ) ;
else typedArray [ i ] = ( unsigned char ) typedAdditionalArray [ i ] ;
2021-03-14 13:09:31 +02:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8 :
{
2021-06-26 13:45:15 +02:00
char * typedArray = ( char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2021-03-14 13:09:31 +02:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( char ) ( typedAdditionalArray [ i ] / ( USHRT_MAX / CHAR_MAX ) ) ;
else typedArray [ i ] = ( char ) typedAdditionalArray [ i ] ;
2021-03-14 13:09:31 +02:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16 :
2019-05-14 14:06:17 +02:00
{
2021-06-26 13:45:15 +02:00
short * typedArray = ( short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2019-05-14 14:06:17 +02:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( short ) ( typedAdditionalArray [ i ] / ( USHRT_MAX / SHRT_MAX ) ) ;
else typedArray [ i ] = ( short ) typedAdditionalArray [ i ] ;
2019-05-14 14:06:17 +02:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32f :
{
2021-07-27 23:35:54 +02:00
float * typedArray = ( float * ) array ;
2021-06-26 13:45:15 +02:00
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( float ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32u :
2021-01-28 12:29:06 +02:00
{
2021-07-27 23:35:54 +02:00
unsigned int * typedArray = ( unsigned int * ) array ;
2021-06-26 13:45:15 +02:00
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned int ) typedAdditionalArray [ i ] ;
} break ;
2021-06-30 16:39:07 +02:00
default :
2021-06-26 13:45:15 +02:00
{
2021-06-26 14:22:00 +03:00
RL_FREE ( array ) ;
RL_FREE ( additionalArray ) ;
return NULL ;
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16 :
{
2021-06-26 13:45:15 +02:00
short * typedAdditionalArray = ( short * ) additionalArray ;
switch ( type )
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8u :
2021-03-14 13:09:31 +02:00
{
2021-06-26 13:45:15 +02:00
unsigned char * typedArray = ( unsigned char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2021-01-28 12:29:06 +02:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( unsigned char ) ( typedAdditionalArray [ i ] / ( SHRT_MAX / UCHAR_MAX ) ) ;
else typedArray [ i ] = ( unsigned char ) typedAdditionalArray [ i ] ;
2021-02-24 08:25:20 +00:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8 :
{
2021-06-26 13:45:15 +02:00
char * typedArray = ( char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2021-02-24 08:25:20 +00:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( char ) ( typedAdditionalArray [ i ] / ( SHRT_MAX / CHAR_MAX ) ) ;
else typedArray [ i ] = ( char ) typedAdditionalArray [ i ] ;
2021-01-28 12:29:06 +02:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16u :
{
2021-06-26 13:45:15 +02:00
unsigned short * typedArray = ( unsigned short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned short ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32f :
2021-05-26 21:23:13 +03:00
{
2021-06-26 13:45:15 +02:00
float * typedArray = ( float * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( float ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32u :
{
2021-06-26 13:45:15 +02:00
unsigned int * typedArray = ( unsigned int * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned int ) typedAdditionalArray [ i ] ;
} break ;
default :
{
2021-06-26 14:22:00 +03:00
RL_FREE ( array ) ;
RL_FREE ( additionalArray ) ;
return NULL ;
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32f :
{
2021-06-26 13:45:15 +02:00
float * typedAdditionalArray = ( float * ) additionalArray ;
switch ( type )
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8u :
{
2021-06-26 13:45:15 +02:00
unsigned char * typedArray = ( unsigned char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned char ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8 :
{
2021-06-26 13:45:15 +02:00
char * typedArray = ( char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( char ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16u :
{
2021-06-26 13:45:15 +02:00
unsigned short * typedArray = ( unsigned short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned short ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16 :
{
2021-06-26 13:45:15 +02:00
short * typedArray = ( short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( short ) typedAdditionalArray [ i ] ;
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32u :
{
2021-06-26 13:45:15 +02:00
unsigned int * typedArray = ( unsigned int * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( unsigned int ) typedAdditionalArray [ i ] ;
} break ;
default :
{
2021-06-26 14:22:00 +03:00
RL_FREE ( array ) ;
RL_FREE ( additionalArray ) ;
return NULL ;
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32u :
{
2021-06-26 13:45:15 +02:00
unsigned int * typedAdditionalArray = ( unsigned int * ) additionalArray ;
switch ( type )
{
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8u :
{
2021-06-26 13:45:15 +02:00
unsigned char * typedArray = ( unsigned char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2021-06-26 14:22:00 +03:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( unsigned char ) ( typedAdditionalArray [ i ] / ( UINT_MAX / UCHAR_MAX ) ) ;
else typedArray [ i ] = ( unsigned char ) typedAdditionalArray [ i ] ;
2021-05-26 21:23:13 +03:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_8 :
{
2021-06-26 13:45:15 +02:00
char * typedArray = ( char * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2021-05-26 21:23:13 +03:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( char ) ( typedAdditionalArray [ i ] / ( UINT_MAX / CHAR_MAX ) ) ;
else typedArray [ i ] = ( char ) typedAdditionalArray [ i ] ;
2021-06-26 14:22:00 +03:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16u :
{
2021-06-26 13:45:15 +02:00
unsigned short * typedArray = ( unsigned short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2021-06-26 14:22:00 +03:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( unsigned short ) ( typedAdditionalArray [ i ] / ( UINT_MAX / USHRT_MAX ) ) ;
else typedArray [ i ] = ( unsigned short ) typedAdditionalArray [ i ] ;
2021-06-26 14:22:00 +03:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_16 :
{
2021-06-26 13:45:15 +02:00
short * typedArray = ( short * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + )
2021-06-26 14:22:00 +03:00
{
2021-06-26 13:45:15 +02:00
if ( adjustOnDownCasting ) typedArray [ i ] = ( short ) ( typedAdditionalArray [ i ] / ( UINT_MAX / SHRT_MAX ) ) ;
else typedArray [ i ] = ( short ) typedAdditionalArray [ i ] ;
2021-05-26 21:23:13 +03:00
}
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
case cgltf_component_type_r_32f :
{
2021-06-26 13:45:15 +02:00
float * typedArray = ( float * ) array ;
for ( unsigned int i = 0 ; i < count * typeElements ; i + + ) typedArray [ i ] = ( float ) typedAdditionalArray [ i ] ;
} break ;
default :
{
2021-06-26 14:22:00 +03:00
RL_FREE ( array ) ;
RL_FREE ( additionalArray ) ;
return NULL ;
2021-06-26 13:45:15 +02:00
} break ;
2019-05-14 14:06:17 +02:00
}
2021-06-26 13:45:15 +02:00
} break ;
default :
{
2021-06-26 14:22:00 +03:00
RL_FREE ( array ) ;
RL_FREE ( additionalArray ) ;
return NULL ;
2021-06-26 13:45:15 +02:00
} break ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
RL_FREE ( additionalArray ) ;
return array ;
}
}
2019-04-09 13:23:51 +02:00
2021-06-26 14:22:00 +03:00
// LoadGLTF loads in model data from given filename, supporting both .gltf and .glb
static Model LoadGLTF ( const char * fileName )
{
/***********************************************************************************
2021-03-19 19:43:44 +01:00
2021-06-26 14:22:00 +03:00
Function implemented by Wilhem Barbier ( @ wbrbr ) , with modifications by Tyler Bezera ( @ gamerfiend ) and Hristo Stamenov ( @ object71 )
2021-03-19 19:43:44 +01:00
2021-06-26 14:22:00 +03:00
Features :
- Supports . gltf and . glb files
- Supports embedded ( base64 ) or external textures
- Loads all raylib supported material textures , values and colors
- Supports multiple mesh per model and multiple primitives per model
2021-03-19 19:43:44 +01:00
2021-06-26 14:22:00 +03:00
Some restrictions ( not exhaustive ) :
- Triangle - only meshes
- Not supported node hierarchies or transforms
- Only supports unsigned short indices ( no byte / unsigned int )
- Only supports float for texture coordinates ( no byte / unsigned short )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Model model = { 0 } ;
// glTF file loading
unsigned int dataSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
if ( fileData = = NULL ) return model ;
2021-03-19 19:43:44 +01:00
2021-06-26 14:22:00 +03:00
// glTF data loading
cgltf_options options = { 0 } ;
cgltf_data * data = NULL ;
cgltf_result result = cgltf_parse ( & options , fileData , dataSize , & data ) ;
if ( result = = cgltf_result_success )
{
TRACELOG ( LOG_INFO , " MODEL: [%s] glTF meshes (%s) count: %i " , fileName , ( data - > file_type = = 2 ) ? " glb " : " gltf " , data - > meshes_count ) ;
TRACELOG ( LOG_INFO , " MODEL: [%s] glTF materials (%s) count: %i " , fileName , ( data - > file_type = = 2 ) ? " glb " : " gltf " , data - > materials_count ) ;
// Read data buffers
result = cgltf_load_buffers ( & options , data , fileName ) ;
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-06-26 13:45:15 +02:00
if ( data - > scenes_count > 1 ) TRACELOG ( LOG_INFO , " MODEL: [%s] Has multiple scenes but only the first one will be loaded " , fileName ) ;
2021-06-26 14:22:00 +03:00
2021-09-01 23:09:30 +02:00
int primitiveCount = 0 ;
2021-06-26 14:22:00 +03:00
for ( unsigned int i = 0 ; i < data - > scene - > nodes_count ; i + + )
{
2021-09-01 23:09:30 +02:00
GetGLTFPrimitiveCount ( data - > scene - > nodes [ i ] , & primitiveCount ) ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
// Process glTF data and map to model
2021-09-01 23:09:30 +02:00
model . meshCount = primitiveCount ;
2021-06-26 14:22:00 +03:00
model . meshes = RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
model . materialCount = ( int ) data - > materials_count + 1 ;
model . materials = RL_MALLOC ( model . materialCount * sizeof ( Material ) ) ;
model . meshMaterial = RL_MALLOC ( model . meshCount * sizeof ( int ) ) ;
model . boneCount = ( int ) data - > nodes_count ;
model . bones = RL_CALLOC ( model . boneCount , sizeof ( BoneInfo ) ) ;
model . bindPose = RL_CALLOC ( model . boneCount , sizeof ( Transform ) ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
InitGLTFBones ( & model , data ) ;
LoadGLTFMaterial ( & model , fileName , data ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
int primitiveIndex = 0 ;
2021-06-26 13:45:15 +02:00
for ( unsigned int i = 0 ; i < data - > scene - > nodes_count ; i + + )
2021-06-26 14:22:00 +03:00
{
Matrix staticTransform = MatrixIdentity ( ) ;
LoadGLTFNode ( data , data - > scene - > nodes [ i ] , & model , staticTransform , & primitiveIndex , fileName ) ;
2019-04-09 13:23:51 +02:00
}
2021-03-19 19:43:44 +01:00
2019-04-04 13:33:54 +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
2020-04-30 19:51:57 +02:00
RL_FREE ( 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
}
2021-01-28 12:29:06 +02:00
2021-08-28 12:01:16 +02:00
static void InitGLTFBones ( Model * model , const cgltf_data * data )
2021-02-02 11:49:42 +02:00
{
2021-03-14 13:09:31 +02:00
for ( unsigned int j = 0 ; j < data - > nodes_count ; j + + )
2021-02-02 11:49:42 +02:00
{
2021-03-14 13:09:31 +02:00
strcpy ( model - > bones [ j ] . name , data - > nodes [ j ] . name = = 0 ? " ANIMJOINT " : data - > nodes [ j ] . name ) ;
2021-03-22 12:45:46 -07:00
model - > bones [ j ] . parent = ( data - > nodes [ j ] . parent ! = NULL ) ? ( int ) ( data - > nodes [ j ] . parent - data - > nodes ) : - 1 ;
2021-03-14 13:09:31 +02:00
}
2021-03-19 19:43:44 +01:00
2021-03-14 13:09:31 +02:00
for ( unsigned int i = 0 ; i < data - > nodes_count ; i + + )
{
2021-03-31 17:55:46 +02:00
if ( data - > nodes [ i ] . has_translation ) memcpy ( & model - > bindPose [ i ] . translation , data - > nodes [ i ] . translation , 3 * sizeof ( float ) ) ;
2021-03-14 13:09:31 +02:00
else model - > bindPose [ i ] . translation = Vector3Zero ( ) ;
2021-03-31 17:55:46 +02:00
if ( data - > nodes [ i ] . has_rotation ) memcpy ( & model - > bindPose [ i ] . rotation , data - > nodes [ i ] . rotation , 4 * sizeof ( float ) ) ;
2021-03-14 13:09:31 +02:00
else model - > bindPose [ i ] . rotation = QuaternionIdentity ( ) ;
model - > bindPose [ i ] . rotation = QuaternionNormalize ( model - > bindPose [ i ] . rotation ) ;
2021-03-19 19:43:44 +01:00
2021-03-31 17:55:46 +02:00
if ( data - > nodes [ i ] . has_scale ) memcpy ( & model - > bindPose [ i ] . scale , data - > nodes [ i ] . scale , 3 * sizeof ( float ) ) ;
2021-03-14 13:09:31 +02:00
else model - > bindPose [ i ] . scale = Vector3One ( ) ;
}
2021-03-19 19:43:44 +01:00
2021-03-14 13:09:31 +02:00
{
2021-08-28 12:01:16 +02:00
bool * completedBones = RL_CALLOC ( model - > boneCount , sizeof ( bool ) ) ;
2021-03-14 13:09:31 +02:00
int numberCompletedBones = 0 ;
2021-06-03 20:25:28 +02:00
while ( numberCompletedBones < model - > boneCount )
{
2021-03-14 13:09:31 +02:00
for ( int i = 0 ; i < model - > boneCount ; i + + )
{
if ( completedBones [ i ] ) continue ;
2021-06-03 20:25:28 +02:00
if ( model - > bones [ i ] . parent < 0 )
{
2021-03-14 13:09:31 +02:00
completedBones [ i ] = true ;
numberCompletedBones + + ;
continue ;
}
if ( ! completedBones [ model - > bones [ i ] . parent ] ) continue ;
Transform * currentTransform = & model - > bindPose [ i ] ;
BoneInfo * currentBone = & model - > bones [ i ] ;
int root = currentBone - > parent ;
2021-06-03 20:25:28 +02:00
if ( root > = model - > boneCount ) root = 0 ;
2021-03-14 13:09:31 +02:00
Transform * parentTransform = & model - > bindPose [ root ] ;
currentTransform - > rotation = QuaternionMultiply ( parentTransform - > rotation , currentTransform - > rotation ) ;
currentTransform - > translation = Vector3RotateByQuaternion ( currentTransform - > translation , parentTransform - > rotation ) ;
currentTransform - > translation = Vector3Add ( currentTransform - > translation , parentTransform - > translation ) ;
2021-04-05 13:47:52 +02:00
currentTransform - > scale = Vector3Multiply ( currentTransform - > scale , parentTransform - > scale ) ;
2021-03-14 13:09:31 +02:00
completedBones [ i ] = true ;
numberCompletedBones + + ;
}
}
RL_FREE ( completedBones ) ;
}
}
2021-06-03 20:25:28 +02:00
static void LoadGLTFMaterial ( Model * model , const char * fileName , const cgltf_data * data )
2021-03-14 13:09:31 +02:00
{
for ( int i = 0 ; i < model - > materialCount - 1 ; i + + )
{
model - > materials [ i ] = LoadMaterialDefault ( ) ;
Color tint = ( Color ) { 255 , 255 , 255 , 255 } ;
const char * texPath = GetDirectoryPath ( fileName ) ;
// Ensure material follows raylib support for PBR (metallic/roughness flow)
if ( data - > materials [ i ] . has_pbr_metallic_roughness )
2021-02-02 11:49:42 +02:00
{
2021-03-31 17:55:46 +02:00
tint . r = ( unsigned char ) ( data - > materials [ i ] . pbr_metallic_roughness . base_color_factor [ 0 ] * 255 ) ;
tint . g = ( unsigned char ) ( data - > materials [ i ] . pbr_metallic_roughness . base_color_factor [ 1 ] * 255 ) ;
tint . b = ( unsigned char ) ( data - > materials [ i ] . pbr_metallic_roughness . base_color_factor [ 2 ] * 255 ) ;
tint . a = ( unsigned char ) ( data - > materials [ i ] . pbr_metallic_roughness . base_color_factor [ 3 ] * 255 ) ;
2021-03-14 13:09:31 +02:00
model - > materials [ i ] . maps [ MATERIAL_MAP_ALBEDO ] . color = tint ;
if ( data - > materials [ i ] . pbr_metallic_roughness . base_color_texture . texture )
{
Image albedo = LoadImageFromCgltfImage ( data - > materials [ i ] . pbr_metallic_roughness . base_color_texture . texture - > image , texPath , tint ) ;
model - > materials [ i ] . maps [ MATERIAL_MAP_ALBEDO ] . texture = LoadTextureFromImage ( albedo ) ;
UnloadImage ( albedo ) ;
}
tint = WHITE ; // Set tint to white after it's been used by Albedo
if ( data - > materials [ i ] . pbr_metallic_roughness . metallic_roughness_texture . texture )
{
Image metallicRoughness = LoadImageFromCgltfImage ( data - > materials [ i ] . pbr_metallic_roughness . metallic_roughness_texture . texture - > image , texPath , tint ) ;
model - > materials [ i ] . maps [ MATERIAL_MAP_ROUGHNESS ] . texture = LoadTextureFromImage ( metallicRoughness ) ;
float roughness = data - > materials [ i ] . pbr_metallic_roughness . roughness_factor ;
model - > materials [ i ] . maps [ MATERIAL_MAP_ROUGHNESS ] . value = roughness ;
float metallic = data - > materials [ i ] . pbr_metallic_roughness . metallic_factor ;
model - > materials [ i ] . maps [ MATERIAL_MAP_METALNESS ] . value = metallic ;
UnloadImage ( metallicRoughness ) ;
}
if ( data - > materials [ i ] . normal_texture . texture )
{
Image normalImage = LoadImageFromCgltfImage ( data - > materials [ i ] . normal_texture . texture - > image , texPath , tint ) ;
model - > materials [ i ] . maps [ MATERIAL_MAP_NORMAL ] . texture = LoadTextureFromImage ( normalImage ) ;
UnloadImage ( normalImage ) ;
}
if ( data - > materials [ i ] . occlusion_texture . texture )
{
Image occulsionImage = LoadImageFromCgltfImage ( data - > materials [ i ] . occlusion_texture . texture - > image , texPath , tint ) ;
model - > materials [ i ] . maps [ MATERIAL_MAP_OCCLUSION ] . texture = LoadTextureFromImage ( occulsionImage ) ;
UnloadImage ( occulsionImage ) ;
}
if ( data - > materials [ i ] . emissive_texture . texture )
{
Image emissiveImage = LoadImageFromCgltfImage ( data - > materials [ i ] . emissive_texture . texture - > image , texPath , tint ) ;
model - > materials [ i ] . maps [ MATERIAL_MAP_EMISSION ] . texture = LoadTextureFromImage ( emissiveImage ) ;
tint . r = ( unsigned char ) ( data - > materials [ i ] . emissive_factor [ 0 ] * 255 ) ;
tint . g = ( unsigned char ) ( data - > materials [ i ] . emissive_factor [ 1 ] * 255 ) ;
tint . b = ( unsigned char ) ( data - > materials [ i ] . emissive_factor [ 2 ] * 255 ) ;
model - > materials [ i ] . maps [ MATERIAL_MAP_EMISSION ] . color = tint ;
UnloadImage ( emissiveImage ) ;
}
}
}
2021-03-19 19:43:44 +01:00
2021-03-14 13:09:31 +02:00
model - > materials [ model - > materialCount - 1 ] = LoadMaterialDefault ( ) ;
}
2021-09-23 21:06:51 +02:00
static void BindGLTFPrimitiveToBones ( Model * model , cgltf_node * node , const cgltf_data * data , int primitiveIndex )
2021-03-14 13:09:31 +02:00
{
2021-09-23 21:06:51 +02:00
int nodeId = node - data - > nodes ;
if ( model - > meshes [ primitiveIndex ] . boneIds = = NULL )
2021-03-14 13:09:31 +02:00
{
2021-09-23 21:06:51 +02:00
model - > meshes [ primitiveIndex ] . boneIds = RL_CALLOC ( model - > meshes [ primitiveIndex ] . vertexCount * 4 , sizeof ( int ) ) ;
model - > meshes [ primitiveIndex ] . boneWeights = RL_CALLOC ( model - > meshes [ primitiveIndex ] . vertexCount * 4 , sizeof ( float ) ) ;
for ( int b = 0 ; b < model - > meshes [ primitiveIndex ] . vertexCount * 4 ; b + + )
2021-03-14 13:09:31 +02:00
{
2021-09-23 21:06:51 +02:00
if ( b % 4 = = 0 )
2021-03-14 13:09:31 +02:00
{
2021-09-23 21:06:51 +02:00
model - > meshes [ primitiveIndex ] . boneIds [ b ] = nodeId ;
model - > meshes [ primitiveIndex ] . boneWeights [ b ] = 1.0f ;
}
else
{
model - > meshes [ primitiveIndex ] . boneIds [ b ] = 0 ;
model - > meshes [ primitiveIndex ] . boneWeights [ b ] = 0.0f ;
2021-03-14 13:09:31 +02:00
}
}
}
}
2021-01-28 12:29:06 +02:00
// LoadGLTF loads in animation data from given filename
2021-09-22 07:04:10 -04:00
static ModelAnimation * LoadGLTFModelAnimations ( const char * fileName , unsigned int * animCount )
2021-01-28 12:29:06 +02:00
{
/***********************************************************************************
Function implemented by Hristo Stamenov ( @ object71 )
Features :
- Supports . gltf and . glb files
Some restrictions ( not exhaustive ) :
- . . .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-03-02 02:03:52 +01:00
2021-01-28 12:29:06 +02:00
// glTF file loading
unsigned int dataSize = 0 ;
unsigned char * fileData = LoadFileData ( fileName , & dataSize ) ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
ModelAnimation * animations = NULL ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( fileData = = NULL ) return animations ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
// glTF data loading
cgltf_options options = { 0 } ;
cgltf_data * data = NULL ;
cgltf_result result = cgltf_parse ( & options , fileData , dataSize , & data ) ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
if ( result = = cgltf_result_success )
{
TRACELOG ( LOG_INFO , " MODEL: [%s] glTF animations (%s) count: %i " , fileName , ( data - > file_type = = 2 ) ? " glb " :
" gltf " , data - > animations_count ) ;
2021-02-24 08:25:20 +00:00
result = cgltf_load_buffers ( & options , data , fileName ) ;
if ( result ! = cgltf_result_success ) TRACELOG ( LOG_WARNING , " MODEL: [%s] unable to load glTF animations data " , fileName ) ;
2021-01-28 12:29:06 +02:00
animations = RL_MALLOC ( data - > animations_count * sizeof ( ModelAnimation ) ) ;
2021-09-22 07:04:10 -04:00
* animCount = ( unsigned int ) data - > animations_count ;
2021-03-09 01:11:08 +02:00
2021-01-28 12:29:06 +02:00
for ( unsigned int a = 0 ; a < data - > animations_count ; a + + )
{
2021-02-02 11:49:42 +02:00
// gltf animation consists of the following structures:
2021-06-26 14:22:00 +03:00
// - nodes - bones are part of the node system (the whole node system is animatable)
2021-02-02 11:49:42 +02:00
// - channels - single transformation type on a single bone
2021-06-26 14:22:00 +03:00
// - node - animatable node
2021-02-02 11:49:42 +02:00
// - transformation type (path) - translation, rotation, scale
// - sampler - animation samples
// - input - points in time this transformation happens
// - output - the transformation amount at the given input points in time
// - interpolation - the type of interpolation to use between the frames
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
cgltf_animation * animation = data - > animations + a ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
ModelAnimation * output = animations + a ;
2021-03-19 19:43:44 +01:00
2021-02-02 11:49:42 +02:00
// 30 frames sampled per second
2021-06-26 14:22:00 +03:00
const float timeStep = ( 1.0f / 60.0f ) ;
2021-02-02 11:49:42 +02:00
float animationDuration = 0.0f ;
2021-03-19 19:43:44 +01:00
2021-02-02 11:49:42 +02:00
// Getting the max animation time to consider for animation duration
2021-02-09 13:23:06 +01:00
for ( unsigned int i = 0 ; i < animation - > channels_count ; i + + )
2021-02-02 11:49:42 +02:00
{
2021-08-28 12:01:16 +02:00
cgltf_animation_channel * channel = animation - > channels + i ;
2021-02-20 10:42:32 -08:00
int frameCounts = ( int ) channel - > sampler - > input - > count ;
2021-02-02 11:49:42 +02:00
float lastFrameTime = 0.0f ;
2021-03-19 19:43:44 +01:00
2021-06-26 13:45:15 +02:00
if ( ReadGLTFValue ( channel - > sampler - > input , frameCounts - 1 , & lastFrameTime ) )
2021-02-02 11:49:42 +02:00
{
animationDuration = fmaxf ( lastFrameTime , animationDuration ) ;
}
}
2021-03-19 19:43:44 +01:00
2021-06-03 20:25:28 +02:00
output - > frameCount = ( int ) ( animationDuration / timeStep ) ;
2021-02-20 10:42:32 -08:00
output - > boneCount = ( int ) data - > nodes_count ;
2021-01-28 12:29:06 +02:00
output - > bones = RL_MALLOC ( output - > boneCount * sizeof ( BoneInfo ) ) ;
output - > framePoses = RL_MALLOC ( output - > frameCount * sizeof ( Transform * ) ) ;
2021-02-02 11:49:42 +02:00
// output->framerate = // TODO: Use framerate instead of const timestep
2021-03-19 19:43:44 +01:00
2021-02-02 11:49:42 +02:00
// Name and parent bones
2021-06-17 11:17:39 +02:00
for ( int j = 0 ; j < output - > boneCount ; j + + )
2021-01-28 12:29:06 +02:00
{
strcpy ( output - > bones [ j ] . name , data - > nodes [ j ] . name = = 0 ? " ANIMJOINT " : data - > nodes [ j ] . name ) ;
2021-02-24 08:25:20 +00:00
output - > bones [ j ] . parent = ( data - > nodes [ j ] . parent ! = NULL ) ? ( int ) ( data - > nodes [ j ] . parent - data - > nodes ) : - 1 ;
2021-01-28 12:29:06 +02:00
}
2021-02-02 11:49:42 +02:00
// Allocate data for frames
// Initiate with zero bone translations
2021-02-09 13:23:06 +01:00
for ( int frame = 0 ; frame < output - > frameCount ; frame + + )
2021-01-28 12:29:06 +02:00
{
2021-05-20 19:25:09 +02:00
output - > framePoses [ frame ] = RL_MALLOC ( output - > boneCount * sizeof ( Transform ) ) ;
2021-03-19 19:43:44 +01:00
2021-08-08 13:02:51 +02:00
for ( int i = 0 ; i < output - > boneCount ; i + + )
2021-01-28 12:29:06 +02:00
{
2021-06-26 13:45:15 +02:00
if ( data - > nodes [ i ] . has_translation ) memcpy ( & output - > framePoses [ frame ] [ i ] . translation , data - > nodes [ i ] . translation , 3 * sizeof ( float ) ) ;
2021-06-26 14:22:00 +03:00
else output - > framePoses [ frame ] [ i ] . translation = Vector3Zero ( ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
if ( data - > nodes [ i ] . has_rotation ) memcpy ( & output - > framePoses [ frame ] [ i ] , data - > nodes [ i ] . rotation , 4 * sizeof ( float ) ) ;
2021-06-26 14:22:00 +03:00
else output - > framePoses [ frame ] [ i ] . rotation = QuaternionIdentity ( ) ;
2021-06-30 16:39:07 +02:00
2021-01-28 12:29:06 +02:00
output - > framePoses [ frame ] [ i ] . rotation = QuaternionNormalize ( output - > framePoses [ frame ] [ i ] . rotation ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
if ( data - > nodes [ i ] . has_scale ) memcpy ( & output - > framePoses [ frame ] [ i ] . scale , data - > nodes [ i ] . scale , 3 * sizeof ( float ) ) ;
2021-06-26 14:22:00 +03:00
else output - > framePoses [ frame ] [ i ] . scale = Vector3One ( ) ;
2021-01-28 12:29:06 +02:00
}
}
2021-03-19 19:43:44 +01:00
2021-02-02 11:49:42 +02:00
// for each single transformation type on single bone
2021-02-09 13:23:06 +01:00
for ( unsigned int channelId = 0 ; channelId < animation - > channels_count ; channelId + + )
2021-01-28 12:29:06 +02:00
{
2021-08-28 12:01:16 +02:00
cgltf_animation_channel * channel = animation - > channels + channelId ;
cgltf_animation_sampler * sampler = channel - > sampler ;
2021-03-19 19:43:44 +01:00
2021-02-20 10:42:32 -08:00
int boneId = ( int ) ( channel - > target_node - data - > nodes ) ;
2021-03-19 19:43:44 +01:00
2021-02-09 13:23:06 +01:00
for ( int frame = 0 ; frame < output - > frameCount ; frame + + )
2021-01-28 12:29:06 +02:00
{
2021-02-02 11:49:42 +02:00
bool shouldSkipFurtherTransformation = true ;
int outputMin = 0 ;
int outputMax = 0 ;
2021-03-31 17:55:46 +02:00
float frameTime = frame * timeStep ;
2021-02-02 11:49:42 +02:00
float lerpPercent = 0.0f ;
2021-03-19 19:43:44 +01:00
2021-02-02 11:49:42 +02:00
// For this transformation:
// getting between which input values the current frame time position
// and also what is the percent to use in the linear interpolation later
2021-02-09 13:23:06 +01:00
for ( unsigned int j = 0 ; j < sampler - > input - > count ; j + + )
2021-02-02 11:49:42 +02:00
{
float inputFrameTime ;
2021-06-26 13:45:15 +02:00
if ( ReadGLTFValue ( sampler - > input , j , & inputFrameTime ) )
2021-01-28 12:29:06 +02:00
{
2021-02-09 13:23:06 +01:00
if ( frameTime < inputFrameTime )
2021-02-02 11:49:42 +02:00
{
shouldSkipFurtherTransformation = false ;
2021-02-24 08:25:20 +00:00
outputMin = ( j = = 0 ) ? 0 : j - 1 ;
2021-02-02 11:49:42 +02:00
outputMax = j ;
2021-03-19 19:43:44 +01:00
2021-02-02 11:49:42 +02:00
float previousInputTime = 0.0f ;
2021-06-26 13:45:15 +02:00
if ( ReadGLTFValue ( sampler - > input , outputMin , & previousInputTime ) )
2021-02-02 11:49:42 +02:00
{
2021-03-31 17:55:46 +02:00
if ( ( inputFrameTime - previousInputTime ) ! = 0 )
2021-03-09 01:11:08 +02:00
{
lerpPercent = ( frameTime - previousInputTime ) / ( inputFrameTime - previousInputTime ) ;
}
2021-02-02 11:49:42 +02:00
}
2021-03-19 19:43:44 +01:00
2021-02-02 11:49:42 +02:00
break ;
}
2021-03-19 19:43:44 +01:00
}
2021-03-02 02:03:52 +01:00
else break ;
2021-02-02 11:49:42 +02:00
}
2021-03-19 19:43:44 +01:00
2021-02-02 11:49:42 +02:00
// If the current transformation has no information for the current frame time point
2021-03-02 02:03:52 +01:00
if ( shouldSkipFurtherTransformation ) continue ;
2021-06-30 16:39:07 +02:00
2021-03-02 02:03:52 +01:00
if ( channel - > target_path = = cgltf_animation_path_type_translation )
{
2021-02-02 11:49:42 +02:00
Vector3 translationStart ;
Vector3 translationEnd ;
2021-03-19 19:43:44 +01:00
2021-06-26 14:22:00 +03:00
float values [ 3 ] ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
bool success = ReadGLTFValue ( sampler - > output , outputMin , values ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
translationStart . x = values [ 0 ] ;
translationStart . y = values [ 1 ] ;
translationStart . z = values [ 2 ] ;
2021-06-30 16:39:07 +02:00
2021-09-23 21:06:51 +02:00
success = ReadGLTFValue ( sampler - > output , outputMax , values ) & & success ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
translationEnd . x = values [ 0 ] ;
translationEnd . y = values [ 1 ] ;
translationEnd . z = values [ 2 ] ;
2021-03-19 19:43:44 +01:00
2021-09-23 21:06:51 +02:00
if ( success )
{
output - > framePoses [ frame ] [ boneId ] . translation = Vector3Lerp ( translationStart , translationEnd , lerpPercent ) ;
}
2021-01-28 12:29:06 +02:00
}
2021-03-02 02:03:52 +01:00
if ( channel - > target_path = = cgltf_animation_path_type_rotation )
{
2021-06-26 14:22:00 +03:00
Vector4 rotationStart ;
Vector4 rotationEnd ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
float values [ 4 ] ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
bool success = ReadGLTFValue ( sampler - > output , outputMin , & values ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
rotationStart . x = values [ 0 ] ;
rotationStart . y = values [ 1 ] ;
rotationStart . z = values [ 2 ] ;
rotationStart . w = values [ 3 ] ;
2021-06-30 16:39:07 +02:00
2021-09-23 21:06:51 +02:00
success = ReadGLTFValue ( sampler - > output , outputMax , & values ) & & success ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
rotationEnd . x = values [ 0 ] ;
rotationEnd . y = values [ 1 ] ;
rotationEnd . z = values [ 2 ] ;
rotationEnd . w = values [ 3 ] ;
2021-03-19 19:43:44 +01:00
2021-02-09 13:23:06 +01:00
if ( success )
2021-01-28 12:29:06 +02:00
{
2021-06-26 14:22:00 +03:00
output - > framePoses [ frame ] [ boneId ] . rotation = QuaternionNlerp ( rotationStart , rotationEnd , lerpPercent ) ;
2021-01-28 12:29:06 +02:00
}
}
2021-03-02 02:03:52 +01:00
if ( channel - > target_path = = cgltf_animation_path_type_scale )
{
2021-02-02 11:49:42 +02:00
Vector3 scaleStart ;
Vector3 scaleEnd ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
float values [ 3 ] ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
bool success = ReadGLTFValue ( sampler - > output , outputMin , & values ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
scaleStart . x = values [ 0 ] ;
scaleStart . y = values [ 1 ] ;
scaleStart . z = values [ 2 ] ;
2021-06-30 16:39:07 +02:00
2021-09-23 21:06:51 +02:00
success = ReadGLTFValue ( sampler - > output , outputMax , & values ) & & success ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
scaleEnd . x = values [ 0 ] ;
scaleEnd . y = values [ 1 ] ;
scaleEnd . z = values [ 2 ] ;
2021-03-19 19:43:44 +01:00
2021-09-23 21:06:51 +02:00
if ( success )
{
output - > framePoses [ frame ] [ boneId ] . scale = Vector3Lerp ( scaleStart , scaleEnd , lerpPercent ) ;
}
2021-01-28 12:29:06 +02:00
}
}
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
// Build frameposes
2021-02-09 13:23:06 +01:00
for ( int frame = 0 ; frame < output - > frameCount ; frame + + )
2021-01-28 12:29:06 +02:00
{
2021-03-02 02:03:52 +01:00
bool * completedBones = RL_CALLOC ( output - > boneCount , sizeof ( bool ) ) ;
2021-02-24 08:25:20 +00:00
int numberCompletedBones = 0 ;
2021-03-02 02:03:52 +01:00
while ( numberCompletedBones < output - > boneCount )
{
2021-02-24 08:25:20 +00:00
for ( int i = 0 ; i < output - > boneCount ; i + + )
2021-01-28 12:29:06 +02:00
{
2021-09-23 21:06:51 +02:00
if ( completedBones [ i ] )
{
continue ;
}
2021-02-24 08:25:20 +00:00
2021-03-02 02:03:52 +01:00
if ( output - > bones [ i ] . parent < 0 )
{
2021-02-24 08:25:20 +00:00
completedBones [ i ] = true ;
numberCompletedBones + + ;
continue ;
}
2021-09-23 21:06:51 +02:00
if ( ! completedBones [ output - > bones [ i ] . parent ] )
{
continue ;
}
2021-02-24 08:25:20 +00:00
2021-01-28 12:29:06 +02:00
output - > framePoses [ frame ] [ i ] . rotation = QuaternionMultiply ( output - > framePoses [ frame ] [ output - > bones [ i ] . parent ] . rotation , output - > framePoses [ frame ] [ i ] . rotation ) ;
output - > framePoses [ frame ] [ i ] . translation = Vector3RotateByQuaternion ( output - > framePoses [ frame ] [ i ] . translation , output - > framePoses [ frame ] [ output - > bones [ i ] . parent ] . rotation ) ;
output - > framePoses [ frame ] [ i ] . translation = Vector3Add ( output - > framePoses [ frame ] [ i ] . translation , output - > framePoses [ frame ] [ output - > bones [ i ] . parent ] . translation ) ;
output - > framePoses [ frame ] [ i ] . scale = Vector3Multiply ( output - > framePoses [ frame ] [ i ] . scale , output - > framePoses [ frame ] [ output - > bones [ i ] . parent ] . scale ) ;
2021-02-24 08:25:20 +00:00
completedBones [ i ] = true ;
numberCompletedBones + + ;
2021-01-28 12:29:06 +02:00
}
}
2021-02-24 08:25:20 +00:00
RL_FREE ( completedBones ) ;
2021-01-28 12:29:06 +02:00
}
}
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
cgltf_free ( data ) ;
}
else TRACELOG ( LOG_WARNING , " : [%s] Failed to load glTF data " , fileName ) ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
RL_FREE ( fileData ) ;
2021-03-19 19:43:44 +01:00
2021-01-28 12:29:06 +02:00
return animations ;
}
2021-09-23 21:06:51 +02:00
void LoadGLTFMesh ( cgltf_data * data , cgltf_node * node , Model * outModel , Matrix currentTransform , int * primitiveIndex , const char * fileName )
2021-06-26 14:22:00 +03:00
{
2021-09-23 21:06:51 +02:00
cgltf_mesh * mesh = node - > mesh ;
2021-06-26 14:22:00 +03:00
for ( unsigned int p = 0 ; p < mesh - > primitives_count ; p + + )
{
for ( unsigned int j = 0 ; j < mesh - > primitives [ p ] . attributes_count ; j + + )
{
if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_position )
{
cgltf_accessor * acc = mesh - > primitives [ p ] . attributes [ j ] . data ;
outModel - > meshes [ ( * primitiveIndex ) ] . vertexCount = ( int ) acc - > count ;
int bufferSize = outModel - > meshes [ ( * primitiveIndex ) ] . vertexCount * 3 * sizeof ( float ) ;
outModel - > meshes [ ( * primitiveIndex ) ] . animVertices = RL_MALLOC ( bufferSize ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . vertices = ReadGLTFValuesAs ( acc , cgltf_component_type_r_32f , false ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
// Transform using the nodes matrix attributes
2021-08-08 13:02:51 +02:00
for ( int v = 0 ; v < outModel - > meshes [ ( * primitiveIndex ) ] . vertexCount ; v + + )
2021-06-26 14:22:00 +03:00
{
Vector3 vertex = {
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . vertices [ ( v * 3 + 0 ) ] ,
outModel - > meshes [ ( * primitiveIndex ) ] . vertices [ ( v * 3 + 1 ) ] ,
outModel - > meshes [ ( * primitiveIndex ) ] . vertices [ ( v * 3 + 2 ) ] } ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
vertex = Vector3Transform ( vertex , currentTransform ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . vertices [ ( v * 3 + 0 ) ] = vertex . x ;
outModel - > meshes [ ( * primitiveIndex ) ] . vertices [ ( v * 3 + 1 ) ] = vertex . y ;
outModel - > meshes [ ( * primitiveIndex ) ] . vertices [ ( v * 3 + 2 ) ] = vertex . z ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
memcpy ( outModel - > meshes [ ( * primitiveIndex ) ] . animVertices , outModel - > meshes [ ( * primitiveIndex ) ] . vertices , bufferSize ) ;
}
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_normal )
{
cgltf_accessor * acc = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
int bufferSize = ( int ) ( acc - > count * 3 * sizeof ( float ) ) ;
outModel - > meshes [ ( * primitiveIndex ) ] . animNormals = RL_MALLOC ( bufferSize ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . normals = ReadGLTFValuesAs ( acc , cgltf_component_type_r_32f , false ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
// Transform using the nodes matrix attributes
2021-08-08 13:02:51 +02:00
for ( int v = 0 ; v < outModel - > meshes [ ( * primitiveIndex ) ] . vertexCount ; v + + )
2021-06-26 14:22:00 +03:00
{
Vector3 normal = {
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . normals [ ( v * 3 + 0 ) ] ,
outModel - > meshes [ ( * primitiveIndex ) ] . normals [ ( v * 3 + 1 ) ] ,
outModel - > meshes [ ( * primitiveIndex ) ] . normals [ ( v * 3 + 2 ) ] } ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
normal = Vector3Transform ( normal , currentTransform ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . normals [ ( v * 3 + 0 ) ] = normal . x ;
outModel - > meshes [ ( * primitiveIndex ) ] . normals [ ( v * 3 + 1 ) ] = normal . y ;
outModel - > meshes [ ( * primitiveIndex ) ] . normals [ ( v * 3 + 2 ) ] = normal . z ;
2021-06-26 14:22:00 +03:00
}
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
memcpy ( outModel - > meshes [ ( * primitiveIndex ) ] . animNormals , outModel - > meshes [ ( * primitiveIndex ) ] . normals , bufferSize ) ;
}
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_texcoord )
{
cgltf_accessor * acc = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . texcoords = ReadGLTFValuesAs ( acc , cgltf_component_type_r_32f , false ) ;
2021-06-26 14:22:00 +03:00
}
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_joints )
{
cgltf_accessor * acc = mesh - > primitives [ p ] . attributes [ j ] . data ;
unsigned int boneCount = acc - > count ;
2021-06-26 13:45:15 +02:00
unsigned int totalBoneWeights = boneCount * 4 ;
outModel - > meshes [ ( * primitiveIndex ) ] . boneIds = RL_MALLOC ( totalBoneWeights * sizeof ( int ) ) ;
short * bones = ReadGLTFValuesAs ( acc , cgltf_component_type_r_16 , false ) ;
2021-09-23 21:06:51 +02:00
// Find skin joint
2021-06-26 14:22:00 +03:00
for ( unsigned int a = 0 ; a < totalBoneWeights ; a + + )
{
outModel - > meshes [ ( * primitiveIndex ) ] . boneIds [ a ] = 0 ;
2021-06-26 13:45:15 +02:00
if ( bones [ a ] < 0 ) continue ;
2021-09-23 21:06:51 +02:00
cgltf_node * skinJoint = node - > skin - > joints [ bones [ a ] ] ;
unsigned int skinJointId = skinJoint - data - > nodes ;
outModel - > meshes [ ( * primitiveIndex ) ] . boneIds [ a ] = skinJointId ;
2021-06-26 14:22:00 +03:00
}
RL_FREE ( bones ) ;
}
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_weights )
{
cgltf_accessor * acc = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . boneWeights = ReadGLTFValuesAs ( acc , cgltf_component_type_r_32f , false ) ;
2021-06-26 14:22:00 +03:00
}
else if ( mesh - > primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_color )
{
cgltf_accessor * acc = mesh - > primitives [ p ] . attributes [ j ] . data ;
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . colors = ReadGLTFValuesAs ( acc , cgltf_component_type_r_8u , true ) ;
2021-06-26 14:22:00 +03:00
}
}
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
cgltf_accessor * acc = mesh - > primitives [ p ] . indices ;
if ( acc )
{
outModel - > meshes [ ( * primitiveIndex ) ] . triangleCount = acc - > count / 3 ;
2021-06-26 13:45:15 +02:00
outModel - > meshes [ ( * primitiveIndex ) ] . indices = ReadGLTFValuesAs ( acc , cgltf_component_type_r_16u , false ) ;
2021-06-26 14:22:00 +03:00
}
else
{
// Unindexed mesh
outModel - > meshes [ ( * primitiveIndex ) ] . triangleCount = outModel - > meshes [ ( * primitiveIndex ) ] . vertexCount / 3 ;
}
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
if ( mesh - > primitives [ p ] . material )
{
// Compute the offset
outModel - > meshMaterial [ ( * primitiveIndex ) ] = ( int ) ( mesh - > primitives [ p ] . material - data - > materials ) ;
}
2021-06-26 13:45:15 +02:00
else outModel - > meshMaterial [ ( * primitiveIndex ) ] = outModel - > materialCount - 1 ;
2021-09-23 21:06:51 +02:00
BindGLTFPrimitiveToBones ( outModel , node , data , * primitiveIndex ) ;
2021-06-30 16:39:07 +02:00
2021-06-26 14:22:00 +03:00
( * primitiveIndex ) = ( * primitiveIndex ) + 1 ;
}
}
2021-09-05 21:15:40 +02:00
static Matrix GetNodeTransformationMatrix ( cgltf_node * node , Matrix current )
2021-06-26 14:22:00 +03:00
{
2021-09-05 21:15:40 +02:00
if ( node - > has_matrix )
{
Matrix nodeTransform = {
2021-06-26 13:45:15 +02:00
node - > matrix [ 0 ] , node - > matrix [ 4 ] , node - > matrix [ 8 ] , node - > matrix [ 12 ] ,
node - > matrix [ 1 ] , node - > matrix [ 5 ] , node - > matrix [ 9 ] , node - > matrix [ 13 ] ,
node - > matrix [ 2 ] , node - > matrix [ 6 ] , node - > matrix [ 10 ] , node - > matrix [ 14 ] ,
node - > matrix [ 3 ] , node - > matrix [ 7 ] , node - > matrix [ 11 ] , node - > matrix [ 15 ] } ;
2021-09-05 21:15:40 +02:00
current = MatrixMultiply ( nodeTransform , current ) ;
}
if ( node - > has_translation )
{
Matrix tl = MatrixTranslate ( node - > translation [ 0 ] , node - > translation [ 1 ] , node - > translation [ 2 ] ) ;
current = MatrixMultiply ( tl , current ) ;
}
if ( node - > has_rotation )
{
Matrix rot = QuaternionToMatrix ( ( Quaternion ) { node - > rotation [ 0 ] , node - > rotation [ 1 ] , node - > rotation [ 2 ] , node - > rotation [ 3 ] } ) ;
current = MatrixMultiply ( rot , current ) ;
}
if ( node - > has_scale )
{
Matrix scale = MatrixScale ( node - > scale [ 0 ] , node - > scale [ 1 ] , node - > scale [ 2 ] ) ;
current = MatrixMultiply ( scale , current ) ;
}
return current ;
}
2021-06-30 16:39:07 +02:00
2021-09-05 21:15:40 +02:00
void LoadGLTFNode ( cgltf_data * data , cgltf_node * node , Model * outModel , Matrix currentTransform , int * primitiveIndex , const char * fileName )
{
// Apply the transforms if they exist (Will still be applied even if no mesh is present to support emptys and bone structures)
Matrix localTransform = GetNodeTransformationMatrix ( node , MatrixIdentity ( ) ) ;
currentTransform = MatrixMultiply ( localTransform , currentTransform ) ;
// Load mesh if it exists
if ( node - > mesh ! = NULL )
{
// Check if skinning is enabled and load Mesh accordingly
Matrix vertexTransform = currentTransform ;
2021-09-23 21:06:51 +02:00
if ( ( node - > skin ! = NULL ) & & ( node - > parent ! = NULL ) )
2021-09-05 21:15:40 +02:00
{
vertexTransform = localTransform ;
TRACELOG ( LOG_WARNING , " MODEL: GLTF Node %s is skinned but not root node! Parent transformations will be ignored (NODE_SKINNED_MESH_NON_ROOT) " , node - > name ) ;
}
2021-09-23 21:06:51 +02:00
LoadGLTFMesh ( data , node , outModel , vertexTransform , primitiveIndex , fileName ) ;
2021-09-05 21:15:40 +02:00
}
2021-06-26 13:45:15 +02:00
for ( unsigned int i = 0 ; i < node - > children_count ; i + + ) LoadGLTFNode ( data , node - > children [ i ] , outModel , currentTransform , primitiveIndex , fileName ) ;
2021-06-26 14:22:00 +03:00
}
2021-08-28 12:01:16 +02:00
static void GetGLTFPrimitiveCount ( cgltf_node * node , int * outCount )
2021-06-26 14:22:00 +03:00
{
2021-06-26 13:45:15 +02:00
if ( node - > mesh ! = NULL ) * outCount + = node - > mesh - > primitives_count ;
2021-06-30 16:39:07 +02:00
2021-06-26 13:45:15 +02:00
for ( unsigned int i = 0 ; i < node - > children_count ; i + + ) GetGLTFPrimitiveCount ( node - > children [ i ] , outCount ) ;
2021-06-26 14:22:00 +03:00
}
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 } ;
int nbvertices = 0 ;
int meshescount = 0 ;
2021-09-10 15:24:01 +02:00
unsigned int readed = 0 ;
unsigned char * fileData ;
2021-09-22 21:56:38 +02:00
2021-09-10 15:24:01 +02:00
//Read vox file into buffer
fileData = LoadFileData ( fileName , & readed ) ;
if ( fileData = = 0 )
{
TRACELOG ( LOG_WARNING , " MODEL: [%s] Failed to load VOX file " , fileName ) ;
return model ;
}
//Read and build voxarray description
2021-09-05 20:39:34 +02:00
VoxArray3D voxarray = { 0 } ;
2021-09-10 15:24:01 +02:00
int ret = Vox_LoadFromMemory ( fileData , readed , & 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-09-05 20:39:34 +02:00
model . meshes = ( Mesh * ) MemAlloc ( model . meshCount * sizeof ( Mesh ) ) ;
2021-09-04 19:55:09 +02:00
2021-09-05 20:39:34 +02:00
model . meshMaterial = ( int * ) MemAlloc ( model . meshCount * sizeof ( int ) ) ;
2021-09-04 19:55:09 +02:00
model . materialCount = 1 ;
2021-09-05 20:39:34 +02:00
model . materials = ( Material * ) MemAlloc ( 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-09-05 20:39:34 +02:00
Vector3 * pvertices = voxarray . vertices . array ; // 6*4 = 12 vertices per voxel
Color * pcolors = voxarray . colors . array ;
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
for ( int idxMesh = 0 ; idxMesh < meshescount ; idxMesh + + )
{
2021-09-05 20:39:34 +02:00
Mesh * pmesh = & model . meshes [ idxMesh ] ;
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 ;
2021-09-04 19:55:09 +02:00
pmesh - > vertices = MemAlloc ( size ) ;
memcpy ( pmesh - > vertices , pvertices , size ) ;
2021-09-22 21:56:38 +02:00
// Copy indices
2021-09-23 00:18:47 +02:00
// TODO: Compute globals indices array
2021-09-04 19:55:09 +02:00
size = voxarray . indices . used * sizeof ( unsigned short ) ;
pmesh - > indices = MemAlloc ( size ) ;
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-09-04 19:55:09 +02:00
pmesh - > colors = MemAlloc ( size ) ;
memcpy ( pmesh - > colors , pcolors , size ) ;
// First material index
model . meshMaterial [ idxMesh ] = 0 ;
2021-09-05 20:39:34 +02:00
// Upload mesh data to GPU
2021-09-04 19:55:09 +02:00
UploadMesh ( pmesh , false ) ;
verticesRemain - = verticesMax ;
pvertices + = verticesMax ;
pcolors + = verticesMax ;
}
2021-09-10 15:24:01 +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