Update C sources

This commit is contained in:
Milan Nikolic 2022-08-27 16:00:43 +02:00
parent dd222de786
commit b83dec57b5
No known key found for this signature in database
GPG key ID: 9229D0EAA3AA4E75
14 changed files with 2900 additions and 1289 deletions

View file

@ -6,7 +6,7 @@
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2018-2021 Ahmad Fatoum & Ramon Santamaria (@raysan5) * Copyright (c) 2018-2022 Ahmad Fatoum & Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -26,7 +26,17 @@
**********************************************************************************************/ **********************************************************************************************/
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module: core - Configuration Flags // Module selection - Some modules could be avoided
// Mandatory modules: rcore, rlgl, utils
//------------------------------------------------------------------------------------
#define SUPPORT_MODULE_RSHAPES 1
#define SUPPORT_MODULE_RTEXTURES 1
#define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures
#define SUPPORT_MODULE_RMODELS 1
#define SUPPORT_MODULE_RAUDIO 1
//------------------------------------------------------------------------------------
// Module: rcore - Configuration Flags
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital // Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital
#define SUPPORT_CAMERA_SYSTEM 1 #define SUPPORT_CAMERA_SYSTEM 1
@ -36,8 +46,6 @@
#define SUPPORT_MOUSE_GESTURES 1 #define SUPPORT_MOUSE_GESTURES 1
// Reconfigure standard input to receive key inputs, works with SSH connection. // Reconfigure standard input to receive key inputs, works with SSH connection.
#define SUPPORT_SSH_KEYBOARD_RPI 1 #define SUPPORT_SSH_KEYBOARD_RPI 1
// Draw a mouse pointer on screen
//#define SUPPORT_MOUSE_CURSOR_POINT 1
// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
#define SUPPORT_WINMM_HIGHRES_TIMER 1 #define SUPPORT_WINMM_HIGHRES_TIMER 1
@ -53,8 +61,6 @@
#define SUPPORT_GIF_RECORDING 1 #define SUPPORT_GIF_RECORDING 1
// Support CompressData() and DecompressData() functions // Support CompressData() and DecompressData() functions
#define SUPPORT_COMPRESSION_API 1 #define SUPPORT_COMPRESSION_API 1
// Support saving binary data automatically to a generated storage.data file. This file is managed internally.
#define SUPPORT_DATA_STORAGE 1
// Support automatic generated events, loading and recording of those events when required // Support automatic generated events, loading and recording of those events when required
//#define SUPPORT_EVENTS_AUTOMATION 1 //#define SUPPORT_EVENTS_AUTOMATION 1
// Support custom frame control, only for advance users // Support custom frame control, only for advance users
@ -62,21 +68,19 @@
// Enabling this flag allows manual control of the frame processes, use at your own risk // Enabling this flag allows manual control of the frame processes, use at your own risk
//#define SUPPORT_CUSTOM_FRAME_CONTROL 1 //#define SUPPORT_CUSTOM_FRAME_CONTROL 1
// core: Configuration values // rcore: Configuration values
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
#if defined(__linux__) #define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity
#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
#else
#define MAX_FILEPATH_LENGTH 512 // Maximum length supported for filepaths
#endif
#define MAX_GAMEPADS 4 // Max number of gamepads supported #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
#define MAX_GAMEPAD_AXIS 8 // Max number of axis supported (per gamepad) #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
#define MAX_GAMEPAD_BUTTONS 32 // Max bumber of buttons supported (per gamepad) #define MAX_GAMEPADS 4 // Maximum number of gamepads supported
#define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad)
#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
#define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
#define MAX_KEY_PRESSED_QUEUE 16 // Max number of characters in the key input queue #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
#define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
#define STORAGE_DATA_FILE "storage.data" // Automatic storage filename
#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB #define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB
@ -124,7 +128,7 @@
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module: shapes - Configuration Flags // Module: rshapes - Configuration Flags
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Use QUADS instead of TRIANGLES for drawing when possible // Use QUADS instead of TRIANGLES for drawing when possible
// Some lines-based shapes could still use lines // Some lines-based shapes could still use lines
@ -132,7 +136,7 @@
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module: textures - Configuration Flags // Module: rtextures - Configuration Flags
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Selecte desired fileformats to be supported for image data loading // Selecte desired fileformats to be supported for image data loading
#define SUPPORT_FILEFORMAT_PNG 1 #define SUPPORT_FILEFORMAT_PNG 1
@ -140,6 +144,7 @@
//#define SUPPORT_FILEFORMAT_TGA 1 //#define SUPPORT_FILEFORMAT_TGA 1
#define SUPPORT_FILEFORMAT_JPG 1 #define SUPPORT_FILEFORMAT_JPG 1
#define SUPPORT_FILEFORMAT_GIF 1 #define SUPPORT_FILEFORMAT_GIF 1
#define SUPPORT_FILEFORMAT_QOI 1
//#define SUPPORT_FILEFORMAT_PSD 1 //#define SUPPORT_FILEFORMAT_PSD 1
#define SUPPORT_FILEFORMAT_DDS 1 #define SUPPORT_FILEFORMAT_DDS 1
#define SUPPORT_FILEFORMAT_HDR 1 #define SUPPORT_FILEFORMAT_HDR 1
@ -148,7 +153,7 @@
//#define SUPPORT_FILEFORMAT_PKM 1 //#define SUPPORT_FILEFORMAT_PKM 1
//#define SUPPORT_FILEFORMAT_PVR 1 //#define SUPPORT_FILEFORMAT_PVR 1
// Support image export functionality (.png, .bmp, .tga, .jpg) // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi)
#define SUPPORT_IMAGE_EXPORT 1 #define SUPPORT_IMAGE_EXPORT 1
// Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) // Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)
#define SUPPORT_IMAGE_GENERATION 1 #define SUPPORT_IMAGE_GENERATION 1
@ -158,7 +163,7 @@
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module: text - Configuration Flags // Module: rtext - Configuration Flags
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Default font is loaded on window initialization to be available for the user to render simple text // Default font is loaded on window initialization to be available for the user to render simple text
// NOTE: If enabled, uses external module functions to load default raylib font // NOTE: If enabled, uses external module functions to load default raylib font
@ -171,7 +176,7 @@
// If not defined, still some functions are supported: TextLength(), TextFormat() // If not defined, still some functions are supported: TextLength(), TextFormat()
#define SUPPORT_TEXT_MANIPULATION 1 #define SUPPORT_TEXT_MANIPULATION 1
// text: Configuration values // rtext: Configuration values
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
// TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit() // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
@ -179,7 +184,7 @@
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module: models - Configuration Flags // Module: rmodels - Configuration Flags
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Selected desired model fileformats to be supported for loading // Selected desired model fileformats to be supported for loading
#define SUPPORT_FILEFORMAT_OBJ 1 #define SUPPORT_FILEFORMAT_OBJ 1
@ -187,17 +192,18 @@
#define SUPPORT_FILEFORMAT_IQM 1 #define SUPPORT_FILEFORMAT_IQM 1
#define SUPPORT_FILEFORMAT_GLTF 1 #define SUPPORT_FILEFORMAT_GLTF 1
#define SUPPORT_FILEFORMAT_VOX 1 #define SUPPORT_FILEFORMAT_VOX 1
#define SUPPORT_FILEFORMAT_M3D 1
// Support procedural mesh generation functions, uses external par_shapes.h library // Support procedural mesh generation functions, uses external par_shapes.h library
// NOTE: Some generated meshes DO NOT include generated texture coordinates // NOTE: Some generated meshes DO NOT include generated texture coordinates
#define SUPPORT_MESH_GENERATION 1 #define SUPPORT_MESH_GENERATION 1
// models: Configuration values // rmodels: Configuration values
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
#define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported #define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported
#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh #define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module: audio - Configuration Flags // Module: raudio - Configuration Flags
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Desired audio fileformats to be supported for loading // Desired audio fileformats to be supported for loading
#define SUPPORT_FILEFORMAT_WAV 1 #define SUPPORT_FILEFORMAT_WAV 1
@ -207,7 +213,7 @@
#define SUPPORT_FILEFORMAT_MP3 1 #define SUPPORT_FILEFORMAT_MP3 1
//#define SUPPORT_FILEFORMAT_FLAC 1 //#define SUPPORT_FILEFORMAT_FLAC 1
// audio: Configuration values // raudio: Configuration values
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
#define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit) #define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit)
#define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo #define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/********************************************************************************************** /**********************************************************************************************
* *
* raylib v4.0 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * raylib v4.2 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
* *
* FEATURES: * FEATURES:
* - NO external dependencies, all required libraries included with raylib * - NO external dependencies, all required libraries included with raylib
@ -33,8 +33,8 @@
* *
* OPTIONAL DEPENDENCIES (included): * OPTIONAL DEPENDENCIES (included):
* [rcore] msf_gif (Miles Fogle) for GIF recording * [rcore] msf_gif (Miles Fogle) for GIF recording
* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorythm * [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm
* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorythm * [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm
* [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) * [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...)
* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) * [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)
* [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms * [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms
@ -43,6 +43,7 @@
* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation * [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation
* [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) * [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL)
* [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF) * [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF)
* [rmodels] Model3D (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d)
* [raudio] dr_wav (David Reid) for WAV audio file loading * [raudio] dr_wav (David Reid) for WAV audio file loading
* [raudio] dr_flac (David Reid) for FLAC audio file loading * [raudio] dr_flac (David Reid) for FLAC audio file loading
* [raudio] dr_mp3 (David Reid) for MP3 audio file loading * [raudio] dr_mp3 (David Reid) for MP3 audio file loading
@ -56,7 +57,7 @@
* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software: * BSD-like license that allows static linking with closed source software:
* *
* Copyright (c) 2013-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2013-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -80,12 +81,15 @@
#include <stdarg.h> // Required for: va_list - Only used by TraceLogCallback #include <stdarg.h> // Required for: va_list - Only used by TraceLogCallback
#define RAYLIB_VERSION "4.0" #define RAYLIB_VERSION "4.2"
// Function specifiers in case library is build/used as a shared library (Windows) // Function specifiers in case library is build/used as a shared library (Windows)
// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
#if defined(_WIN32) #if defined(_WIN32)
#if defined(BUILD_LIBTYPE_SHARED) #if defined(BUILD_LIBTYPE_SHARED)
#if defined(__TINYC__)
#define __declspec(x) __attribute__((x))
#endif
#define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
#elif defined(USE_LIBTYPE_SHARED) #elif defined(USE_LIBTYPE_SHARED)
#define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
@ -110,6 +114,7 @@
#endif #endif
// Allow custom memory allocators // Allow custom memory allocators
// NOTE: Require recompiling raylib sources
#ifndef RL_MALLOC #ifndef RL_MALLOC
#define RL_MALLOC(sz) malloc(sz) #define RL_MALLOC(sz) malloc(sz)
#endif #endif
@ -177,10 +182,10 @@
// Structures Definition // Structures Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Boolean type // Boolean type
#if defined(__STDC__) && __STDC_VERSION__ >= 199901L #if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
#include <stdbool.h> #include <stdbool.h>
#elif !defined(__cplusplus) && !defined(bool) #elif !defined(__cplusplus) && !defined(bool)
typedef enum bool { false, true } bool; typedef enum bool { false = 0, true = !false } bool;
#define RL_BOOL_TYPE #define RL_BOOL_TYPE
#endif #endif
@ -322,7 +327,7 @@ typedef struct Mesh {
// Vertex attributes data // Vertex attributes data
float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0)
float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
float *texcoords2; // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) float *texcoords2; // Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5)
float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
@ -425,11 +430,15 @@ typedef struct Wave {
void *data; // Buffer data pointer void *data; // Buffer data pointer
} Wave; } Wave;
// Opaque structs declaration
// NOTE: Actual structs are defined internally in raudio module
typedef struct rAudioBuffer rAudioBuffer; typedef struct rAudioBuffer rAudioBuffer;
typedef struct rAudioProcessor rAudioProcessor;
// AudioStream, custom audio stream // AudioStream, custom audio stream
typedef struct AudioStream { typedef struct AudioStream {
rAudioBuffer *buffer; // Pointer to internal data used by the audio system rAudioBuffer *buffer; // Pointer to internal data used by the audio system
rAudioProcessor *processor; // Pointer to internal data processor, useful for audio effects
unsigned int sampleRate; // Frequency (samples per second) unsigned int sampleRate; // Frequency (samples per second)
unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
@ -478,6 +487,13 @@ typedef struct VrStereoConfig {
float scaleIn[2]; // VR distortion scale in float scaleIn[2]; // VR distortion scale in
} VrStereoConfig; } VrStereoConfig;
// File path list
typedef struct FilePathList {
unsigned int capacity; // Filepaths max entries
unsigned int count; // Filepaths entries count
char **paths; // Filepaths entries
} FilePathList;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Enumerators Definition // Enumerators Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -497,6 +513,7 @@ typedef enum {
FLAG_WINDOW_ALWAYS_RUN = 0x00000100, // Set to allow windows running while minimized FLAG_WINDOW_ALWAYS_RUN = 0x00000100, // Set to allow windows running while minimized
FLAG_WINDOW_TRANSPARENT = 0x00000010, // Set to allow transparent framebuffer FLAG_WINDOW_TRANSPARENT = 0x00000010, // Set to allow transparent framebuffer
FLAG_WINDOW_HIGHDPI = 0x00002000, // Set to support HighDPI FLAG_WINDOW_HIGHDPI = 0x00002000, // Set to support HighDPI
FLAG_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED
FLAG_MSAA_4X_HINT = 0x00000020, // Set to try enabling MSAA 4X FLAG_MSAA_4X_HINT = 0x00000020, // Set to try enabling MSAA 4X
FLAG_INTERLACED_HINT = 0x00010000 // Set to try enabling interlaced video format (for V3D) FLAG_INTERLACED_HINT = 0x00010000 // Set to try enabling interlaced video format (for V3D)
} ConfigFlags; } ConfigFlags;
@ -799,7 +816,7 @@ typedef enum {
// NOTE 1: Filtering considers mipmaps if available in the texture // NOTE 1: Filtering considers mipmaps if available in the texture
// NOTE 2: Filter is accordingly set for minification and magnification // NOTE 2: Filter is accordingly set for minification and magnification
typedef enum { typedef enum {
TEXTURE_FILTER_POINT = 0, // No filter, just pixel aproximation TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation
TEXTURE_FILTER_BILINEAR, // Linear filtering TEXTURE_FILTER_BILINEAR, // Linear filtering
TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps)
TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x
@ -839,7 +856,8 @@ typedef enum {
BLEND_MULTIPLIED, // Blend textures multiplying colors BLEND_MULTIPLIED, // Blend textures multiplying colors
BLEND_ADD_COLORS, // Blend textures adding colors (alternative) BLEND_ADD_COLORS, // Blend textures adding colors (alternative)
BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative)
BLEND_CUSTOM // Belnd textures using custom src/dst factors (use rlSetBlendMode()) BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha
BLEND_CUSTOM // Blend textures using custom src/dst factors (use rlSetBlendMode())
} BlendMode; } BlendMode;
// Gesture // Gesture
@ -913,7 +931,7 @@ RLAPI bool IsWindowMaximized(void); // Check if wi
RLAPI bool IsWindowFocused(void); // Check if window is currently focused (only PLATFORM_DESKTOP) RLAPI bool IsWindowFocused(void); // Check if window is currently focused (only PLATFORM_DESKTOP)
RLAPI bool IsWindowResized(void); // Check if window has been resized last frame RLAPI bool IsWindowResized(void); // Check if window has been resized last frame
RLAPI bool IsWindowState(unsigned int flag); // Check if one specific window flag is enabled RLAPI bool IsWindowState(unsigned int flag); // Check if one specific window flag is enabled
RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP)
RLAPI void ClearWindowState(unsigned int flags); // Clear window configuration state flags RLAPI void ClearWindowState(unsigned int flags); // Clear window configuration state flags
RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP)
RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable (only PLATFORM_DESKTOP)
@ -925,14 +943,17 @@ RLAPI void SetWindowPosition(int x, int y); // Set window
RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode)
RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)
RLAPI void SetWindowSize(int width, int height); // Set window dimensions RLAPI void SetWindowSize(int width, int height); // Set window dimensions
RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP)
RLAPI void *GetWindowHandle(void); // Get native window handle RLAPI void *GetWindowHandle(void); // Get native window handle
RLAPI int GetScreenWidth(void); // Get current screen width RLAPI int GetScreenWidth(void); // Get current screen width
RLAPI int GetScreenHeight(void); // Get current screen height RLAPI int GetScreenHeight(void); // Get current screen height
RLAPI int GetRenderWidth(void); // Get current render width (it considers HiDPI)
RLAPI int GetRenderHeight(void); // Get current render height (it considers HiDPI)
RLAPI int GetMonitorCount(void); // Get number of connected monitors RLAPI int GetMonitorCount(void); // Get number of connected monitors
RLAPI int GetCurrentMonitor(void); // Get current connected monitor RLAPI int GetCurrentMonitor(void); // Get current connected monitor
RLAPI Vector2 GetMonitorPosition(int monitor); // Get specified monitor position RLAPI Vector2 GetMonitorPosition(int monitor); // Get specified monitor position
RLAPI int GetMonitorWidth(int monitor); // Get specified monitor width (max available by monitor) RLAPI int GetMonitorWidth(int monitor); // Get specified monitor width (current video mode used by monitor)
RLAPI int GetMonitorHeight(int monitor); // Get specified monitor height (max available by monitor) RLAPI int GetMonitorHeight(int monitor); // Get specified monitor height (current video mode used by monitor)
RLAPI int GetMonitorPhysicalWidth(int monitor); // Get specified monitor physical width in millimetres RLAPI int GetMonitorPhysicalWidth(int monitor); // Get specified monitor physical width in millimetres
RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specified monitor physical height in millimetres RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specified monitor physical height in millimetres
RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate
@ -941,6 +962,8 @@ RLAPI Vector2 GetWindowScaleDPI(void); // Get window
RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor
RLAPI void SetClipboardText(const char *text); // Set clipboard text content RLAPI void SetClipboardText(const char *text); // Set clipboard text content
RLAPI const char *GetClipboardText(void); // Get clipboard text content RLAPI const char *GetClipboardText(void); // Get clipboard text content
RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling
RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling
// Custom frame control functions // Custom frame control functions
// NOTE: Those functions are intended for advance users that want full control over the frame processing // NOTE: Those functions are intended for advance users that want full control over the frame processing
@ -948,7 +971,7 @@ RLAPI const char *GetClipboardText(void); // Get clipboa
// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL // To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL
RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing)
RLAPI void PollInputEvents(void); // Register all input events RLAPI void PollInputEvents(void); // Register all input events
RLAPI void WaitTime(float ms); // Wait for some milliseconds (halt program execution) RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution)
// Cursor-related functions // Cursor-related functions
RLAPI void ShowCursor(void); // Shows cursor RLAPI void ShowCursor(void); // Shows cursor
@ -998,9 +1021,9 @@ RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Get a ray t
RLAPI Matrix GetCameraMatrix(Camera camera); // Get camera transform matrix (view matrix) RLAPI Matrix GetCameraMatrix(Camera camera); // Get camera transform matrix (view matrix)
RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix
RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Get the screen space position for a 3d world space position RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Get the screen space position for a 3d world space position
RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position
RLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position RLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position
RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the screen space position for a 2d camera world space position RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the screen space position for a 2d camera world space position
RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position
// Timing-related functions // Timing-related functions
RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum)
@ -1020,6 +1043,8 @@ RLAPI void *MemAlloc(int size); // Internal me
RLAPI void *MemRealloc(void *ptr, int size); // Internal memory reallocator RLAPI void *MemRealloc(void *ptr, int size); // Internal memory reallocator
RLAPI void MemFree(void *ptr); // Internal memory free RLAPI void MemFree(void *ptr); // Internal memory free
RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available)
// Set custom callbacks // Set custom callbacks
// WARNING: Callbacks setup is intended for advance users // WARNING: Callbacks setup is intended for advance users
RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log
@ -1032,37 +1057,36 @@ RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom
RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read)
RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData()
RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write), returns true on success RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write), returns true on success
RLAPI bool ExportDataAsCode(const char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success
RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string
RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText()
RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success
RLAPI bool FileExists(const char *fileName); // Check if file exists RLAPI bool FileExists(const char *fileName); // Check if file exists
RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists
RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav) RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav)
RLAPI int GetFileLength(const char *fileName); // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h)
RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png') RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png')
RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string
RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string)
RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string) RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string)
RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string) RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string)
RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string)
RLAPI char **GetDirectoryFiles(const char *dirPath, int *count); // Get filenames in a directory path (memory should be freed) RLAPI const char *GetApplicationDirectory(void); // Get the directory if the running application (uses static string)
RLAPI void ClearDirectoryFiles(void); // Clear directory files paths buffers (free memory)
RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success
RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory
RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths
RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan
RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths
RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window
RLAPI char **GetDroppedFiles(int *count); // Get dropped files names (memory should be freed) RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths
RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory) RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths
RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time)
// Compression/Encoding functionality // Compression/Encoding functionality
RLAPI unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLength); // Compress data (DEFLATE algorithm) RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree()
RLAPI unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength); // Decompress data (DEFLATE algorithm) RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree()
RLAPI char *EncodeDataBase64(const unsigned char *data, int dataLength, int *outputLength); // Encode data to Base64 string RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree()
RLAPI unsigned char *DecodeDataBase64(unsigned char *data, int *outputLength); // Decode Base64 string data RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree()
// Persistent storage management
RLAPI bool SaveStorageValue(unsigned int position, int value); // Save integer value to storage file (to defined position), returns true on success
RLAPI int LoadStorageValue(unsigned int position); // Load integer value from storage file (from defined position)
RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Input Handling Functions (Module: core) // Input Handling Functions (Module: core)
@ -1101,7 +1125,8 @@ RLAPI Vector2 GetMouseDelta(void); // Get mouse delta
RLAPI void SetMousePosition(int x, int y); // Set mouse position XY RLAPI void SetMousePosition(int x, int y); // Set mouse position XY
RLAPI void SetMouseOffset(int offsetX, int offsetY); // Set mouse offset RLAPI void SetMouseOffset(int offsetX, int offsetY); // Set mouse offset
RLAPI void SetMouseScale(float scaleX, float scaleY); // Set mouse scaling RLAPI void SetMouseScale(float scaleX, float scaleY); // Set mouse scaling
RLAPI float GetMouseWheelMove(void); // Get mouse wheel movement Y RLAPI float GetMouseWheelMove(void); // Get mouse wheel movement for X or Y, whichever is larger
RLAPI Vector2 GetMouseWheelMoveV(void); // Get mouse wheel movement for both X and Y
RLAPI void SetMouseCursor(int cursor); // Set mouse cursor RLAPI void SetMouseCursor(int cursor); // Set mouse cursor
// Input-related functions: touch // Input-related functions: touch
@ -1316,13 +1341,14 @@ RLAPI int GetPixelDataSize(int width, int height, int format); // G
// Font loading/unloading functions // Font loading/unloading functions
RLAPI Font GetFontDefault(void); // Get the default Font RLAPI Font GetFontDefault(void); // Get the default Font
RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM)
RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // Load font from file with extended parameters RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set
RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style)
RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'
RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type); // Load font data for further use RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type); // Load font data for further use
RLAPI Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **recs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info RLAPI Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **recs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info
RLAPI void UnloadFontData(GlyphInfo *chars, int glyphCount); // Unload font chars info data (RAM) RLAPI void UnloadFontData(GlyphInfo *chars, int glyphCount); // Unload font chars info data (RAM)
RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM) RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM)
RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success
// Text drawing functions // Text drawing functions
RLAPI void DrawFPS(int posX, int posY); // Draw current FPS RLAPI void DrawFPS(int posX, int posY); // Draw current FPS
@ -1330,6 +1356,7 @@ RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color co
RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters
RLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation) RLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation)
RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint) RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint)
RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint)
// Text font info functions // Text font info functions
RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
@ -1344,7 +1371,7 @@ RLAPI void UnloadCodepoints(int *codepoints); // Unload
RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string
RLAPI int GetCodepoint(const char *text, int *bytesProcessed); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure RLAPI int GetCodepoint(const char *text, int *bytesProcessed); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
RLAPI const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) RLAPI const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode one codepoint into UTF-8 byte array (array length returned as parameter)
RLAPI char *TextCodepointsToUTF8(int *codepoints, int length); // Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!) RLAPI char *TextCodepointsToUTF8(const int *codepoints, int length); // Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!)
// Text strings management functions (no UTF-8 strings, only byte chars) // Text strings management functions (no UTF-8 strings, only byte chars)
// NOTE: Some strings allocate memory internally for returned strings, just be careful! // NOTE: Some strings allocate memory internally for returned strings, just be careful!
@ -1414,14 +1441,13 @@ RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source,
// Mesh management functions // Mesh management functions
RLAPI void UploadMesh(Mesh *mesh, bool dynamic); // Upload mesh vertex data in GPU and provide VAO/VBO ids RLAPI void UploadMesh(Mesh *mesh, bool dynamic); // Upload mesh vertex data in GPU and provide VAO/VBO ids
RLAPI void UpdateMeshBuffer(Mesh mesh, int index, void *data, int dataSize, int offset); // Update mesh vertex data in GPU for a specific buffer index RLAPI void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset); // Update mesh vertex data in GPU for a specific buffer index
RLAPI void UnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU RLAPI void UnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU
RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform
RLAPI void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms RLAPI void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms
RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success
RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits
RLAPI void GenMeshTangents(Mesh *mesh); // Compute mesh tangents RLAPI void GenMeshTangents(Mesh *mesh); // Compute mesh tangents
RLAPI void GenMeshBinormals(Mesh *mesh); // Compute mesh binormals
// Mesh generation functions // Mesh generation functions
RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh
@ -1456,7 +1482,6 @@ RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2);
RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere
RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere
RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box
RLAPI RayCollision GetRayCollisionModel(Ray ray, Model model); // Get collision info between ray and model
RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh
RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle
RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad
@ -1464,6 +1489,7 @@ RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Audio Loading and Playing Functions (Module: audio) // Audio Loading and Playing Functions (Module: audio)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
typedef void (*AudioCallback)(void *bufferData, unsigned int frames);
// Audio device management functions // Audio device management functions
RLAPI void InitAudioDevice(void); // Initialize audio device and context RLAPI void InitAudioDevice(void); // Initialize audio device and context
@ -1493,15 +1519,16 @@ RLAPI int GetSoundsPlaying(void); // Get num
RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level)
RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (0.5 is center)
RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave
RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range
RLAPI float *LoadWaveSamples(Wave wave); // Load samples data from wave as a floats array RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format
RLAPI float *LoadWaveSamples(Wave wave); // Load samples data from wave as a 32bit float data array
RLAPI void UnloadWaveSamples(float *samples); // Unload samples data loaded with LoadWaveSamples() RLAPI void UnloadWaveSamples(float *samples); // Unload samples data loaded with LoadWaveSamples()
// Music management functions // Music management functions
RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file
RLAPI Music LoadMusicStreamFromMemory(const char *fileType, unsigned char *data, int dataSize); // Load music stream from data RLAPI Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize); // Load music stream from data
RLAPI void UnloadMusicStream(Music music); // Unload music stream RLAPI void UnloadMusicStream(Music music); // Unload music stream
RLAPI void PlayMusicStream(Music music); // Start music playing RLAPI void PlayMusicStream(Music music); // Start music playing
RLAPI bool IsMusicStreamPlaying(Music music); // Check if music is playing RLAPI bool IsMusicStreamPlaying(Music music); // Check if music is playing
@ -1512,6 +1539,7 @@ RLAPI void ResumeMusicStream(Music music); // Resume
RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds) RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds)
RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level)
RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level)
RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (0.5 is center)
RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds) RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds)
RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds)
@ -1527,7 +1555,12 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check i
RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream
RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level)
RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level)
RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered)
RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams
RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data
RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream
RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream
#if defined(__cplusplus) #if defined(__cplusplus)
} }

