2014-09-16 22:51:31 +02:00
/**********************************************************************************************
2013-11-18 23:38:44 +01:00
*
2017-03-19 12:52:58 +01:00
* raylib . models - Basic functions to deal with 3 d shapes and 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
* 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
*
2020-01-05 20:01:54 +01:00
* Copyright ( c ) 2013 - 2020 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)
# include "config.h" // Defines module configuration flags
# endif
2017-12-28 19:27:02 +01:00
# include "utils.h" // Required for: fopen() Android mapping
2014-09-16 22:51:31 +02:00
2020-02-28 12:54:39 +01:00
# include <stdlib.h> // Required for: malloc(), free()
2020-02-04 16:55:24 +01:00
# include <stdio.h> // Required for: FILE, fopen(), fclose()
# include <string.h> // Required for: strncmp() [Used in LoadModelAnimations()], strlen() [Used in LoadTextureFromCgltfImage()]
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
2016-11-16 18:46:13 +01:00
# include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
2013-11-18 23:38:44 +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
2019-05-20 11:13:38 +02:00
# include "external/stb_image.h" // glTF texture images loading
2018-09-05 10:59:05 +02:00
# endif
# 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
2013-11-18 23:38:44 +01:00
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
2019-08-08 08:57:21 +01:00
# define MAX_MESH_VBO 7 // Maximum number of vbo per mesh
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
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
2018-09-05 10:59:05 +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
{
rlBegin ( RL_LINES ) ;
rlColor4ub ( color . r , color . g , color . b , color . a ) ;
rlVertex3f ( startPos . x , startPos . y , startPos . z ) ;
rlVertex3f ( endPos . x , endPos . y , endPos . z ) ;
rlEnd ( ) ;
}
2020-01-29 12:21:22 +01:00
// Draw a point in 3D space, actually a small line
2019-11-22 00:33:50 +01:00
void DrawPoint3D ( Vector3 position , Color color )
{
2019-11-21 18:30:19 -05:00
if ( rlCheckBufferLimit ( 8 ) ) rlglDraw ( ) ;
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 ) ;
rlVertex3f ( 0.0 , 0.0 , 0.0 ) ;
rlVertex3f ( 0.0 , 0.0 , 0.1 ) ;
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
{
2019-01-11 18:54:48 +01:00
if ( rlCheckBufferLimit ( 2 * 36 ) ) rlglDraw ( ) ;
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 ( ) ;
}
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
2019-01-11 18:54:48 +01:00
if ( rlCheckBufferLimit ( 36 ) ) rlglDraw ( ) ;
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
2019-01-11 18:54:48 +01:00
if ( rlCheckBufferLimit ( 36 ) ) rlglDraw ( ) ;
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
2019-04-14 22:29:14 +02:00
if ( rlCheckBufferLimit ( 36 ) ) rlglDraw ( ) ;
2014-04-04 20:11:57 +02:00
2014-04-19 16:36:49 +02:00
rlEnableTexture ( 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
2014-04-04 20:11:57 +02:00
rlDisableTexture ( ) ;
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 ;
if ( rlCheckBufferLimit ( numVertex ) ) rlglDraw ( ) ;
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
{
2016-10-17 00:03:38 +02:00
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) * sinf ( DEG2RAD * ( j * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) * cosf ( DEG2RAD * ( j * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( ( j + 1 ) * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( ( j + 1 ) * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( j * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( j * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) * sinf ( DEG2RAD * ( j * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) * cosf ( DEG2RAD * ( j * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i ) ) ) * sinf ( DEG2RAD * ( ( j + 1 ) * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i ) ) ) * cosf ( DEG2RAD * ( ( j + 1 ) * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( ( j + 1 ) * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( ( j + 1 ) * 360 / 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 ;
if ( rlCheckBufferLimit ( numVertex ) ) rlglDraw ( ) ;
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
{
2016-10-17 00:03:38 +02:00
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) * sinf ( DEG2RAD * ( j * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) * cosf ( DEG2RAD * ( j * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( ( j + 1 ) * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( ( j + 1 ) * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( ( j + 1 ) * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( ( j + 1 ) * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( j * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( j * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * sinf ( DEG2RAD * ( j * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * ( i + 1 ) ) ) * cosf ( DEG2RAD * ( j * 360 / slices ) ) ) ;
rlVertex3f ( cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) * sinf ( DEG2RAD * ( j * 360 / slices ) ) ,
sinf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) ,
cosf ( DEG2RAD * ( 270 + ( 180 / ( rings + 1 ) ) * i ) ) * cosf ( DEG2RAD * ( j * 360 / 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 ;
if ( rlCheckBufferLimit ( numVertex ) ) rlglDraw ( ) ;
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
2017-08-04 18:34:51 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360 / sides ) ) * radiusBottom ) ; //Bottom Right
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360 / 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
2017-08-04 18:34:51 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360 / 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 ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360 / 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 ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360 / 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 ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360 / 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 ;
if ( rlCheckBufferLimit ( numVertex ) ) rlglDraw ( ) ;
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 ) ;
2017-08-04 18:34:51 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360 / sides ) ) * radiusBottom ) ;
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusBottom , 0 , cosf ( DEG2RAD * ( i + 360 / sides ) ) * radiusBottom ) ;
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360 / sides ) ) * radiusTop ) ;
2014-09-03 16:51:28 +02:00
2017-08-04 18:34:51 +02:00
rlVertex3f ( sinf ( DEG2RAD * ( i + 360 / sides ) ) * radiusTop , height , cosf ( DEG2RAD * ( i + 360 / 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
{
2019-04-14 22:29:14 +02:00
if ( rlCheckBufferLimit ( 4 ) ) rlglDraw ( ) ;
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
2019-04-14 22:29:14 +02:00
if ( rlCheckBufferLimit ( slices * 4 ) ) rlglDraw ( ) ;
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
}
2014-04-04 20:11:57 +02:00
// Draw gizmo
void DrawGizmo ( Vector3 position )
2013-11-18 23:38:44 +01:00
{
2013-11-23 13:30:54 +01:00
// NOTE: RGB = XYZ
2015-12-21 16:42:13 +01:00
float length = 1.0f ;
2014-04-04 20:11:57 +02:00
2014-03-25 12:40:35 +01:00
rlPushMatrix ( ) ;
2017-07-21 17:19:28 +02:00
rlTranslatef ( position . x , position . y , position . z ) ;
2017-08-04 18:34:51 +02:00
rlScalef ( length , length , length ) ;
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
rlBegin ( RL_LINES ) ;
rlColor3f ( 1.0f , 0.0f , 0.0f ) ; rlVertex3f ( 0.0f , 0.0f , 0.0f ) ;
rlColor3f ( 1.0f , 0.0f , 0.0f ) ; rlVertex3f ( 1.0f , 0.0f , 0.0f ) ;
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
rlColor3f ( 0.0f , 1.0f , 0.0f ) ; rlVertex3f ( 0.0f , 0.0f , 0.0f ) ;
rlColor3f ( 0.0f , 1.0f , 0.0f ) ; rlVertex3f ( 0.0f , 1.0f , 0.0f ) ;
2014-09-03 16:51:28 +02:00
2014-04-04 20:11:57 +02:00
rlColor3f ( 0.0f , 0.0f , 1.0f ) ; rlVertex3f ( 0.0f , 0.0f , 0.0f ) ;
rlColor3f ( 0.0f , 0.0f , 1.0f ) ; rlVertex3f ( 0.0f , 0.0f , 1.0f ) ;
2014-09-03 16:51:28 +02:00
rlEnd ( ) ;
2014-04-04 20:11:57 +02:00
rlPopMatrix ( ) ;
}
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)
if ( IsFileExtension ( fileName , " .gltf " ) | | IsFileExtension ( fileName , " .glb " ) ) model = LoadGLTF ( 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-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] No meshes can be loaded, 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-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] No meshes can be loaded, and can't create a default mesh. The raylib mesh generation is not supported (SUPPORT_MESH_GENERATION). " , 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)
for ( int i = 0 ; i < model . meshCount ; i + + ) rlLoadMesh ( & model . meshes [ i ] , false ) ;
}
2019-03-29 20:22:30 +01:00
if ( model . materialCount = = 0 )
{
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] No materials can be loaded, 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
}
2016-08-16 11:09:55 +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
2016-12-25 01:59:23 +01:00
// Unload model from memory (RAM and/or VRAM)
2016-03-05 16:17:54 +01:00
void UnloadModel ( Model model )
{
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
2019-08-08 10:18:12 +02:00
// As the user could be sharing shaders and textures between models,
// we don't unload the material but just free it's maps, the user
// is responsible for freeing models shaders and textures
2019-08-08 08:57:21 +01:00
for ( int i = 0 ; i < model . materialCount ; i + + ) RL_FREE ( model . materials [ i ] . maps ) ;
2019-04-04 13:50:52 +02:00
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-02-03 19:13:24 +01:00
TRACELOG ( LOG_INFO , " Unloaded model data from RAM and VRAM " ) ;
2017-07-17 00:33:40 +02:00
}
2016-08-16 11:09:55 +02:00
2019-04-05 13:15:56 +02:00
// Load meshes from model file
Mesh * LoadMeshes ( const char * fileName , int * meshCount )
{
Mesh * meshes = NULL ;
int count = 0 ;
2019-09-13 12:29:52 +01:00
2019-04-05 13:15:56 +02:00
// TODO: Load meshes from file (OBJ, IQM, GLTF)
2019-09-13 12:29:52 +01:00
2019-04-05 13:15:56 +02:00
* meshCount = count ;
return meshes ;
}
2017-07-17 00:33:40 +02:00
// Unload mesh from memory (RAM and/or VRAM)
2019-08-07 00:32:44 +02:00
void UnloadMesh ( Mesh mesh )
2017-07-17 00:33:40 +02:00
{
rlUnloadMesh ( mesh ) ;
2019-08-07 00:32:44 +02:00
RL_FREE ( mesh . vboId ) ;
2016-05-07 18:07:15 +02:00
}
2018-09-17 16:56:02 +02:00
// Export mesh data to file
void ExportMesh ( Mesh mesh , const char * fileName )
2018-04-04 12:00:54 +02:00
{
2018-09-17 16:56:02 +02:00
bool success = false ;
2019-02-21 18:45:19 +01:00
2018-09-17 16:56:02 +02:00
if ( IsFileExtension ( fileName , " .obj " ) )
2018-04-04 12:00:54 +02:00
{
2018-09-17 16:56:02 +02:00
FILE * objFile = fopen ( fileName , " wt " ) ;
2019-02-21 18:45:19 +01:00
2019-02-06 10:51:06 +01:00
fprintf ( objFile , " # ////////////////////////////////////////////////////////////////////////////////// \n " ) ;
fprintf ( objFile , " # // // \n " ) ;
fprintf ( objFile , " # // rMeshOBJ exporter v1.0 - Mesh exported as triangle faces and not optimized // \n " ) ;
fprintf ( objFile , " # // // \n " ) ;
fprintf ( objFile , " # // more info and bugs-report: github.com/raysan5/raylib // \n " ) ;
fprintf ( objFile , " # // feedback and support: ray[at]raylib.com // \n " ) ;
fprintf ( objFile , " # // // \n " ) ;
fprintf ( objFile , " # // Copyright (c) 2018 Ramon Santamaria (@raysan5) // \n " ) ;
fprintf ( objFile , " # // // \n " ) ;
fprintf ( objFile , " # ////////////////////////////////////////////////////////////////////////////////// \n \n " ) ;
fprintf ( objFile , " # Vertex Count: %i \n " , mesh . vertexCount ) ;
fprintf ( objFile , " # Triangle Count: %i \n \n " , mesh . triangleCount ) ;
2019-02-21 18:45:19 +01:00
2018-09-17 16:56:02 +02:00
fprintf ( objFile , " 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 )
{
fprintf ( objFile , " v %.2f %.2f %.2f \n " , mesh . vertices [ v ] , mesh . vertices [ v + 1 ] , mesh . vertices [ v + 2 ] ) ;
}
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 )
{
fprintf ( objFile , " vt %.2f %.2f \n " , mesh . texcoords [ v ] , mesh . texcoords [ v + 1 ] ) ;
}
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 )
{
fprintf ( objFile , " vn %.2f %.2f %.2f \n " , mesh . normals [ v ] , mesh . normals [ v + 1 ] , mesh . normals [ v + 2 ] ) ;
}
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 )
{
fprintf ( objFile , " 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 ) ;
}
2019-02-21 18:45:19 +01:00
2018-09-17 16:56:02 +02:00
fprintf ( objFile , " \n " ) ;
2019-02-21 18:45:19 +01:00
2018-09-17 16:56:02 +02:00
fclose ( objFile ) ;
2019-02-21 18:45:19 +01:00
2018-09-17 16:56:02 +02:00
success = true ;
2018-04-04 12:00:54 +02:00
}
2018-09-17 16:56:02 +02:00
else if ( IsFileExtension ( fileName , " .raw " ) ) { } // TODO: Support additional file formats to export mesh vertex data
2018-04-04 12:00:54 +02:00
2020-02-03 19:13:24 +01:00
if ( success ) TRACELOG ( LOG_INFO , " Mesh exported successfully: %s " , fileName ) ;
else TRACELOG ( LOG_WARNING , " Mesh could not be exported. " ) ;
2018-04-04 12:00:54 +02: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 " ) )
{
tinyobj_material_t * mats ;
int result = tinyobj_parse_mtl_file ( & mats , & count , fileName ) ;
2019-10-03 21:05:21 +02:00
if ( result ! = TINYOBJ_SUCCESS ) {
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] Could not parse Materials file " , fileName ) ;
2019-10-03 21:05:21 +02:00
}
2019-04-05 13:15:56 +02:00
// TODO: Process materials to return
tinyobj_materials_free ( mats , count ) ;
}
# else
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] Materials file not supported " , fileName ) ;
2019-04-05 13:15:56 +02:00
# endif
// Set materials shader to default (DIFFUSE, SPECULAR, NORMAL)
for ( int i = 0 ; i < count ; i + + ) materials [ i ] . shader = GetShaderDefault ( ) ;
* 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
material . shader = GetShaderDefault ( ) ;
material . maps [ MAP_DIFFUSE ] . texture = GetTextureDefault ( ) ; // White texture (1x1 pixel)
//material.maps[MAP_NORMAL].texture; // NOTE: By default, not set
//material.maps[MAP_SPECULAR].texture; // NOTE: By default, not set
material . maps [ MAP_DIFFUSE ] . color = WHITE ; // Diffuse color
material . maps [ MAP_SPECULAR ] . color = WHITE ; // Specular color
return material ;
}
// Unload material from memory
void UnloadMaterial ( Material material )
{
// Unload material shader (avoid unloading default shader, managed by raylib)
if ( material . shader . id ! = GetShaderDefault ( ) . id ) UnloadShader ( material . shader ) ;
// Unload loaded texture maps (avoid unloading default texture, managed by raylib)
for ( int i = 0 ; i < MAX_MATERIAL_MAPS ; i + + )
{
if ( material . maps [ i ] . texture . id ! = GetTextureDefault ( ) . id ) rlDeleteTextures ( material . maps [ i ] . texture . id ) ;
}
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
}
// Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...)
// 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-02-03 19:13:24 +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
ModelAnimation * LoadModelAnimations ( const char * filename , int * animCount )
{
# define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
# define IQM_VERSION 2 // only IQM version 2 supported
typedef struct IQMHeader {
char magic [ 16 ] ;
unsigned int version ;
unsigned int filesize ;
unsigned int flags ;
unsigned int num_text , ofs_text ;
unsigned int num_meshes , ofs_meshes ;
unsigned int num_vertexarrays , num_vertexes , ofs_vertexarrays ;
unsigned int num_triangles , ofs_triangles , ofs_adjacency ;
unsigned int num_joints , ofs_joints ;
unsigned int num_poses , ofs_poses ;
unsigned int num_anims , ofs_anims ;
unsigned int num_frames , num_framechannels , ofs_frames , ofs_bounds ;
unsigned int num_comment , ofs_comment ;
unsigned int num_extensions , ofs_extensions ;
} IQMHeader ;
typedef struct IQMPose {
int parent ;
unsigned int mask ;
float channeloffset [ 10 ] ;
float channelscale [ 10 ] ;
} IQMPose ;
typedef struct IQMAnim {
unsigned int name ;
unsigned int first_frame , num_frames ;
float framerate ;
unsigned int flags ;
} IQMAnim ;
2019-09-13 12:29:52 +01:00
2020-01-28 16:40:12 +01:00
FILE * iqmFile = NULL ;
2019-04-05 13:15:56 +02:00
IQMHeader iqm ;
iqmFile = fopen ( filename , " rb " ) ;
if ( ! iqmFile )
{
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_ERROR , " [%s] Unable to open file " , filename ) ;
2019-04-05 13:15:56 +02:00
}
2019-08-27 12:23:30 +02:00
// Read IQM header
2019-04-05 13:15:56 +02:00
fread ( & iqm , sizeof ( IQMHeader ) , 1 , iqmFile ) ;
if ( strncmp ( iqm . magic , IQM_MAGIC , sizeof ( IQM_MAGIC ) ) )
{
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_ERROR , " Magic Number \" %s \" does not match. " , iqm . magic ) ;
2019-04-05 13:15:56 +02:00
fclose ( iqmFile ) ;
2019-10-29 14:57:19 +00:00
return NULL ;
2019-04-05 13:15:56 +02:00
}
if ( iqm . version ! = IQM_VERSION )
{
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_ERROR , " IQM version %i is incorrect. " , iqm . version ) ;
2019-04-05 13:15:56 +02:00
fclose ( iqmFile ) ;
2020-02-03 19:26:28 +01:00
2019-10-29 14:57:19 +00:00
return NULL ;
2019-04-05 13:15:56 +02:00
}
2019-08-27 12:23:30 +02:00
// Get bones data
IQMPose * poses = RL_MALLOC ( iqm . num_poses * sizeof ( IQMPose ) ) ;
2019-04-05 13:15:56 +02:00
fseek ( iqmFile , iqm . ofs_poses , SEEK_SET ) ;
2019-08-27 12:23:30 +02:00
fread ( poses , iqm . num_poses * sizeof ( IQMPose ) , 1 , iqmFile ) ;
2019-04-05 13:15:56 +02:00
2019-08-27 12:23:30 +02:00
// Get animations data
2019-08-06 23:08:58 +02:00
* animCount = iqm . num_anims ;
IQMAnim * anim = RL_MALLOC ( iqm . num_anims * sizeof ( IQMAnim ) ) ;
2019-04-05 13:15:56 +02:00
fseek ( iqmFile , iqm . ofs_anims , SEEK_SET ) ;
2019-08-06 23:08:58 +02:00
fread ( anim , iqm . num_anims * sizeof ( IQMAnim ) , 1 , iqmFile ) ;
ModelAnimation * animations = RL_MALLOC ( iqm . num_anims * sizeof ( ModelAnimation ) ) ;
2019-04-05 13:15:56 +02:00
// frameposes
2019-08-27 12:23:30 +02:00
unsigned short * framedata = RL_MALLOC ( iqm . num_frames * iqm . num_framechannels * sizeof ( unsigned short ) ) ;
2019-04-05 13:15:56 +02:00
fseek ( iqmFile , iqm . ofs_frames , SEEK_SET ) ;
2019-08-27 12:23:30 +02:00
fread ( framedata , iqm . num_frames * iqm . num_framechannels * sizeof ( unsigned short ) , 1 , iqmFile ) ;
2019-04-05 13:15:56 +02:00
2019-08-27 12:23:30 +02:00
for ( int a = 0 ; a < iqm . num_anims ; a + + )
2019-08-06 23:08:58 +02:00
{
animations [ a ] . frameCount = anim [ a ] . num_frames ;
animations [ a ] . boneCount = iqm . num_poses ;
2019-08-27 12:23:30 +02:00
animations [ a ] . bones = RL_MALLOC ( iqm . num_poses * sizeof ( BoneInfo ) ) ;
animations [ a ] . framePoses = RL_MALLOC ( anim [ a ] . num_frames * sizeof ( Transform * ) ) ;
//animations[a].framerate = anim.framerate; // TODO: Use framerate?
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
for ( int j = 0 ; j < iqm . num_poses ; j + + )
2019-04-05 13:15:56 +02:00
{
2019-08-06 23:08:58 +02:00
strcpy ( animations [ a ] . bones [ j ] . name , " ANIMJOINTNAME " ) ;
animations [ a ] . bones [ j ] . parent = poses [ j ] . parent ;
}
2019-04-05 13:15:56 +02:00
2019-08-27 12:23:30 +02:00
for ( int j = 0 ; j < anim [ a ] . num_frames ; j + + ) animations [ a ] . framePoses [ j ] = RL_MALLOC ( iqm . num_poses * sizeof ( Transform ) ) ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
int dcounter = anim [ a ] . first_frame * iqm . num_framechannels ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
for ( int frame = 0 ; frame < anim [ a ] . num_frames ; frame + + )
{
for ( int i = 0 ; i < iqm . num_poses ; i + + )
2019-04-05 13:15:56 +02:00
{
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . translation . x = poses [ i ] . channeloffset [ 0 ] ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
if ( poses [ i ] . mask & 0x01 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 0 ] ;
dcounter + + ;
}
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . translation . y = poses [ i ] . channeloffset [ 1 ] ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
if ( poses [ i ] . mask & 0x02 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 1 ] ;
dcounter + + ;
}
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . translation . z = poses [ i ] . channeloffset [ 2 ] ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
if ( poses [ i ] . mask & 0x04 )
{
animations [ a ] . framePoses [ frame ] [ i ] . translation . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 2 ] ;
dcounter + + ;
}
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation . x = poses [ i ] . channeloffset [ 3 ] ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
if ( poses [ i ] . mask & 0x08 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 3 ] ;
dcounter + + ;
}
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation . y = poses [ i ] . channeloffset [ 4 ] ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
if ( poses [ i ] . mask & 0x10 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 4 ] ;
dcounter + + ;
}
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation . z = poses [ i ] . channeloffset [ 5 ] ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
if ( poses [ i ] . mask & 0x20 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 5 ] ;
dcounter + + ;
}
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation . w = poses [ i ] . channeloffset [ 6 ] ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
if ( poses [ i ] . mask & 0x40 )
{
animations [ a ] . framePoses [ frame ] [ i ] . rotation . w + = framedata [ dcounter ] * poses [ i ] . channelscale [ 6 ] ;
dcounter + + ;
}
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . scale . x = poses [ i ] . channeloffset [ 7 ] ;
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
if ( poses [ i ] . mask & 0x80 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . x + = framedata [ dcounter ] * poses [ i ] . channelscale [ 7 ] ;
dcounter + + ;
}
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . scale . y = poses [ i ] . channeloffset [ 8 ] ;
if ( poses [ i ] . mask & 0x100 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . y + = framedata [ dcounter ] * poses [ i ] . channelscale [ 8 ] ;
dcounter + + ;
}
animations [ a ] . framePoses [ frame ] [ i ] . scale . z = poses [ i ] . channeloffset [ 9 ] ;
if ( poses [ i ] . mask & 0x200 )
{
animations [ a ] . framePoses [ frame ] [ i ] . scale . z + = framedata [ dcounter ] * poses [ i ] . channelscale [ 9 ] ;
dcounter + + ;
}
2019-04-05 13:15:56 +02:00
2019-08-06 23:08:58 +02:00
animations [ a ] . framePoses [ frame ] [ i ] . rotation = QuaternionNormalize ( animations [ a ] . framePoses [ frame ] [ i ] . rotation ) ;
}
2019-04-05 13:15:56 +02:00
}
2019-08-06 23:08:58 +02:00
// Build frameposes
for ( int frame = 0 ; frame < anim [ a ] . num_frames ; frame + + )
2019-04-05 13:15:56 +02:00
{
2019-08-06 23:08:58 +02:00
for ( int i = 0 ; i < animations [ a ] . boneCount ; i + + )
2019-04-05 13:15:56 +02:00
{
2019-08-06 23:08:58 +02:00
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 ) ;
2020-01-23 21:10:12 +01:00
animations [ a ] . framePoses [ frame ] [ i ] . scale = Vector3Multiply ( animations [ a ] . framePoses [ frame ] [ i ] . scale , animations [ a ] . framePoses [ frame ] [ animations [ a ] . bones [ i ] . parent ] . scale ) ;
2019-08-06 23:08:58 +02:00
}
2019-04-05 13:15:56 +02:00
}
}
}
2019-04-23 14:55:35 +02:00
RL_FREE ( framedata ) ;
RL_FREE ( poses ) ;
2019-08-06 23:08:58 +02:00
RL_FREE ( anim ) ;
2019-09-13 12:29:52 +01:00
2019-04-05 13:15:56 +02:00
fclose ( iqmFile ) ;
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 + + )
{
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 } ;
2020-03-17 13:40:07 +01:00
//Vector3 inScale = { 0 }; // Not used...
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 vCounter = 0 ;
int boneCounter = 0 ;
int boneId = 0 ;
2019-04-05 13:15:56 +02:00
2019-08-27 12:23:30 +02:00
for ( int i = 0 ; i < model . meshes [ m ] . vertexCount ; i + + )
{
boneId = model . meshes [ m ] . boneIds [ boneCounter ] ;
inTranslation = model . bindPose [ boneId ] . translation ;
inRotation = model . bindPose [ boneId ] . rotation ;
2020-03-17 13:40:07 +01:00
//inScale = model.bindPose[boneId].scale;
2019-08-27 12:23:30 +02: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)
animVertex = ( Vector3 ) { model . meshes [ m ] . vertices [ vCounter ] , model . meshes [ m ] . vertices [ vCounter + 1 ] , model . meshes [ m ] . vertices [ vCounter + 2 ] } ;
2020-01-23 21:10:12 +01:00
animVertex = Vector3Multiply ( animVertex , outScale ) ;
2019-08-27 12:23:30 +02:00
animVertex = Vector3Subtract ( animVertex , inTranslation ) ;
animVertex = Vector3RotateByQuaternion ( animVertex , QuaternionMultiply ( outRotation , QuaternionInvert ( inRotation ) ) ) ;
animVertex = Vector3Add ( animVertex , outTranslation ) ;
model . meshes [ m ] . animVertices [ vCounter ] = animVertex . x ;
model . meshes [ m ] . animVertices [ vCounter + 1 ] = animVertex . y ;
model . meshes [ m ] . animVertices [ vCounter + 2 ] = animVertex . z ;
// Normals processing
// NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
animNormal = ( Vector3 ) { model . meshes [ m ] . normals [ vCounter ] , model . meshes [ m ] . normals [ vCounter + 1 ] , model . meshes [ m ] . normals [ vCounter + 2 ] } ;
animNormal = Vector3RotateByQuaternion ( animNormal , QuaternionMultiply ( outRotation , QuaternionInvert ( inRotation ) ) ) ;
model . meshes [ m ] . animNormals [ vCounter ] = animNormal . x ;
model . meshes [ m ] . animNormals [ vCounter + 1 ] = animNormal . y ;
model . meshes [ m ] . animNormals [ vCounter + 2 ] = animNormal . z ;
vCounter + = 3 ;
boneCounter + = 4 ;
}
// Upload new vertex data to GPU for model drawing
rlUpdateBuffer ( model . meshes [ m ] . vboId [ 0 ] , model . meshes [ m ] . animVertices , model . meshes [ m ] . vertexCount * 3 * sizeof ( float ) ) ; // Update vertex position
2019-12-29 12:17:36 +01:00
rlUpdateBuffer ( model . meshes [ m ] . vboId [ 2 ] , model . meshes [ m ] . animNormals , model . meshes [ m ] . vertexCount * 3 * sizeof ( float ) ) ; // Update vertex normals
2019-08-27 12:23:30 +02:00
}
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 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
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 ) ) ;
2018-12-15 23:31:56 +01:00
for ( int i = 0 , v = 0 ; i < 360 ; i + = 360 / sides , v + = 3 )
{
vertices [ v ] = ( Vector3 ) { 0.0f , 0.0f , 0.0f } ;
vertices [ v + 1 ] = ( Vector3 ) { sinf ( DEG2RAD * i ) * radius , 0.0f , cosf ( DEG2RAD * i ) * radius } ;
vertices [ v + 2 ] = ( Vector3 ) { sinf ( DEG2RAD * ( i + 360 / sides ) ) * radius , 0.0f , cosf ( DEG2RAD * ( i + 360 / sides ) ) * radius } ;
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)
2019-02-21 18:45:19 +01:00
rlLoadMesh ( & mesh , false ) ;
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 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2017-09-18 00:59:22 +02:00
# 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 ) ) ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
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)
2019-02-21 18:45:19 +01:00
rlLoadMesh ( & 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 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
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)
2019-02-21 18:45:19 +01:00
rlLoadMesh ( & mesh , false ) ;
2017-09-18 00:59:22 +02:00
return mesh ;
}
// Generate sphere mesh (standard sphere)
RLAPI Mesh GenMeshSphere ( float radius , int rings , int slices )
{
Mesh mesh = { 0 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2017-09-18 00:59:22 +02:00
par_shapes_mesh * sphere = par_shapes_create_parametric_sphere ( slices , rings ) ;
par_shapes_scale ( sphere , radius , radius , radius ) ;
2019-02-21 18:45:19 +01:00
// NOTE: Soft normals are computed internally
2019-04-23 14:55:35 +02: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
mesh . vertexCount = sphere - > ntriangles * 3 ;
mesh . triangleCount = sphere - > ntriangles ;
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
2017-09-18 00:59:22 +02: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
2017-09-18 00:59:22 +02:00
mesh . texcoords [ k * 2 ] = sphere - > tcoords [ sphere - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = sphere - > tcoords [ sphere - > triangles [ k ] * 2 + 1 ] ;
}
par_shapes_free_mesh ( sphere ) ;
2019-02-21 18:45:19 +01:00
2017-07-21 09:34:09 +02:00
// Upload vertex data to GPU (static mesh)
2019-02-21 18:45:19 +01:00
rlLoadMesh ( & mesh , false ) ;
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)
RLAPI Mesh GenMeshHemiSphere ( float radius , int rings , int slices )
{
Mesh mesh = { 0 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2017-09-18 00:59:22 +02:00
par_shapes_mesh * sphere = par_shapes_create_hemisphere ( slices , rings ) ;
par_shapes_scale ( sphere , radius , radius , radius ) ;
2019-02-21 18:45:19 +01:00
// NOTE: Soft normals are computed internally
2019-04-23 14:55:35 +02: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
mesh . vertexCount = sphere - > ntriangles * 3 ;
mesh . triangleCount = sphere - > ntriangles ;
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
2017-09-18 00:59:22 +02: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
2017-09-18 00:59:22 +02:00
mesh . texcoords [ k * 2 ] = sphere - > tcoords [ sphere - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = sphere - > tcoords [ sphere - > triangles [ k ] * 2 + 1 ] ;
}
par_shapes_free_mesh ( sphere ) ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// Upload vertex data to GPU (static mesh)
2019-02-21 18:45:19 +01:00
rlLoadMesh ( & mesh , false ) ;
2017-09-18 00:59:22 +02:00
return mesh ;
}
// Generate cylinder mesh
Mesh GenMeshCylinder ( float radius , float height , int slices )
{
Mesh mesh = { 0 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2017-09-18 00:59:22 +02: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
2019-02-21 18:45:19 +01:00
// slices, and "stacks" like a number of stacked rings.
2017-09-18 00:59:22 +02:00
// Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
par_shapes_mesh * cylinder = par_shapes_create_cylinder ( slices , 8 ) ;
par_shapes_scale ( cylinder , radius , radius , height ) ;
par_shapes_rotate ( cylinder , - PI / 2.0f , ( float [ ] ) { 1 , 0 , 0 } ) ;
2020-02-10 10:56:48 +01:00
par_shapes_rotate ( cylinder , PI / 2.0f , ( float [ ] ) { 0 , 1 , 0 } ) ;
2017-09-18 00:59:22 +02:00
// 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 ) ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// 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 } ) ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
par_shapes_merge_and_free ( cylinder , capTop ) ;
par_shapes_merge_and_free ( cylinder , capBottom ) ;
2019-02-21 18:45:19 +01:00
2019-04-23 14:55:35 +02:00
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 ) ) ;
2017-09-18 00:59:22 +02:00
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
2017-09-18 00:59:22 +02: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
2017-09-18 00:59:22 +02:00
mesh . texcoords [ k * 2 ] = cylinder - > tcoords [ cylinder - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = cylinder - > tcoords [ cylinder - > triangles [ k ] * 2 + 1 ] ;
}
par_shapes_free_mesh ( cylinder ) ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// Upload vertex data to GPU (static mesh)
2019-02-21 18:45:19 +01:00
rlLoadMesh ( & mesh , false ) ;
2017-09-18 00:59:22 +02:00
return mesh ;
}
// Generate torus mesh
Mesh GenMeshTorus ( float radius , float size , int radSeg , int sides )
{
Mesh mesh = { 0 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2017-09-18 00:59:22 +02:00
if ( radius > 1.0f ) radius = 1.0f ;
else if ( radius < 0.1f ) radius = 0.1f ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02: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 ) ;
2019-04-23 14:55:35 +02: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
mesh . vertexCount = torus - > ntriangles * 3 ;
mesh . triangleCount = torus - > ntriangles ;
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
2017-09-18 00:59:22 +02: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
2017-09-18 00:59:22 +02:00
mesh . texcoords [ k * 2 ] = torus - > tcoords [ torus - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = torus - > tcoords [ torus - > triangles [ k ] * 2 + 1 ] ;
}
par_shapes_free_mesh ( torus ) ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// Upload vertex data to GPU (static mesh)
2019-02-21 18:45:19 +01:00
rlLoadMesh ( & mesh , false ) ;
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 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
if ( radius > 3.0f ) radius = 3.0f ;
else if ( radius < 0.5f ) radius = 0.5f ;
par_shapes_mesh * knot = par_shapes_create_trefoil_knot ( radSeg , sides , radius ) ;
par_shapes_scale ( knot , size , size , size ) ;
2019-04-23 14:55:35 +02: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
mesh . vertexCount = knot - > ntriangles * 3 ;
mesh . triangleCount = knot - > ntriangles ;
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
2017-09-18 00:59:22 +02: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
2017-09-18 00:59:22 +02:00
mesh . texcoords [ k * 2 ] = knot - > tcoords [ knot - > triangles [ k ] * 2 ] ;
mesh . texcoords [ k * 2 + 1 ] = knot - > tcoords [ knot - > triangles [ k ] * 2 + 1 ] ;
}
par_shapes_free_mesh ( knot ) ;
2019-02-21 18:45:19 +01:00
2017-09-18 00:59:22 +02:00
// Upload vertex data to GPU (static mesh)
2019-02-21 18:45:19 +01:00
rlLoadMesh ( & mesh , false ) ;
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 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
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
2016-02-11 15:51:04 +01:00
Color * pixels = GetImageData ( 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
2014-09-03 16:51:28 +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
2020-03-24 13:27:49 +01:00
Vector3 vA ;
Vector3 vB ;
Vector3 vC ;
Vector3 vN ;
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
2019-04-23 14:55:35 +02:00
RL_FREE ( pixels ) ;
2019-02-21 18:45:19 +01:00
2017-07-21 09:34:09 +02:00
// Upload vertex data to GPU (static mesh)
rlLoadMesh ( & 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
{
2016-05-08 15:24:02 +02:00
Mesh mesh = { 0 } ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2014-07-23 00:06:24 +02:00
2015-07-13 18:19:29 +02:00
Color * cubicmapPixels = GetImageData ( 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 } ;
Vector3 n5 = { 0.0f , 0.0f , 1.0f } ;
Vector3 n6 = { 0.0f , 0.0f , - 1.0f } ;
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
2014-07-23 00:06:24 +02:00
// We check pixel color to be WHITE, we will full cubes
2015-05-04 23:46:31 +02:00
if ( ( cubicmapPixels [ z * cubicmap . width + x ] . r = = 255 ) & &
( cubicmapPixels [ z * cubicmap . width + x ] . g = = 255 ) & &
( cubicmapPixels [ z * cubicmap . width + x ] . b = = 255 ) )
2014-07-23 00:06:24 +02:00
{
// Define triangles (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)
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
2015-05-04 23:46:31 +02:00
if ( ( ( z < cubicmap . height - 1 ) & &
( cubicmapPixels [ ( z + 1 ) * cubicmap . width + x ] . r = = 0 ) & &
( cubicmapPixels [ ( z + 1 ) * cubicmap . width + x ] . g = = 0 ) & &
( cubicmapPixels [ ( z + 1 ) * cubicmap . width + x ] . b = = 0 ) ) | | ( 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
2014-07-23 00:06:24 +02:00
if ( ( ( z > 0 ) & &
2015-05-04 23:46:31 +02:00
( cubicmapPixels [ ( z - 1 ) * cubicmap . width + x ] . r = = 0 ) & &
( cubicmapPixels [ ( z - 1 ) * cubicmap . width + x ] . g = = 0 ) & &
( cubicmapPixels [ ( z - 1 ) * cubicmap . width + x ] . b = = 0 ) ) | | ( 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
2015-05-04 23:46:31 +02:00
if ( ( ( x < cubicmap . width - 1 ) & &
( cubicmapPixels [ z * cubicmap . width + ( x + 1 ) ] . r = = 0 ) & &
( cubicmapPixels [ z * cubicmap . width + ( x + 1 ) ] . g = = 0 ) & &
( cubicmapPixels [ z * cubicmap . width + ( x + 1 ) ] . b = = 0 ) ) | | ( 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
2014-07-23 00:06:24 +02:00
if ( ( ( x > 0 ) & &
2015-05-04 23:46:31 +02:00
( cubicmapPixels [ z * cubicmap . width + ( x - 1 ) ] . r = = 0 ) & &
( cubicmapPixels [ z * cubicmap . width + ( x - 1 ) ] . g = = 0 ) & &
( cubicmapPixels [ z * cubicmap . width + ( x - 1 ) ] . b = = 0 ) ) | | ( 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
2015-05-04 23:46:31 +02:00
else if ( ( cubicmapPixels [ z * cubicmap . width + x ] . r = = 0 ) & &
( cubicmapPixels [ z * cubicmap . width + x ] . g = = 0 ) & &
( cubicmapPixels [ z * cubicmap . width + x ] . b = = 0 ) )
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
2019-04-23 14:55:35 +02:00
RL_FREE ( cubicmapPixels ) ; // Free image pixel data
2019-02-21 18:45:19 +01:00
2017-07-21 09:34:09 +02:00
// Upload vertex data to GPU (static mesh)
2019-02-21 18:45:19 +01:00
rlLoadMesh ( & 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
BoundingBox MeshBoundingBox ( 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
void MeshTangents ( Mesh * mesh )
2017-07-17 00:33:40 +02:00
{
2019-04-23 14:55:35 +02:00
if ( mesh - > tangents = = NULL ) mesh - > tangents = ( float * ) RL_MALLOC ( mesh - > vertexCount * 4 * sizeof ( float ) ) ;
2020-02-03 19:13:24 +01:00
else TRACELOG ( LOG_WARNING , " Mesh tangents already exist " ) ;
2017-07-17 00:33:40 +02:00
2019-04-23 14:55:35 +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 ;
float r = ( div = = 0.0f ) ? 0.0f : 1.0f / div ;
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
for ( int i = 0 ; i < mesh - > vertexCount ; + + i )
{
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...
# 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 ;
# else
Vector3OrthoNormalize ( & normal , & tangent ) ;
mesh - > tangents [ i * 4 + 0 ] = tangent . x ;
mesh - > tangents [ i * 4 + 1 ] = tangent . y ;
mesh - > tangents [ i * 4 + 2 ] = tangent . z ;
mesh - > tangents [ i * 4 + 3 ] = ( Vector3DotProduct ( Vector3CrossProduct ( normal , tangent ) , tan2 [ i ] ) < 0.0f ) ? - 1.0f : 1.0f ;
# endif
}
2019-04-23 14:55:35 +02:00
RL_FREE ( tan1 ) ;
RL_FREE ( tan2 ) ;
2019-09-13 12:29:52 +01:00
2019-04-05 16:43:09 +02:00
// Load a new tangent attributes buffer
mesh - > vboId [ LOC_VERTEX_TANGENT ] = rlLoadAttribBuffer ( mesh - > vaoId , LOC_VERTEX_TANGENT , mesh - > tangents , mesh - > vertexCount * 4 * sizeof ( float ) , false ) ;
2019-04-05 13:15:56 +02:00
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_INFO , " Tangents computed for mesh " ) ;
2017-07-17 00:33:40 +02:00
}
2019-04-05 13:15:56 +02:00
// Compute mesh binormals (aka bitangent)
void MeshBinormals ( 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
{
2019-10-21 17:55:37 +02:00
// TODO: Review color + tint premultiplication mechanism
Color color = model . materials [ model . meshMaterial [ i ] ] . maps [ MAP_DIFFUSE ] . color ;
2020-02-03 19:26:28 +01:00
2019-10-21 17:55:37 +02:00
Color colorTint = WHITE ;
2019-10-22 23:31:04 +02:00
colorTint . r = ( ( ( float ) color . r / 255.0 ) * ( ( float ) tint . r / 255.0 ) ) * 255 ;
colorTint . g = ( ( ( float ) color . g / 255.0 ) * ( ( float ) tint . g / 255.0 ) ) * 255 ;
colorTint . b = ( ( ( float ) color . b / 255.0 ) * ( ( float ) tint . b / 255.0 ) ) * 255 ;
colorTint . a = ( ( ( float ) color . a / 255.0 ) * ( ( float ) tint . a / 255.0 ) ) * 255 ;
2020-02-03 19:26:28 +01:00
2019-10-21 17:55:37 +02:00
model . materials [ model . meshMaterial [ i ] ] . maps [ MAP_DIFFUSE ] . color = colorTint ;
2019-03-29 16:48:23 +01:00
rlDrawMesh ( model . meshes [ i ] , model . materials [ model . meshMaterial [ i ] ] , model . transform ) ;
2019-10-21 17:55:37 +02:00
model . materials [ model . meshMaterial [ i ] ] . maps [ 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
2014-03-16 20:59:02 +01:00
void DrawBillboard ( Camera camera , Texture2D texture , Vector3 center , float size , Color tint )
2013-11-18 23:38:44 +01:00
{
2018-08-04 10:32:16 +02:00
Rectangle sourceRec = { 0.0f , 0.0f , ( float ) texture . width , ( float ) texture . height } ;
2016-08-16 11:09:55 +02:00
2016-03-01 19:00:12 +01:00
DrawBillboardRec ( camera , texture , sourceRec , center , size , tint ) ;
2013-11-18 23:38:44 +01:00
}
// Draw a billboard (part of a texture defined by a rectangle)
2014-03-16 20:59:02 +01:00
void DrawBillboardRec ( Camera camera , Texture2D texture , Rectangle sourceRec , Vector3 center , float size , Color tint )
2013-11-18 23:38:44 +01:00
{
2014-03-16 20:59:02 +01:00
// NOTE: Billboard size will maintain sourceRec aspect ratio, size will represent billboard width
2016-03-01 19:00:12 +01:00
Vector2 sizeRatio = { size , size * ( float ) sourceRec . height / sourceRec . width } ;
2014-03-16 20:59:02 +01: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
2016-03-01 19:00:12 +01:00
// NOTE: Billboard locked on axis-Y
Vector3 up = { 0.0f , 1.0f , 0.0f } ;
2014-09-03 16:51:28 +02:00
/*
2014-11-24 17:23:05 +01:00
a - - - - - - - b
2014-03-16 20:59:02 +01:00
| |
| * |
| |
2014-11-24 17:23:05 +01:00
d - - - - - - - c
2014-09-03 16:51:28 +02:00
*/
2018-03-16 13:47:01 +01:00
right = Vector3Scale ( right , sizeRatio . x / 2 ) ;
up = Vector3Scale ( up , sizeRatio . y / 2 ) ;
2014-03-16 20:59:02 +01:00
2017-07-22 22:15:50 +02:00
Vector3 p1 = Vector3Add ( right , up ) ;
Vector3 p2 = Vector3Subtract ( right , up ) ;
2014-03-16 20:59:02 +01:00
2017-07-22 22:15:50 +02:00
Vector3 a = Vector3Subtract ( center , p2 ) ;
Vector3 b = Vector3Add ( center , p1 ) ;
Vector3 c = Vector3Add ( center , p2 ) ;
Vector3 d = Vector3Subtract ( center , p1 ) ;
2014-09-03 16:51:28 +02:00
2019-07-22 00:12:51 +02:00
if ( rlCheckBufferLimit ( 4 ) ) rlglDraw ( ) ;
2014-04-19 16:36:49 +02:00
rlEnableTexture ( 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
2016-09-12 19:36:41 +02:00
rlTexCoord2f ( ( float ) sourceRec . x / texture . width , ( float ) sourceRec . y / texture . height ) ;
2014-03-25 12:40:35 +01:00
rlVertex3f ( a . x , a . y , a . z ) ;
2015-02-02 00:53:49 +01:00
2014-11-24 17:23:05 +01:00
// Top-left corner for texture and quad
2016-09-12 19:36:41 +02:00
rlTexCoord2f ( ( float ) sourceRec . x / texture . width , ( float ) ( sourceRec . y + sourceRec . height ) / texture . height ) ;
2014-11-24 17:23:05 +01:00
rlVertex3f ( d . x , d . y , d . z ) ;
2015-02-02 00:53:49 +01:00
2014-03-16 20:59:02 +01:00
// Top-right corner for texture and quad
2016-09-12 19:36:41 +02:00
rlTexCoord2f ( ( float ) ( sourceRec . x + sourceRec . width ) / texture . width , ( float ) ( sourceRec . y + sourceRec . height ) / texture . height ) ;
2014-03-25 12:40:35 +01:00
rlVertex3f ( c . x , c . y , c . z ) ;
2014-09-03 16:51:28 +02:00
2014-11-24 17:23:05 +01:00
// Bottom-right corner for texture and quad
2016-09-12 19:36:41 +02:00
rlTexCoord2f ( ( float ) ( sourceRec . x + sourceRec . width ) / texture . width , ( float ) sourceRec . y / texture . height ) ;
2014-11-24 17:23:05 +01:00
rlVertex3f ( b . x , b . y , b . z ) ;
2014-03-25 12:40:35 +01:00
rlEnd ( ) ;
2014-09-03 16:51:28 +02:00
2014-03-25 12:40:35 +01:00
rlDisableTexture ( ) ;
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
{
Vector3 size ;
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
}
2015-07-13 18:19:29 +02:00
// Detect collision between two spheres
2015-02-09 18:35:25 +01:00
bool CheckCollisionSpheres ( Vector3 centerA , float radiusA , Vector3 centerB , float radiusB )
{
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
/*
2019-08-08 08:57:21 +01:00
float dx = centerA . x - centerB . x ; // X distance between centers
float dy = centerA . y - centerB . y ; // Y distance between centers
float dz = centerA . z - centerB . 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
if ( distance < = ( radiusA + radiusB ) ) collision = true ;
*/
2019-09-13 12:29:52 +01:00
2019-05-09 16:09:49 +02:00
// Check for distances squared to avoid sqrtf()
2019-05-09 16:10:55 +02:00
if ( Vector3DotProduct ( Vector3Subtract ( centerB , centerA ) , Vector3Subtract ( centerB , centerA ) ) < = ( radiusA + radiusB ) * ( radiusA + radiusB ) ) 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
}
2015-07-13 18:19:29 +02:00
// Detect collision between two boxes
// 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 ;
}
2015-07-13 18:19:29 +02:00
// Detect 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 ;
}
2016-01-19 20:27:41 +01:00
// Detect collision between ray and sphere
2019-08-07 00:32:44 +02:00
bool CheckCollisionRaySphere ( Ray ray , Vector3 center , float radius )
2016-01-19 20:27:41 +01:00
{
bool collision = false ;
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 distance = Vector3Length ( raySpherePos ) ;
float vector = Vector3DotProduct ( raySpherePos , ray . direction ) ;
2019-08-07 00:32:44 +02:00
float d = radius * radius - ( distance * distance - vector * vector ) ;
2016-08-16 11:09:55 +02:00
2016-05-21 18:22:15 +02:00
if ( d > = 0.0f ) collision = true ;
2016-08-16 11:09:55 +02:00
2016-01-20 19:28:47 +01:00
return collision ;
}
// Detect collision between ray and sphere with extended parameters and collision point detection
2019-08-07 00:32:44 +02:00
bool CheckCollisionRaySphereEx ( Ray ray , Vector3 center , float radius , Vector3 * collisionPoint )
2016-01-20 19:28:47 +01:00
{
bool collision = false ;
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 distance = Vector3Length ( raySpherePos ) ;
float vector = Vector3DotProduct ( raySpherePos , ray . direction ) ;
2019-08-07 00:32:44 +02:00
float d = radius * radius - ( distance * distance - vector * vector ) ;
2016-08-16 11:09:55 +02:00
2016-05-21 18:22:15 +02:00
if ( d > = 0.0f ) collision = true ;
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
2016-01-20 19:28:47 +01:00
float collisionDistance = 0 ;
2016-08-16 11:09:55 +02:00
2019-08-07 00:32:44 +02:00
if ( distance < radius ) collisionDistance = vector + sqrtf ( d ) ;
2016-11-02 00:50:08 +01:00
else collisionDistance = vector - sqrtf ( d ) ;
2019-02-21 18:45:19 +01:00
2018-04-02 15:16:45 +02:00
// Calculate collision point
Vector3 cPoint = Vector3Add ( ray . position , Vector3Scale ( ray . direction , collisionDistance ) ) ;
2016-08-16 11:09:55 +02:00
2016-01-20 19:28:47 +01:00
collisionPoint - > x = cPoint . x ;
collisionPoint - > y = cPoint . y ;
collisionPoint - > z = cPoint . z ;
2016-08-16 11:09:55 +02:00
2016-01-19 20:27:41 +01:00
return collision ;
}
// Detect collision between ray and bounding box
2016-03-01 20:54:02 +01:00
bool CheckCollisionRayBox ( Ray ray , BoundingBox box )
2016-01-07 16:18:24 +01:00
{
bool collision = false ;
2016-08-16 11:09:55 +02:00
2016-01-07 16:18:24 +01:00
float t [ 8 ] ;
2016-03-01 20:54:02 +01:00
t [ 0 ] = ( box . min . x - ray . position . x ) / ray . direction . x ;
t [ 1 ] = ( box . max . x - ray . position . x ) / ray . direction . x ;
t [ 2 ] = ( box . min . y - ray . position . y ) / ray . direction . y ;
t [ 3 ] = ( box . max . y - ray . position . y ) / ray . direction . y ;
t [ 4 ] = ( box . min . z - ray . position . z ) / ray . direction . z ;
t [ 5 ] = ( box . max . z - ray . position . z ) / ray . direction . z ;
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
2016-01-07 16:18:24 +01:00
collision = ! ( t [ 7 ] < 0 | | t [ 6 ] > t [ 7 ] ) ;
2016-08-16 11:09:55 +02:00
2016-01-07 16:18:24 +01:00
return collision ;
}
2018-04-06 12:04:09 -04:00
// Get collision info between ray and model
2019-08-07 00:32:44 +02:00
RayHitInfo GetCollisionRayModel ( Ray ray , Model model )
2018-04-06 12:04:09 -04:00
{
RayHitInfo result = { 0 } ;
2019-08-07 00:32:44 +02:00
for ( int m = 0 ; m < model . meshCount ; m + + )
2018-04-06 12:04:09 -04:00
{
2019-03-29 16:48:23 +01:00
// Check if meshhas vertex data on CPU for testing
2019-08-07 00:32:44 +02:00
if ( model . meshes [ m ] . vertices ! = NULL )
2018-04-06 12:04:09 -04:00
{
2019-03-29 16:48:23 +01:00
// model->mesh.triangleCount may not be set, vertexCount is more reliable
2019-08-07 00:32:44 +02:00
int triangleCount = model . meshes [ m ] . vertexCount / 3 ;
2019-03-29 16:48:23 +01:00
// Test against all triangles in mesh
for ( int i = 0 ; i < triangleCount ; i + + )
{
Vector3 a , b , c ;
2019-08-07 00:32:44 +02:00
Vector3 * vertdata = ( Vector3 * ) model . meshes [ m ] . vertices ;
2019-02-21 18:45:19 +01:00
2019-08-07 00:32:44 +02:00
if ( model . meshes [ m ] . indices )
2019-03-29 16:48:23 +01:00
{
2019-08-07 00:32:44 +02:00
a = vertdata [ model . meshes [ m ] . indices [ i * 3 + 0 ] ] ;
b = vertdata [ model . meshes [ m ] . indices [ i * 3 + 1 ] ] ;
c = vertdata [ model . meshes [ m ] . indices [ i * 3 + 2 ] ] ;
2019-03-29 16:48:23 +01:00
}
else
{
a = vertdata [ i * 3 + 0 ] ;
b = vertdata [ i * 3 + 1 ] ;
c = vertdata [ i * 3 + 2 ] ;
}
2018-04-06 12:04:09 -04:00
2019-08-07 00:32:44 +02:00
a = Vector3Transform ( a , model . transform ) ;
b = Vector3Transform ( b , model . transform ) ;
c = Vector3Transform ( c , model . transform ) ;
2018-04-06 12:04:09 -04:00
2019-03-29 16:48:23 +01:00
RayHitInfo triHitInfo = GetCollisionRayTriangle ( ray , a , b , c ) ;
if ( triHitInfo . hit )
{
// Save the closest hit triangle
if ( ( ! result . hit ) | | ( result . distance > triHitInfo . distance ) ) result = triHitInfo ;
}
}
2017-01-05 19:33:05 +01:00
}
}
2017-01-28 23:02:30 +01:00
return result ;
2017-01-05 19:33:05 +01:00
}
// Get collision info between ray and triangle
// NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
RayHitInfo GetCollisionRayTriangle ( Ray ray , Vector3 p1 , Vector3 p2 , Vector3 p3 )
{
# define EPSILON 0.000001 // A small number
Vector3 edge1 , edge2 ;
Vector3 p , q , tv ;
float det , invDet , u , v , t ;
RayHitInfo result = { 0 } ;
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!
if ( ( det > - EPSILON ) & & ( det < EPSILON ) ) return result ;
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
if ( ( u < 0.0f ) | | ( u > 1.0f ) ) return result ;
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
if ( ( v < 0.0f ) | | ( ( u + v ) > 1.0f ) ) return result ;
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
result . hit = true ;
result . distance = t ;
result . hit = true ;
2018-04-02 15:16:45 +02:00
result . normal = Vector3Normalize ( Vector3CrossProduct ( edge1 , edge2 ) ) ;
result . position = Vector3Add ( ray . position , Vector3Scale ( ray . direction , t ) ) ;
2017-01-05 19:33:05 +01:00
}
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
return result ;
}
// Get collision info between ray and ground plane (Y-normal plane)
RayHitInfo GetCollisionRayGround ( Ray ray , float groundHeight )
{
# define EPSILON 0.000001 // A small number
RayHitInfo result = { 0 } ;
2020-02-28 12:54:39 +01:00
if ( fabsf ( ray . direction . y ) > EPSILON )
2017-01-05 19:33:05 +01:00
{
2018-04-02 15:16:45 +02:00
float distance = ( ray . position . y - groundHeight ) / - ray . direction . y ;
2017-01-28 23:02:30 +01:00
2018-04-02 15:16:45 +02:00
if ( distance > = 0.0 )
2017-01-05 19:33:05 +01:00
{
result . hit = true ;
2018-04-02 15:16:45 +02:00
result . distance = distance ;
2017-05-10 00:57:48 +02:00
result . normal = ( Vector3 ) { 0.0 , 1.0 , 0.0 } ;
2018-04-02 15:16:45 +02:00
result . position = Vector3Add ( ray . position , Vector3Scale ( ray . direction , distance ) ) ;
2017-01-05 19:33:05 +01:00
}
}
2017-01-28 23:02:30 +01:00
2017-01-05 19:33:05 +01:00
return result ;
}
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
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
2019-03-29 17:15:22 +01:00
tinyobj_attrib_t attrib ;
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
2019-03-29 17:15:22 +01:00
int dataLength = 0 ;
2019-03-29 17:28:10 +01:00
char * data = NULL ;
2019-04-01 12:17:29 +02:00
2019-03-29 17:28:10 +01:00
// Load model data
FILE * objFile = fopen ( fileName , " rb " ) ;
if ( objFile ! = NULL )
{
fseek ( objFile , 0 , SEEK_END ) ;
2019-04-01 00:16:56 +02:00
long length = ftell ( objFile ) ; // Get file size
fseek ( objFile , 0 , SEEK_SET ) ; // Reset file pointer
2019-03-29 17:28:10 +01:00
2019-04-23 14:55:35 +02:00
data = ( char * ) RL_MALLOC ( length ) ;
2019-03-29 17:28:10 +01:00
2019-04-01 00:16:56 +02:00
fread ( data , length , 1 , objFile ) ;
dataLength = length ;
2019-03-29 17:28:10 +01:00
fclose ( objFile ) ;
}
2019-04-01 00:16:56 +02:00
if ( data ! = NULL )
2014-07-23 00:06:24 +02:00
{
2019-03-29 17:15:22 +01:00
unsigned int flags = TINYOBJ_FLAG_TRIANGULATE ;
int ret = tinyobj_parse_obj ( & attrib , & meshes , & meshCount , & materials , & materialCount , data , dataLength , flags ) ;
2019-04-04 13:50:52 +02:00
2020-02-03 19:13:24 +01:00
if ( ret ! = TINYOBJ_SUCCESS ) TRACELOG ( LOG_WARNING , " [%s] Model data could not be loaded " , fileName ) ;
else TRACELOG ( LOG_INFO , " [%s] Model data loaded successfully: %i meshes / %i materials " , fileName , meshCount , materialCount ) ;
2019-04-04 13:50:52 +02:00
2019-04-01 00:16:56 +02:00
// Init model meshes array
2019-05-15 15:12:56 +02:00
// TODO: Support multiple meshes... in the meantime, only one mesh is returned
//model.meshCount = meshCount;
model . meshCount = 1 ;
2019-08-27 11:16:43 +02:00
model . meshes = ( Mesh * ) RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
2019-04-04 13:50:52 +02: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 ) ) ;
}
2019-09-13 12:29:52 +01:00
2019-04-23 14:55:35 +02:00
model . meshMaterial = ( int * ) RL_CALLOC ( model . meshCount , sizeof ( int ) ) ;
2019-04-04 13:50:52 +02:00
/*
2019-04-01 12:17:29 +02:00
// Multiple meshes data reference
// NOTE: They are provided as a faces offset
typedef struct {
char * name ; // group name or object name
unsigned int face_offset ;
unsigned int length ;
} tinyobj_shape_t ;
*/
2019-04-04 13:50:52 +02:00
2019-04-01 00:16:56 +02:00
// Init model meshes
for ( int m = 0 ; m < 1 ; m + + )
2014-07-23 00:06:24 +02:00
{
2019-04-01 00:16:56 +02:00
Mesh mesh = { 0 } ;
memset ( & mesh , 0 , sizeof ( Mesh ) ) ;
mesh . vertexCount = attrib . num_faces * 3 ;
mesh . triangleCount = attrib . num_faces ;
2019-08-27 11:16:43 +02:00
mesh . vertices = ( float * ) RL_CALLOC ( mesh . vertexCount * 3 , sizeof ( float ) ) ;
mesh . texcoords = ( float * ) RL_CALLOC ( mesh . vertexCount * 2 , sizeof ( float ) ) ;
mesh . normals = ( float * ) RL_CALLOC ( mesh . vertexCount * 3 , sizeof ( float ) ) ;
2019-08-27 10:56:49 +02:00
mesh . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2019-04-04 13:50:52 +02:00
2019-04-01 00:16:56 +02:00
int vCount = 0 ;
int vtCount = 0 ;
int vnCount = 0 ;
2019-04-04 13:50:52 +02:00
2019-04-01 00:16:56 +02:00
for ( int f = 0 ; f < attrib . num_faces ; f + + )
{
2019-04-01 12:17:29 +02:00
// Get indices for the face
2019-04-01 00:16:56 +02:00
tinyobj_vertex_index_t idx0 = attrib . faces [ 3 * f + 0 ] ;
tinyobj_vertex_index_t idx1 = attrib . faces [ 3 * f + 1 ] ;
tinyobj_vertex_index_t idx2 = attrib . faces [ 3 * f + 2 ] ;
2019-04-04 13:50:52 +02:00
2020-02-03 19:13:24 +01:00
// TRACELOGD("Face %i index: v %i/%i/%i . vt %i/%i/%i . vn %i/%i/%i\n", f, idx0.v_idx, idx1.v_idx, idx2.v_idx, idx0.vt_idx, idx1.vt_idx, idx2.vt_idx, idx0.vn_idx, idx1.vn_idx, idx2.vn_idx);
2019-04-04 13:50:52 +02:00
2019-04-01 12:17:29 +02:00
// Fill vertices buffer (float) using vertex index of the face
2019-04-01 00:16:56 +02:00
for ( int v = 0 ; v < 3 ; v + + ) { mesh . vertices [ vCount + v ] = attrib . vertices [ idx0 . v_idx * 3 + v ] ; } vCount + = 3 ;
for ( int v = 0 ; v < 3 ; v + + ) { mesh . vertices [ vCount + v ] = attrib . vertices [ idx1 . v_idx * 3 + v ] ; } vCount + = 3 ;
for ( int v = 0 ; v < 3 ; v + + ) { mesh . vertices [ vCount + v ] = attrib . vertices [ idx2 . v_idx * 3 + v ] ; } vCount + = 3 ;
2019-04-04 13:50:52 +02:00
2019-04-01 12:17:29 +02:00
// Fill texcoords buffer (float) using vertex index of the face
// NOTE: Y-coordinate must be flipped upside-down
mesh . texcoords [ vtCount + 0 ] = attrib . texcoords [ idx0 . vt_idx * 2 + 0 ] ;
mesh . texcoords [ vtCount + 1 ] = 1.0f - attrib . texcoords [ idx0 . vt_idx * 2 + 1 ] ; vtCount + = 2 ;
mesh . texcoords [ vtCount + 0 ] = attrib . texcoords [ idx1 . vt_idx * 2 + 0 ] ;
mesh . texcoords [ vtCount + 1 ] = 1.0f - attrib . texcoords [ idx1 . vt_idx * 2 + 1 ] ; vtCount + = 2 ;
mesh . texcoords [ vtCount + 0 ] = attrib . texcoords [ idx2 . vt_idx * 2 + 0 ] ;
mesh . texcoords [ vtCount + 1 ] = 1.0f - attrib . texcoords [ idx2 . vt_idx * 2 + 1 ] ; vtCount + = 2 ;
2019-04-04 13:50:52 +02:00
2019-04-01 12:17:29 +02:00
// Fill normals buffer (float) using vertex index of the face
2019-04-01 00:16:56 +02:00
for ( int v = 0 ; v < 3 ; v + + ) { mesh . normals [ vnCount + v ] = attrib . normals [ idx0 . vn_idx * 3 + v ] ; } vnCount + = 3 ;
for ( int v = 0 ; v < 3 ; v + + ) { mesh . normals [ vnCount + v ] = attrib . normals [ idx1 . vn_idx * 3 + v ] ; } vnCount + = 3 ;
for ( int v = 0 ; v < 3 ; v + + ) { mesh . normals [ vnCount + v ] = attrib . normals [ idx2 . vn_idx * 3 + v ] ; } vnCount + = 3 ;
}
model . meshes [ m ] = mesh ; // Assign mesh data to model
2019-04-04 13:33:54 +02:00
2019-04-01 12:41:32 +02:00
// Assign mesh material for current mesh
model . meshMaterial [ m ] = attrib . material_ids [ m ] ;
2019-09-19 12:28:01 +01:00
2019-09-19 13:36:55 +02:00
// Set unfound materials to default
if ( model . meshMaterial [ m ] = = - 1 ) model . meshMaterial [ m ] = 0 ;
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
for ( int m = 0 ; m < materialCount ; m + + )
{
2019-04-01 12:41:32 +02:00
// Init material to default
// NOTE: Uses default shader, only MAP_DIFFUSE supported
model . materials [ m ] = LoadMaterialDefault ( ) ;
2019-04-04 13:50:52 +02:00
2019-04-01 00:16:56 +02:00
/*
typedef struct {
char * name ;
float ambient [ 3 ] ;
float diffuse [ 3 ] ;
float specular [ 3 ] ;
float transmittance [ 3 ] ;
float emission [ 3 ] ;
float shininess ;
float ior ; // index of refraction
float dissolve ; // 1 == opaque; 0 == fully transparent
2019-12-01 12:55:33 +01:00
// illumination model (Ref: http://www.fileformat.info/format/material/)
2019-04-01 00:16:56 +02:00
int illum ;
int pad0 ;
char * ambient_texname ; // map_Ka
char * diffuse_texname ; // map_Kd
char * specular_texname ; // map_Ks
char * specular_highlight_texname ; // map_Ns
char * bump_texname ; // map_bump, bump
char * displacement_texname ; // disp
char * alpha_texname ; // map_d
} tinyobj_material_t ;
*/
2019-04-04 13:50:52 +02:00
2019-04-10 22:39:42 +02:00
model . materials [ m ] . maps [ MAP_DIFFUSE ] . texture = GetTextureDefault ( ) ; // Get default texture, in case no texture is defined
2019-09-13 12:29:52 +01:00
2019-04-10 00:44:24 +02:00
if ( materials [ m ] . diffuse_texname ! = NULL ) model . materials [ m ] . maps [ MAP_DIFFUSE ] . texture = LoadTexture ( materials [ m ] . diffuse_texname ) ; //char *diffuse_texname; // map_Kd
2019-04-01 12:41:32 +02:00
model . materials [ m ] . maps [ MAP_DIFFUSE ] . color = ( Color ) { ( float ) ( materials [ m ] . diffuse [ 0 ] * 255.0f ) , ( float ) ( materials [ m ] . diffuse [ 1 ] * 255.0f ) , ( float ) ( materials [ m ] . diffuse [ 2 ] * 255.0f ) , 255 } ; //float diffuse[3];
model . materials [ m ] . maps [ MAP_DIFFUSE ] . value = 0.0f ;
2019-04-04 13:50:52 +02:00
2019-04-10 00:44:24 +02:00
if ( materials [ m ] . specular_texname ! = NULL ) model . materials [ m ] . maps [ MAP_SPECULAR ] . texture = LoadTexture ( materials [ m ] . specular_texname ) ; //char *specular_texname; // map_Ks
2019-04-01 12:41:32 +02:00
model . materials [ m ] . maps [ MAP_SPECULAR ] . color = ( Color ) { ( float ) ( materials [ m ] . specular [ 0 ] * 255.0f ) , ( float ) ( materials [ m ] . specular [ 1 ] * 255.0f ) , ( float ) ( materials [ m ] . specular [ 2 ] * 255.0f ) , 255 } ; //float specular[3];
model . materials [ m ] . maps [ MAP_SPECULAR ] . value = 0.0f ;
2019-04-04 13:50:52 +02:00
2019-04-10 00:44:24 +02:00
if ( materials [ m ] . bump_texname ! = NULL ) model . materials [ m ] . maps [ MAP_NORMAL ] . texture = LoadTexture ( materials [ m ] . bump_texname ) ; //char *bump_texname; // map_bump, bump
2019-04-01 12:41:32 +02:00
model . materials [ m ] . maps [ MAP_NORMAL ] . color = WHITE ;
model . materials [ m ] . maps [ MAP_NORMAL ] . value = materials [ m ] . shininess ;
2019-04-04 13:50:52 +02:00
2019-04-01 12:41:32 +02:00
model . materials [ m ] . maps [ MAP_EMISSION ] . color = ( Color ) { ( float ) ( materials [ m ] . emission [ 0 ] * 255.0f ) , ( float ) ( materials [ m ] . emission [ 1 ] * 255.0f ) , ( float ) ( materials [ m ] . emission [ 2 ] * 255.0f ) , 255 } ; //float emission[3];
2019-04-10 00:44:24 +02:00
if ( materials [ m ] . displacement_texname ! = NULL ) model . materials [ m ] . maps [ 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 ) ;
2019-09-22 21:47:19 +02:00
RL_FREE ( data ) ;
2014-04-19 16:36:49 +02:00
}
2014-09-03 16:51:28 +02:00
2019-03-29 17:15:22 +01:00
// NOTE: At this point we have all model data loaded
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_INFO , " [%s] Model loaded successfully in RAM (CPU) " , fileName ) ;
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
{
2019-04-04 13:33:54 +02:00
# define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
# define IQM_VERSION 2 // only IQM version 2 supported
# define BONE_NAME_LENGTH 32 // BoneInfo name string length
# define MESH_NAME_LENGTH 32 // Mesh name string length
// 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 ,
IQM_COLOR = 6 , // NOTE: Vertex colors unused by default
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-01-28 16:40:12 +01:00
FILE * iqmFile = NULL ;
2019-04-04 13:33:54 +02:00
IQMHeader iqm ;
IQMMesh * imesh ;
IQMTriangle * tri ;
IQMVertexArray * va ;
IQMJoint * ijoint ;
float * vertex = NULL ;
float * normal = NULL ;
float * text = NULL ;
char * blendi = NULL ;
unsigned char * blendw = NULL ;
iqmFile = fopen ( fileName , " rb " ) ;
if ( iqmFile = = NULL )
{
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] IQM file could not be opened " , fileName ) ;
2019-04-04 13:33:54 +02:00
return model ;
}
fread ( & iqm , sizeof ( IQMHeader ) , 1 , iqmFile ) ; // Read IQM header
if ( strncmp ( iqm . magic , IQM_MAGIC , sizeof ( IQM_MAGIC ) ) )
{
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] IQM file does not seem to be valid " , fileName ) ;
2019-04-04 13:33:54 +02:00
fclose ( iqmFile ) ;
return model ;
}
if ( iqm . version ! = IQM_VERSION )
{
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] IQM file version is not supported (%i). " , fileName , iqm . version ) ;
2019-04-04 13:33:54 +02:00
fclose ( iqmFile ) ;
return model ;
}
// Meshes data processing
2019-04-23 14:55:35 +02:00
imesh = RL_MALLOC ( sizeof ( IQMMesh ) * iqm . num_meshes ) ;
2019-04-04 13:33:54 +02:00
fseek ( iqmFile , iqm . ofs_meshes , SEEK_SET ) ;
fread ( imesh , sizeof ( IQMMesh ) * iqm . num_meshes , 1 , iqmFile ) ;
model . meshCount = iqm . 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
2019-08-27 11:16:43 +02:00
char name [ MESH_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
{
2019-08-27 11:16:43 +02:00
fseek ( iqmFile , iqm . ofs_text + imesh [ i ] . name , SEEK_SET ) ;
2019-04-04 13:33:54 +02:00
fread ( name , sizeof ( char ) * MESH_NAME_LENGTH , 1 , iqmFile ) ; // Mesh name not used...
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-09-13 12:29:52 +01:00
2019-08-27 10:56:49 +02:00
model . meshes [ i ] . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2019-04-04 13:33:54 +02:00
}
// Triangles data processing
2019-08-27 11:16:43 +02:00
tri = RL_MALLOC ( iqm . num_triangles * sizeof ( IQMTriangle ) ) ;
2019-04-04 13:33:54 +02:00
fseek ( iqmFile , iqm . ofs_triangles , SEEK_SET ) ;
2019-08-27 11:16:43 +02:00
fread ( tri , iqm . num_triangles * sizeof ( IQMTriangle ) , 1 , iqmFile ) ;
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 ;
2019-04-09 13:23:51 +02:00
for ( int i = imesh [ m ] . first_triangle ; i < ( imesh [ m ] . first_triangle + imesh [ m ] . num_triangles ) ; i + + )
2019-04-04 13:33:54 +02:00
{
// IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around
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
2019-08-27 11:16:43 +02:00
va = RL_MALLOC ( iqm . num_vertexarrays * sizeof ( IQMVertexArray ) ) ;
2019-04-04 13:33:54 +02:00
fseek ( iqmFile , iqm . ofs_vertexarrays , SEEK_SET ) ;
2019-08-27 11:16:43 +02:00
fread ( va , iqm . num_vertexarrays * sizeof ( IQMVertexArray ) , 1 , iqmFile ) ;
2019-04-04 13:33:54 +02:00
for ( int i = 0 ; i < iqm . num_vertexarrays ; i + + )
{
switch ( va [ i ] . type )
{
case IQM_POSITION :
{
2019-08-27 11:16:43 +02:00
vertex = RL_MALLOC ( iqm . num_vertexes * 3 * sizeof ( float ) ) ;
2019-04-04 13:33:54 +02:00
fseek ( iqmFile , va [ i ] . offset , SEEK_SET ) ;
2019-08-27 11:16:43 +02:00
fread ( vertex , iqm . num_vertexes * 3 * sizeof ( float ) , 1 , iqmFile ) ;
2019-04-04 13:33:54 +02:00
for ( int m = 0 ; m < iqm . num_meshes ; m + + )
{
int vCounter = 0 ;
for ( int i = imesh [ m ] . first_vertex * 3 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 3 ; i + + )
{
model . meshes [ m ] . vertices [ vCounter ] = vertex [ i ] ;
model . meshes [ m ] . animVertices [ vCounter ] = vertex [ i ] ;
vCounter + + ;
}
}
} break ;
case IQM_NORMAL :
{
2019-08-27 11:16:43 +02:00
normal = RL_MALLOC ( iqm . num_vertexes * 3 * sizeof ( float ) ) ;
2019-04-04 13:33:54 +02:00
fseek ( iqmFile , va [ i ] . offset , SEEK_SET ) ;
2019-08-27 11:16:43 +02:00
fread ( normal , iqm . num_vertexes * 3 * sizeof ( float ) , 1 , iqmFile ) ;
2019-04-04 13:33:54 +02:00
for ( int m = 0 ; m < iqm . num_meshes ; m + + )
{
int vCounter = 0 ;
for ( int i = imesh [ m ] . first_vertex * 3 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 3 ; i + + )
{
model . meshes [ m ] . normals [ vCounter ] = normal [ i ] ;
model . meshes [ m ] . animNormals [ vCounter ] = normal [ i ] ;
vCounter + + ;
}
}
} break ;
case IQM_TEXCOORD :
{
2019-08-27 11:16:43 +02:00
text = RL_MALLOC ( iqm . num_vertexes * 2 * sizeof ( float ) ) ;
2019-04-04 13:33:54 +02:00
fseek ( iqmFile , va [ i ] . offset , SEEK_SET ) ;
2019-08-27 11:16:43 +02:00
fread ( text , iqm . num_vertexes * 2 * sizeof ( float ) , 1 , iqmFile ) ;
2019-04-04 13:33:54 +02:00
for ( int m = 0 ; m < iqm . num_meshes ; m + + )
{
int vCounter = 0 ;
for ( int i = imesh [ m ] . first_vertex * 2 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 2 ; i + + )
{
model . meshes [ m ] . texcoords [ vCounter ] = text [ i ] ;
vCounter + + ;
}
}
} break ;
case IQM_BLENDINDEXES :
{
2019-08-27 11:16:43 +02:00
blendi = RL_MALLOC ( iqm . num_vertexes * 4 * sizeof ( char ) ) ;
2019-04-04 13:33:54 +02:00
fseek ( iqmFile , va [ i ] . offset , SEEK_SET ) ;
2019-08-27 11:16:43 +02:00
fread ( blendi , iqm . num_vertexes * 4 * sizeof ( char ) , 1 , iqmFile ) ;
2019-04-04 13:33:54 +02:00
for ( int m = 0 ; m < iqm . num_meshes ; m + + )
{
int boneCounter = 0 ;
for ( int i = imesh [ m ] . first_vertex * 4 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 4 ; i + + )
{
model . meshes [ m ] . boneIds [ boneCounter ] = blendi [ i ] ;
boneCounter + + ;
}
}
} break ;
case IQM_BLENDWEIGHTS :
{
2019-08-27 11:16:43 +02:00
blendw = RL_MALLOC ( iqm . num_vertexes * 4 * sizeof ( unsigned char ) ) ;
fseek ( iqmFile , va [ i ] . offset , SEEK_SET ) ;
fread ( blendw , iqm . num_vertexes * 4 * sizeof ( unsigned char ) , 1 , iqmFile ) ;
2019-04-04 13:33:54 +02:00
for ( int m = 0 ; m < iqm . num_meshes ; m + + )
{
int boneCounter = 0 ;
for ( int i = imesh [ m ] . first_vertex * 4 ; i < ( imesh [ m ] . first_vertex + imesh [ m ] . num_vertexes ) * 4 ; i + + )
{
model . meshes [ m ] . boneWeights [ boneCounter ] = blendw [ i ] / 255.0f ;
boneCounter + + ;
}
}
} break ;
}
}
// Bones (joints) data processing
2019-08-27 11:16:43 +02:00
ijoint = RL_MALLOC ( iqm . num_joints * sizeof ( IQMJoint ) ) ;
2019-04-04 13:33:54 +02:00
fseek ( iqmFile , iqm . ofs_joints , SEEK_SET ) ;
2019-08-27 11:16:43 +02:00
fread ( ijoint , iqm . num_joints * sizeof ( IQMJoint ) , 1 , iqmFile ) ;
2019-04-04 13:33:54 +02:00
model . boneCount = iqm . num_joints ;
2019-08-27 11:16:43 +02:00
model . bones = RL_MALLOC ( iqm . num_joints * sizeof ( BoneInfo ) ) ;
model . bindPose = RL_MALLOC ( iqm . num_joints * sizeof ( Transform ) ) ;
2019-04-04 13:33:54 +02:00
for ( int i = 0 ; i < iqm . num_joints ; i + + )
{
// Bones
model . bones [ i ] . parent = ijoint [ i ] . parent ;
fseek ( iqmFile , iqm . ofs_text + ijoint [ i ] . name , SEEK_SET ) ;
2019-08-27 11:16:43 +02:00
fread ( model . bones [ i ] . name , BONE_NAME_LENGTH * sizeof ( char ) , 1 , iqmFile ) ;
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
}
}
fclose ( iqmFile ) ;
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
}
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-02-03 19:13:24 +01:00
if ( image - > uri [ i ] = = 0 ) TRACELOG ( LOG_WARNING , " CGLTF Image: Invalid data URI " ) ;
2019-10-21 08:38:23 -07:00
else
{
int size ;
unsigned char * data = DecodeBase64 ( image - > uri + i + 1 , & size ) ;
int w , h ;
unsigned char * raw = stbi_load_from_memory ( data , size , & w , & h , NULL , 4 ) ;
2020-02-22 01:17:30 -08:00
rimage = LoadImagePro ( raw , w , h , UNCOMPRESSED_R8G8B8A8 ) ;
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
{
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 ) ;
int n = image - > buffer_view - > offset ;
int stride = image - > buffer_view - > stride ? image - > buffer_view - > stride : 1 ;
for ( int i = 0 ; i < image - > buffer_view - > size ; i + + )
{
data [ i ] = ( ( unsigned char * ) image - > buffer_view - > buffer - > data ) [ n ] ;
n + = stride ;
}
int w , h ;
unsigned char * raw = stbi_load_from_memory ( data , image - > buffer_view - > size , & w , & h , NULL , 4 ) ;
2019-10-21 17:55:37 +02:00
free ( data ) ;
2019-10-21 08:38:23 -07:00
2020-02-22 01:17:30 -08:00
rimage = LoadImagePro ( raw , w , h , UNCOMPRESSED_R8G8B8A8 ) ;
2019-10-23 09:42:17 +01:00
free ( raw ) ;
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 = LoadImageEx ( & tint , 1 , 1 ) ;
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
}
2020-02-22 01:17:30 -08:00
// LoadGLTF loads in model data from given filename, supporting both .gltf and .glb
2019-03-29 16:48:23 +01:00
static Model LoadGLTF ( const char * fileName )
2018-09-05 10:59:05 +02:00
{
2019-05-20 11:13:38 +02:00
/***********************************************************************************
2019-09-13 12:29:52 +01:00
2020-02-26 20:23:55 +01:00
Function implemented by Wilhem Barbier ( @ wbrbr ) , with modifications by Tyler Bezera ( @ gamerfiend )
2019-09-13 12:29:52 +01:00
2019-05-20 11:13:38 +02:00
Features :
- Supports . gltf and . glb files
- Supports embedded ( base64 ) or external textures
2020-02-22 01:17:30 -08:00
- Loads all raylib supported material textures , values and colors
2019-05-20 11:13:38 +02:00
- Supports multiple mesh per model and multiple primitives per model
2019-09-13 12:29:52 +01:00
2019-05-20 11:13:38 +02:00
Some restrictions ( not exhaustive ) :
- Triangle - only meshes
- Not supported node hierarchies or transforms
2019-08-08 08:57:21 +01:00
- Only supports unsigned short indices ( no byte / unsigned int )
2019-05-20 11:13:38 +02:00
- Only supports float for texture coordinates ( no byte / unsigned short )
2019-09-13 12:29:52 +01:00
2019-05-20 11:13:38 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-09-13 12:29:52 +01:00
2019-05-20 11:13:38 +02:00
# define LOAD_ACCESSOR(type, nbcomp, acc, dst) \
{ \
int n = 0 ; \
type * buf = ( type * ) acc - > buffer_view - > buffer - > data + acc - > buffer_view - > offset / sizeof ( type ) + acc - > offset / sizeof ( type ) ; \
for ( int k = 0 ; k < acc - > count ; k + + ) { \
for ( int l = 0 ; l < nbcomp ; l + + ) { \
dst [ nbcomp * k + l ] = buf [ n + l ] ; \
} \
n + = acc - > stride / sizeof ( type ) ; \
} \
}
2019-09-13 12:29:52 +01:00
2019-03-29 16:48:23 +01:00
Model model = { 0 } ;
2019-02-21 18:45:19 +01:00
2019-02-18 18:46:17 +01:00
// glTF file loading
2018-09-05 10:59:05 +02:00
FILE * gltfFile = fopen ( fileName , " rb " ) ;
2019-02-21 18:45:19 +01:00
2018-09-05 10:59:05 +02:00
if ( gltfFile = = NULL )
{
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] glTF file could not be opened " , fileName ) ;
2019-03-29 16:48:23 +01:00
return model ;
2018-09-05 10:59:05 +02:00
}
2018-09-14 13:00:48 +02:00
fseek ( gltfFile , 0 , SEEK_END ) ;
int size = ftell ( gltfFile ) ;
fseek ( gltfFile , 0 , SEEK_SET ) ;
2018-09-05 10:59:05 +02:00
2019-04-23 14:55:35 +02:00
void * buffer = RL_MALLOC ( size ) ;
2018-09-14 13:00:48 +02:00
fread ( buffer , size , 1 , gltfFile ) ;
2019-02-21 18:45:19 +01:00
2018-09-14 13:00:48 +02:00
fclose ( gltfFile ) ;
2018-09-05 10:59:05 +02:00
2019-02-18 18:46:17 +01:00
// glTF data loading
2019-04-09 13:23:51 +02:00
cgltf_options options = { 0 } ;
2019-05-14 14:06:17 +02:00
cgltf_data * data = NULL ;
2018-09-14 13:00:48 +02:00
cgltf_result result = cgltf_parse ( & options , buffer , size , & data ) ;
2019-02-21 18:45:19 +01:00
2018-09-14 13:00:48 +02:00
if ( result = = cgltf_result_success )
{
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_INFO , " [%s][%s] Model meshes/materials: %i/%i " , fileName , ( data - > file_type = = 2 ) ? " glb " : " gltf " , data - > meshes_count , data - > materials_count ) ;
2019-04-09 13:23:51 +02:00
// Read data buffers
result = cgltf_load_buffers ( & options , data , fileName ) ;
2020-02-03 19:13:24 +01:00
if ( result ! = cgltf_result_success ) TRACELOG ( LOG_INFO , " [%s][%s] Error loading mesh/material buffers " , fileName , ( data - > file_type = = 2 ) ? " glb " : " gltf " ) ;
2019-04-09 13:23:51 +02:00
2019-05-20 11:13:38 +02:00
int primitivesCount = 0 ;
2019-09-13 12:29:52 +01:00
2019-05-20 11:13:38 +02:00
for ( int i = 0 ; i < data - > meshes_count ; i + + ) primitivesCount + = ( int ) data - > meshes [ i ] . primitives_count ;
2019-05-14 14:06:17 +02:00
2019-04-09 13:23:51 +02:00
// Process glTF data and map to model
2019-05-20 11:13:38 +02:00
model . meshCount = primitivesCount ;
2019-05-14 14:06:17 +02:00
model . meshes = RL_CALLOC ( model . meshCount , sizeof ( Mesh ) ) ;
model . materialCount = data - > materials_count + 1 ;
2019-08-07 00:32:44 +02:00
model . materials = RL_MALLOC ( model . materialCount * sizeof ( Material ) ) ;
model . meshMaterial = RL_MALLOC ( model . meshCount * sizeof ( int ) ) ;
2019-09-13 12:29:52 +01:00
2019-08-27 10:56:49 +02:00
for ( int i = 0 ; i < model . meshCount ; i + + ) model . meshes [ i ] . vboId = ( unsigned int * ) RL_CALLOC ( MAX_MESH_VBO , sizeof ( unsigned int ) ) ;
2019-05-14 14:06:17 +02:00
for ( int i = 0 ; i < model . materialCount - 1 ; i + + )
{
2019-10-21 08:38:23 -07:00
model . materials [ i ] = LoadMaterialDefault ( ) ;
Color tint = ( Color ) { 255 , 255 , 255 , 255 } ;
2019-05-20 11:13:38 +02:00
const char * texPath = GetDirectoryPath ( fileName ) ;
2019-09-13 12:29:52 +01:00
2019-10-21 08:38:23 -07:00
//Ensure material follows raylib support for PBR (metallic/roughness flow)
2020-02-03 19:26:28 +01:00
if ( data - > materials [ i ] . has_pbr_metallic_roughness )
2019-10-21 17:55:37 +02:00
{
2020-02-22 01:17:30 -08: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 ) ;
2019-09-13 12:29:52 +01:00
2020-02-22 01:17:30 -08:00
model . materials [ i ] . maps [ MAP_ALBEDO ] . color = tint ;
2019-09-13 12:29:52 +01:00
2020-02-03 19:26:28 +01:00
if ( data - > materials [ i ] . pbr_metallic_roughness . base_color_texture . texture )
2019-10-21 17:55:37 +02:00
{
2020-02-22 01:17:30 -08:00
Image albedo = LoadImageFromCgltfImage ( data - > materials [ i ] . pbr_metallic_roughness . base_color_texture . texture - > image , texPath , tint ) ;
model . materials [ i ] . maps [ MAP_ALBEDO ] . texture = LoadTextureFromImage ( albedo ) ;
UnloadImage ( albedo ) ;
2019-05-14 14:06:17 +02:00
}
2019-10-21 08:38:23 -07:00
2020-02-22 01:17:30 -08:00
//Set tint to white after it's been used by Albedo
2019-10-21 17:55:37 +02:00
tint = WHITE ;
2019-10-21 08:38:23 -07:00
2019-10-21 17:55:37 +02:00
if ( data - > materials [ i ] . pbr_metallic_roughness . metallic_roughness_texture . texture )
{
2020-02-22 01:17:30 -08:00
Image metallicRoughness = LoadImageFromCgltfImage ( data - > materials [ i ] . pbr_metallic_roughness . metallic_roughness_texture . texture - > image , texPath , tint ) ;
model . materials [ i ] . maps [ MAP_ROUGHNESS ] . texture = LoadTextureFromImage ( metallicRoughness ) ;
float roughness = data - > materials [ i ] . pbr_metallic_roughness . roughness_factor ;
model . materials [ i ] . maps [ MAP_ROUGHNESS ] . value = roughness ;
float metallic = data - > materials [ i ] . pbr_metallic_roughness . metallic_factor ;
model . materials [ i ] . maps [ MAP_METALNESS ] . value = metallic ;
UnloadImage ( metallicRoughness ) ;
2019-05-14 14:06:17 +02:00
}
2020-02-22 01:17:30 -08:00
2019-09-13 12:29:52 +01:00
2020-02-03 19:26:28 +01:00
if ( data - > materials [ i ] . normal_texture . texture )
2019-10-21 17:55:37 +02:00
{
2020-02-22 01:17:30 -08:00
Image normalImage = LoadImageFromCgltfImage ( data - > materials [ i ] . normal_texture . texture - > image , texPath , tint ) ;
model . materials [ i ] . maps [ MAP_NORMAL ] . texture = LoadTextureFromImage ( normalImage ) ;
UnloadImage ( normalImage ) ;
2019-10-21 08:38:23 -07:00
}
2020-02-03 19:26:28 +01:00
if ( data - > materials [ i ] . occlusion_texture . texture )
2019-10-21 17:55:37 +02:00
{
2020-02-22 01:17:30 -08:00
Image occulsionImage = LoadImageFromCgltfImage ( data - > materials [ i ] . occlusion_texture . texture - > image , texPath , tint ) ;
model . materials [ i ] . maps [ 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 [ 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 [ MAP_EMISSION ] . color = tint ;
UnloadImage ( emissiveImage ) ;
2019-10-21 08:38:23 -07:00
}
2019-05-14 14:06:17 +02:00
}
}
2019-09-13 12:29:52 +01:00
2019-05-20 11:13:38 +02:00
model . materials [ model . materialCount - 1 ] = LoadMaterialDefault ( ) ;
2019-05-14 14:06:17 +02:00
2019-05-20 11:13:38 +02:00
int primitiveIndex = 0 ;
2019-09-13 12:29:52 +01:00
2019-05-14 14:06:17 +02:00
for ( int i = 0 ; i < data - > meshes_count ; i + + )
2019-04-09 13:23:51 +02:00
{
2019-05-14 14:06:17 +02:00
for ( int p = 0 ; p < data - > meshes [ i ] . primitives_count ; p + + )
2019-04-09 13:23:51 +02:00
{
2019-05-14 14:06:17 +02:00
for ( int j = 0 ; j < data - > meshes [ i ] . primitives [ p ] . attributes_count ; j + + )
{
if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_position )
{
2019-05-20 11:13:38 +02:00
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
model . meshes [ primitiveIndex ] . vertexCount = acc - > count ;
model . meshes [ primitiveIndex ] . vertices = RL_MALLOC ( sizeof ( float ) * model . meshes [ primitiveIndex ] . vertexCount * 3 ) ;
2019-05-14 14:06:17 +02:00
2019-05-20 11:13:38 +02:00
LOAD_ACCESSOR ( float , 3 , acc , model . meshes [ primitiveIndex ] . vertices )
2019-05-14 14:06:17 +02:00
}
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_normal )
{
2019-05-20 11:13:38 +02:00
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
model . meshes [ primitiveIndex ] . normals = RL_MALLOC ( sizeof ( float ) * acc - > count * 3 ) ;
2019-05-14 14:06:17 +02:00
2019-05-20 11:13:38 +02:00
LOAD_ACCESSOR ( float , 3 , acc , model . meshes [ primitiveIndex ] . normals )
2019-05-14 14:06:17 +02:00
}
else if ( data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . type = = cgltf_attribute_type_texcoord )
{
2019-05-20 11:13:38 +02:00
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . attributes [ j ] . data ;
2019-09-13 12:29:52 +01:00
2019-05-14 14:06:17 +02:00
if ( acc - > component_type = = cgltf_component_type_r_32f )
{
2019-05-20 11:13:38 +02:00
model . meshes [ primitiveIndex ] . texcoords = RL_MALLOC ( sizeof ( float ) * acc - > count * 2 ) ;
LOAD_ACCESSOR ( float , 2 , acc , model . meshes [ primitiveIndex ] . texcoords )
2019-05-14 14:06:17 +02:00
}
else
{
2019-09-12 16:26:37 +02:00
// TODO: Support normalized unsigned byte/unsigned short texture coordinates
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] Texture coordinates must be float " , fileName ) ;
2019-05-14 14:06:17 +02:00
}
}
}
2019-04-09 13:23:51 +02:00
2019-05-20 11:13:38 +02:00
cgltf_accessor * acc = data - > meshes [ i ] . primitives [ p ] . indices ;
2019-09-13 12:29:52 +01:00
2019-05-14 14:06:17 +02:00
if ( acc )
{
if ( acc - > component_type = = cgltf_component_type_r_16u )
{
2019-05-20 11:13:38 +02:00
model . meshes [ primitiveIndex ] . triangleCount = acc - > count / 3 ;
model . meshes [ primitiveIndex ] . indices = RL_MALLOC ( sizeof ( unsigned short ) * model . meshes [ primitiveIndex ] . triangleCount * 3 ) ;
LOAD_ACCESSOR ( unsigned short , 1 , acc , model . meshes [ primitiveIndex ] . indices )
2019-05-14 14:06:17 +02:00
}
else
{
2019-09-12 16:26:37 +02:00
// TODO: Support unsigned byte/unsigned int
2020-02-03 19:13:24 +01:00
TRACELOG ( LOG_WARNING , " [%s] Indices must be unsigned short " , fileName ) ;
2019-05-14 14:06:17 +02:00
}
}
else
{
2019-05-20 11:13:38 +02:00
// Unindexed mesh
model . meshes [ primitiveIndex ] . triangleCount = model . meshes [ primitiveIndex ] . vertexCount / 3 ;
2019-05-14 14:06:17 +02:00
}
2019-02-21 18:45:19 +01:00
2019-05-14 14:06:17 +02:00
if ( data - > meshes [ i ] . primitives [ p ] . material )
{
2019-05-20 11:13:38 +02:00
// Compute the offset
model . meshMaterial [ primitiveIndex ] = data - > meshes [ i ] . primitives [ p ] . material - data - > materials ;
2019-05-14 14:06:17 +02:00
}
else
{
2019-05-20 11:13:38 +02:00
model . meshMaterial [ primitiveIndex ] = model . materialCount - 1 ; ;
2019-05-14 14:06:17 +02:00
}
2019-09-13 12:29:52 +01:00
2019-05-20 11:13:38 +02:00
primitiveIndex + + ;
2019-04-09 13:23:51 +02:00
}
}
2019-02-21 18:45:19 +01:00
2019-04-04 13:33:54 +02:00
cgltf_free ( data ) ;
2018-09-14 13:00:48 +02:00
}
2020-02-03 19:13:24 +01:00
else TRACELOG ( LOG_WARNING , " [%s] glTF data could not be loaded " , fileName ) ;
2018-09-05 10:59:05 +02:00
2019-05-14 14:06:17 +02:00
RL_FREE ( buffer ) ;
2019-03-29 16:48:23 +01:00
return model ;
2018-09-05 10:59:05 +02:00
}
# endif