View file

@ -18,14 +18,14 @@
* - Functions are always self-contained, no function use another raymath function inside, * - Functions are always self-contained, no function use another raymath function inside,
* required code is directly re-implemented inside * required code is directly re-implemented inside
* - Functions input parameters are always received by value (2 unavoidable exceptions) * - Functions input parameters are always received by value (2 unavoidable exceptions)
* - Functions use always a "result" anmed variable for return * - Functions use always a "result" variable for return
* - Functions are always defined inline * - Functions are always defined inline
* - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) * - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience)
* *
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2015-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2015-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -77,6 +77,10 @@
#define PI 3.14159265358979323846f #define PI 3.14159265358979323846f
#endif #endif
#ifndef EPSILON
#define EPSILON 0.000001f
#endif
#ifndef DEG2RAD #ifndef DEG2RAD
#define DEG2RAD (PI/180.0f) #define DEG2RAD (PI/180.0f)
#endif #endif
@ -154,7 +158,7 @@ typedef struct float16 {
float v[16]; float v[16];
} float16; } float16;
#include <math.h> // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), fminf(), fmaxf(), fabs() #include <math.h> // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabs()
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definition - Utils math // Module Functions Definition - Utils math
@ -194,6 +198,22 @@ RMAPI float Remap(float value, float inputStart, float inputEnd, float outputSta
return result; return result;
} }
// Wrap input value from min to max
RMAPI float Wrap(float value, float min, float max)
{
float result = value - (max - min)*floorf((value - min)/(max - min));
return result;
}
// Check whether two given floats are almost equal
RMAPI int FloatEquals(float x, float y)
{
int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y))));
return result;
}
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definition - Vector2 math // Module Functions Definition - Vector2 math
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -278,12 +298,18 @@ RMAPI float Vector2Distance(Vector2 v1, Vector2 v2)
return result; return result;
} }
// Calculate angle from two vectors in X-axis // Calculate square distance between two vectors
RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2)
{
float result = ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y));
return result;
}
// Calculate angle from two vectors
RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) RMAPI float Vector2Angle(Vector2 v1, Vector2 v2)
{ {
float result = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI); float result = atan2f(v2.y, v2.x) - atan2f(v1.y, v1.x);
if (result < 0) result += 360.0f;
return result; return result;
} }
@ -328,13 +354,29 @@ RMAPI Vector2 Vector2Normalize(Vector2 v)
if (length > 0) if (length > 0)
{ {
result.x = v.x*1.0f/length; float ilength = 1.0f/length;
result.y = v.y*1.0f/length; result.x = v.x*ilength;
result.y = v.y*ilength;
} }
return result; return result;
} }
// Transforms a Vector2 by a given Matrix
RMAPI Vector2 Vector2Transform(Vector2 v, Matrix mat)
{
Vector2 result = { 0 };
float x = v.x;
float y = v.y;
float z = 0;
result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12;
result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13;
return result;
}
// Calculate linear interpolation between two vectors // Calculate linear interpolation between two vectors
RMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount) RMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount)
{ {
@ -364,8 +406,11 @@ RMAPI Vector2 Vector2Rotate(Vector2 v, float angle)
{ {
Vector2 result = { 0 }; Vector2 result = { 0 };
result.x = v.x*cosf(angle) - v.y*sinf(angle); float cosres = cosf(angle);
result.y = v.x*sinf(angle) + v.y*cosf(angle); float sinres = sinf(angle);
result.x = v.x*cosres - v.y*sinres;
result.y = v.x*sinres + v.y*cosres;
return result; return result;
} }
@ -389,6 +434,62 @@ RMAPI Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance)
return result; return result;
} }
// Invert the given vector
RMAPI Vector2 Vector2Invert(Vector2 v)
{
Vector2 result = { 1.0f/v.x, 1.0f/v.y };
return result;
}
// Clamp the components of the vector between
// min and max values specified by the given vectors
RMAPI Vector2 Vector2Clamp(Vector2 v, Vector2 min, Vector2 max)
{
Vector2 result = { 0 };
result.x = fminf(max.x, fmaxf(min.x, v.x));
result.y = fminf(max.y, fmaxf(min.y, v.y));
return result;
}
// Clamp the magnitude of the vector between two min and max values
RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max)
{
Vector2 result = v;
float length = (v.x*v.x) + (v.y*v.y);
if (length > 0.0f)
{
length = sqrtf(length);
if (length < min)
{
float scale = min/length;
result.x = v.x*scale;
result.y = v.y*scale;
}
else if (length > max)
{
float scale = max/length;
result.x = v.x*scale;
result.y = v.y*scale;
}
}
return result;
}
// Check whether two given vectors are almost equal
RMAPI int Vector2Equals(Vector2 p, Vector2 q)
{
int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&
((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y)))));
return result;
}
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definition - Vector3 math // Module Functions Definition - Vector3 math
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -473,14 +574,14 @@ RMAPI Vector3 Vector3Perpendicular(Vector3 v)
float min = (float) fabs(v.x); float min = (float) fabs(v.x);
Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f};
if (fabs(v.y) < min) if (fabsf(v.y) < min)
{ {
min = (float) fabs(v.y); min = (float) fabs(v.y);
Vector3 tmp = {0.0f, 1.0f, 0.0f}; Vector3 tmp = {0.0f, 1.0f, 0.0f};
cardinalAxis = tmp; cardinalAxis = tmp;
} }
if (fabs(v.z) < min) if (fabsf(v.z) < min)
{ {
Vector3 tmp = {0.0f, 0.0f, 1.0f}; Vector3 tmp = {0.0f, 0.0f, 1.0f};
cardinalAxis = tmp; cardinalAxis = tmp;
@ -531,17 +632,28 @@ RMAPI float Vector3Distance(Vector3 v1, Vector3 v2)
return result; return result;
} }
// Calculate angle between two vectors in XY and XZ // Calculate square distance between two vectors
RMAPI Vector2 Vector3Angle(Vector3 v1, Vector3 v2) RMAPI float Vector3DistanceSqr(Vector3 v1, Vector3 v2)
{ {
Vector2 result = { 0 }; float result = 0.0f;
float dx = v2.x - v1.x; float dx = v2.x - v1.x;
float dy = v2.y - v1.y; float dy = v2.y - v1.y;
float dz = v2.z - v1.z; float dz = v2.z - v1.z;
result = dx*dx + dy*dy + dz*dz;
result.x = atan2f(dx, dz); // Angle in XZ return result;
result.y = atan2f(dy, sqrtf(dx*dx + dz*dz)); // Angle in XY }
// Calculate angle between two vectors
RMAPI float Vector3Angle(Vector3 v1, Vector3 v2)
{
float result = 0.0f;
Vector3 cross = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x };
float len = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z);
float dot = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
result = atan2f(len, dot);
return result; return result;
} }
@ -641,6 +753,58 @@ RMAPI Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q)
return result; return result;
} }
// Rotates a vector around an axis
RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle)
{
// Using Euler-Rodrigues Formula
// Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula
Vector3 result = v;
// Vector3Normalize(axis);
float length = sqrtf(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z);
if (length == 0.0f) length = 1.0f;
float ilength = 1.0f / length;
axis.x *= ilength;
axis.y *= ilength;
axis.z *= ilength;
angle /= 2.0f;
float a = sinf(angle);
float b = axis.x * a;
float c = axis.y * a;
float d = axis.z * a;
a = cosf(angle);
Vector3 w = { b, c, d };
// Vector3CrossProduct(w, v)
Vector3 wv = { w.y * v.z - w.z * v.y, w.z * v.x - w.x * v.z, w.x * v.y - w.y * v.x };
// Vector3CrossProduct(w, wv)
Vector3 wwv = { w.y * wv.z - w.z * wv.y, w.z * wv.x - w.x * wv.z, w.x * wv.y - w.y * wv.x };
// Vector3Scale(wv, 2 * a)
a *= 2;
wv.x *= a;
wv.y *= a;
wv.z *= a;
// Vector3Scale(wwv, 2)
wwv.x *= 2;
wwv.y *= 2;
wwv.z *= 2;
result.x += wv.x;
result.y += wv.y;
result.z += wv.z;
result.x += wwv.x;
result.y += wwv.y;
result.z += wwv.z;
return result;
}
// Calculate linear interpolation between two vectors // Calculate linear interpolation between two vectors
RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount)
{ {
@ -815,6 +979,92 @@ RMAPI float3 Vector3ToFloatV(Vector3 v)
return buffer; return buffer;
} }
// Invert the given vector
RMAPI Vector3 Vector3Invert(Vector3 v)
{
Vector3 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z };
return result;
}
// Clamp the components of the vector between
// min and max values specified by the given vectors
RMAPI Vector3 Vector3Clamp(Vector3 v, Vector3 min, Vector3 max)
{
Vector3 result = { 0 };
result.x = fminf(max.x, fmaxf(min.x, v.x));
result.y = fminf(max.y, fmaxf(min.y, v.y));
result.z = fminf(max.z, fmaxf(min.z, v.z));
return result;
}
// Clamp the magnitude of the vector between two values
RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max)
{
Vector3 result = v;
float length = (v.x*v.x) + (v.y*v.y) + (v.z*v.z);
if (length > 0.0f)
{
length = sqrtf(length);
if (length < min)
{
float scale = min/length;
result.x = v.x*scale;
result.y = v.y*scale;
result.z = v.z*scale;
}
else if (length > max)
{
float scale = max/length;
result.x = v.x*scale;
result.y = v.y*scale;
result.z = v.z*scale;
}
}
return result;
}
// Check whether two given vectors are almost equal
RMAPI int Vector3Equals(Vector3 p, Vector3 q)
{
int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&
((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) &&
((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z)))));
return result;
}
// Compute the direction of a refracted ray where v specifies the
// normalized direction of the incoming ray, n specifies the
// normalized normal vector of the interface of two optical media,
// and r specifies the ratio of the refractive index of the medium
// from where the ray comes to the refractive index of the medium
// on the other side of the surface
RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r)
{
Vector3 result = { 0 };
float dot = v.x*n.x + v.y*n.y + v.z*n.z;
float d = 1.0f - r*r*(1.0f - dot*dot);
if (d >= 0.0f)
{
d = sqrtf(d);
v.x = r*v.x - (r*dot + d)*n.x;
v.y = r*v.y - (r*dot + d)*n.y;
v.z = r*v.z - (r*dot + d)*n.z;
result = v;
}
return result;
}
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definition - Matrix math // Module Functions Definition - Matrix math
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -920,45 +1170,6 @@ RMAPI Matrix MatrixInvert(Matrix mat)
return result; return result;
} }
// Normalize provided matrix
RMAPI Matrix MatrixNormalize(Matrix mat)
{
Matrix result = { 0 };
// Cache the matrix values (speed optimization)
float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3;
float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7;
float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11;
float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15;
// MatrixDeterminant(mat)
float det = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 +
a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 +
a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 +
a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 +
a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 +
a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33;
result.m0 = mat.m0/det;
result.m1 = mat.m1/det;
result.m2 = mat.m2/det;
result.m3 = mat.m3/det;
result.m4 = mat.m4/det;
result.m5 = mat.m5/det;
result.m6 = mat.m6/det;
result.m7 = mat.m7/det;
result.m8 = mat.m8/det;
result.m9 = mat.m9/det;
result.m10 = mat.m10/det;
result.m11 = mat.m11/det;
result.m12 = mat.m12/det;
result.m13 = mat.m13/det;
result.m14 = mat.m14/det;
result.m15 = mat.m15/det;
return result;
}
// Get identity matrix // Get identity matrix
RMAPI Matrix MatrixIdentity(void) RMAPI Matrix MatrixIdentity(void)
{ {
@ -1102,7 +1313,8 @@ RMAPI Matrix MatrixRotate(Vector3 axis, float angle)
return result; return result;
} }
// Get x-rotation matrix (angle in radians) // Get x-rotation matrix
// NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateX(float angle) RMAPI Matrix MatrixRotateX(float angle)
{ {
Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,
@ -1114,14 +1326,15 @@ RMAPI Matrix MatrixRotateX(float angle)
float sinres = sinf(angle); float sinres = sinf(angle);
result.m5 = cosres; result.m5 = cosres;
result.m6 = -sinres; result.m6 = sinres;
result.m9 = sinres; result.m9 = -sinres;
result.m10 = cosres; result.m10 = cosres;
return result; return result;
} }
// Get y-rotation matrix (angle in radians) // Get y-rotation matrix
// NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateY(float angle) RMAPI Matrix MatrixRotateY(float angle)
{ {
Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,
@ -1133,14 +1346,15 @@ RMAPI Matrix MatrixRotateY(float angle)
float sinres = sinf(angle); float sinres = sinf(angle);
result.m0 = cosres; result.m0 = cosres;
result.m2 = sinres; result.m2 = -sinres;
result.m8 = -sinres; result.m8 = sinres;
result.m10 = cosres; result.m10 = cosres;
return result; return result;
} }
// Get z-rotation matrix (angle in radians) // Get z-rotation matrix
// NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateZ(float angle) RMAPI Matrix MatrixRotateZ(float angle)
{ {
Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,
@ -1152,74 +1366,76 @@ RMAPI Matrix MatrixRotateZ(float angle)
float sinres = sinf(angle); float sinres = sinf(angle);
result.m0 = cosres; result.m0 = cosres;
result.m1 = -sinres; result.m1 = sinres;
result.m4 = sinres; result.m4 = -sinres;
result.m5 = cosres; result.m5 = cosres;
return result; return result;
} }
// Get xyz-rotation matrix (angles in radians) // Get xyz-rotation matrix
RMAPI Matrix MatrixRotateXYZ(Vector3 ang) // NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateXYZ(Vector3 angle)
{ {
Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity()
float cosz = cosf(-ang.z); float cosz = cosf(-angle.z);
float sinz = sinf(-ang.z); float sinz = sinf(-angle.z);
float cosy = cosf(-ang.y); float cosy = cosf(-angle.y);
float siny = sinf(-ang.y); float siny = sinf(-angle.y);
float cosx = cosf(-ang.x); float cosx = cosf(-angle.x);
float sinx = sinf(-ang.x); float sinx = sinf(-angle.x);
result.m0 = cosz*cosy; result.m0 = cosz*cosy;
result.m4 = (cosz*siny*sinx) - (sinz*cosx); result.m1 = (cosz*siny*sinx) - (sinz*cosx);
result.m8 = (cosz*siny*cosx) + (sinz*sinx); result.m2 = (cosz*siny*cosx) + (sinz*sinx);
result.m1 = sinz*cosy; result.m4 = sinz*cosy;
result.m5 = (sinz*siny*sinx) + (cosz*cosx); result.m5 = (sinz*siny*sinx) + (cosz*cosx);
result.m9 = (sinz*siny*cosx) - (cosz*sinx); result.m6 = (sinz*siny*cosx) - (cosz*sinx);
result.m2 = -siny; result.m8 = -siny;
result.m6 = cosy*sinx; result.m9 = cosy*sinx;
result.m10= cosy*cosx; result.m10= cosy*cosx;
return result; return result;
} }
// Get zyx-rotation matrix (angles in radians) // Get zyx-rotation matrix
RMAPI Matrix MatrixRotateZYX(Vector3 ang) // NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateZYX(Vector3 angle)
{ {
Matrix result = { 0 }; Matrix result = { 0 };
float cz = cosf(ang.z); float cz = cosf(angle.z);
float sz = sinf(ang.z); float sz = sinf(angle.z);
float cy = cosf(ang.y); float cy = cosf(angle.y);
float sy = sinf(ang.y); float sy = sinf(angle.y);
float cx = cosf(ang.x); float cx = cosf(angle.x);
float sx = sinf(ang.x); float sx = sinf(angle.x);
result.m0 = cz*cy; result.m0 = cz*cy;
result.m1 = cz*sy*sx - cx*sz; result.m4 = cz*sy*sx - cx*sz;
result.m2 = sz*sx + cz*cx*sy; result.m8 = sz*sx + cz*cx*sy;
result.m3 = 0;
result.m4 = cy*sz;
result.m5 = cz*cx + sz*sy*sx;
result.m6 = cx*sz*sy - cz*sx;
result.m7 = 0;
result.m8 = -sy;
result.m9 = cy*sx;
result.m10 = cy*cx;
result.m11 = 0;
result.m12 = 0; result.m12 = 0;
result.m1 = cy*sz;
result.m5 = cz*cx + sz*sy*sx;
result.m9 = cx*sz*sy - cz*sx;
result.m13 = 0; result.m13 = 0;
result.m2 = -sy;
result.m6 = cy*sx;
result.m10 = cy*cx;
result.m14 = 0; result.m14 = 0;
result.m3 = 0;
result.m7 = 0;
result.m11 = 0;
result.m15 = 1; result.m15 = 1;
return result; return result;
@ -1269,7 +1485,7 @@ RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top,
} }
// Get perspective projection matrix // Get perspective projection matrix
// NOTE: Angle should be provided in radians // NOTE: Fovy angle must be provided in radians
RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double far) RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double far)
{ {
Matrix result = { 0 }; Matrix result = { 0 };
@ -1478,10 +1694,9 @@ RMAPI Quaternion QuaternionInvert(Quaternion q)
{ {
Quaternion result = q; Quaternion result = q;
float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); float lengthSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
float lengthSq = length*length;
if (lengthSq != 0.0) if (lengthSq != 0.0f)
{ {
float invLength = 1.0f/lengthSq; float invLength = 1.0f/lengthSq;
@ -1515,12 +1730,10 @@ RMAPI Quaternion QuaternionScale(Quaternion q, float mul)
{ {
Quaternion result = { 0 }; Quaternion result = { 0 };
float qax = q.x, qay = q.y, qaz = q.z, qaw = q.w; result.x = q.x*mul;
result.y = q.y*mul;
result.x = qax*mul + qaw*mul + qay*mul - qaz*mul; result.z = q.z*mul;
result.y = qay*mul + qaw*mul + qaz*mul - qax*mul; result.w = q.w*mul;
result.z = qaz*mul + qaw*mul + qax*mul - qay*mul;
result.w = qaw*mul - qax*mul - qay*mul - qaz*mul;
return result; return result;
} }
@ -1584,14 +1797,14 @@ RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount)
cosHalfTheta = -cosHalfTheta; cosHalfTheta = -cosHalfTheta;
} }
if (fabs(cosHalfTheta) >= 1.0f) result = q1; if (fabsf(cosHalfTheta) >= 1.0f) result = q1;
else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount);
else else
{ {
float halfTheta = acosf(cosHalfTheta); float halfTheta = acosf(cosHalfTheta);
float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta);
if (fabs(sinHalfTheta) < 0.001f) if (fabsf(sinHalfTheta) < 0.001f)
{ {
result.x = (q1.x*0.5f + q2.x*0.5f); result.x = (q1.x*0.5f + q2.x*0.5f);
result.y = (q1.y*0.5f + q2.y*0.5f); result.y = (q1.y*0.5f + q2.y*0.5f);
@ -1646,30 +1859,60 @@ RMAPI Quaternion QuaternionFromMatrix(Matrix mat)
{ {
Quaternion result = { 0 }; Quaternion result = { 0 };
if ((mat.m0 > mat.m5) && (mat.m0 > mat.m10)) float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10;
{ float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10;
float s = sqrtf(1.0f + mat.m0 - mat.m5 - mat.m10)*2; float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10;
float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5;
result.x = 0.25f*s; int biggestIndex = 0;
result.y = (mat.m4 + mat.m1)/s; float fourBiggestSquaredMinus1 = fourWSquaredMinus1;
result.z = (mat.m2 + mat.m8)/s; if (fourXSquaredMinus1 > fourBiggestSquaredMinus1)
result.w = (mat.m9 - mat.m6)/s;
}
else if (mat.m5 > mat.m10)
{ {
float s = sqrtf(1.0f + mat.m5 - mat.m0 - mat.m10)*2; fourBiggestSquaredMinus1 = fourXSquaredMinus1;
result.x = (mat.m4 + mat.m1)/s; biggestIndex = 1;
result.y = 0.25f*s;
result.z = (mat.m9 + mat.m6)/s;
result.w = (mat.m2 - mat.m8)/s;
} }
else
if (fourYSquaredMinus1 > fourBiggestSquaredMinus1)
{ {
float s = sqrtf(1.0f + mat.m10 - mat.m0 - mat.m5)*2; fourBiggestSquaredMinus1 = fourYSquaredMinus1;
result.x = (mat.m2 + mat.m8)/s; biggestIndex = 2;
result.y = (mat.m9 + mat.m6)/s; }
result.z = 0.25f*s;
result.w = (mat.m4 - mat.m1)/s; if (fourZSquaredMinus1 > fourBiggestSquaredMinus1)
{
fourBiggestSquaredMinus1 = fourZSquaredMinus1;
biggestIndex = 3;
}
float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f) * 0.5f;
float mult = 0.25f / biggestVal;
switch (biggestIndex)
{
case 0:
result.w = biggestVal;
result.x = (mat.m6 - mat.m9) * mult;
result.y = (mat.m8 - mat.m2) * mult;
result.z = (mat.m1 - mat.m4) * mult;
break;
case 1:
result.x = biggestVal;
result.w = (mat.m6 - mat.m9) * mult;
result.y = (mat.m1 + mat.m4) * mult;
result.z = (mat.m8 + mat.m2) * mult;
break;
case 2:
result.y = biggestVal;
result.w = (mat.m8 - mat.m2) * mult;
result.x = (mat.m1 + mat.m4) * mult;
result.z = (mat.m6 + mat.m9) * mult;
break;
case 3:
result.z = biggestVal;
result.w = (mat.m1 - mat.m4) * mult;
result.x = (mat.m8 + mat.m2) * mult;
result.y = (mat.m6 + mat.m9) * mult;
break;
} }
return result; return result;
@ -1709,7 +1952,7 @@ RMAPI Matrix QuaternionToMatrix(Quaternion q)
} }
// Get rotation quaternion for an angle and axis // Get rotation quaternion for an angle and axis
// NOTE: angle must be provided in radians // NOTE: Angle must be provided in radians
RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle)
{ {
Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f };
@ -1757,7 +2000,7 @@ RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle)
// Get the rotation angle and axis for a given quaternion // Get the rotation angle and axis for a given quaternion
RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle)
{ {
if (fabs(q.w) > 1.0f) if (fabsf(q.w) > 1.0f)
{ {
// QuaternionNormalize(q); // QuaternionNormalize(q);
float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
@ -1850,4 +2093,19 @@ RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat)
return result; return result;
} }
// Check whether two given quaternions are almost equal
RMAPI int QuaternionEquals(Quaternion p, Quaternion q)
{
int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&
((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) &&
((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) &&
((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) ||
(((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) &&
((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) &&
((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) &&
((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w))))));
return result;
}
#endif // RAYMATH_H #endif // RAYMATH_H

View file

@ -22,7 +22,7 @@
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2015-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2015-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -103,7 +103,7 @@
// Module Functions Declaration // Module Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#ifdef __cplusplus #if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions extern "C" { // Prevents name mangling of functions
#endif #endif
@ -119,7 +119,7 @@ void SetCameraMoveControls(int keyFront, int keyBack,
int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras) int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras)
#endif #endif
#ifdef __cplusplus #if defined(__cplusplus)
} }
#endif #endif
@ -150,7 +150,7 @@ void SetCameraMoveControls(int keyFront, int keyBack,
#endif #endif
// Camera mouse movement sensitivity // Camera mouse movement sensitivity
#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f #define CAMERA_MOUSE_MOVE_SENSITIVITY 0.5f // TODO: it should be independant of framerate
#define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f #define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f
// FREE_CAMERA // FREE_CAMERA
@ -163,7 +163,7 @@ void SetCameraMoveControls(int keyFront, int keyBack,
#define CAMERA_FREE_PANNING_DIVIDER 5.1f #define CAMERA_FREE_PANNING_DIVIDER 5.1f
// ORBITAL_CAMERA // ORBITAL_CAMERA
#define CAMERA_ORBITAL_SPEED 0.01f // Radians per frame #define CAMERA_ORBITAL_SPEED 0.5f // Radians per second
// FIRST_PERSON // FIRST_PERSON
//#define CAMERA_FIRST_PERSON_MOUSE_SENSITIVITY 0.003f //#define CAMERA_FIRST_PERSON_MOUSE_SENSITIVITY 0.003f
@ -171,9 +171,11 @@ void SetCameraMoveControls(int keyFront, int keyBack,
#define CAMERA_FIRST_PERSON_MIN_CLAMP 89.0f #define CAMERA_FIRST_PERSON_MIN_CLAMP 89.0f
#define CAMERA_FIRST_PERSON_MAX_CLAMP -89.0f #define CAMERA_FIRST_PERSON_MAX_CLAMP -89.0f
#define CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 8.0f // When walking, y-position of the player moves up-down at step frequency (swinging) but
#define CAMERA_FIRST_PERSON_STEP_DIVIDER 30.0f // also the body slightly tilts left-right on every step, when all the body weight is left over one foot (tilting)
#define CAMERA_FIRST_PERSON_WAVING_DIVIDER 200.0f #define CAMERA_FIRST_PERSON_STEP_FREQUENCY 1.8f // Step frequency when walking (steps per second)
#define CAMERA_FIRST_PERSON_SWINGING_DELTA 0.03f // Maximum up-down swinging distance when walking
#define CAMERA_FIRST_PERSON_TILTING_DELTA 0.005f // Maximum left-right tilting distance when walking
// THIRD_PERSON // THIRD_PERSON
//#define CAMERA_THIRD_PERSON_MOUSE_SENSITIVITY 0.003f //#define CAMERA_THIRD_PERSON_MOUSE_SENSITIVITY 0.003f
@ -183,7 +185,7 @@ void SetCameraMoveControls(int keyFront, int keyBack,
#define CAMERA_THIRD_PERSON_OFFSET (Vector3){ 0.4f, 0.0f, 0.0f } #define CAMERA_THIRD_PERSON_OFFSET (Vector3){ 0.4f, 0.0f, 0.0f }
// PLAYER (used by camera) // PLAYER (used by camera)
#define PLAYER_MOVEMENT_SENSITIVITY 20.0f #define PLAYER_MOVEMENT_SENSITIVITY 2.0f
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Types and Structures Definition // Types and Structures Definition
@ -204,7 +206,6 @@ typedef struct {
float targetDistance; // Camera distance from position to target float targetDistance; // Camera distance from position to target
float playerEyesPosition; // Player eyes position from ground (in meters) float playerEyesPosition; // Player eyes position from ground (in meters)
Vector2 angle; // Camera angle in plane XZ Vector2 angle; // Camera angle in plane XZ
Vector2 previousMousePosition; // Previous mouse position
// Camera movement control keys // Camera movement control keys
int moveControl[6]; // Move controls (CAMERA_FIRST_PERSON) int moveControl[6]; // Move controls (CAMERA_FIRST_PERSON)
@ -221,7 +222,6 @@ static CameraData CAMERA = { // Global CAMERA state context
.targetDistance = 0, .targetDistance = 0,
.playerEyesPosition = 1.85f, .playerEyesPosition = 1.85f,
.angle = { 0 }, .angle = { 0 },
.previousMousePosition = { 0 },
.moveControl = { 'W', 'S', 'D', 'A', 'E', 'Q' }, .moveControl = { 'W', 'S', 'D', 'A', 'E', 'Q' },
.smoothZoomControl = 341, // raylib: KEY_LEFT_CONTROL .smoothZoomControl = 341, // raylib: KEY_LEFT_CONTROL
.altControl = 342, // raylib: KEY_LEFT_ALT .altControl = 342, // raylib: KEY_LEFT_ALT
@ -265,8 +265,6 @@ void SetCameraMode(Camera camera, int mode)
CAMERA.playerEyesPosition = camera.position.y; // Init player eyes position to camera Y position CAMERA.playerEyesPosition = camera.position.y; // Init player eyes position to camera Y position
CAMERA.previousMousePosition = GetMousePosition(); // Init mouse position
// Lock cursor for first person and third person cameras // Lock cursor for first person and third person cameras
if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor(); if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor();
else EnableCursor(); else EnableCursor();
@ -281,13 +279,12 @@ void SetCameraMode(Camera camera, int mode)
// Keys: IsKeyDown() // Keys: IsKeyDown()
void UpdateCamera(Camera *camera) void UpdateCamera(Camera *camera)
{ {
static int swingCounter = 0; // Used for 1st person swinging movement static float swingCounter = 0.0f; // Used for 1st person swinging movement
// TODO: Compute CAMERA.targetDistance and CAMERA.angle here (?) // TODO: Compute CAMERA.targetDistance and CAMERA.angle here (?)
// Mouse movement detection // Mouse movement detection
Vector2 mousePositionDelta = { 0.0f, 0.0f }; Vector2 mousePositionDelta = GetMouseDelta();
Vector2 mousePosition = GetMousePosition();
float mouseWheelMove = GetMouseWheelMove(); float mouseWheelMove = GetMouseWheelMove();
// Keys input detection // Keys input detection
@ -302,14 +299,6 @@ void UpdateCamera(Camera *camera)
IsKeyDown(CAMERA.moveControl[MOVE_UP]), IsKeyDown(CAMERA.moveControl[MOVE_UP]),
IsKeyDown(CAMERA.moveControl[MOVE_DOWN]) }; IsKeyDown(CAMERA.moveControl[MOVE_DOWN]) };
if (CAMERA.mode != CAMERA_CUSTOM)
{
mousePositionDelta.x = mousePosition.x - CAMERA.previousMousePosition.x;
mousePositionDelta.y = mousePosition.y - CAMERA.previousMousePosition.y;
CAMERA.previousMousePosition = mousePosition;
}
// Support for multiple automatic camera modes // Support for multiple automatic camera modes
// NOTE: In case of CAMERA_CUSTOM nothing happens here, user must update it manually // NOTE: In case of CAMERA_CUSTOM nothing happens here, user must update it manually
switch (CAMERA.mode) switch (CAMERA.mode)
@ -388,9 +377,9 @@ void UpdateCamera(Camera *camera)
else else
{ {
// Camera panning // Camera panning
camera->target.x += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); camera->target.x += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER);
camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER);
camera->target.z += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); camera->target.z += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER);
} }
} }
@ -402,7 +391,7 @@ void UpdateCamera(Camera *camera)
} break; } break;
case CAMERA_ORBITAL: // Camera just orbits around target, only zoom allowed case CAMERA_ORBITAL: // Camera just orbits around target, only zoom allowed
{ {
CAMERA.angle.x += CAMERA_ORBITAL_SPEED; // Camera orbit angle CAMERA.angle.x += CAMERA_ORBITAL_SPEED*GetFrameTime(); // Camera orbit angle
CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom
// Camera distance clamp // Camera distance clamp
@ -419,20 +408,20 @@ void UpdateCamera(Camera *camera)
camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] -
sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - sinf(CAMERA.angle.x)*direction[MOVE_FRONT] -
cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + cosf(CAMERA.angle.x)*direction[MOVE_LEFT] +
cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime();
camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] -
sinf(CAMERA.angle.y)*direction[MOVE_BACK] + sinf(CAMERA.angle.y)*direction[MOVE_BACK] +
1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime();
camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] -
cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + cosf(CAMERA.angle.x)*direction[MOVE_FRONT] +
sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - sinf(CAMERA.angle.x)*direction[MOVE_LEFT] -
sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime();
// Camera orientation calculation // Camera orientation calculation
CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); CAMERA.angle.x -= mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY*GetFrameTime();
CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); CAMERA.angle.y -= mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY*GetFrameTime();
// Angle clamp // Angle clamp
if (CAMERA.angle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD; if (CAMERA.angle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD;
@ -490,15 +479,17 @@ void UpdateCamera(Camera *camera)
camera->target.y = camera->position.y - matTransform.m13; camera->target.y = camera->position.y - matTransform.m13;
camera->target.z = camera->position.z - matTransform.m14; camera->target.z = camera->position.z - matTransform.m14;
// If movement detected (some key pressed), increase swinging
for (int i = 0; i < 6; i++) if (direction[i]) { swingCounter++; break; }
// Camera position update // Camera position update
// NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position' // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position'
camera->position.y = CAMERA.playerEyesPosition - sinf(swingCounter/CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER)/CAMERA_FIRST_PERSON_STEP_DIVIDER; camera->position.y = CAMERA.playerEyesPosition;
camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; // Camera swinging (y-movement), only when walking (some key pressed)
camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; for (int i = 0; i < 6; i++) if (direction[i]) { swingCounter += GetFrameTime(); break; }
camera->position.y -= sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_SWINGING_DELTA;
// Camera waiving (xz-movement), only when walking (some key pressed)
camera->up.x = sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_TILTING_DELTA;
camera->up.z = -sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_TILTING_DELTA;
} break; } break;
case CAMERA_THIRD_PERSON: // Camera moves as in a third-person game, following target at a distance, controls are configurable case CAMERA_THIRD_PERSON: // Camera moves as in a third-person game, following target at a distance, controls are configurable
@ -506,16 +497,16 @@ void UpdateCamera(Camera *camera)
camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] -
sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - sinf(CAMERA.angle.x)*direction[MOVE_FRONT] -
cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + cosf(CAMERA.angle.x)*direction[MOVE_LEFT] +
cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime();
camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] -
sinf(CAMERA.angle.y)*direction[MOVE_BACK] + sinf(CAMERA.angle.y)*direction[MOVE_BACK] +
1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime();
camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] -
cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + cosf(CAMERA.angle.x)*direction[MOVE_FRONT] +
sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - sinf(CAMERA.angle.x)*direction[MOVE_LEFT] -
sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime();
// Camera orientation calculation // Camera orientation calculation
CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY);

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,7 @@
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2014-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2014-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -62,10 +62,10 @@
// NOTE: Below types are required for GESTURES_STANDALONE usage // NOTE: Below types are required for GESTURES_STANDALONE usage
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Boolean type // Boolean type
#if defined(__STDC__) && __STDC_VERSION__ >= 199901L #if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
#include <stdbool.h> #include <stdbool.h>
#elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE) #elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE)
typedef enum bool { false, true } bool; typedef enum bool { false = 0, true = !false } bool;
#endif #endif
#if !defined(RL_VECTOR2_TYPE) #if !defined(RL_VECTOR2_TYPE)
@ -118,7 +118,7 @@ typedef struct {
// Module Functions Declaration // Module Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#ifdef __cplusplus #if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions extern "C" { // Prevents name mangling of functions
#endif #endif
@ -137,7 +137,7 @@ Vector2 GetGesturePinchVector(void); // Get gesture pinch del
float GetGesturePinchAngle(void); // Get gesture pinch angle float GetGesturePinchAngle(void); // Get gesture pinch angle
#endif #endif
#ifdef __cplusplus #if defined(__cplusplus)
} }
#endif #endif

View file

@ -85,7 +85,7 @@
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2014-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2014-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -358,11 +358,11 @@ typedef struct rlRenderBatch {
float currentDepth; // Current depth value for next draw float currentDepth; // Current depth value for next draw
} rlRenderBatch; } rlRenderBatch;
#if defined(__STDC__) && __STDC_VERSION__ >= 199901L #if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
#include <stdbool.h> #include <stdbool.h>
#elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE) #elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE)
// Boolean type // Boolean type
typedef enum bool { false, true } bool; typedef enum bool { false = 0, true = !false } bool;
#endif #endif
#if !defined(RL_MATRIX_TYPE) #if !defined(RL_MATRIX_TYPE)
@ -418,7 +418,7 @@ typedef enum {
// NOTE 1: Filtering considers mipmaps if available in the texture // NOTE 1: Filtering considers mipmaps if available in the texture
// NOTE 2: Filter is accordingly set for minification and magnification // NOTE 2: Filter is accordingly set for minification and magnification
typedef enum { typedef enum {
RL_TEXTURE_FILTER_POINT = 0, // No filter, just pixel aproximation RL_TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation
RL_TEXTURE_FILTER_BILINEAR, // Linear filtering RL_TEXTURE_FILTER_BILINEAR, // Linear filtering
RL_TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) RL_TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps)
RL_TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x RL_TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x
@ -433,7 +433,8 @@ typedef enum {
RL_BLEND_MULTIPLIED, // Blend textures multiplying colors RL_BLEND_MULTIPLIED, // Blend textures multiplying colors
RL_BLEND_ADD_COLORS, // Blend textures adding colors (alternative) RL_BLEND_ADD_COLORS, // Blend textures adding colors (alternative)
RL_BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) RL_BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative)
RL_BLEND_CUSTOM // Belnd textures using custom src/dst factors (use SetBlendModeCustom()) RL_BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha
RL_BLEND_CUSTOM // Blend textures using custom src/dst factors (use rlSetBlendFactors())
} rlBlendMode; } rlBlendMode;
// Shader location point type // Shader location point type
@ -597,7 +598,9 @@ RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffer
RLAPI void rlglClose(void); // De-inititialize rlgl (buffers, shaders, textures) RLAPI void rlglClose(void); // De-inititialize rlgl (buffers, shaders, textures)
RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required) RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required)
RLAPI int rlGetVersion(void); // Get current OpenGL version RLAPI int rlGetVersion(void); // Get current OpenGL version
RLAPI void rlSetFramebufferWidth(int width); // Set current framebuffer width
RLAPI int rlGetFramebufferWidth(void); // Get default framebuffer width RLAPI int rlGetFramebufferWidth(void); // Get default framebuffer width
RLAPI void rlSetFramebufferHeight(int height); // Set current framebuffer height
RLAPI int rlGetFramebufferHeight(void); // Get default framebuffer height RLAPI int rlGetFramebufferHeight(void); // Get default framebuffer height
RLAPI unsigned int rlGetTextureIdDefault(void); // Get default texture id RLAPI unsigned int rlGetTextureIdDefault(void); // Get default texture id
@ -619,25 +622,26 @@ RLAPI void rlSetTexture(unsigned int id); // Set current texture for r
// Vertex buffers management // Vertex buffers management
RLAPI unsigned int rlLoadVertexArray(void); // Load vertex array (vao) if supported RLAPI unsigned int rlLoadVertexArray(void); // Load vertex array (vao) if supported
RLAPI unsigned int rlLoadVertexBuffer(void *buffer, int size, bool dynamic); // Load a vertex buffer attribute RLAPI unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic); // Load a vertex buffer attribute
RLAPI unsigned int rlLoadVertexBufferElement(void *buffer, int size, bool dynamic); // Load a new attributes element buffer RLAPI unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic); // Load a new attributes element buffer
RLAPI void rlUpdateVertexBuffer(unsigned int bufferId, void *data, int dataSize, int offset); // Update GPU buffer with new data RLAPI void rlUpdateVertexBuffer(unsigned int bufferId, const void *data, int dataSize, int offset); // Update GPU buffer with new data
RLAPI void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset); // Update vertex buffer elements with new data
RLAPI void rlUnloadVertexArray(unsigned int vaoId); RLAPI void rlUnloadVertexArray(unsigned int vaoId);
RLAPI void rlUnloadVertexBuffer(unsigned int vboId); RLAPI void rlUnloadVertexBuffer(unsigned int vboId);
RLAPI void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, void *pointer); RLAPI void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, const void *pointer);
RLAPI void rlSetVertexAttributeDivisor(unsigned int index, int divisor); RLAPI void rlSetVertexAttributeDivisor(unsigned int index, int divisor);
RLAPI void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count); // Set vertex attribute default value RLAPI void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count); // Set vertex attribute default value
RLAPI void rlDrawVertexArray(int offset, int count); RLAPI void rlDrawVertexArray(int offset, int count);
RLAPI void rlDrawVertexArrayElements(int offset, int count, void *buffer); RLAPI void rlDrawVertexArrayElements(int offset, int count, const void *buffer);
RLAPI void rlDrawVertexArrayInstanced(int offset, int count, int instances); RLAPI void rlDrawVertexArrayInstanced(int offset, int count, int instances);
RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, void *buffer, int instances); RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances);
// Textures management // Textures management
RLAPI unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU
RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo) RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo)
RLAPI unsigned int rlLoadTextureCubemap(void *data, int size, int format); // Load texture cubemap RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format); // Load texture cubemap
RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update GPU texture with new data RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update GPU texture with new data
RLAPI void rlGetGlTextureFormats(int format, int *glInternalFormat, int *glFormat, int *glType); // Get OpenGL internal formats RLAPI void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats
RLAPI const char *rlGetPixelFormatName(unsigned int format); // Get name string for pixel format RLAPI const char *rlGetPixelFormatName(unsigned int format); // Get name string for pixel format
RLAPI void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory RLAPI void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory
RLAPI void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps); // Generate mipmap data for selected texture RLAPI void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps); // Generate mipmap data for selected texture
@ -713,7 +717,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#include <OpenGL/glext.h> // OpenGL extensions library #include <OpenGL/glext.h> // OpenGL extensions library
#else #else
// APIENTRY for OpenGL function pointer declarations is required // APIENTRY for OpenGL function pointer declarations is required
#ifndef APIENTRY #if !defined(APIENTRY)
#if defined(_WIN32) #if defined(_WIN32)
#define APIENTRY __stdcall #define APIENTRY __stdcall
#else #else
@ -925,8 +929,8 @@ typedef struct rlglData {
int glBlendDstFactor; // Blending destination factor int glBlendDstFactor; // Blending destination factor
int glBlendEquation; // Blending equation int glBlendEquation; // Blending equation
int framebufferWidth; // Default framebuffer width int framebufferWidth; // Current framebuffer width
int framebufferHeight; // Default framebuffer height int framebufferHeight; // Current framebuffer height
} State; // Renderer state } State; // Renderer state
struct { struct {
@ -1228,6 +1232,7 @@ void rlOrtho(double left, double right, double bottom, double top, double znear,
#endif #endif
// Set the viewport area (transformation from normalized device coordinates to window coordinates) // Set the viewport area (transformation from normalized device coordinates to window coordinates)
// NOTE: We store current viewport dimensions
void rlViewport(int x, int y, int width, int height) void rlViewport(int x, int y, int width, int height)
{ {
glViewport(x, y, width, height); glViewport(x, y, width, height);
@ -1512,6 +1517,11 @@ void rlTextureParameters(unsigned int id, int param, int value)
{ {
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
#if !defined(GRAPHICS_API_OPENGL_11)
// Reset anisotropy filter, in case it was set
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
#endif
switch (param) switch (param)
{ {
case RL_TEXTURE_WRAP_S: case RL_TEXTURE_WRAP_S:
@ -1535,7 +1545,7 @@ void rlTextureParameters(unsigned int id, int param, int value)
if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value);
else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f) else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f)
{ {
TRACELOG(RL_LOG_WARNING, "GL: Maximum anisotropic filter level supported is %iX", id, RLGL.ExtSupported.maxAnisotropyLevel); TRACELOG(RL_LOG_WARNING, "GL: Maximum anisotropic filter level supported is %iX", id, (int)RLGL.ExtSupported.maxAnisotropyLevel);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value);
} }
else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported");
@ -1778,7 +1788,12 @@ void rlSetBlendMode(int mode)
case RL_BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; case RL_BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break;
case RL_BLEND_ADD_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; case RL_BLEND_ADD_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); break;
case RL_BLEND_SUBTRACT_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_SUBTRACT); break; case RL_BLEND_SUBTRACT_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_SUBTRACT); break;
case RL_BLEND_CUSTOM: glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation); break; case RL_BLEND_ALPHA_PREMULTIPLY: glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break;
case RL_BLEND_CUSTOM:
{
// NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactors()
glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation);
} break;
default: break; default: break;
} }
@ -2212,6 +2227,22 @@ int rlGetVersion(void)
return glVersion; return glVersion;
} }
// Set current framebuffer width
void rlSetFramebufferWidth(int width)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
RLGL.State.framebufferWidth = width;
#endif
}
// Set current framebuffer height
void rlSetFramebufferHeight(int height)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
RLGL.State.framebufferHeight = height;
#endif
}
// Get default framebuffer width // Get default framebuffer width
int rlGetFramebufferWidth(void) int rlGetFramebufferWidth(void)
{ {
@ -2594,6 +2625,9 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
glUseProgram(0); // Unbind shader program glUseProgram(0); // Unbind shader program
} }
// Restore viewport to default measures
if (eyeCount == 2) rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight);
//------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------
// Reset batch buffers // Reset batch buffers
@ -2676,12 +2710,12 @@ bool rlCheckRenderBatchLimit(int vCount)
// Textures data management // Textures data management
//----------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------
// Convert image data to OpenGL texture (returns OpenGL valid Id) // Convert image data to OpenGL texture (returns OpenGL valid Id)
unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount) unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount)
{ {
glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding
unsigned int id = 0; unsigned int id = 0;
glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding
// Check texture format support by OpenGL 1.1 (compressed textures not supported) // Check texture format support by OpenGL 1.1 (compressed textures not supported)
#if defined(GRAPHICS_API_OPENGL_11) #if defined(GRAPHICS_API_OPENGL_11)
if (format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) if (format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)
@ -2738,7 +2772,7 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi
{ {
unsigned int mipSize = rlGetPixelDataSize(mipWidth, mipHeight, format); unsigned int mipSize = rlGetPixelDataSize(mipWidth, mipHeight, format);
int glInternalFormat, glFormat, glType; unsigned int glInternalFormat, glFormat, glType;
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset);
@ -2878,7 +2912,7 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer)
// Load texture cubemap // Load texture cubemap
// NOTE: Cubemap data is expected to be 6 images in a single data array (one after the other), // NOTE: Cubemap data is expected to be 6 images in a single data array (one after the other),
// expected the following convention: +X, -X, +Y, -Y, +Z, -Z // expected the following convention: +X, -X, +Y, -Y, +Z, -Z
unsigned int rlLoadTextureCubemap(void *data, int size, int format) unsigned int rlLoadTextureCubemap(const void *data, int size, int format)
{ {
unsigned int id = 0; unsigned int id = 0;
@ -2888,7 +2922,7 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format)
glGenTextures(1, &id); glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_CUBE_MAP, id); glBindTexture(GL_TEXTURE_CUBE_MAP, id);
int glInternalFormat, glFormat, glType; unsigned int glInternalFormat, glFormat, glType;
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
if (glInternalFormat != -1) if (glInternalFormat != -1)
@ -2960,22 +2994,22 @@ void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int h
{ {
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
int glInternalFormat, glFormat, glType; unsigned int glInternalFormat, glFormat, glType;
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB))
{ {
glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, (unsigned char *)data); glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, data);
} }
else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to update for current texture format (%i)", id, format); else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to update for current texture format (%i)", id, format);
} }
// Get OpenGL internal formats and data type from raylib PixelFormat // Get OpenGL internal formats and data type from raylib PixelFormat
void rlGetGlTextureFormats(int format, int *glInternalFormat, int *glFormat, int *glType) void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType)
{ {
*glInternalFormat = -1; *glInternalFormat = 0;
*glFormat = -1; *glFormat = 0;
*glType = -1; *glType = 0;
switch (format) switch (format)
{ {
@ -3081,12 +3115,9 @@ void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
if ((texIsPOT) || (RLGL.ExtSupported.texNPOT)) if ((texIsPOT) || (RLGL.ExtSupported.texNPOT))
{ {
//glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorythm: GL_FASTEST, GL_NICEST, GL_DONT_CARE //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorithm: GL_FASTEST, GL_NICEST, GL_DONT_CARE
glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps
#define MIN(a,b) (((a)<(b))? (a):(b)) #define MIN(a,b) (((a)<(b))? (a):(b))
#define MAX(a,b) (((a)>(b))? (a):(b)) #define MAX(a,b) (((a)>(b))? (a):(b))
@ -3121,7 +3152,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format)
// GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.)
glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1);
int glInternalFormat, glFormat, glType; unsigned int glInternalFormat, glFormat, glType;
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
unsigned int size = rlGetPixelDataSize(width, height, format); unsigned int size = rlGetPixelDataSize(width, height, format);
@ -3164,7 +3195,6 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format)
return pixels; return pixels;
} }
// Read screen pixel data (color buffer) // Read screen pixel data (color buffer)
unsigned char *rlReadScreenPixels(int width, int height) unsigned char *rlReadScreenPixels(int width, int height)
{ {
@ -3313,7 +3343,7 @@ void rlUnloadFramebuffer(unsigned int id)
// Vertex data management // Vertex data management
//----------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------
// Load a new attributes buffer // Load a new attributes buffer
unsigned int rlLoadVertexBuffer(void *buffer, int size, bool dynamic) unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic)
{ {
unsigned int id = 0; unsigned int id = 0;
@ -3327,7 +3357,7 @@ unsigned int rlLoadVertexBuffer(void *buffer, int size, bool dynamic)
} }
// Load a new attributes element buffer // Load a new attributes element buffer
unsigned int rlLoadVertexBufferElement(void *buffer, int size, bool dynamic) unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic)
{ {
unsigned int id = 0; unsigned int id = 0;
@ -3374,7 +3404,7 @@ void rlDisableVertexBufferElement(void)
// Update vertex buffer with new data // Update vertex buffer with new data
// NOTE: dataSize and offset must be provided in bytes // NOTE: dataSize and offset must be provided in bytes
void rlUpdateVertexBuffer(unsigned int id, void *data, int dataSize, int offset) void rlUpdateVertexBuffer(unsigned int id, const void *data, int dataSize, int offset)
{ {
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
glBindBuffer(GL_ARRAY_BUFFER, id); glBindBuffer(GL_ARRAY_BUFFER, id);
@ -3384,7 +3414,7 @@ void rlUpdateVertexBuffer(unsigned int id, void *data, int dataSize, int offset)
// Update vertex buffer elements with new data // Update vertex buffer elements with new data
// NOTE: dataSize and offset must be provided in bytes // NOTE: dataSize and offset must be provided in bytes
void rlUpdateVertexBufferElements(unsigned int id, void *data, int dataSize, int offset) void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset)
{ {
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id);
@ -3437,9 +3467,9 @@ void rlDrawVertexArray(int offset, int count)
} }
// Draw vertex array elements // Draw vertex array elements
void rlDrawVertexArrayElements(int offset, int count, void *buffer) void rlDrawVertexArrayElements(int offset, int count, const void *buffer)
{ {
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (unsigned short *)buffer + offset); glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset);
} }
// Draw vertex array instanced // Draw vertex array instanced
@ -3451,10 +3481,10 @@ void rlDrawVertexArrayInstanced(int offset, int count, int instances)
} }
// Draw vertex array elements instanced // Draw vertex array elements instanced
void rlDrawVertexArrayElementsInstanced(int offset, int count, void *buffer, int instances) void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances)
{ {
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (unsigned short *)buffer + offset, instances); glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset, instances);
#endif #endif
} }
@ -3495,7 +3525,7 @@ unsigned int rlLoadVertexArray(void)
} }
// Set vertex attribute // Set vertex attribute
void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, void *pointer) void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, const void *pointer)
{ {
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
glVertexAttribPointer(index, compSize, type, normalized, stride, pointer); glVertexAttribPointer(index, compSize, type, normalized, stride, pointer);
@ -3541,41 +3571,52 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
unsigned int id = 0; unsigned int id = 0;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
unsigned int vertexShaderId = RLGL.State.defaultVShaderId; unsigned int vertexShaderId = 0;
unsigned int fragmentShaderId = RLGL.State.defaultFShaderId; unsigned int fragmentShaderId = 0;
// Compile vertex shader (if provided)
if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER); if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER);
if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER); // In case no vertex shader was provided or compilation failed, we use default vertex shader
if (vertexShaderId == 0) vertexShaderId = RLGL.State.defaultVShaderId;
// Compile fragment shader (if provided)
if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER);
// In case no fragment shader was provided or compilation failed, we use default fragment shader
if (fragmentShaderId == 0) fragmentShaderId = RLGL.State.defaultFShaderId;
// In case vertex and fragment shader are the default ones, no need to recompile, we can just assign the default shader program id
if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId; if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId;
else else
{ {
// One of or both shader are new, we need to compile a new shader program
id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId); id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId);
// We can detach and delete vertex/fragment shaders (if not default ones)
// NOTE: We detach shader before deletion to make sure memory is freed
if (vertexShaderId != RLGL.State.defaultVShaderId) if (vertexShaderId != RLGL.State.defaultVShaderId)
{ {
// Detach shader before deletion to make sure memory is freed
glDetachShader(id, vertexShaderId); glDetachShader(id, vertexShaderId);
glDeleteShader(vertexShaderId); glDeleteShader(vertexShaderId);
} }
if (fragmentShaderId != RLGL.State.defaultFShaderId) if (fragmentShaderId != RLGL.State.defaultFShaderId)
{ {
// Detach shader before deletion to make sure memory is freed
glDetachShader(id, fragmentShaderId); glDetachShader(id, fragmentShaderId);
glDeleteShader(fragmentShaderId); glDeleteShader(fragmentShaderId);
} }
// In case shader program loading failed, we assign default shader
if (id == 0) if (id == 0)
{ {
TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code"); // In case shader loading fails, we return the default shader
TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader");
id = RLGL.State.defaultShaderId; id = RLGL.State.defaultShaderId;
} }
} /*
else
{
// Get available shader uniforms // Get available shader uniforms
// NOTE: This information is useful for debug... // NOTE: This information is useful for debug...
int uniformCount = -1; int uniformCount = -1;
glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount);
for (int i = 0; i < uniformCount; i++) for (int i = 0; i < uniformCount; i++)
@ -3589,9 +3630,11 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name); glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name);
name[namelen] = 0; name[namelen] = 0;
TRACELOGD("SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); TRACELOGD("SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name));
} }
}
*/
}
#endif #endif
return id; return id;
@ -3629,7 +3672,7 @@ unsigned int rlCompileShader(const char *shaderCode, int type)
if (maxLength > 0) if (maxLength > 0)
{ {
int length = 0; int length = 0;
char *log = RL_CALLOC(maxLength, sizeof(char)); char *log = (char *)RL_CALLOC(maxLength, sizeof(char));
glGetShaderInfoLog(shader, maxLength, &length, log); glGetShaderInfoLog(shader, maxLength, &length, log);
TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shader, log); TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shader, log);
RL_FREE(log); RL_FREE(log);
@ -3691,7 +3734,7 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId)
if (maxLength > 0) if (maxLength > 0)
{ {
int length = 0; int length = 0;
char *log = RL_CALLOC(maxLength, sizeof(char)); char *log = (char *)RL_CALLOC(maxLength, sizeof(char));
glGetProgramInfoLog(program, maxLength, &length, log); glGetProgramInfoLog(program, maxLength, &length, log);
TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log); TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log);
RL_FREE(log); RL_FREE(log);
@ -3858,7 +3901,7 @@ unsigned int rlLoadComputeShaderProgram(unsigned int shaderId)
if (maxLength > 0) if (maxLength > 0)
{ {
int length = 0; int length = 0;
char *log = RL_CALLOC(maxLength, sizeof(char)); char *log = (char *)RL_CALLOC(maxLength, sizeof(char));
glGetProgramInfoLog(program, maxLength, &length, log); glGetProgramInfoLog(program, maxLength, &length, log);
TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log); TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log);
RL_FREE(log); RL_FREE(log);
@ -3899,6 +3942,8 @@ unsigned int rlLoadShaderBuffer(unsigned long long size, const void *data, int u
glGenBuffers(1, &ssbo); glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY); glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY);
glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, 0);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
#endif #endif
return ssbo; return ssbo;
@ -3965,7 +4010,7 @@ void rlCopyBuffersElements(unsigned int destId, unsigned int srcId, unsigned lon
void rlBindImageTexture(unsigned int id, unsigned int index, unsigned int format, int readonly) void rlBindImageTexture(unsigned int id, unsigned int index, unsigned int format, int readonly)
{ {
#if defined(GRAPHICS_API_OPENGL_43) #if defined(GRAPHICS_API_OPENGL_43)
int glInternalFormat = 0, glFormat = 0, glType = 0; unsigned int glInternalFormat = 0, glFormat = 0, glType = 0;
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
glBindImageTexture(index, id, 0, 0, 0, readonly ? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat); glBindImageTexture(index, id, 0, 0, 0, readonly ? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat);
@ -4336,7 +4381,8 @@ static void rlLoadShaderDefault(void)
"} \n"; "} \n";
#endif #endif
// NOTE: Compiled vertex/fragment shaders are kept for re-use // NOTE: Compiled vertex/fragment shaders are not deleted,
// they are kept for re-use as default shaders in case some shader loading fails
RLGL.State.defaultVShaderId = rlCompileShader(defaultVShaderCode, GL_VERTEX_SHADER); // Compile default vertex shader RLGL.State.defaultVShaderId = rlCompileShader(defaultVShaderCode, GL_VERTEX_SHADER); // Compile default vertex shader
RLGL.State.defaultFShaderId = rlCompileShader(defaultFShaderCode, GL_FRAGMENT_SHADER); // Compile default fragment shader RLGL.State.defaultFShaderId = rlCompileShader(defaultFShaderCode, GL_FRAGMENT_SHADER); // Compile default fragment shader

View file

@ -4,12 +4,15 @@
* *
* CONFIGURATION: * CONFIGURATION:
* *
* #define SUPPORT_MODULE_RMODELS
* rmodels module is included in the build
*
* #define SUPPORT_FILEFORMAT_OBJ * #define SUPPORT_FILEFORMAT_OBJ
* #define SUPPORT_FILEFORMAT_MTL * #define SUPPORT_FILEFORMAT_MTL
* #define SUPPORT_FILEFORMAT_IQM * #define SUPPORT_FILEFORMAT_IQM
* #define SUPPORT_FILEFORMAT_GLTF * #define SUPPORT_FILEFORMAT_GLTF
* #define SUPPORT_FILEFORMAT_VOX * #define SUPPORT_FILEFORMAT_VOX
* * #define SUPPORT_FILEFORMAT_M3D
* Selected desired fileformats to be supported for model data loading. * Selected desired fileformats to be supported for model data loading.
* *
* #define SUPPORT_MESH_GENERATION * #define SUPPORT_MESH_GENERATION
@ -19,7 +22,7 @@
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2013-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2013-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -45,6 +48,8 @@
#include "config.h" // Defines module configuration flags #include "config.h" // Defines module configuration flags
#endif #endif
#if defined(SUPPORT_MODULE_RMODELS)
#include "utils.h" // Required for: TRACELOG(), LoadFileData(), LoadFileText(), SaveFileText() #include "utils.h" // Required for: TRACELOG(), LoadFileData(), LoadFileText(), SaveFileText()
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
#include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality #include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality
@ -82,6 +87,15 @@
#include "external/vox_loader.h" // VOX file format loading (MagikaVoxel) #include "external/vox_loader.h" // VOX file format loading (MagikaVoxel)
#endif #endif
#if defined(SUPPORT_FILEFORMAT_M3D)
#define M3D_MALLOC RL_MALLOC
#define M3D_REALLOC RL_REALLOC
#define M3D_FREE RL_FREE
#define M3D_IMPLEMENTATION
#include "external/m3d.h" // Model3D file format loading
#endif
#if defined(SUPPORT_MESH_GENERATION) #if defined(SUPPORT_MESH_GENERATION)
#define PAR_MALLOC(T, N) ((T*)RL_MALLOC(N*sizeof(T))) #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_CALLOC(T, N) ((T*)RL_CALLOC(N*sizeof(T), 1))
@ -137,6 +151,10 @@ static Model LoadGLTF(const char *fileName); // Load GLTF mesh data
#if defined(SUPPORT_FILEFORMAT_VOX) #if defined(SUPPORT_FILEFORMAT_VOX)
static Model LoadVOX(const char *filename); // Load VOX mesh data static Model LoadVOX(const char *filename); // Load VOX mesh data
#endif #endif
#if defined(SUPPORT_FILEFORMAT_M3D)
static Model LoadM3D(const char *filename); // Load M3D mesh data
static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount); // Load M3D animation data
#endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definition // Module Functions Definition
@ -196,7 +214,7 @@ void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rota
// Draw a color-filled triangle (vertex in counter-clockwise order!) // Draw a color-filled triangle (vertex in counter-clockwise order!)
void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color) void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color)
{ {
rlCheckRenderBatchLimit(3); rlCheckRenderBatchLimit(8);
rlBegin(RL_TRIANGLES); rlBegin(RL_TRIANGLES);
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
@ -918,11 +936,14 @@ Model LoadModel(const char *fileName)
if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName); if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_GLTF) #if defined(SUPPORT_FILEFORMAT_GLTF)
if (IsFileExtension(fileName, ".gltf;.glb")) model = LoadGLTF(fileName); if (IsFileExtension(fileName, ".gltf") || IsFileExtension(fileName, ".glb")) model = LoadGLTF(fileName);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_VOX) #if defined(SUPPORT_FILEFORMAT_VOX)
if (IsFileExtension(fileName, ".vox")) model = LoadVOX(fileName); if (IsFileExtension(fileName, ".vox")) model = LoadVOX(fileName);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_M3D)
if (IsFileExtension(fileName, ".m3d")) model = LoadM3D(fileName);
#endif
// Make sure model transform is set to identity matrix! // Make sure model transform is set to identity matrix!
model.transform = MatrixIdentity(); model.transform = MatrixIdentity();
@ -1083,7 +1104,7 @@ void UploadMesh(Mesh *mesh, bool dynamic)
mesh->vaoId = rlLoadVertexArray(); mesh->vaoId = rlLoadVertexArray();
rlEnableVertexArray(mesh->vaoId); rlEnableVertexArray(mesh->vaoId);
// NOTE: Attributes must be uploaded considering default locations points // NOTE: Vertex attributes must be uploaded considering default locations points and available vertex data
// Enable vertex attributes: position (shader-location = 0) // Enable vertex attributes: position (shader-location = 0)
void *vertices = mesh->animVertices != NULL ? mesh->animVertices : mesh->vertices; void *vertices = mesh->animVertices != NULL ? mesh->animVertices : mesh->vertices;
@ -1096,6 +1117,9 @@ void UploadMesh(Mesh *mesh, bool dynamic)
rlSetVertexAttribute(1, 2, RL_FLOAT, 0, 0, 0); rlSetVertexAttribute(1, 2, RL_FLOAT, 0, 0, 0);
rlEnableVertexAttribute(1); rlEnableVertexAttribute(1);
// WARNING: When setting default vertex attribute values, the values for each generic vertex attribute
// is part of current state and it is maintained even if a different program object is used
if (mesh->normals != NULL) if (mesh->normals != NULL)
{ {
// Enable vertex attributes: normals (shader-location = 2) // Enable vertex attributes: normals (shader-location = 2)
@ -1106,7 +1130,8 @@ void UploadMesh(Mesh *mesh, bool dynamic)
} }
else else
{ {
// Default color vertex attribute set to WHITE // Default vertex attribute: normal
// WARNING: Default value provided to shader if location available
float value[3] = { 1.0f, 1.0f, 1.0f }; float value[3] = { 1.0f, 1.0f, 1.0f };
rlSetVertexAttributeDefault(2, value, SHADER_ATTRIB_VEC3, 3); rlSetVertexAttributeDefault(2, value, SHADER_ATTRIB_VEC3, 3);
rlDisableVertexAttribute(2); rlDisableVertexAttribute(2);
@ -1121,8 +1146,9 @@ void UploadMesh(Mesh *mesh, bool dynamic)
} }
else else
{ {
// Default color vertex attribute set to WHITE // Default vertex attribute: color
float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; // WARNING: Default value provided to shader if location available
float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; // WHITE
rlSetVertexAttributeDefault(3, value, SHADER_ATTRIB_VEC4, 4); rlSetVertexAttributeDefault(3, value, SHADER_ATTRIB_VEC4, 4);
rlDisableVertexAttribute(3); rlDisableVertexAttribute(3);
} }
@ -1136,7 +1162,8 @@ void UploadMesh(Mesh *mesh, bool dynamic)
} }
else else
{ {
// Default tangents vertex attribute // Default vertex attribute: tangent
// WARNING: Default value provided to shader if location available
float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
rlSetVertexAttributeDefault(4, value, SHADER_ATTRIB_VEC4, 4); rlSetVertexAttributeDefault(4, value, SHADER_ATTRIB_VEC4, 4);
rlDisableVertexAttribute(4); rlDisableVertexAttribute(4);
@ -1151,7 +1178,8 @@ void UploadMesh(Mesh *mesh, bool dynamic)
} }
else else
{ {
// Default texcoord2 vertex attribute // Default vertex attribute: texcoord2
// WARNING: Default value provided to shader if location available
float value[2] = { 0.0f, 0.0f }; float value[2] = { 0.0f, 0.0f };
rlSetVertexAttributeDefault(5, value, SHADER_ATTRIB_VEC2, 2); rlSetVertexAttributeDefault(5, value, SHADER_ATTRIB_VEC2, 2);
rlDisableVertexAttribute(5); rlDisableVertexAttribute(5);
@ -1170,7 +1198,7 @@ void UploadMesh(Mesh *mesh, bool dynamic)
} }
// Update mesh vertex data in GPU for a specific buffer index // Update mesh vertex data in GPU for a specific buffer index
void UpdateMeshBuffer(Mesh mesh, int index, void *data, int dataSize, int offset) void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset)
{ {
rlUpdateVertexBuffer(mesh.vboId[index], data, dataSize, offset); rlUpdateVertexBuffer(mesh.vboId[index], data, dataSize, offset);
} }
@ -1289,8 +1317,10 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
} }
} }
// Try binding vertex array objects (VAO) // Try binding vertex array objects (VAO) or use VBOs if not possible
// or use VBOs if not possible // WARNING: UploadMesh() enables all vertex attributes available in mesh and sets default attribute values
// for shader expected vertex attributes that are not provided by the mesh (i.e. colors)
// This could be a dangerous approach because different meshes with different shaders can enable/disable some attributes
if (!rlEnableVertexArray(mesh.vaoId)) if (!rlEnableVertexArray(mesh.vaoId))
{ {
// Bind mesh VBO data: vertex position (shader-location = 0) // Bind mesh VBO data: vertex position (shader-location = 0)
@ -1322,10 +1352,10 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
} }
else else
{ {
// Set default value for unused attribute // Set default value for defined vertex attribute in shader but not provided by mesh
// NOTE: Required when using default shader and no VAO support // WARNING: It could result in GPU undefined behaviour
float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC2, 4); rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4);
rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
} }
} }
@ -1349,6 +1379,9 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]); if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]);
} }
// WARNING: Disable vertex attribute color input if mesh can not provide that data (despite location being enabled in shader)
if (mesh.vboId[3] == 0) rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
int eyeCount = 1; int eyeCount = 1;
if (rlIsStereoRenderEnabled()) eyeCount = 2; if (rlIsStereoRenderEnabled()) eyeCount = 2;
@ -1374,6 +1407,8 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
// Unbind all binded texture maps // Unbind all binded texture maps
for (int i = 0; i < MAX_MATERIAL_MAPS; i++) for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
{
if (material.maps[i].texture.id > 0)
{ {
// Select current shader texture slot // Select current shader texture slot
rlActiveTextureSlot(i); rlActiveTextureSlot(i);
@ -1384,6 +1419,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
(i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap(); (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap();
else rlDisableTexture(); else rlDisableTexture();
} }
}
// Disable all possible vertex array objects (or VBOs) // Disable all possible vertex array objects (or VBOs)
rlDisableVertexArray(); rlDisableVertexArray();
@ -1400,7 +1436,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
} }
// Draw multiple mesh instances with material and different transforms // Draw multiple mesh instances with material and different transforms
void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int instances) void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances)
{ {
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// Instancing required variables // Instancing required variables
@ -1540,7 +1576,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int ins
// Set default value for unused attribute // Set default value for unused attribute
// NOTE: Required when using default shader and no VAO support // NOTE: Required when using default shader and no VAO support
float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC2, 4); rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4);
rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
} }
} }
@ -1564,6 +1600,9 @@ void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int ins
if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]); if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]);
} }
// WARNING: Disable vertex attribute color input if mesh can not provide that data (despite location being enabled in shader)
if (mesh.vboId[3] == 0) rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
int eyeCount = 1; int eyeCount = 1;
if (rlIsStereoRenderEnabled()) eyeCount = 2; if (rlIsStereoRenderEnabled()) eyeCount = 2;
@ -1589,6 +1628,8 @@ void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int ins
// Unbind all binded texture maps // Unbind all binded texture maps
for (int i = 0; i < MAX_MATERIAL_MAPS; i++) for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
{
if (material.maps[i].texture.id > 0)
{ {
// Select current shader texture slot // Select current shader texture slot
rlActiveTextureSlot(i); rlActiveTextureSlot(i);
@ -1599,6 +1640,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int ins
(i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap(); (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap();
else rlDisableTexture(); else rlDisableTexture();
} }
}
// Disable all possible vertex array objects (or VBOs) // Disable all possible vertex array objects (or VBOs)
rlDisableVertexArray(); rlDisableVertexArray();
@ -1620,7 +1662,7 @@ void UnloadMesh(Mesh mesh)
// Unload rlgl mesh vboId data // Unload rlgl mesh vboId data
rlUnloadVertexArray(mesh.vaoId); rlUnloadVertexArray(mesh.vaoId);
for (int i = 0; i < MAX_MESH_VERTEX_BUFFERS; i++) rlUnloadVertexBuffer(mesh.vboId[i]); if (mesh.vboId != NULL) for (int i = 0; i < MAX_MESH_VERTEX_BUFFERS; i++) rlUnloadVertexBuffer(mesh.vboId[i]);
RL_FREE(mesh.vboId); RL_FREE(mesh.vboId);
RL_FREE(mesh.vertices); RL_FREE(mesh.vertices);
@ -1645,13 +1687,13 @@ bool ExportMesh(Mesh mesh, const char *fileName)
if (IsFileExtension(fileName, ".obj")) if (IsFileExtension(fileName, ".obj"))
{ {
// Estimated data size, it should be enough... // Estimated data size, it should be enough...
int dataSize = mesh.vertexCount/3* (int)strlen("v 0000.00f 0000.00f 0000.00f") + int dataSize = mesh.vertexCount*(int)strlen("v 0000.00f 0000.00f 0000.00f") +
mesh.vertexCount/2* (int)strlen("vt 0.000f 0.00f") + mesh.vertexCount*(int)strlen("vt 0.000f 0.00f") +
mesh.vertexCount/3* (int)strlen("vn 0.000f 0.00f 0.00f") + mesh.vertexCount*(int)strlen("vn 0.000f 0.00f 0.00f") +
mesh.triangleCount/3* (int)strlen("f 00000/00000/00000 00000/00000/00000 00000/00000/00000"); mesh.triangleCount*(int)strlen("f 00000/00000/00000 00000/00000/00000 00000/00000/00000");
// NOTE: Text data buffer size is estimated considering mesh data size // NOTE: Text data buffer size is estimated considering mesh data size
char *txtData = (char *)RL_CALLOC(dataSize + 2000, sizeof(char)); char *txtData = (char *)RL_CALLOC(dataSize*2 + 2000, sizeof(char));
int byteCount = 0; int byteCount = 0;
byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n"); byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n");
@ -1661,7 +1703,7 @@ bool ExportMesh(Mesh mesh, const char *fileName)
byteCount += sprintf(txtData + byteCount, "# // more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "# // more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "# // feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "# // feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "# // //\n"); byteCount += sprintf(txtData + byteCount, "# // //\n");
byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018-2022 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "# // //\n"); byteCount += sprintf(txtData + byteCount, "# // //\n");
byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n\n"); byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n\n");
byteCount += sprintf(txtData + byteCount, "# Vertex Count: %i\n", mesh.vertexCount); byteCount += sprintf(txtData + byteCount, "# Vertex Count: %i\n", mesh.vertexCount);
@ -1684,9 +1726,22 @@ bool ExportMesh(Mesh mesh, const char *fileName)
byteCount += sprintf(txtData + byteCount, "vn %.3f %.3f %.3f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]); byteCount += sprintf(txtData + byteCount, "vn %.3f %.3f %.3f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]);
} }
for (int i = 0; i < mesh.triangleCount; i += 3) if (mesh.indices != NULL)
{ {
byteCount += sprintf(txtData + byteCount, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i, i, i, i + 1, i + 1, i + 1, i + 2, i + 2, i + 2); for (int i = 0, v = 0; i < mesh.triangleCount; i++, v += 3)
{
byteCount += sprintf(txtData + byteCount, "f %i/%i/%i %i/%i/%i %i/%i/%i\n",
mesh.indices[v] + 1, mesh.indices[v] + 1, mesh.indices[v] + 1,
mesh.indices[v + 1] + 1, mesh.indices[v + 1] + 1, mesh.indices[v + 1] + 1,
mesh.indices[v + 2] + 1, mesh.indices[v + 2] + 1, mesh.indices[v + 2] + 1);
}
}
else
{
for (int i = 0, v = 1; i < mesh.triangleCount; i++, v += 3)
{
byteCount += sprintf(txtData + byteCount, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", v, v, v, v + 1, v + 1, v + 1, v + 2, v + 2, v + 2);
}
} }
byteCount += sprintf(txtData + byteCount, "\n"); byteCount += sprintf(txtData + byteCount, "\n");
@ -1704,7 +1759,6 @@ bool ExportMesh(Mesh mesh, const char *fileName)
return success; return success;
} }
// Load materials from model file // Load materials from model file
Material *LoadMaterials(const char *fileName, int *materialCount) Material *LoadMaterials(const char *fileName, int *materialCount)
{ {
@ -1771,10 +1825,13 @@ void UnloadMaterial(Material material)
if (material.shader.id != rlGetShaderIdDefault()) UnloadShader(material.shader); if (material.shader.id != rlGetShaderIdDefault()) UnloadShader(material.shader);
// Unload loaded texture maps (avoid unloading default texture, managed by raylib) // Unload loaded texture maps (avoid unloading default texture, managed by raylib)
if (material.maps != NULL)
{
for (int i = 0; i < MAX_MATERIAL_MAPS; i++) for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
{ {
if (material.maps[i].texture.id != rlGetTextureIdDefault()) rlUnloadTexture(material.maps[i].texture.id); if (material.maps[i].texture.id != rlGetTextureIdDefault()) rlUnloadTexture(material.maps[i].texture.id);
} }
}
RL_FREE(material.maps); RL_FREE(material.maps);
} }
@ -1802,6 +1859,9 @@ ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCoun
#if defined(SUPPORT_FILEFORMAT_IQM) #if defined(SUPPORT_FILEFORMAT_IQM)
if (IsFileExtension(fileName, ".iqm")) animations = LoadModelAnimationsIQM(fileName, animCount); if (IsFileExtension(fileName, ".iqm")) animations = LoadModelAnimationsIQM(fileName, animCount);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_M3D)
if (IsFileExtension(fileName, ".m3d")) animations = LoadModelAnimationsM3D(fileName, animCount);
#endif
#if defined(SUPPORT_FILEFORMAT_GLTF) #if defined(SUPPORT_FILEFORMAT_GLTF)
//if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadModelAnimationGLTF(fileName, animCount); //if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadModelAnimationGLTF(fileName, animCount);
#endif #endif
@ -2412,13 +2472,13 @@ Mesh GenMeshCylinder(float radius, float height, int slices)
par_shapes_mesh *cylinder = par_shapes_create_cylinder(slices, 8); par_shapes_mesh *cylinder = par_shapes_create_cylinder(slices, 8);
par_shapes_scale(cylinder, radius, radius, height); par_shapes_scale(cylinder, radius, radius, height);
par_shapes_rotate(cylinder, -PI/2.0f, (float[]){ 1, 0, 0 }); par_shapes_rotate(cylinder, -PI/2.0f, (float[]){ 1, 0, 0 });
par_shapes_rotate(cylinder, PI/2.0f, (float[]){ 0, 1, 0 });
// Generate an orientable disk shape (top cap) // 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 }); 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); capTop->tcoords = PAR_MALLOC(float, 2*capTop->npoints);
for (int i = 0; i < 2*capTop->npoints; i++) capTop->tcoords[i] = 0.0f; 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_rotate(capTop, -PI/2.0f, (float[]){ 1, 0, 0 });
par_shapes_rotate(capTop, 90*DEG2RAD, (float[]){ 0, 1, 0 });
par_shapes_translate(capTop, 0, height, 0); par_shapes_translate(capTop, 0, height, 0);
// Generate an orientable disk shape (bottom cap) // Generate an orientable disk shape (bottom cap)
@ -2426,6 +2486,7 @@ Mesh GenMeshCylinder(float radius, float height, int slices)
capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints); capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints);
for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f; for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f;
par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 }); par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 });
par_shapes_rotate(capBottom, -90*DEG2RAD, (float[]){ 0, 1, 0 });
par_shapes_merge_and_free(cylinder, capTop); par_shapes_merge_and_free(cylinder, capTop);
par_shapes_merge_and_free(cylinder, capBottom); par_shapes_merge_and_free(cylinder, capBottom);
@ -2610,7 +2671,7 @@ Mesh GenMeshKnot(float radius, float size, int radSeg, int sides)
// NOTE: Vertex data is uploaded to GPU // NOTE: Vertex data is uploaded to GPU
Mesh GenMeshHeightmap(Image heightmap, Vector3 size) Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
{ {
#define GRAY_VALUE(c) ((c.r+c.g+c.b)/3) #define GRAY_VALUE(c) ((c.r+c.g+c.b)/3.0f)
Mesh mesh = { 0 }; Mesh mesh = { 0 };
@ -2633,8 +2694,6 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
int tcCounter = 0; // Used to count texcoords float by float int tcCounter = 0; // Used to count texcoords float by float
int nCounter = 0; // Used to count normals float by float int nCounter = 0; // Used to count normals float by float
int trisCounter = 0;
Vector3 scaleFactor = { size.x/mapX, size.y/255.0f, size.z/mapZ }; Vector3 scaleFactor = { size.x/mapX, size.y/255.0f, size.z/mapZ };
Vector3 vA = { 0 }; Vector3 vA = { 0 };
@ -2651,15 +2710,15 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
// one triangle - 3 vertex // one triangle - 3 vertex
mesh.vertices[vCounter] = (float)x*scaleFactor.x; mesh.vertices[vCounter] = (float)x*scaleFactor.x;
mesh.vertices[vCounter + 1] = (float)GRAY_VALUE(pixels[x + z*mapX])*scaleFactor.y; mesh.vertices[vCounter + 1] = GRAY_VALUE(pixels[x + z*mapX])*scaleFactor.y;
mesh.vertices[vCounter + 2] = (float)z*scaleFactor.z; mesh.vertices[vCounter + 2] = (float)z*scaleFactor.z;
mesh.vertices[vCounter + 3] = (float)x*scaleFactor.x; 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 + 4] = GRAY_VALUE(pixels[x + (z + 1)*mapX])*scaleFactor.y;
mesh.vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z; mesh.vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z;
mesh.vertices[vCounter + 6] = (float)(x + 1)*scaleFactor.x; 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 + 7] = GRAY_VALUE(pixels[(x + 1) + z*mapX])*scaleFactor.y;
mesh.vertices[vCounter + 8] = (float)z*scaleFactor.z; mesh.vertices[vCounter + 8] = (float)z*scaleFactor.z;
// another triangle - 3 vertex // another triangle - 3 vertex
@ -2672,7 +2731,7 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
mesh.vertices[vCounter + 14] = mesh.vertices[vCounter + 5]; mesh.vertices[vCounter + 14] = mesh.vertices[vCounter + 5];
mesh.vertices[vCounter + 15] = (float)(x + 1)*scaleFactor.x; 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 + 16] = GRAY_VALUE(pixels[(x + 1) + (z + 1)*mapX])*scaleFactor.y;
mesh.vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z; mesh.vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z;
vCounter += 18; // 6 vertex, 18 floats vCounter += 18; // 6 vertex, 18 floats
@ -2729,7 +2788,6 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
} }
nCounter += 18; // 6 vertex, 18 floats nCounter += 18; // 6 vertex, 18 floats
trisCounter += 2;
} }
} }
@ -3223,19 +3281,6 @@ void GenMeshTangents(Mesh *mesh)
TRACELOG(LOG_INFO, "MESH: Tangents data computed and uploaded for provided mesh"); TRACELOG(LOG_INFO, "MESH: Tangents data computed and uploaded for provided mesh");
} }
// Compute mesh binormals (aka bitangent)
void GenMeshBinormals(Mesh *mesh)
{
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 = { 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]);
// TODO: Register computed binormal in mesh->binormal?
}
}
// Draw a model (with texture if set) // Draw a model (with texture if set)
void DrawModel(Model model, Vector3 position, float scale, Color tint) void DrawModel(Model model, Vector3 position, float scale, Color tint)
{ {
@ -3264,10 +3309,10 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota
Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color; Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color;
Color colorTint = WHITE; Color colorTint = WHITE;
colorTint.r = (unsigned char)((((float)color.r/255.0)*((float)tint.r/255.0))*255.0f); colorTint.r = (unsigned char)((((float)color.r/255.0f)*((float)tint.r/255.0f))*255.0f);
colorTint.g = (unsigned char)((((float)color.g/255.0)*((float)tint.g/255.0))*255.0f); colorTint.g = (unsigned char)((((float)color.g/255.0f)*((float)tint.g/255.0f))*255.0f);
colorTint.b = (unsigned char)((((float)color.b/255.0)*((float)tint.b/255.0))*255.0f); colorTint.b = (unsigned char)((((float)color.b/255.0f)*((float)tint.b/255.0f))*255.0f);
colorTint.a = (unsigned char)((((float)color.a/255.0)*((float)tint.a/255.0))*255.0f); colorTint.a = (unsigned char)((((float)color.a/255.0f)*((float)tint.a/255.0f))*255.0f);
model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint; model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint;
DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform);
@ -3315,7 +3360,7 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector
void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint) void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint)
{ {
// NOTE: Billboard size will maintain source rectangle aspect ratio, size will represent billboard width // NOTE: Billboard size will maintain source rectangle aspect ratio, size will represent billboard width
Vector2 sizeRatio = { size.y, size.x*(float)source.height/source.width }; Vector2 sizeRatio = { size.x*(float)source.width/source.height, size.y };
Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
@ -3376,7 +3421,7 @@ void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector
bottomRight = Vector3Add(bottomRight, position); bottomRight = Vector3Add(bottomRight, position);
bottomLeft = Vector3Add(bottomLeft, position); bottomLeft = Vector3Add(bottomLeft, position);
rlCheckRenderBatchLimit(4); rlCheckRenderBatchLimit(8);
rlSetTexture(texture.id); rlSetTexture(texture.id);
@ -3620,31 +3665,12 @@ RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform)
return collision; return collision;
} }
// Get collision info between ray and model
RayCollision GetRayCollisionModel(Ray ray, Model model)
{
RayCollision collision = { 0 };
for (int m = 0; m < model.meshCount; m++)
{
RayCollision meshHitInfo = GetRayCollisionMesh(ray, model.meshes[m], model.transform);
if (meshHitInfo.hit)
{
// Save the closest hit mesh
if ((!collision.hit) || (collision.distance > meshHitInfo.distance)) collision = meshHitInfo;
}
}
return collision;
}
// Get collision info between ray and triangle // Get collision info between ray and triangle
// NOTE: The points are expected to be in counter-clockwise winding // NOTE: The points are expected to be in counter-clockwise winding
// NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm // NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3)
{ {
#define EPSILON 0.000001 // A small number #define EPSILON 0.000001f // A small number
RayCollision collision = { 0 }; RayCollision collision = { 0 };
Vector3 edge1 = { 0 }; Vector3 edge1 = { 0 };
@ -4642,7 +4668,6 @@ static Model LoadGLTF(const char *fileName)
// Load our model data: meshes and materials // Load our model data: meshes and materials
model.meshCount = primitivesCount; model.meshCount = primitivesCount;
model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
for (int i = 0; i < model.meshCount; i++) model.meshes[i].vboId = (unsigned int*)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int));
// NOTE: We keep an extra slot for default material, in case some mesh requires it // NOTE: We keep an extra slot for default material, in case some mesh requires it
model.materialCount = (int)data->materials_count + 1; model.materialCount = (int)data->materials_count + 1;
@ -4672,13 +4697,12 @@ static Model LoadGLTF(const char *fileName)
model.materials[j].maps[MATERIAL_MAP_ALBEDO].texture = LoadTextureFromImage(imAlbedo); model.materials[j].maps[MATERIAL_MAP_ALBEDO].texture = LoadTextureFromImage(imAlbedo);
UnloadImage(imAlbedo); UnloadImage(imAlbedo);
} }
}
// Load base color factor (tint) // Load base color factor (tint)
model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255); model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255);
model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255); model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255);
model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255); model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255);
model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255); model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255);
}
// Load metallic/roughness texture // Load metallic/roughness texture
if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture) if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture)
@ -4845,7 +4869,7 @@ static Model LoadGLTF(const char *fileName)
LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
// Convert data to raylib color data type (4 bytes) // Convert data to raylib color data type (4 bytes)
for (int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(((float)temp[c]/65535.0f)*255.0f); for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(((float)temp[c]/65535.0f)*255.0f);
RL_FREE(temp); RL_FREE(temp);
} }
@ -4859,7 +4883,7 @@ static Model LoadGLTF(const char *fileName)
LOAD_ATTRIBUTE(attribute, 4, float, temp); LOAD_ATTRIBUTE(attribute, 4, float, temp);
// Convert data to raylib color data type (4 bytes), we expect the color data normalized // Convert data to raylib color data type (4 bytes), we expect the color data normalized
for (int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(temp[c]*255.0f); for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(temp[c]*255.0f);
RL_FREE(temp); RL_FREE(temp);
} }
@ -4894,7 +4918,7 @@ static Model LoadGLTF(const char *fileName)
LOAD_ATTRIBUTE(attribute, 1, unsigned int, temp); LOAD_ATTRIBUTE(attribute, 1, unsigned int, temp);
// Convert data to raylib indices data type (unsigned short) // Convert data to raylib indices data type (unsigned short)
for (int d = 0; d < attribute->count; d++) model.meshes[meshIndex].indices[d] = (unsigned short)temp[d]; for (unsigned int d = 0; d < attribute->count; d++) model.meshes[meshIndex].indices[d] = (unsigned short)temp[d];
TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data converted from u32 to u16, possible loss of data", fileName); TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data converted from u32 to u16, possible loss of data", fileName);
@ -4906,7 +4930,7 @@ static Model LoadGLTF(const char *fileName)
// Assign to the primitive mesh the corresponding material index // Assign to the primitive mesh the corresponding material index
// NOTE: If no material defined, mesh uses the already assigned default material (index: 0) // NOTE: If no material defined, mesh uses the already assigned default material (index: 0)
for (int m = 0; m < data->materials_count; m++) for (unsigned int m = 0; m < data->materials_count; m++)
{ {
// The primitive actually keeps the pointer to the corresponding material, // The primitive actually keeps the pointer to the corresponding material,
// raylib instead assigns to the mesh the by its index, as loaded in model.materials array // raylib instead assigns to the mesh the by its index, as loaded in model.materials array
@ -4923,6 +4947,63 @@ static Model LoadGLTF(const char *fileName)
} }
} }
/*
// TODO: Load glTF meshes animation data
// REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skins
// REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skinned-mesh-attributes
//----------------------------------------------------------------------------------------------------
for (unsigned int i = 0, meshIndex = 0; i < data->meshes_count; i++)
{
for (unsigned int p = 0; p < data->meshes[i].primitives_count; p++)
{
// NOTE: We only support primitives defined by triangles
if (data->meshes[i].primitives[p].type != cgltf_primitive_type_triangles) continue;
for (unsigned int j = 0; j < data->meshes[i].primitives[p].attributes_count; j++)
{
// NOTE: JOINTS_1 + WEIGHT_1 will be used for +4 joints influencing a vertex -> Not supported by raylib
if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_joints) // JOINTS_n (vec4: 4 bones max per vertex / u8, u16)
{
cgltf_accessor *attribute = data->meshes[i].primitives[p].attributes[j].data;
if ((attribute->component_type == cgltf_component_type_r_8u) && (attribute->type == cgltf_type_vec4))
{
// Init raylib mesh bone ids to copy glTF attribute data
model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
// Load 4 components of unsigned char data type into mesh.boneIds
// TODO: It seems LOAD_ATTRIBUTE() macro does not work as expected in some cases,
// for cgltf_attribute_type_joints we have:
// - data.meshes[0] (256 vertices)
// - 256 values, provided as cgltf_type_vec4 of bytes (4 byte per joint, stride 4)
LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].boneIds)
}
else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format not supported, use vec4 u8", fileName);
}
else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_weights) // WEIGHTS_n (vec4 / u8, u16, f32)
{
cgltf_accessor *attribute = data->meshes[i].primitives[p].attributes[j].data;
if ((attribute->component_type == cgltf_component_type_r_32f) && (attribute->type == cgltf_type_vec4))
{
// Init raylib mesh bone weight to copy glTF attribute data
model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
// Load 4 components of float data type into mesh.boneWeights
// for cgltf_attribute_type_weights we have:
// - data.meshes[0] (256 vertices)
// - 256 values, provided as cgltf_type_vec4 of float (4 byte per joint, stride 16)
LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].boneWeights)
}
else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint weight attribute data format not supported, use vec4 float", fileName);
}
}
meshIndex++; // Move to next mesh
}
}
*/
// Free all cgltf loaded data // Free all cgltf loaded data
cgltf_free(data); cgltf_free(data);
} }
@ -5039,3 +5120,360 @@ static Model LoadVOX(const char *fileName)
return model; return model;
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_M3D)
// Hook LoadFileData()/UnloadFileData() calls to M3D loaders
unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, len); }
void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); }
// Load M3D mesh data
static Model LoadM3D(const char *fileName)
{
Model model = { 0 };
m3d_t *m3d = NULL;
m3dp_t *prop = NULL;
unsigned int bytesRead = 0;
unsigned char *fileData = LoadFileData(fileName, &bytesRead);
int i, j, k, l, n, mi = -2;
if (fileData != NULL)
{
m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL);
if (!m3d || M3D_ERR_ISFATAL(m3d->errcode))
{
TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d ? m3d->errcode : -2);
if (m3d) m3d_free(m3d);
UnloadFileData(fileData);
return model;
}
else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i faces/%i materials", fileName, m3d->numface, m3d->nummaterial);
// no face? this is probably just a material library
if (!m3d->numface)
{
m3d_free(m3d);
UnloadFileData(fileData);
return model;
}
if (m3d->nummaterial > 0)
{
model.meshCount = model.materialCount = m3d->nummaterial;
TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", model.materialCount);
}
else
{
model.meshCount = model.materialCount = 1;
TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material");
}
model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
model.materials = (Material *)RL_CALLOC(model.materialCount + 1, sizeof(Material));
// Map no material to index 0 with default shader, everything else materialid + 1
model.materials[0] = LoadMaterialDefault();
for (i = l = 0, k = -1; i < m3d->numface; i++, l++)
{
// Materials are grouped together
if (mi != m3d->face[i].materialid)
{
// there should be only one material switch per material kind, but be bulletproof for unoptimal model files
if (k + 1 >= model.meshCount)
{
model.meshCount++;
model.meshes = (Mesh *)RL_REALLOC(model.meshes, model.meshCount*sizeof(Mesh));
memset(&model.meshes[model.meshCount - 1], 0, sizeof(Mesh));
model.meshMaterial = (int *)RL_REALLOC(model.meshMaterial, model.meshCount*sizeof(int));
}
k++;
mi = m3d->face[i].materialid;
for (j = i, l = 0; (j < m3d->numface) && (mi == m3d->face[j].materialid); j++, l++);
model.meshes[k].vertexCount = l*3;
model.meshes[k].triangleCount = l;
model.meshes[k].vertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, sizeof(float));
model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
// without material, we rely on vertex colors
if (mi == M3D_UNDEF && model.meshes[k].colors == NULL)
{
model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
for (j = 0; j < model.meshes[k].vertexCount*4; j += 4) memcpy(&model.meshes[k].colors[j], &WHITE, 4);
}
if (m3d->numbone && m3d->numskin)
{
model.meshes[k].boneIds = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
model.meshes[k].boneWeights = (float *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(float));
model.meshes[k].animVertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
model.meshes[k].animNormals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
}
model.meshMaterial[k] = mi + 1;
l = 0;
}
// Process meshes per material, add triangles
model.meshes[k].vertices[l * 9 + 0] = m3d->vertex[m3d->face[i].vertex[0]].x*m3d->scale;
model.meshes[k].vertices[l * 9 + 1] = m3d->vertex[m3d->face[i].vertex[0]].y*m3d->scale;
model.meshes[k].vertices[l * 9 + 2] = m3d->vertex[m3d->face[i].vertex[0]].z*m3d->scale;
model.meshes[k].vertices[l * 9 + 3] = m3d->vertex[m3d->face[i].vertex[1]].x*m3d->scale;
model.meshes[k].vertices[l * 9 + 4] = m3d->vertex[m3d->face[i].vertex[1]].y*m3d->scale;
model.meshes[k].vertices[l * 9 + 5] = m3d->vertex[m3d->face[i].vertex[1]].z*m3d->scale;
model.meshes[k].vertices[l * 9 + 6] = m3d->vertex[m3d->face[i].vertex[2]].x*m3d->scale;
model.meshes[k].vertices[l * 9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y*m3d->scale;
model.meshes[k].vertices[l * 9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z*m3d->scale;
if (mi == M3D_UNDEF)
{
// without vertex color (full transparency), we use the default color
if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xFF000000)
memcpy(&model.meshes[k].colors[l * 12 + 0], &m3d->vertex[m3d->face[i].vertex[0]].color, 4);
if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xFF000000)
memcpy(&model.meshes[k].colors[l * 12 + 4], &m3d->vertex[m3d->face[i].vertex[1]].color, 4);
if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xFF000000)
memcpy(&model.meshes[k].colors[l * 12 + 8], &m3d->vertex[m3d->face[i].vertex[2]].color, 4);
}
if (m3d->face[i].texcoord[0] != M3D_UNDEF)
{
model.meshes[k].texcoords[l * 6 + 0] = m3d->tmap[m3d->face[i].texcoord[0]].u;
model.meshes[k].texcoords[l * 6 + 1] = 1.0 - m3d->tmap[m3d->face[i].texcoord[0]].v;
model.meshes[k].texcoords[l * 6 + 2] = m3d->tmap[m3d->face[i].texcoord[1]].u;
model.meshes[k].texcoords[l * 6 + 3] = 1.0 - m3d->tmap[m3d->face[i].texcoord[1]].v;
model.meshes[k].texcoords[l * 6 + 4] = m3d->tmap[m3d->face[i].texcoord[2]].u;
model.meshes[k].texcoords[l * 6 + 5] = 1.0 - m3d->tmap[m3d->face[i].texcoord[2]].v;
}
if (m3d->face[i].normal[0] != M3D_UNDEF)
{
model.meshes[k].normals[l * 9 + 0] = m3d->vertex[m3d->face[i].normal[0]].x;
model.meshes[k].normals[l * 9 + 1] = m3d->vertex[m3d->face[i].normal[0]].y;
model.meshes[k].normals[l * 9 + 2] = m3d->vertex[m3d->face[i].normal[0]].z;
model.meshes[k].normals[l * 9 + 3] = m3d->vertex[m3d->face[i].normal[1]].x;
model.meshes[k].normals[l * 9 + 4] = m3d->vertex[m3d->face[i].normal[1]].y;
model.meshes[k].normals[l * 9 + 5] = m3d->vertex[m3d->face[i].normal[1]].z;
model.meshes[k].normals[l * 9 + 6] = m3d->vertex[m3d->face[i].normal[2]].x;
model.meshes[k].normals[l * 9 + 7] = m3d->vertex[m3d->face[i].normal[2]].y;
model.meshes[k].normals[l * 9 + 8] = m3d->vertex[m3d->face[i].normal[2]].z;
}
// Add skin (vertex / bone weight pairs)
if (m3d->numbone && m3d->numskin) {
for (n = 0; n < 3; n++) {
int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid;
// check if there's a skin for this mesh, should be, just failsafe
if (skinid != M3D_UNDEF && skinid < m3d->numskin)
{
for (j = 0; j < 4; j++)
{
model.meshes[k].boneIds[l * 12 + n * 4 + j] = m3d->skin[skinid].boneid[j];
model.meshes[k].boneWeights[l * 12 + n * 4 + j] = m3d->skin[skinid].weight[j];
}
}
}
}
}
// Load materials
for (i = 0; i < m3d->nummaterial; i++)
{
model.materials[i + 1] = LoadMaterialDefault();
for (j = 0; j < m3d->material[i].numprop; j++)
{
prop = &m3d->material[i].prop[j];
switch (prop->type)
{
case m3dp_Kd:
{
memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].color, &prop->value.color, 4);
model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f;
} break;
case m3dp_Ks:
{
memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].color, &prop->value.color, 4);
} break;
case m3dp_Ns:
{
model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].value = prop->value.fnum;
} break;
case m3dp_Ke:
{
memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].color, &prop->value.color, 4);
model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].value = 0.0f;
} break;
case m3dp_Pm:
{
model.materials[i + 1].maps[MATERIAL_MAP_METALNESS].value = prop->value.fnum;
} break;
case m3dp_Pr:
{
model.materials[i + 1].maps[MATERIAL_MAP_ROUGHNESS].value = prop->value.fnum;
} break;
case m3dp_Ps:
{
model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].color = WHITE;
model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].value = prop->value.fnum;
} break;
default:
{
if (prop->type >= 128)
{
Image image = { 0 };
image.data = m3d->texture[prop->value.textureid].d;
image.width = m3d->texture[prop->value.textureid].w;
image.height = m3d->texture[prop->value.textureid].h;
image.mipmaps = 1;
image.format = (m3d->texture[prop->value.textureid].f == 4)? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 :
((m3d->texture[prop->value.textureid].f == 3)? PIXELFORMAT_UNCOMPRESSED_R8G8B8 :
((m3d->texture[prop->value.textureid].f == 2)? PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA : PIXELFORMAT_UNCOMPRESSED_GRAYSCALE));
switch (prop->type)
{
case m3dp_map_Kd: model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTextureFromImage(image); break;
case m3dp_map_Ks: model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].texture = LoadTextureFromImage(image); break;
case m3dp_map_Ke: model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].texture = LoadTextureFromImage(image); break;
case m3dp_map_Km: model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].texture = LoadTextureFromImage(image); break;
case m3dp_map_Ka: model.materials[i + 1].maps[MATERIAL_MAP_OCCLUSION].texture = LoadTextureFromImage(image); break;
case m3dp_map_Pm: model.materials[i + 1].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(image); break;
default: break;
}
}
} break;
}
}
}
// Load bones
if(m3d->numbone)
{
model.boneCount = m3d->numbone;
model.bones = RL_MALLOC(m3d->numbone*sizeof(BoneInfo));
model.bindPose = RL_MALLOC(m3d->numbone*sizeof(Transform));
for (i = 0; i < m3d->numbone; i++)
{
model.bones[i].parent = m3d->bone[i].parent;
strncpy(model.bones[i].name, m3d->bone[i].name, sizeof(model.bones[i].name));
model.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x;
model.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y;
model.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z;
model.bindPose[i].rotation.x = m3d->vertex[m3d->bone[i].ori].x;
model.bindPose[i].rotation.y = m3d->vertex[m3d->bone[i].ori].y;
model.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z;
model.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w;
// TODO: if the orientation quaternion not normalized, then that's encoding scaling
model.bindPose[i].rotation = QuaternionNormalize(model.bindPose[i].rotation);
model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f;
}
}
// Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets
// called, but not before, however DrawMesh uses these if they exists (so not good if they are left empty).
if (m3d->numbone && m3d->numskin)
{
for(i = 0; i < model.meshCount; i++)
{
memcpy(model.meshes[i].animVertices, model.meshes[i].vertices, model.meshes[i].vertexCount*3*sizeof(float));
memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float));
}
}
m3d_free(m3d);
UnloadFileData(fileData);
}
return model;
}
// Load M3D animation data
#define M3D_ANIMDELAY 17 // that's roughly ~1000 msec / 60 FPS (16.666666* msec)
static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount)
{
m3d_t *m3d = NULL;
unsigned int bytesRead = 0;
unsigned char *fileData = LoadFileData(fileName, &bytesRead);
ModelAnimation *animations = NULL;
int i, j;
*animCount = 0;
if (fileData != NULL)
{
m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL);
if (!m3d || M3D_ERR_ISFATAL(m3d->errcode))
{
TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d ? m3d->errcode : -2);
UnloadFileData(fileData);
return NULL;
}
else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins", fileName,
m3d->numaction, m3d->numbone, m3d->numskin);
// no animation or bone+skin?
if (!m3d->numaction || !m3d->numbone || !m3d->numskin)
{
m3d_free(m3d);
UnloadFileData(fileData);
return NULL;
}
animations = RL_MALLOC(m3d->numaction*sizeof(ModelAnimation));
*animCount = m3d->numaction;
for (unsigned int a = 0; a < m3d->numaction; a++)
{
animations[a].frameCount = m3d->action[a].durationmsec / M3D_ANIMDELAY;
animations[a].boneCount = m3d->numbone;
animations[a].bones = RL_MALLOC(m3d->numbone*sizeof(BoneInfo));
animations[a].framePoses = RL_MALLOC(animations[a].frameCount*sizeof(Transform *));
// strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name));
TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount);
for (i = 0; i < m3d->numbone; i++)
{
animations[a].bones[i].parent = m3d->bone[i].parent;
strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name));
}
// M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at
// regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones
for (i = 0; i < animations[a].frameCount; i++)
{
animations[a].framePoses[i] = RL_MALLOC(m3d->numbone*sizeof(Transform));
m3db_t *pose = m3d_pose(m3d, a, i * M3D_ANIMDELAY);
if (pose != NULL)
{
for (j = 0; j < m3d->numbone; j++)
{
animations[a].framePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x;
animations[a].framePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y;
animations[a].framePoses[i][j].translation.z = m3d->vertex[pose[j].pos].z;
animations[a].framePoses[i][j].rotation.x = m3d->vertex[pose[j].ori].x;
animations[a].framePoses[i][j].rotation.y = m3d->vertex[pose[j].ori].y;
animations[a].framePoses[i][j].rotation.z = m3d->vertex[pose[j].ori].z;
animations[a].framePoses[i][j].rotation.w = m3d->vertex[pose[j].ori].w;
animations[a].framePoses[i][j].rotation = QuaternionNormalize(animations[a].framePoses[i][j].rotation);
animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f;
}
RL_FREE(pose);
}
}
}
m3d_free(m3d);
UnloadFileData(fileData);
}
return animations;
}
#endif
#endif // SUPPORT_MODULE_RMODELS

View file

@ -17,13 +17,16 @@
* *
* CONFIGURATION: * CONFIGURATION:
* *
* #define SUPPORT_MODULE_RSHAPES
* rshapes module is included in the build
*
* #define SUPPORT_QUADS_DRAW_MODE * #define SUPPORT_QUADS_DRAW_MODE
* Use QUADS instead of TRIANGLES for drawing when possible. Lines-based shapes still use LINES * Use QUADS instead of TRIANGLES for drawing when possible. Lines-based shapes still use LINES
* *
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2013-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2013-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -49,6 +52,8 @@
#include "config.h" // Defines module configuration flags #include "config.h" // Defines module configuration flags
#endif #endif
#if defined(SUPPORT_MODULE_RSHAPES)
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
#include <math.h> // Required for: sinf(), asinf(), cosf(), acosf(), sqrtf(), fabsf() #include <math.h> // Required for: sinf(), asinf(), cosf(), acosf(), sqrtf(), fabsf()
@ -76,7 +81,7 @@
// Global Variables Definition // Global Variables Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (usually a white pixel) Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (usually a white pixel)
Rectangle texShapesRec = { 0, 0, 1, 1 }; // Texture source rectangle used on shapes drawing Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Declaration // Module specific Functions Declaration
@ -780,7 +785,6 @@ void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3,
rlSetTexture(texShapes.id); rlSetTexture(texShapes.id);
rlPushMatrix();
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
rlNormal3f(0.0f, 0.0f, 1.0f); rlNormal3f(0.0f, 0.0f, 1.0f);
@ -801,7 +805,6 @@ void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3,
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(rec.x + rec.width, rec.y); rlVertex2f(rec.x + rec.width, rec.y);
rlEnd(); rlEnd();
rlPopMatrix();
rlSetTexture(0); rlSetTexture(0);
} }
@ -1810,3 +1813,5 @@ static float EaseCubicInOut(float t, float b, float c, float d)
return 0.5f*c*(t*t*t + 2.0f) + b; return 0.5f*c*(t*t*t + 2.0f) + b;
} }
#endif // SUPPORT_MODULE_RSHAPES

View file

@ -4,6 +4,9 @@
* *
* CONFIGURATION: * CONFIGURATION:
* *
* #define SUPPORT_MODULE_RTEXT
* rtext module is included in the build
*
* #define SUPPORT_FILEFORMAT_FNT * #define SUPPORT_FILEFORMAT_FNT
* #define SUPPORT_FILEFORMAT_TTF * #define SUPPORT_FILEFORMAT_TTF
* Selected desired fileformats to be supported for loading. Some of those formats are * Selected desired fileformats to be supported for loading. Some of those formats are
@ -22,12 +25,12 @@
* *
* DEPENDENCIES: * DEPENDENCIES:
* stb_truetype - Load TTF file and rasterize characters data * stb_truetype - Load TTF file and rasterize characters data
* stb_rect_pack - Rectangles packing algorythms, required for font atlas generation * stb_rect_pack - Rectangles packing algorithms, required for font atlas generation
* *
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2013-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2013-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -53,6 +56,8 @@
#include "config.h" // Defines module configuration flags #include "config.h" // Defines module configuration flags
#endif #endif
#if defined(SUPPORT_MODULE_RTEXT)
#include "utils.h" // Required for: LoadFileText() #include "utils.h" // Required for: LoadFileText()
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro() #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro()
@ -224,7 +229,7 @@ extern void LoadFontDefault(void)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Allocate space for our characters info data // Allocate space for our characters info data
// NOTE: This memory should be freed at end! --> CloseWindow() // NOTE: This memory must be freed at end! --> Done by CloseWindow()
defaultFont.glyphs = (GlyphInfo *)RL_MALLOC(defaultFont.glyphCount*sizeof(GlyphInfo)); defaultFont.glyphs = (GlyphInfo *)RL_MALLOC(defaultFont.glyphCount*sizeof(GlyphInfo));
defaultFont.recs = (Rectangle *)RL_MALLOC(defaultFont.glyphCount*sizeof(Rectangle)); defaultFont.recs = (Rectangle *)RL_MALLOC(defaultFont.glyphCount*sizeof(Rectangle));
@ -311,7 +316,7 @@ Font LoadFont(const char *fileName)
Font font = { 0 }; Font font = { 0 };
#if defined(SUPPORT_FILEFORMAT_TTF) #if defined(SUPPORT_FILEFORMAT_TTF)
if (IsFileExtension(fileName, ".ttf;.otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS); if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
else else
#endif #endif
#if defined(SUPPORT_FILEFORMAT_FNT) #if defined(SUPPORT_FILEFORMAT_FNT)
@ -329,7 +334,11 @@ Font LoadFont(const char *fileName)
TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName); TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
font = GetFontDefault(); font = GetFontDefault();
} }
else SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default we set point filter (best performance) else
{
SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default we set point filter (best performance)
TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", FONT_TTF_DEFAULT_SIZE, FONT_TTF_DEFAULT_NUMCHARS);
}
return font; return font;
} }
@ -515,7 +524,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
UnloadImage(atlas); UnloadImage(atlas);
// TRACELOG(LOG_INFO, "FONT: Font loaded successfully (%i glyphs)", font.glyphCount); TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
} }
else font = GetFontDefault(); else font = GetFontDefault();
} }
@ -677,8 +686,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// NOTE 2: SDF font characters already contain an internal padding, // NOTE 2: SDF font characters already contain an internal padding,
// so image size would result bigger than default font type // so image size would result bigger than default font type
float requiredArea = 0; float requiredArea = 0;
for (int i = 0; i < glyphCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(chars[i].image.height + 2*padding)); for (int i = 0; i < glyphCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(fontSize + 2*padding));
float guessSize = sqrtf(requiredArea)*1.3f; float guessSize = sqrtf(requiredArea)*1.4f;
int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT
atlas.width = imageSize; // Atlas bitmap width atlas.width = imageSize; // Atlas bitmap width
@ -690,7 +699,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// DEBUG: We can see padding in the generated image setting a gray background... // DEBUG: We can see padding in the generated image setting a gray background...
//for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100; //for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100;
if (packMethod == 0) // Use basic packing algorythm if (packMethod == 0) // Use basic packing algorithm
{ {
int offsetX = padding; int offsetX = padding;
int offsetY = padding; int offsetY = padding;
@ -725,11 +734,23 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// height is bigger than fontSize, it could be up to (fontSize + 8) // height is bigger than fontSize, it could be up to (fontSize + 8)
offsetY += (fontSize + 2*padding); offsetY += (fontSize + 2*padding);
if (offsetY > (atlas.height - fontSize - padding)) break; if (offsetY > (atlas.height - fontSize - padding))
{
for(int j = i + 1; j < glyphCount; j++)
{
TRACELOG(LOG_WARNING, "FONT: Failed to package character (%i)", j);
// make sure remaining recs contain valid data
recs[j].x = 0;
recs[j].y = 0;
recs[j].width = 0;
recs[j].height = 0;
}
break;
} }
} }
} }
else if (packMethod == 1) // Use Skyline rect packing algorythm (stb_pack_rect) }
else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect)
{ {
stbrp_context *context = (stbrp_context *)RL_MALLOC(sizeof(*context)); stbrp_context *context = (stbrp_context *)RL_MALLOC(sizeof(*context));
stbrp_node *nodes = (stbrp_node *)RL_MALLOC(glyphCount*sizeof(*nodes)); stbrp_node *nodes = (stbrp_node *)RL_MALLOC(glyphCount*sizeof(*nodes));
@ -796,11 +817,14 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// Unload font glyphs info data (RAM) // Unload font glyphs info data (RAM)
void UnloadFontData(GlyphInfo *glyphs, int glyphCount) void UnloadFontData(GlyphInfo *glyphs, int glyphCount)
{
if (glyphs != NULL)
{ {
for (int i = 0; i < glyphCount; i++) UnloadImage(glyphs[i].image); for (int i = 0; i < glyphCount; i++) UnloadImage(glyphs[i].image);
RL_FREE(glyphs); RL_FREE(glyphs);
} }
}
// Unload Font from GPU memory (VRAM) // Unload Font from GPU memory (VRAM)
void UnloadFont(Font font) void UnloadFont(Font font)
@ -816,15 +840,170 @@ void UnloadFont(Font font)
} }
} }
// Export font as code file, returns true on success
bool ExportFontAsCode(Font font, const char *fileName)
{
bool success = false;
#ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20
#endif
#define MAX_FONT_DATA_SIZE 1024*1024 // 1 MB
// Get file name from path
char fileNamePascal[256] = { 0 };
strcpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)));
// NOTE: Text data buffer size is estimated considering image data size in bytes
// and requiring 6 char bytes for every byte: "0x00, "
char *txtData = (char *)RL_CALLOC(MAX_FONT_DATA_SIZE, sizeof(char));
int byteCount = 0;
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// FontAsCode exporter v1.0 - Font data exported as an array of bytes //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2022 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// ---------------------------------------------------------------------------------- //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// TODO: Fill the information and license of the exported font here: //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Font name: .... //\n");
byteCount += sprintf(txtData + byteCount, "// Font creator: .... //\n");
byteCount += sprintf(txtData + byteCount, "// Font LICENSE: .... //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
// Support font export and initialization
// NOTE: This mechanism is highly coupled to raylib
Image image = LoadImageFromTexture(font.texture);
if (image.format != PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) TRACELOG(LOG_WARNING, "Font export as code: Font image format is not GRAY+ALPHA!");
int imageDataSize = GetPixelDataSize(image.width, image.height, image.format);
// Image data is usually GRAYSCALE + ALPHA and can be reduced to GRAYSCALE
//ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
#define SUPPORT_COMPRESSED_FONT_ATLAS
#if defined(SUPPORT_COMPRESSED_FONT_ATLAS)
// WARNING: Data is compressed using raylib CompressData() DEFLATE,
// it requires to be decompressed with raylib DecompressData(), that requires
// compiling raylib with SUPPORT_COMPRESSION_API config flag enabled
// Compress font image data
int compDataSize = 0;
unsigned char *compData = CompressData(image.data, imageDataSize, &compDataSize);
// Save font image data (compressed)
byteCount += sprintf(txtData + byteCount, "#define COMPRESSED_DATA_SIZE_FONT_%s %i\n\n", TextToUpper(fileNamePascal), compDataSize);
byteCount += sprintf(txtData + byteCount, "// Font image pixels data compressed (DEFLATE)\n");
byteCount += sprintf(txtData + byteCount, "// NOTE: Original pixel data simplified to GRAYSCALE\n");
byteCount += sprintf(txtData + byteCount, "static unsigned char fontData_%s[COMPRESSED_DATA_SIZE_FONT_%s] = { ", fileNamePascal, TextToUpper(fileNamePascal));
for (int i = 0; i < compDataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%02x,\n " : "0x%02x, "), compData[i]);
byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", compData[compDataSize - 1]);
MemFree(compData);
#else
// Save font image data (uncompressed)
byteCount += sprintf(txtData + byteCount, "// Font image pixels data\n");
byteCount += sprintf(txtData + byteCount, "// NOTE: 2 bytes per pixel, GRAY + ALPHA channels\n");
byteCount += sprintf(txtData + byteCount, "static unsigned char fontImageData_%s[%i] = { ", fileNamePascal, imageDataSize);
for (int i = 0; i < imageDataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%02x,\n " : "0x%02x, "), ((unsigned char *)imFont.data)[i]);
byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", ((unsigned char *)imFont.data)[imageDataSize - 1]);
#endif
// Save font recs data
byteCount += sprintf(txtData + byteCount, "// Font characters rectangles data\n");
byteCount += sprintf(txtData + byteCount, "static const Rectangle fontRecs_%s[%i] = {\n", fileNamePascal, font.glyphCount);
for (int i = 0; i < font.glyphCount; i++)
{
byteCount += sprintf(txtData + byteCount, " { %1.0f, %1.0f, %1.0f , %1.0f },\n", font.recs[i].x, font.recs[i].y, font.recs[i].width, font.recs[i].height);
}
byteCount += sprintf(txtData + byteCount, "};\n\n");
// Save font glyphs data
// NOTE: Glyphs image data not saved (grayscale pixels),
// it could be generated from image and recs
byteCount += sprintf(txtData + byteCount, "// Font glyphs info data\n");
byteCount += sprintf(txtData + byteCount, "// NOTE: No glyphs.image data provided\n");
byteCount += sprintf(txtData + byteCount, "static const GlyphInfo fontGlyphs_%s[%i] = {\n", fileNamePascal, font.glyphCount);
for (int i = 0; i < font.glyphCount; i++)
{
byteCount += sprintf(txtData + byteCount, " { %i, %i, %i, %i, { 0 }},\n", font.glyphs[i].value, font.glyphs[i].offsetX, font.glyphs[i].offsetY, font.glyphs[i].advanceX);
}
byteCount += sprintf(txtData + byteCount, "};\n\n");
// Custom font loading function
byteCount += sprintf(txtData + byteCount, "// Font loading function: %s\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, "static Font LoadFont_%s(void)\n{\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, " Font font = { 0 };\n\n");
byteCount += sprintf(txtData + byteCount, " font.baseSize = %i;\n", font.baseSize);
byteCount += sprintf(txtData + byteCount, " font.glyphCount = %i;\n", font.glyphCount);
byteCount += sprintf(txtData + byteCount, " font.glyphPadding = %i;\n\n", font.glyphPadding);
byteCount += sprintf(txtData + byteCount, " // Custom font loading\n");
#if defined(SUPPORT_COMPRESSED_FONT_ATLAS)
byteCount += sprintf(txtData + byteCount, " // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function\n");
byteCount += sprintf(txtData + byteCount, " int fontDataSize_%s = 0;\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, " unsigned char *data = DecompressData(fontData_%s, COMPRESSED_DATA_SIZE_FONT_%s, &fontDataSize_%s);\n", fileNamePascal, TextToUpper(fileNamePascal), fileNamePascal);
byteCount += sprintf(txtData + byteCount, " Image imFont = { data, %i, %i, 1, %i };\n\n", image.width, image.height, image.format);
#else
byteCount += sprintf(txtData + byteCount, " Image imFont = { fontImageData_%s, %i, %i, 1, %i };\n\n", styleName, image.width, image.height, image.format);
#endif
byteCount += sprintf(txtData + byteCount, " // Load texture from image\n");
byteCount += sprintf(txtData + byteCount, " font.texture = LoadTextureFromImage(imFont);\n");
#if defined(SUPPORT_COMPRESSED_FONT_ATLAS)
byteCount += sprintf(txtData + byteCount, " UnloadImage(imFont); // Uncompressed data can be unloaded from memory\n\n");
#endif
// We have two possible mechanisms to assign font.recs and font.glyphs data,
// that data is already available as global arrays, we two options to assign that data:
// - 1. Data copy. This option consumes more memory and Font MUST be unloaded by user, requiring additional code.
// - 2. Data assignment. This option consumes less memory and Font MUST NOT be unloaded by user because data is on protected DATA segment
//#define SUPPORT_FONT_DATA_COPY
#if defined(SUPPORT_FONT_DATA_COPY)
byteCount += sprintf(txtData + byteCount, " // Copy glyph recs data from global fontRecs\n");
byteCount += sprintf(txtData + byteCount, " // NOTE: Required to avoid issues if trying to free font\n");
byteCount += sprintf(txtData + byteCount, " font.recs = (Rectangle *)malloc(font.glyphCount*sizeof(Rectangle));\n");
byteCount += sprintf(txtData + byteCount, " memcpy(font.recs, fontRecs_%s, font.glyphCount*sizeof(Rectangle));\n\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, " // Copy font glyph info data from global fontChars\n");
byteCount += sprintf(txtData + byteCount, " // NOTE: Required to avoid issues if trying to free font\n");
byteCount += sprintf(txtData + byteCount, " font.glyphs = (GlyphInfo *)malloc(font.glyphCount*sizeof(GlyphInfo));\n");
byteCount += sprintf(txtData + byteCount, " memcpy(font.glyphs, fontGlyphs_%s, font.glyphCount*sizeof(GlyphInfo));\n\n", fileNamePascal);
#else
byteCount += sprintf(txtData + byteCount, " // Assign glyph recs and info data directly\n");
byteCount += sprintf(txtData + byteCount, " // WARNING: This font data must not be unloaded\n");
byteCount += sprintf(txtData + byteCount, " font.recs = fontRecs_%s;\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, " font.glyphs = fontGlyphs_%s;\n\n", fileNamePascal);
#endif
byteCount += sprintf(txtData + byteCount, " return font;\n");
byteCount += sprintf(txtData + byteCount, "}\n");
UnloadImage(image);
// NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData);
RL_FREE(txtData);
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Font as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export font as code", fileName);
return success;
}
// Draw current FPS // Draw current FPS
// NOTE: Uses default font // NOTE: Uses default font
void DrawFPS(int posX, int posY) void DrawFPS(int posX, int posY)
{ {
Color color = LIME; // good fps Color color = LIME; // Good FPS
int fps = GetFPS(); int fps = GetFPS();
if (fps < 30 && fps >= 15) color = ORANGE; // warning FPS if ((fps < 30) && (fps >= 15)) color = ORANGE; // Warning FPS
else if (fps < 15) color = RED; // bad FPS else if (fps < 15) color = RED; // Low FPS
DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, color); DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, color);
} }
@ -875,7 +1054,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
{ {
// NOTE: Fixed line spacing of 1.5 line-height // NOTE: Fixed line spacing of 1.5 line-height
// TODO: Support custom line spacing defined by user // TODO: Support custom line spacing defined by user
textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor); textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor);
textOffsetX = 0.0f; textOffsetX = 0.0f;
} }
else else
@ -931,10 +1110,42 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz
DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint); DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint);
} }
// Draw multiple character (codepoints)
void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint)
{
int textOffsetY = 0; // Offset between lines (on line break '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
for (int i = 0; i < count; i++)
{
int index = GetGlyphIndex(font, codepoints[i]);
if (codepoints[i] == '\n')
{
// NOTE: Fixed line spacing of 1.5 line-height
// TODO: Support custom line spacing defined by user
textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor);
textOffsetX = 0.0f;
}
else
{
if ((codepoints[i] != ' ') && (codepoints[i] != '\t'))
{
DrawTextCodepoint(font, codepoints[i], (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, tint);
}
if (font.glyphs[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing);
else textOffsetX += ((float)font.glyphs[index].advanceX*scaleFactor + spacing);
}
}
}
// Measure string width for default font // Measure string width for default font
int MeasureText(const char *text, int fontSize) int MeasureText(const char *text, int fontSize)
{ {
Vector2 vec = { 0.0f, 0.0f }; Vector2 textSize = { 0.0f, 0.0f };
// Check if default font has been loaded // Check if default font has been loaded
if (GetFontDefault().texture.id != 0) if (GetFontDefault().texture.id != 0)
@ -943,15 +1154,19 @@ int MeasureText(const char *text, int fontSize)
if (fontSize < defaultFontSize) fontSize = defaultFontSize; if (fontSize < defaultFontSize) fontSize = defaultFontSize;
int spacing = fontSize/defaultFontSize; int spacing = fontSize/defaultFontSize;
vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing); textSize = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing);
} }
return (int)vec.x; return (int)textSize.x;
} }
// Measure string size for Font // Measure string size for Font
Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing) Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing)
{ {
Vector2 textSize = { 0 };
if ((font.texture.id == 0) || (text == NULL)) return textSize;
int size = TextLength(text); // Get size in bytes of text int size = TextLength(text); // Get size in bytes of text
int tempByteCounter = 0; // Used to count longer text line num chars int tempByteCounter = 0; // Used to count longer text line num chars
int byteCounter = 0; int byteCounter = 0;
@ -996,11 +1211,10 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
if (tempTextWidth < textWidth) tempTextWidth = textWidth; if (tempTextWidth < textWidth) tempTextWidth = textWidth;
Vector2 vec = { 0 }; textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure
vec.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure textSize.y = textHeight*scaleFactor;
vec.y = textHeight*scaleFactor;
return vec; return textSize;
} }
// Get index position for a unicode character on font // Get index position for a unicode character on font
@ -1179,7 +1393,7 @@ const char *TextSubtext(const char *text, int position, int length)
// Replace text string // Replace text string
// REQUIRES: strlen(), strstr(), strncpy(), strcpy() // REQUIRES: strlen(), strstr(), strncpy(), strcpy()
// WARNING: Returned buffer must be freed by the user (if return != NULL) // WARNING: Allocated memory must be manually freed
char *TextReplace(char *text, const char *replace, const char *by) char *TextReplace(char *text, const char *replace, const char *by)
{ {
// Sanity checks and initialization // Sanity checks and initialization
@ -1228,7 +1442,7 @@ char *TextReplace(char *text, const char *replace, const char *by)
} }
// Insert text in a specific position, moves all text forward // Insert text in a specific position, moves all text forward
// WARNING: Allocated memory should be manually freed // WARNING: Allocated memory must be manually freed
char *TextInsert(const char *text, const char *insert, int position) char *TextInsert(const char *text, const char *insert, int position)
{ {
int textLen = TextLength(text); int textLen = TextLength(text);
@ -1412,8 +1626,8 @@ const char *TextToPascal(const char *text)
// Encode text codepoint into UTF-8 text // Encode text codepoint into UTF-8 text
// REQUIRES: memcpy() // REQUIRES: memcpy()
// WARNING: Allocated memory should be manually freed // WARNING: Allocated memory must be manually freed
char *TextCodepointsToUTF8(int *codepoints, int length) char *TextCodepointsToUTF8(const int *codepoints, int length)
{ {
// We allocate enough memory fo fit all possible codepoints // We allocate enough memory fo fit all possible codepoints
// NOTE: 5 bytes for every codepoint should be enough // NOTE: 5 bytes for every codepoint should be enough
@ -1593,7 +1807,7 @@ int GetCodepoint(const char *text, int *bytesProcessed)
if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) || if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) ||
((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; } ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; }
if ((octet >= 0xe0) && (0 <= 0xef)) if ((octet >= 0xe0) && (octet <= 0xef))
{ {
code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f);
*bytesProcessed = 3; *bytesProcessed = 3;
@ -1795,3 +2009,5 @@ static Font LoadBMFont(const char *fileName)
return font; return font;
} }
#endif #endif
#endif // SUPPORT_MODULE_RTEXT

View file

@ -4,11 +4,15 @@
* *
* CONFIGURATION: * CONFIGURATION:
* *
* #define SUPPORT_MODULE_RTEXTURES
* rtextures module is included in the build
*
* #define SUPPORT_FILEFORMAT_BMP * #define SUPPORT_FILEFORMAT_BMP
* #define SUPPORT_FILEFORMAT_PNG * #define SUPPORT_FILEFORMAT_PNG
* #define SUPPORT_FILEFORMAT_TGA * #define SUPPORT_FILEFORMAT_TGA
* #define SUPPORT_FILEFORMAT_JPG * #define SUPPORT_FILEFORMAT_JPG
* #define SUPPORT_FILEFORMAT_GIF * #define SUPPORT_FILEFORMAT_GIF
* #define SUPPORT_FILEFORMAT_QOI
* #define SUPPORT_FILEFORMAT_PSD * #define SUPPORT_FILEFORMAT_PSD
* #define SUPPORT_FILEFORMAT_PIC * #define SUPPORT_FILEFORMAT_PIC
* #define SUPPORT_FILEFORMAT_HDR * #define SUPPORT_FILEFORMAT_HDR
@ -33,12 +37,12 @@
* DEPENDENCIES: * DEPENDENCIES:
* stb_image - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC) * stb_image - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
* NOTE: stb_image has been slightly modified to support Android platform. * NOTE: stb_image has been slightly modified to support Android platform.
* stb_image_resize - Multiple image resize algorythms * stb_image_resize - Multiple image resize algorithms
* *
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2013-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2013-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -64,11 +68,13 @@
#include "config.h" // Defines module configuration flags #include "config.h" // Defines module configuration flags
#endif #endif
#if defined(SUPPORT_MODULE_RTEXTURES)
#include "utils.h" // Required for: TRACELOG() and fopen() Android mapping #include "utils.h" // Required for: TRACELOG() and fopen() Android mapping
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2 #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2
#include <stdlib.h> // Required for: malloc(), free() #include <stdlib.h> // Required for: malloc(), free()
#include <string.h> // Required for: strlen() [Used in ImageTextEx()] #include <string.h> // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()]
#include <math.h> // Required for: fabsf() #include <math.h> // Required for: fabsf()
#include <stdio.h> // Required for: sprintf() [Used in ExportImageAsCode()] #include <stdio.h> // Required for: sprintf() [Used in ExportImageAsCode()]
@ -124,6 +130,14 @@
// NOTE: Used to read image data (multiple formats support) // NOTE: Used to read image data (multiple formats support)
#endif #endif
#if defined(SUPPORT_FILEFORMAT_QOI)
#define QOI_MALLOC RL_MALLOC
#define QOI_FREE RL_FREE
#define QOI_IMPLEMENTATION
#include "external/qoi.h"
#endif
#if defined(SUPPORT_IMAGE_EXPORT) #if defined(SUPPORT_IMAGE_EXPORT)
#define STBIW_MALLOC RL_MALLOC #define STBIW_MALLOC RL_MALLOC
#define STBIW_FREE RL_FREE #define STBIW_FREE RL_FREE
@ -202,6 +216,7 @@ Image LoadImage(const char *fileName)
defined(SUPPORT_FILEFORMAT_PIC) || \ defined(SUPPORT_FILEFORMAT_PIC) || \
defined(SUPPORT_FILEFORMAT_HDR) || \ defined(SUPPORT_FILEFORMAT_HDR) || \
defined(SUPPORT_FILEFORMAT_PSD) defined(SUPPORT_FILEFORMAT_PSD)
#define STBI_REQUIRED #define STBI_REQUIRED
#endif #endif
@ -286,36 +301,32 @@ Image LoadImageAnim(const char *fileName, int *frames)
} }
// Load image from memory buffer, fileType refers to extension: i.e. ".png" // Load image from memory buffer, fileType refers to extension: i.e. ".png"
// WARNING: File extension must be provided in lower-case
Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize) Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize)
{ {
Image image = { 0 }; Image image = { 0 };
char fileExtLower[16] = { 0 };
strcpy(fileExtLower, TextToLower(fileType));
#if defined(SUPPORT_FILEFORMAT_PNG)
if ((TextIsEqual(fileExtLower, ".png"))
#else
if ((false) if ((false)
#if defined(SUPPORT_FILEFORMAT_PNG)
|| (strcmp(fileType, ".png") == 0)
#endif #endif
#if defined(SUPPORT_FILEFORMAT_BMP) #if defined(SUPPORT_FILEFORMAT_BMP)
|| (TextIsEqual(fileExtLower, ".bmp")) || (strcmp(fileType, ".bmp") == 0)
#endif #endif
#if defined(SUPPORT_FILEFORMAT_TGA) #if defined(SUPPORT_FILEFORMAT_TGA)
|| (TextIsEqual(fileExtLower, ".tga")) || (strcmp(fileType, ".tga") == 0)
#endif #endif
#if defined(SUPPORT_FILEFORMAT_JPG) #if defined(SUPPORT_FILEFORMAT_JPG)
|| (TextIsEqual(fileExtLower, ".jpg") || || ((strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0))
TextIsEqual(fileExtLower, ".jpeg"))
#endif #endif
#if defined(SUPPORT_FILEFORMAT_GIF) #if defined(SUPPORT_FILEFORMAT_GIF)
|| (TextIsEqual(fileExtLower, ".gif")) || (strcmp(fileType, ".gif") == 0)
#endif #endif
#if defined(SUPPORT_FILEFORMAT_PIC) #if defined(SUPPORT_FILEFORMAT_PIC)
|| (TextIsEqual(fileExtLower, ".pic")) || (strcmp(fileType, ".pic") == 0)
#endif #endif
#if defined(SUPPORT_FILEFORMAT_PSD) #if defined(SUPPORT_FILEFORMAT_PSD)
|| (TextIsEqual(fileExtLower, ".psd")) || (strcmp(fileType, ".psd") == 0)
#endif #endif
) )
{ {
@ -340,7 +351,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
#endif #endif
} }
#if defined(SUPPORT_FILEFORMAT_HDR) #if defined(SUPPORT_FILEFORMAT_HDR)
else if (TextIsEqual(fileExtLower, ".hdr")) else if (strcmp(fileType, ".hdr") == 0)
{ {
#if defined(STBI_REQUIRED) #if defined(STBI_REQUIRED)
if (fileData != NULL) if (fileData != NULL)
@ -362,20 +373,31 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
#endif #endif
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_QOI)
else if (strcmp(fileType, ".qoi") == 0)
{
qoi_desc desc = { 0 };
image.data = qoi_decode(fileData, dataSize, &desc, 4);
image.width = desc.width;
image.height = desc.height;
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
image.mipmaps = 1;
}
#endif
#if defined(SUPPORT_FILEFORMAT_DDS) #if defined(SUPPORT_FILEFORMAT_DDS)
else if (TextIsEqual(fileExtLower, ".dds")) image = LoadDDS(fileData, dataSize); else if (strcmp(fileType, ".dds") == 0) image = LoadDDS(fileData, dataSize);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_PKM) #if defined(SUPPORT_FILEFORMAT_PKM)
else if (TextIsEqual(fileExtLower, ".pkm")) image = LoadPKM(fileData, dataSize); else if (strcmp(fileType, ".pkm") == 0) image = LoadPKM(fileData, dataSize);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_KTX) #if defined(SUPPORT_FILEFORMAT_KTX)
else if (TextIsEqual(fileExtLower, ".ktx")) image = LoadKTX(fileData, dataSize); else if (strcmp(fileType, ".ktx") == 0) image = LoadKTX(fileData, dataSize);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_PVR) #if defined(SUPPORT_FILEFORMAT_PVR)
else if (TextIsEqual(fileExtLower, ".pvr")) image = LoadPVR(fileData, dataSize); else if (strcmp(fileType, ".pvr") == 0) image = LoadPVR(fileData, dataSize);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_ASTC) #if defined(SUPPORT_FILEFORMAT_ASTC)
else if (TextIsEqual(fileExtLower, ".astc")) image = LoadASTC(fileData, dataSize); else if (strcmp(fileType, ".astc") == 0) image = LoadASTC(fileData, dataSize);
#endif #endif
else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported"); else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported");
@ -477,7 +499,28 @@ bool ExportImage(Image image, const char *fileName)
else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, channels, imgData); else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, channels, imgData);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_JPG) #if defined(SUPPORT_FILEFORMAT_JPG)
else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100 else if (IsFileExtension(fileName, ".jpg") ||
IsFileExtension(fileName, ".jpeg")) success = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100
#endif
#if defined(SUPPORT_FILEFORMAT_QOI)
else if (IsFileExtension(fileName, ".qoi"))
{
channels = 0;
if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
else TRACELOG(LOG_WARNING, "IMAGE: Image pixel format must be R8G8B8 or R8G8B8A8");
if ((channels == 3) || (channels == 4))
{
qoi_desc desc = { 0 };
desc.width = image.width;
desc.height = image.height;
desc.channels = channels;
desc.colorspace = QOI_SRGB;
success = qoi_write(fileName, imgData, &desc);
}
}
#endif #endif
#if defined(SUPPORT_FILEFORMAT_KTX) #if defined(SUPPORT_FILEFORMAT_KTX)
else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName); else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
@ -523,7 +566,7 @@ bool ExportImageAsCode(Image image, const char *fileName)
byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2021 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2022 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
@ -549,8 +592,8 @@ bool ExportImageAsCode(Image image, const char *fileName)
#endif // SUPPORT_IMAGE_EXPORT #endif // SUPPORT_IMAGE_EXPORT
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName); if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
return success; return success;
} }
@ -743,7 +786,7 @@ Image GenImageCellular(int width, int height, int tileSize)
{ {
int tileX = x/tileSize; int tileX = x/tileSize;
float minDistance = (float)strtod("Inf", NULL); float minDistance = 65536.0f; //(float)strtod("Inf", NULL);
// Check all adjacent tiles // Check all adjacent tiles
for (int i = -1; i < 2; i++) for (int i = -1; i < 2; i++)
@ -832,11 +875,11 @@ Image ImageFromImage(Image image, Rectangle rec)
result.width = (int)rec.width; result.width = (int)rec.width;
result.height = (int)rec.height; result.height = (int)rec.height;
result.data = RL_CALLOC((int)(rec.width*rec.height)*bytesPerPixel, 1); result.data = RL_CALLOC((int)rec.width*(int)rec.height*bytesPerPixel, 1);
result.format = image.format; result.format = image.format;
result.mipmaps = 1; result.mipmaps = 1;
for (int y = 0; y < rec.height; y++) for (int y = 0; y < (int)rec.height; y++)
{ {
memcpy(((unsigned char *)result.data) + y*(int)rec.width*bytesPerPixel, ((unsigned char *)image.data) + ((y + (int)rec.y)*image.width + (int)rec.x)*bytesPerPixel, (int)rec.width*bytesPerPixel); memcpy(((unsigned char *)result.data) + y*(int)rec.width*bytesPerPixel, ((unsigned char *)image.data) + ((y + (int)rec.y)*image.width + (int)rec.x)*bytesPerPixel, (int)rec.width*bytesPerPixel);
} }
@ -1092,35 +1135,41 @@ void ImageToPOT(Image *image, Color fill)
// Create an image from text (default font) // Create an image from text (default font)
Image ImageText(const char *text, int fontSize, Color color) Image ImageText(const char *text, int fontSize, Color color)
{ {
Image imText = { 0 };
#if defined(SUPPORT_MODULE_RTEXT)
int defaultFontSize = 10; // Default Font chars height in pixel int defaultFontSize = 10; // Default Font chars height in pixel
if (fontSize < defaultFontSize) fontSize = defaultFontSize; if (fontSize < defaultFontSize) fontSize = defaultFontSize;
int spacing = fontSize/defaultFontSize; int spacing = fontSize/defaultFontSize;
imText = ImageTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing, color); // WARNING: Module required: rtext
Image imText = ImageTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing, color); #else
imText = GenImageColor(200, 60, BLACK); // Generating placeholder black image rectangle
TRACELOG(LOG_WARNING, "IMAGE: ImageTextEx() requires module: rtext");
#endif
return imText; return imText;
} }
// Create an image from text (custom sprite font) // Create an image from text (custom sprite font)
Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint) Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint)
{ {
Image imText = { 0 };
#if defined(SUPPORT_MODULE_RTEXT)
int size = (int)strlen(text); // Get size in bytes of text int size = (int)strlen(text); // Get size in bytes of text
int textOffsetX = 0; // Image drawing position X int textOffsetX = 0; // Image drawing position X
int textOffsetY = 0; // Offset between lines (on line break '\n') int textOffsetY = 0; // Offset between lines (on line break '\n')
// NOTE: Text image is generated at font base size, later scaled to desired font size // NOTE: Text image is generated at font base size, later scaled to desired font size
Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); // WARNING: Module required: rtext
// Create image to store text // Create image to store text
Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
// Get next codepoint from byte string and glyph index in font // Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0; int codepointByteCount = 0;
int codepoint = GetCodepoint(&text[i], &codepointByteCount); int codepoint = GetCodepoint(&text[i], &codepointByteCount); // WARNING: Module required: rtext
int index = GetGlyphIndex(font, codepoint); int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol moving one byte // but we need to draw all of the bad bytes using the '?' symbol moving one byte
@ -1155,10 +1204,14 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor); TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor);
// Using nearest-neighbor scaling algorithm for default font // Using nearest-neighbor scaling algorithm for default font
// WARNING: Module required: rtext
if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
} }
#else
imText = GenImageColor(200, 60, BLACK); // Generating placeholder black image rectangle
TRACELOG(LOG_WARNING, "IMAGE: ImageTextEx() requires module: rtext");
#endif
return imText; return imText;
} }
@ -1361,10 +1414,12 @@ void ImageResize(Image *image, int newWidth, int newHeight)
// Security check to avoid program crash // Security check to avoid program crash
if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
bool fastPath = true; // Check if we can use a fast path on image scaling
if ((image->format != PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) && (image->format != PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) && (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8) && (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)) fastPath = true; // It can be for 8 bit per channel images with 1 to 4 channels per pixel
if ((image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ||
if (fastPath) (image->format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) ||
(image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) ||
(image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8))
{ {
int bytesPerPixel = GetPixelDataSize(1, 1, image->format); int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
unsigned char *output = (unsigned char *)RL_MALLOC(newWidth*newHeight*bytesPerPixel); unsigned char *output = (unsigned char *)RL_MALLOC(newWidth*newHeight*bytesPerPixel);
@ -1836,10 +1891,10 @@ void ImageColorTint(Image *image, Color color)
unsigned char b = (unsigned char)(((float)pixels[index].b/255*cB)*255.0f); unsigned char b = (unsigned char)(((float)pixels[index].b/255*cB)*255.0f);
unsigned char a = (unsigned char)(((float)pixels[index].a/255*cA)*255.0f); unsigned char a = (unsigned char)(((float)pixels[index].a/255*cA)*255.0f);
pixels[y*image->width + x].r = r; pixels[index].r = r;
pixels[y*image->width + x].g = g; pixels[index].g = g;
pixels[y*image->width + x].b = b; pixels[index].b = b;
pixels[y*image->width + x].a = a; pixels[index].a = a;
} }
} }
@ -1905,25 +1960,25 @@ void ImageColorContrast(Image *image, float contrast)
for (int x = 0; x < image->width; x++) for (int x = 0; x < image->width; x++)
{ {
float pR = (float)pixels[y*image->width + x].r/255.0f; float pR = (float)pixels[y*image->width + x].r/255.0f;
pR -= 0.5; pR -= 0.5f;
pR *= contrast; pR *= contrast;
pR += 0.5; pR += 0.5f;
pR *= 255; pR *= 255;
if (pR < 0) pR = 0; if (pR < 0) pR = 0;
if (pR > 255) pR = 255; if (pR > 255) pR = 255;
float pG = (float)pixels[y*image->width + x].g/255.0f; float pG = (float)pixels[y*image->width + x].g/255.0f;
pG -= 0.5; pG -= 0.5f;
pG *= contrast; pG *= contrast;
pG += 0.5; pG += 0.5f;
pG *= 255; pG *= 255;
if (pG < 0) pG = 0; if (pG < 0) pG = 0;
if (pG > 255) pG = 255; if (pG > 255) pG = 255;
float pB = (float)pixels[y*image->width + x].b/255.0f; float pB = (float)pixels[y*image->width + x].b/255.0f;
pB -= 0.5; pB -= 0.5f;
pB *= contrast; pB *= contrast;
pB += 0.5; pB += 0.5f;
pB *= 255; pB *= 255;
if (pB < 0) pB = 0; if (pB < 0) pB = 0;
if (pB > 255) pB = 255; if (pB > 255) pB = 255;
@ -2358,7 +2413,20 @@ Color GetImageColor(Image image, int x, int y)
// Clear image background with given color // Clear image background with given color
void ImageClearBackground(Image *dst, Color color) void ImageClearBackground(Image *dst, Color color)
{ {
for (int i = 0; i < dst->width*dst->height; ++i) ImageDrawPixel(dst, i%dst->width, i/dst->width, color); // Security check to avoid program crash
if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
// Fill in first pixel based on image format
ImageDrawPixel(dst, 0, 0, color);
unsigned char *pSrcPixel = (unsigned char *)dst->data;
int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
// Repeat the first pixel data throughout the image
for (int i = 1; i < dst->width * dst->height; i++)
{
memcpy(pSrcPixel + i * bytesPerPixel, pSrcPixel, bytesPerPixel);
}
} }
// Draw pixel within an image // Draw pixel within an image
@ -2632,13 +2700,21 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
int ey = sy + (int)rec.height; int ey = sy + (int)rec.height;
int sx = (int)rec.x; int sx = (int)rec.x;
int ex = sx + (int)rec.width;
int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
for (int y = sy; y < ey; y++) for (int y = sy; y < ey; y++)
{ {
for (int x = sx; x < ex; x++) // Fill in the first pixel of the row based on image format
ImageDrawPixel(dst, sx, y, color);
int bytesOffset = ((y * dst->width) + sx) * bytesPerPixel;
unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset;
// Repeat the first pixel data throughout the row
for (int x = 1; x < (int)rec.width; x++)
{ {
ImageDrawPixel(dst, x, y, color); memcpy(pSrcPixel + x * bytesPerPixel, pSrcPixel, bytesPerPixel);
} }
} }
} }
@ -2716,6 +2792,9 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
// [x] Consider fast path: no alpha blending required cases (src has no alpha) // [x] Consider fast path: no alpha blending required cases (src has no alpha)
// [x] Consider fast path: same src/dst format with no alpha -> direct line copy // [x] Consider fast path: same src/dst format with no alpha -> direct line copy
// [-] GetPixelColor(): Get Vector4 instead of Color, easier for ColorAlphaBlend() // [-] GetPixelColor(): Get Vector4 instead of Color, easier for ColorAlphaBlend()
// [ ] Support f32bit channels drawing
// TODO: Support PIXELFORMAT_UNCOMPRESSED_R32, PIXELFORMAT_UNCOMPRESSED_R32G32B32, PIXELFORMAT_UNCOMPRESSED_R32G32B32A32
Color colSrc, colDst, blend; Color colSrc, colDst, blend;
bool blendRequired = true; bool blendRequired = true;
@ -2768,10 +2847,13 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
// Draw text (default font) within an image (destination) // Draw text (default font) within an image (destination)
void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color) void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color)
{ {
#if defined(SUPPORT_MODULE_RTEXT)
Vector2 position = { (float)posX, (float)posY }; Vector2 position = { (float)posX, (float)posY };
// NOTE: For default font, spacing is set to desired font size / default font size (10)
// NOTE: For default font, sapcing is set to desired font size / default font size (10) ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color); // WARNING: Module required: rtext
ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color); #else
TRACELOG(LOG_WARNING, "IMAGE: ImageDrawText() requires module: rtext");
#endif
} }
// Draw text (custom sprite font) within an image (destination) // Draw text (custom sprite font) within an image (destination)
@ -2812,7 +2894,7 @@ Texture2D LoadTextureFromImage(Image image)
{ {
Texture2D texture = { 0 }; Texture2D texture = { 0 };
if ((image.data != NULL) && (image.width != 0) && (image.height != 0)) if ((image.width != 0) && (image.height != 0))
{ {
texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps); texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps);
} }
@ -2849,6 +2931,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
cubemap.height = cubemap.width; cubemap.height = cubemap.width;
} }
// Layout provided or already auto-detected
if (layout != CUBEMAP_LAYOUT_AUTO_DETECT) if (layout != CUBEMAP_LAYOUT_AUTO_DETECT)
{ {
int size = cubemap.width; int size = cubemap.width;
@ -2859,8 +2942,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL) if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL)
{ {
faces = image; faces = ImageCopy(image); // Image data already follows expected convention
for (int i = 0; i < 6; i++) faceRecs[i].y = (float)size*i;
} }
else if (layout == CUBEMAP_LAYOUT_PANORAMA) else if (layout == CUBEMAP_LAYOUT_PANORAMA)
{ {
@ -2894,10 +2976,12 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
ImageFormat(&faces, image.format); ImageFormat(&faces, image.format);
// NOTE: Image formating does not work with compressed textures // NOTE: Image formating does not work with compressed textures
}
for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE); for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
}
// NOTE: Cubemap data is expected to be provided as 6 images in a single data array,
// one after the other (that's a vertical image), following convention: +X, -X, +Y, -Y, +Z, -Z
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format); cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
if (cubemap.id == 0) TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image"); if (cubemap.id == 0) TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
@ -3071,6 +3155,7 @@ void SetTextureWrap(Texture2D texture, int wrap)
{ {
case TEXTURE_WRAP_REPEAT: case TEXTURE_WRAP_REPEAT:
{ {
// NOTE: It only works if NPOT textures are supported, i.e. OpenGL ES 2.0 could not support it
rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_REPEAT); rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_REPEAT);
rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_REPEAT); rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_REPEAT);
} break; } break;
@ -3132,6 +3217,8 @@ void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color
// i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center // i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center
void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint) void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint)
{ {
// WARNING: This solution only works if TEXTURE_WRAP_REPEAT is supported,
// NPOT textures supported is required and OpenGL ES 2.0 could not support it
Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height }; Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height };
Vector2 origin = { 0.0f, 0.0f }; Vector2 origin = { 0.0f, 0.0f };
@ -3143,6 +3230,7 @@ void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangl
void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint) void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint)
{ {
if ((texture.id <= 0) || (scale <= 0.0f)) return; // Wanna see a infinite loop?!...just delete this line! if ((texture.id <= 0) || (scale <= 0.0f)) return; // Wanna see a infinite loop?!...just delete this line!
if ((source.width == 0) || (source.height == 0)) return;
int tileWidth = (int)(source.width*scale), tileHeight = (int)(source.height*scale); int tileWidth = (int)(source.width*scale), tileHeight = (int)(source.height*scale);
if ((dest.width < tileWidth) && (dest.height < tileHeight)) if ((dest.width < tileWidth) && (dest.height < tileHeight))
@ -3406,6 +3494,8 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest,
coordD.x = (nPatchInfo.source.x + nPatchInfo.source.width)/width; coordD.x = (nPatchInfo.source.x + nPatchInfo.source.width)/width;
coordD.y = (nPatchInfo.source.y + nPatchInfo.source.height)/height; coordD.y = (nPatchInfo.source.y + nPatchInfo.source.height)/height;
rlCheckRenderBatchLimit(9 * 3 * 2); // Maxium number of verts that could happen
rlSetTexture(texture.id); rlSetTexture(texture.id);
rlPushMatrix(); rlPushMatrix();
@ -3553,7 +3643,7 @@ void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2
rlSetTexture(texture.id); rlSetTexture(texture.id);
// Texturing is only supported on QUADs // Texturing is only supported on RL_QUADS
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a); rlColor4ub(tint.r, tint.g, tint.b, tint.a);
@ -3721,10 +3811,10 @@ Color ColorAlphaBlend(Color dst, Color src, Color tint)
Color out = WHITE; Color out = WHITE;
// Apply color tint to source color // Apply color tint to source color
src.r = (unsigned char)(((unsigned int)src.r*(unsigned int)tint.r) >> 8); src.r = (unsigned char)(((unsigned int)src.r*((unsigned int)tint.r+1)) >> 8);
src.g = (unsigned char)(((unsigned int)src.g*(unsigned int)tint.g) >> 8); src.g = (unsigned char)(((unsigned int)src.g*((unsigned int)tint.g+1)) >> 8);
src.b = (unsigned char)(((unsigned int)src.b*(unsigned int)tint.b) >> 8); src.b = (unsigned char)(((unsigned int)src.b*((unsigned int)tint.b+1)) >> 8);
src.a = (unsigned char)(((unsigned int)src.a*(unsigned int)tint.a) >> 8); src.a = (unsigned char)(((unsigned int)src.a*((unsigned int)tint.a+1)) >> 8);
//#define COLORALPHABLEND_FLOAT //#define COLORALPHABLEND_FLOAT
#define COLORALPHABLEND_INTEGERS #define COLORALPHABLEND_INTEGERS
@ -4259,6 +4349,7 @@ static Image LoadPKM(const unsigned char *fileData, unsigned int fileSize)
#if defined(SUPPORT_FILEFORMAT_KTX) #if defined(SUPPORT_FILEFORMAT_KTX)
// Load KTX compressed image data (ETC1/ETC2 compression) // Load KTX compressed image data (ETC1/ETC2 compression)
// TODO: Review KTX loading, many things changed!
static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize) static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize)
{ {
unsigned char *fileDataPtr = (unsigned char *)fileData; unsigned char *fileDataPtr = (unsigned char *)fileData;
@ -4333,6 +4424,8 @@ static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize)
if (ktxHeader->glInternalFormat == 0x8D64) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB; if (ktxHeader->glInternalFormat == 0x8D64) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
else if (ktxHeader->glInternalFormat == 0x9274) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB; else if (ktxHeader->glInternalFormat == 0x9274) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
else if (ktxHeader->glInternalFormat == 0x9278) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; else if (ktxHeader->glInternalFormat == 0x9278) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
// TODO: Support uncompressed data formats? Right now it returns format = 0!
} }
} }
@ -4341,11 +4434,12 @@ static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize)
// Save image data as KTX file // Save image data as KTX file
// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018) // NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
// TODO: Review KTX saving, many things changed!
static int SaveKTX(Image image, const char *fileName) static int SaveKTX(Image image, const char *fileName)
{ {
// KTX file Header (64 bytes) // KTX file Header (64 bytes)
// v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
// v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation // v2.0 - http://github.khronos.org/KTX-Specification/ - Final specs by 2021-04-18
typedef struct { typedef struct {
char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n" char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n"
unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
@ -4480,7 +4574,7 @@ static Image LoadPVR(const unsigned char *fileData, unsigned int fileSize)
unsigned int flags; unsigned int flags;
unsigned char channels[4]; // pixelFormat high part unsigned char channels[4]; // pixelFormat high part
unsigned char channelDepth[4]; // pixelFormat low part unsigned char channelDepth[4]; // pixelFormat low part
unsigned int colourSpace; unsigned int colorSpace;
unsigned int channelType; unsigned int channelType;
unsigned int height; unsigned int height;
unsigned int width; unsigned int width;
@ -4759,3 +4853,5 @@ static Vector4 *LoadImageDataNormalized(Image image)
return pixels; return pixels;
} }
#endif // SUPPORT_MODULE_RTEXTURES

View file

@ -11,7 +11,7 @@
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2014-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2014-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -148,6 +148,7 @@ void TraceLog(int logType, const char *text, ...)
strcat(buffer, text); strcat(buffer, text);
strcat(buffer, "\n"); strcat(buffer, "\n");
vprintf(buffer, args); vprintf(buffer, args);
fflush(stdout);
#endif #endif
va_end(args); va_end(args);
@ -268,6 +269,51 @@ bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite)
return success; return success;
} }
// Export data to code (.h), returns true on success
bool ExportDataAsCode(const char *data, unsigned int size, const char *fileName)
{
bool success = false;
#ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20
#endif
// NOTE: Text data buffer size is estimated considering raw data size in bytes
// and requiring 6 char bytes for every byte: "0x00, "
char *txtData = (char *)RL_CALLOC(size*6 + 2000, sizeof(char));
int byteCount = 0;
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// DataAsCode exporter v1.0 - Raw data exported as an array of bytes //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
// Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 };
strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, size);
for (unsigned int i = 0; i < size - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), data[i]);
byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[size - 1]);
// NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData);
RL_FREE(txtData);
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName);
return success;
}
// Load text data from file, returns a '\0' terminated string // Load text data from file, returns a '\0' terminated string
// NOTE: text chars array should be freed manually // NOTE: text chars array should be freed manually
char *LoadFileText(const char *fileName) char *LoadFileText(const char *fileName)

View file

@ -5,7 +5,7 @@
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
* Copyright (c) 2014-2021 Ramon Santamaria (@raysan5) * Copyright (c) 2014-2022 Ramon Santamaria (@raysan5)
* *
* This software is provided "as-is", without any express or implied warranty. In no event * This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software. * will the authors be held liable for any damages arising from the use of this software.
@ -65,7 +65,7 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Declaration // Module Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#ifdef __cplusplus #if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions extern "C" { // Prevents name mangling of functions
#endif #endif
@ -74,7 +74,7 @@ void InitAssetManager(AAssetManager *manager, const char *dataPath); // Initia
FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() -> Read-only! FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() -> Read-only!
#endif #endif
#ifdef __cplusplus #if defined(__cplusplus)
} }
#endif #endif