This commit is contained in:
unkl nik 2023-11-06 06:52:34 +02:00
commit 64b853abaa
59 changed files with 38468 additions and 22154 deletions

View file

@ -40,6 +40,66 @@ jobs:
run: go test -race -vet=off ./... run: go test -race -vet=off ./...
working-directory: raylib working-directory: raylib
test-drm:
strategy:
matrix:
go-version: [1.19.x]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v3
- name: Install package
run: |
sudo apt-get update -y; sudo apt-get -y install libegl1-mesa-dev libdrm-dev
- name: Build
run: go build -tags drm
working-directory: raylib
test-sdl:
strategy:
matrix:
go-version: [1.19.x]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v3
- name: Install package
run: |
sudo apt-get update -y; sudo apt-get -y install libgl1-mesa-dev libsdl2-dev
- name: Build
run: go build -tags sdl
working-directory: raylib
test-wayland:
strategy:
matrix:
go-version: [1.19.x]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v3
- name: Install package
run: |
sudo apt-get update -y; sudo apt-get -y install libgl1-mesa-dev libwayland-dev libxkbcommon-dev
- name: Build
run: go build -tags wayland
working-directory: raylib
test-examples: test-examples:
strategy: strategy:
matrix: matrix:

View file

@ -42,19 +42,15 @@ You can also build binary in [MSYS2](https://msys2.github.io/) shell.
[Android example](https://github.com/gen2brain/raylib-go/tree/master/examples/others/android/example). [Android example](https://github.com/gen2brain/raylib-go/tree/master/examples/others/android/example).
##### Raspberry Pi
[RPi example](https://github.com/gen2brain/raylib-go/tree/master/examples/others/rpi/basic_window).
### Installation ### Installation
go get -v -u github.com/gen2brain/raylib-go/raylib go get -v -u github.com/gen2brain/raylib-go/raylib
### Build tags ### Build tags
* `drm` - build for Linux native mode, including Raspberry Pi 4 and other devices (PLATFORM_DRM) * `drm` - build for Linux native DRM mode, including Raspberry Pi 4 and other devices (PLATFORM_DRM)
* `rpi` - build for Raspberry Pi platform (PLATFORM_RPI) * `sdl` - build for SDL backend instead of internal GLFW (PLATFORM_DESKTOP_SDL)
* `wayland` - build against Wayland libraries * `wayland` - build against Wayland libraries (internal GLFW)
* `noaudio` - disables audio functions * `noaudio` - disables audio functions
* `opengl43` - uses OpenGL 4.3 backend * `opengl43` - uses OpenGL 4.3 backend
* `opengl21` - uses OpenGL 2.1 backend (default is 3.3 on desktop) * `opengl21` - uses OpenGL 2.1 backend (default is 3.3 on desktop)
@ -75,6 +71,7 @@ import rl "github.com/gen2brain/raylib-go/raylib"
func main() { func main() {
rl.InitWindow(800, 450, "raylib [core] example - basic window") rl.InitWindow(800, 450, "raylib [core] example - basic window")
defer rl.CloseWindow() defer rl.CloseWindow()
rl.SetTargetFPS(60) rl.SetTargetFPS(60)
for !rl.WindowShouldClose() { for !rl.WindowShouldClose() {

View file

@ -9,19 +9,22 @@ Export path to Android NDK, point to location where you have unpacked archive:
Add toolchain bin directory to PATH: Add toolchain bin directory to PATH:
export PATH=${ANDROID_NDK_HOME}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin:${PATH} export PATH=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${PATH}
Export sysroot and libdirs: Export sysroot and libdirs:
export ANDROID_SYSROOT=${ANDROID_NDK_HOME}/sysroot export ANDROID_SYSROOT=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot
export ANDROID_PLATFORM=${ANDROID_NDK_HOME}/platforms/android-16/arch-arm
export ANDROID_TOOLCHAIN=${ANDROID_NDK_HOME}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64 export ANDROID_TOOLCHAIN=${ANDROID_NDK_HOME}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
Export API version:
export ANDROID_API=16
And compile shared library: And compile shared library:
CC="arm-linux-androideabi-gcc" \ CC="armv7a-linux-androideabi${ANDROID_API}-clang" \
CGO_CFLAGS="-I${ANDROID_SYSROOT}/usr/include -I${ANDROID_SYSROOT}/usr/include/arm-linux-androideabi --sysroot=${ANDROID_SYSROOT} -D__ANDROID_API__=16" \ CGO_CFLAGS="-I${ANDROID_SYSROOT}/usr/include -I${ANDROID_SYSROOT}/usr/include/arm-linux-androideabi --sysroot=${ANDROID_SYSROOT} -D__ANDROID_API__=${ANDROID_API}" \
CGO_LDFLAGS="-L${ANDROID_SYSROOT}/usr/lib/arm-linux-androideabi -L${ANDROID_PLATFORM}/usr/lib -L${ANDROID_TOOLCHAIN}/arm-linux-androideabi/lib -L${ANDROID_TOOLCHAIN}/lib/gcc/arm-linux-androideabi/4.9.x --sysroot=${ANDROID_PLATFORM}" \ CGO_LDFLAGS="-L${ANDROID_SYSROOT}/usr/lib/arm-linux-androideabi/${ANDROID_API} -L${ANDROID_TOOLCHAIN}/arm-linux-androideabi/lib -L${ANDROID_TOOLCHAIN}/lib/gcc/arm-linux-androideabi/4.9.x --sysroot=${ANDROID_SYSROOT}" \
CGO_ENABLED=1 GOOS=android GOARCH=arm \ CGO_ENABLED=1 GOOS=android GOARCH=arm \
go build -buildmode=c-shared -ldflags="-s -w -extldflags=-Wl,-soname,libexample.so" \ go build -buildmode=c-shared -ldflags="-s -w -extldflags=-Wl,-soname,libexample.so" \
-o=android/libs/armeabi-v7a/libexample.so -o=android/libs/armeabi-v7a/libexample.so
@ -40,3 +43,6 @@ Or with gradle:
./gradlew assembleDebug ./gradlew assembleDebug
If everything is successfully built apk can be found in bin/ directory or in the android/build/outputs in case `gradle` is used. If everything is successfully built apk can be found in bin/ directory or in the android/build/outputs in case `gradle` is used.
For aarch64/arm64 replace `arm-linux-androideabi` with `aarch64-linux-android`, set GOARCH to arm64 and use minimum `ANDROID_API=21`.

View file

@ -1,18 +0,0 @@
### Raspberry Pi example
To cross compile example for Raspberry Pi you will need [RPi toolchain](https://github.com/raspberrypi/tools/tree/master/arm-bcm2708) and [userspace libraries](https://github.com/raspberrypi/firmware) (opt/vc).
Export path to RPi toolchain:
export RPI_HOME=/opt/tools/arm-bcm2708/arm-linux-gnueabihf
Add toolchain bin directory to PATH:
export PATH=${RPI_HOME}/bin:${PATH}
And compile example:
CC=arm-linux-gnueabihf-gcc \
CGO_CFLAGS="-I/opt/vc/include -I/opt/vc/include/interface/vcos -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads --sysroot=${RPI_HOME}/arm-linux-gnueabihf/sysroot" \
CGO_LDFLAGS="-L/opt/vc/lib -L/opt/vc/lib64 --sysroot=${RPI_HOME}/arm-linux-gnueabihf/sysroot" \
CGO_ENABLED=1 GOOS=linux GOARCH=arm go build

View file

@ -1,21 +0,0 @@
package main
import "github.com/gen2brain/raylib-go/raylib"
func main() {
rl.InitWindow(800, 450, "raylib [rpi] example - basic window")
rl.SetTargetFPS(60)
for !rl.WindowShouldClose() {
rl.BeginDrawing()
rl.ClearBackground(rl.RayWhite)
rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LightGray)
rl.EndDrawing()
}
rl.CloseWindow()
}

View file

@ -1,6 +1,6 @@
/********************************************************************************************** /**********************************************************************************************
* *
* raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * raylib v4.6-dev - 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
@ -81,10 +81,10 @@
#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_MAJOR 4 #define RAYLIB_VERSION_MAJOR 5
#define RAYLIB_VERSION_MINOR 5 #define RAYLIB_VERSION_MINOR 0
#define RAYLIB_VERSION_PATCH 0 #define RAYLIB_VERSION_PATCH 0
#define RAYLIB_VERSION "4.5" #define RAYLIB_VERSION "5.0"
// 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
@ -133,12 +133,20 @@
// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) // NOTE: MSVC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized with { } // Plain structures in C++ (without constructors) can be initialized with { }
// This is called aggregate initialization (C++11 feature)
#if defined(__cplusplus) #if defined(__cplusplus)
#define CLITERAL(type) type #define CLITERAL(type) type
#else #else
#define CLITERAL(type) (type) #define CLITERAL(type) (type)
#endif #endif
// Some compilers (mostly macos clang) default to C++98,
// where aggregate initialization can't be used
// So, give a more clear error stating how to fix this
#if !defined(_MSC_VER) && (defined(__cplusplus) && __cplusplus < 201103L)
#error "C++11 or later is required. Add -std=c++11"
#endif
// NOTE: We set some defines with some data types declared by raylib // NOTE: We set some defines with some data types declared by raylib
// Other modules (raymath, rlgl) also require some of those types, so, // Other modules (raymath, rlgl) also require some of those types, so,
// to be able to use those other modules as standalone (not depending on raylib) // to be able to use those other modules as standalone (not depending on raylib)
@ -402,6 +410,7 @@ typedef struct ModelAnimation {
int frameCount; // Number of animation frames int frameCount; // Number of animation frames
BoneInfo *bones; // Bones information (skeleton) BoneInfo *bones; // Bones information (skeleton)
Transform **framePoses; // Poses array by frame Transform **framePoses; // Poses array by frame
char name[32]; // Animation name
} ModelAnimation; } ModelAnimation;
// Ray, ray for raycasting // Ray, ray for raycasting
@ -497,6 +506,20 @@ typedef struct FilePathList {
char **paths; // Filepaths entries char **paths; // Filepaths entries
} FilePathList; } FilePathList;
// Automation event
typedef struct AutomationEvent {
unsigned int frame; // Event frame
unsigned int type; // Event type (AutomationEventType)
int params[4]; // Event parameters (if required)
} AutomationEvent;
// Automation event list
typedef struct AutomationEventList {
unsigned int capacity; // Events max entries (MAX_AUTOMATION_EVENTS)
unsigned int count; // Events entries count
AutomationEvent *events; // Events entries
} AutomationEventList;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Enumerators Definition // Enumerators Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -517,6 +540,7 @@ typedef enum {
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_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED
FLAG_BORDERLESS_WINDOWED_MODE = 0x00008000, // Set to run program in borderless windowed mode
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;
@ -802,6 +826,9 @@ typedef enum {
PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float)
PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float)
PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float)
PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float)
PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float)
PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float)
PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha)
PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha)
PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp
@ -905,8 +932,8 @@ typedef enum {
// Callbacks to hook some internal functions // Callbacks to hook some internal functions
// WARNING: These callbacks are intended for advance users // WARNING: These callbacks are intended for advance users
typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages
typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load binary data
typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize); // FileIO: Save binary data
typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data
typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data
@ -925,8 +952,8 @@ extern "C" { // Prevents name mangling of functions
// Window-related functions // Window-related functions
RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context
RLAPI bool WindowShouldClose(void); // Check if KEY_ESCAPE pressed or Close icon pressed
RLAPI void CloseWindow(void); // Close window and unload OpenGL context RLAPI void CloseWindow(void); // Close window and unload OpenGL context
RLAPI bool WindowShouldClose(void); // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked)
RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully
RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen
RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP) RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP)
@ -938,17 +965,20 @@ RLAPI bool IsWindowState(unsigned int flag); // Check if on
RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP) 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 ToggleBorderlessWindowed(void); // Toggle window state: borderless 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)
RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP)
RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP)
RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP)
RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP)
RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP) RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB)
RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP)
RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window
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 SetWindowMaxSize(int width, int height); // Set window maximum 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 SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP)
RLAPI void SetWindowFocused(void); // Set window focused (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
@ -964,20 +994,12 @@ RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specifi
RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate
RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor
RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor
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 specified 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 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 RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling
// Custom frame control functions
// NOTE: Those functions are intended for advance users that want full control over the frame processing
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
// 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 PollInputEvents(void); // Register all input events
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
RLAPI void HideCursor(void); // Hides cursor RLAPI void HideCursor(void); // Hides cursor
@ -1033,24 +1055,37 @@ RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the
// Timing-related functions // Timing-related functions
RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum)
RLAPI int GetFPS(void); // Get current FPS
RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time)
RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow()
RLAPI int GetFPS(void); // Get current FPS
// Custom frame control functions
// NOTE: Those functions are intended for advance users that want full control over the frame processing
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
// 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 PollInputEvents(void); // Register all input events
RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution)
// Random values generation functions
RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator
RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included)
RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated
RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence
// Misc. functions // Misc. functions
RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included)
RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator
RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format)
RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS)
RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available)
// NOTE: Following functions implemented in module [utils]
//------------------------------------------------------------------
RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level
RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator
RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator RLAPI void *MemRealloc(void *ptr, unsigned 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
@ -1060,13 +1095,16 @@ RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom
RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver
// Files management functions // Files management functions
RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) RLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // 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, int dataSize); // Save data to file from byte array (write), returns true on success
RLAPI bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, 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
//------------------------------------------------------------------
// File system functions
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)
@ -1077,7 +1115,7 @@ RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filenam
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 const char *GetApplicationDirectory(void); // Get the directory if the running application (uses static string) RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string)
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 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 LoadDirectoryFiles(const char *dirPath); // Load directory filepaths
@ -1094,18 +1132,29 @@ RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataS
RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree()
RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree()
// Automation events functionality
RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file
RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file
RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to
RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording
RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set)
RLAPI void StopAutomationEventRecording(void); // Stop recording automation events
RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Input Handling Functions (Module: core) // Input Handling Functions (Module: core)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Input-related functions: keyboard // Input-related functions: keyboard
RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once
RLAPI bool IsKeyPressedRepeat(int key); // Check if a key has been pressed again (Only PLATFORM_DESKTOP)
RLAPI bool IsKeyDown(int key); // Check if a key is being pressed RLAPI bool IsKeyDown(int key); // Check if a key is being pressed
RLAPI bool IsKeyReleased(int key); // Check if a key has been released once RLAPI bool IsKeyReleased(int key); // Check if a key has been released once
RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed
RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty
RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty
RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
// Input-related functions: gamepads // Input-related functions: gamepads
RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available
@ -1146,7 +1195,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t
// Gestures and Touch Handling Functions (Module: rgestures) // Gestures and Touch Handling Functions (Module: rgestures)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags
RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected
RLAPI int GetGestureDetected(void); // Get latest detected gesture RLAPI int GetGestureDetected(void); // Get latest detected gesture
RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds
RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector
@ -1177,6 +1226,8 @@ RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color
RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out
RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point
RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points
RLAPI void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color); // Draw a B-Spline line, minimum 4 points
RLAPI void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color); // Draw a Catmull Rom spline line, minimum 4 points
RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence
RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle
RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle
@ -1184,6 +1235,7 @@ RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle,
RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle
RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version)
RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline
RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version)
RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse
RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline
RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring
@ -1227,6 +1279,7 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2);
// NOTE: These functions do not require GPU access // NOTE: These functions do not require GPU access
RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM)
RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data
RLAPI Image LoadImageSvg(const char *fileNameOrString, int width, int height); // Load image from SVG file data or string with specified size
RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data)
RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png'
RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data
@ -1234,13 +1287,14 @@ RLAPI Image LoadImageFromScreen(void);
RLAPI bool IsImageReady(Image image); // Check if an image is ready RLAPI bool IsImageReady(Image image); // Check if an image is ready
RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM)
RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success
RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer
RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success
// Image generation functions // Image generation functions
RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color
RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient
RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient
RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient
RLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer); // Generate image: square gradient
RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked
RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise
RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise
@ -1267,6 +1321,7 @@ RLAPI void ImageMipmaps(Image *image);
RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
RLAPI void ImageFlipVertical(Image *image); // Flip image vertically RLAPI void ImageFlipVertical(Image *image); // Flip image vertically
RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally
RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359)
RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg
RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg
RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint
@ -1351,13 +1406,13 @@ 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, use NULL for fontChars and 0 for glyphCount to load the default character set RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount 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 *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'
RLAPI bool IsFontReady(Font font); // Check if a font is ready RLAPI bool IsFontReady(Font font); // Check if a font is ready
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 *codepoints, int codepointCount, 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 *glyphs, Rectangle **glyphRecs, 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 *glyphs, 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 RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success
@ -1367,9 +1422,10 @@ 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) RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint)
// Text font info functions // Text font info functions
RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks
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
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found
@ -1485,10 +1541,10 @@ RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture
RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh
// Model animations loading/unloading functions // Model animations loading/unloading functions
RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount); // Load model animations from file RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file
RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose
RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data
RLAPI void UnloadModelAnimations(ModelAnimation *animations, unsigned int count); // Unload animation array data RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data
RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match
// Collision detection functions // Collision detection functions
@ -1511,6 +1567,7 @@ RLAPI void InitAudioDevice(void); // Initial
RLAPI void CloseAudioDevice(void); // Close the audio device and context RLAPI void CloseAudioDevice(void); // Close the audio device and context
RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully
RLAPI void SetMasterVolume(float volume); // Set master volume (listener) RLAPI void SetMasterVolume(float volume); // Set master volume (listener)
RLAPI float GetMasterVolume(void); // Get master volume (listener)
// Wave/Sound loading/unloading functions // Wave/Sound loading/unloading functions
RLAPI Wave LoadWave(const char *fileName); // Load wave data from file RLAPI Wave LoadWave(const char *fileName); // Load wave data from file
@ -1518,10 +1575,12 @@ RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileDat
RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready
RLAPI Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSound(const char *fileName); // Load sound from file
RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data
RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data
RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready
RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data
RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadWave(Wave wave); // Unload wave data
RLAPI void UnloadSound(Sound sound); // Unload sound RLAPI void UnloadSound(Sound sound); // Unload sound
RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data)
RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success
RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success
@ -1575,10 +1634,10 @@ RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan
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 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 AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as <float>s
RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream
RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as <float>s
RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline
#if defined(__cplusplus) #if defined(__cplusplus)

View file

@ -7,6 +7,8 @@ package rl
#include "external/android/native_app_glue/android_native_app_glue.c" #include "external/android/native_app_glue/android_native_app_glue.c"
#cgo android LDFLAGS: -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lm #cgo android LDFLAGS: -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lm
#cgo android CFLAGS: -DPLATFORM_ANDROID -DGRAPHICS_API_OPENGL_ES2 -Iexternal/android/native_app_glue #cgo android CFLAGS: -DPLATFORM_ANDROID -DGRAPHICS_API_OPENGL_ES2 -Iexternal/android/native_app_glue -Wno-implicit-const-int-float-conversion
#cgo android,arm CFLAGS: -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
*/ */
import "C" import "C"

View file

@ -1,5 +1,5 @@
//go:build darwin //go:build darwin && !sdl
// +build darwin // +build darwin,!sdl
package rl package rl

19
raylib/cgo_darwin_sdl.go Normal file
View file

@ -0,0 +1,19 @@
//go:build darwin && sdl
// +build darwin,sdl
package rl
/*
#cgo darwin LDFLAGS: -framework Cocoa -framework IOKit -framework CoreVideo -framework CoreFoundation
#cgo darwin CFLAGS: -x objective-c -Wno-deprecated-declarations -Wno-implicit-const-int-float-conversion -DPLATFORM_DESKTOP_SDL
#cgo darwin pkg-config: sdl2
#cgo darwin,!angle LDFLAGS: -framework OpenGL
#cgo darwin,opengl11,!angle CFLAGS: -DGRAPHICS_API_OPENGL_11
#cgo darwin,opengl21,!angle CFLAGS: -DGRAPHICS_API_OPENGL_21
#cgo darwin,opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_43
#cgo darwin,!opengl11,!opengl21,!opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_33
#cgo darwin,angle CFLAGS: -DGRAPHICS_API_OPENGL_ES2
*/
import "C"

View file

@ -1,5 +1,5 @@
//go:build freebsd && !linux && !drm && !rpi && !android //go:build freebsd && !linux && !drm && !sdl && !android
// +build freebsd,!linux,!drm,!rpi,!android // +build freebsd,!linux,!drm,!sdl,!android
package rl package rl

20
raylib/cgo_freebsd_sdl.go Normal file
View file

@ -0,0 +1,20 @@
//go:build freebsd && !linux && !drm && sdl && !android
// +build freebsd,!linux,!drm,sdl,!android
package rl
/*
#cgo freebsd CFLAGS: -I. -I/usr/local/include -DPLATFORM_DESKTOP_SDL
#cgo freebsd LDFLAGS: -L/usr/local/lib
#cgo freebsd pkg-config: sdl2
#cgo freebsd,!angle LDFLAGS: -lGL
#cgo freebsd,opengl11,!angle CFLAGS: -DGRAPHICS_API_OPENGL_11
#cgo freebsd,opengl21,!angle CFLAGS: -DGRAPHICS_API_OPENGL_21
#cgo freebsd,opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_43
#cgo freebsd,!opengl11,!opengl21,!opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_33
#cgo freebsd,angle CFLAGS: -DGRAPHICS_API_OPENGL_ES2
*/
import "C"

View file

@ -1,5 +1,5 @@
//go:build linux && !drm && !rpi && !android //go:build linux && !drm && !sdl && !android
// +build linux,!drm,!rpi,!android // +build linux,!drm,!sdl,!android
package rl package rl

View file

@ -1,5 +1,5 @@
//go:build linux && drm && !rpi && !android //go:build linux && drm && !sdl && !android
// +build linux,drm,!rpi,!android // +build linux,drm,!sdl,!android
package rl package rl

View file

@ -1,10 +0,0 @@
//go:build linux && rpi && !drm && !android
// +build linux,rpi,!drm,!android
package rl
/*
#cgo linux,rpi LDFLAGS: -L/opt/vc/lib -L/opt/vc/lib64 -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -lvcos -lvchiq_arm -ldl
#cgo linux,rpi CFLAGS: -DPLATFORM_RPI -DGRAPHICS_API_OPENGL_ES2 -I/opt/vc/include -I/opt/vc/include/interface/vcos -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads
*/
import "C"

19
raylib/cgo_linux_sdl.go Normal file
View file

@ -0,0 +1,19 @@
//go:build linux && !drm && sdl && !android
// +build linux,!drm,sdl,!android
package rl
/*
#cgo linux,!angle LDFLAGS: -lm
#cgo linux CFLAGS: -DPLATFORM_DESKTOP_SDL -Wno-stringop-overflow
#cgo linux pkg-config: sdl2
#cgo linux,!angle LDFLAGS: -lGL
#cgo linux,opengl11,!angle CFLAGS: -DGRAPHICS_API_OPENGL_11
#cgo linux,opengl21,!angle CFLAGS: -DGRAPHICS_API_OPENGL_21
#cgo linux,opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_43
#cgo linux,!opengl11,!opengl21,!opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_33
#cgo linux,angle CFLAGS: -DGRAPHICS_API_OPENGL_ES2
*/
import "C"

View file

@ -1,5 +1,5 @@
//go:build openbsd && !linux && !drm && !rpi && !android //go:build openbsd && !linux && !drm && !sdl && !android
// +build openbsd,!linux,!drm,!rpi,!android // +build openbsd,!linux,!drm,!sdl,!android
package rl package rl

20
raylib/cgo_openbsd_sdl.go Normal file
View file

@ -0,0 +1,20 @@
//go:build openbsd && !linux && !drm && sdl && !android
// +build openbsd,!linux,!drm,sdl,!android
package rl
/*
#cgo openbsd CFLAGS: -I. -I/usr/X11R6/include -DPLATFORM_DESKTOP_SDL
#cgo openbsd LDFLAGS: -L/usr/X11R6/lib
#cgo openbsd pkg-config: sdl2
#cgo openbsd,!angle LDFLAGS: -lGL
#cgo openbsd,opengl11,!angle CFLAGS: -DGRAPHICS_API_OPENGL_11
#cgo openbsd,opengl21,!angle CFLAGS: -DGRAPHICS_API_OPENGL_21
#cgo openbsd,opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_43
#cgo openbsd,!opengl11,!opengl21,!opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_33
#cgo openbsd,angle CFLAGS: -DGRAPHICS_API_OPENGL_ES2
*/
import "C"

View file

@ -1,5 +1,5 @@
//go:build windows //go:build windows && !sdl
// +build windows // +build windows,!sdl
package rl package rl
@ -24,7 +24,7 @@ package rl
#include "external/glfw/src/osmesa_context.c" #include "external/glfw/src/osmesa_context.c"
#cgo windows LDFLAGS: -lgdi32 -lwinmm -lole32 #cgo windows LDFLAGS: -lgdi32 -lwinmm -lole32
#cgo windows CFLAGS: -D_GLFW_WIN32 -Iexternal -Iexternal/glfw/include -Iexternal/glfw/deps/mingw -DPLATFORM_DESKTOP #cgo windows CFLAGS: -D_GLFW_WIN32 -Iexternal -Iexternal/glfw/include -Iexternal/glfw/deps/mingw -DPLATFORM_DESKTOP -Wno-stringop-overflow
#cgo windows,!angle LDFLAGS: -lopengl32 #cgo windows,!angle LDFLAGS: -lopengl32

18
raylib/cgo_windows_sdl.go Normal file
View file

@ -0,0 +1,18 @@
//go:build windows && sdl
// +build windows,sdl
package rl
/*
#cgo windows LDFLAGS: -lgdi32 -lwinmm -lole32 -lSDL2
#cgo windows CFLAGS: -Iexternal -DPLATFORM_DESKTOP_SDL -Wno-stringop-overflow
#cgo windows,!angle LDFLAGS: -lopengl32
#cgo windows,opengl11,!angle CFLAGS: -DGRAPHICS_API_OPENGL_11
#cgo windows,opengl21,!angle CFLAGS: -DGRAPHICS_API_OPENGL_21
#cgo windows,opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_43
#cgo windows,!opengl11,!opengl21,!opengl43,!angle CFLAGS: -DGRAPHICS_API_OPENGL_33
#cgo windows,angle CFLAGS: -DGRAPHICS_API_OPENGL_ES2
*/
import "C"

View file

@ -45,6 +45,8 @@
#define SUPPORT_CAMERA_SYSTEM 1 #define SUPPORT_CAMERA_SYSTEM 1
// Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
#define SUPPORT_GESTURES_SYSTEM 1 #define SUPPORT_GESTURES_SYSTEM 1
// Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64
#define SUPPORT_RPRAND_GENERATOR 1
// Mouse gestures are directly mapped like touches and processed by gestures system // Mouse gestures are directly mapped like touches and processed by gestures system
#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.
@ -55,9 +57,7 @@
// Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used // Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used
//#define SUPPORT_BUSY_WAIT_LOOP 1 //#define SUPPORT_BUSY_WAIT_LOOP 1
// Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy // Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy
#define SUPPORT_PARTIALBUSY_WAIT_LOOP #define SUPPORT_PARTIALBUSY_WAIT_LOOP 1
// Wait for events passively (sleeping while no events) instead of polling them actively every frame
//#define SUPPORT_EVENTS_WAITING 1
// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
#define SUPPORT_SCREEN_CAPTURE 1 #define SUPPORT_SCREEN_CAPTURE 1
// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() // Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
@ -65,7 +65,7 @@
// Support CompressData() and DecompressData() functions // Support CompressData() and DecompressData() functions
#define SUPPORT_COMPRESSION_API 1 #define SUPPORT_COMPRESSION_API 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_AUTOMATION_EVENTS 1
// Support custom frame control, only for advance users // Support custom frame control, only for advance users
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
// 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
@ -87,6 +87,7 @@
#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB #define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB
#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module: rlgl - Configuration values // Module: rlgl - Configuration values
@ -141,7 +142,7 @@
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Module: rtextures - Configuration Flags // Module: rtextures - Configuration Flags
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Select the 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
#define SUPPORT_FILEFORMAT_BMP 1 #define SUPPORT_FILEFORMAT_BMP 1
//#define SUPPORT_FILEFORMAT_TGA 1 //#define SUPPORT_FILEFORMAT_TGA 1
@ -150,13 +151,13 @@
#define SUPPORT_FILEFORMAT_QOI 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
//#define SUPPORT_FILEFORMAT_PIC 1 //#define SUPPORT_FILEFORMAT_PIC 1
//#define SUPPORT_FILEFORMAT_PNM 1
//#define SUPPORT_FILEFORMAT_KTX 1 //#define SUPPORT_FILEFORMAT_KTX 1
//#define SUPPORT_FILEFORMAT_ASTC 1 //#define SUPPORT_FILEFORMAT_ASTC 1
//#define SUPPORT_FILEFORMAT_PKM 1 //#define SUPPORT_FILEFORMAT_PKM 1
//#define SUPPORT_FILEFORMAT_PVR 1 //#define SUPPORT_FILEFORMAT_PVR 1
//#define SUPPORT_FILEFORMAT_SVG 1
// Support image export functionality (.png, .bmp, .tga, .jpg, .qoi) // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi)
#define SUPPORT_IMAGE_EXPORT 1 #define SUPPORT_IMAGE_EXPORT 1
@ -181,6 +182,11 @@
// 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
// On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
// at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
// drawing text and shapes with a single draw call [SetShapesTexture()].
#define SUPPORT_FONT_ATLAS_WHITE_REC 1
// rtext: 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:

View file

@ -65,7 +65,8 @@
* *
* `cgltf_num_components` is a tiny utility that tells you the dimensionality of * `cgltf_num_components` is a tiny utility that tells you the dimensionality of
* a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate
* the necessary amount of memory. * the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for
* similar purposes.
* *
* `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to
* floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element
@ -837,6 +838,8 @@ cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size i
cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index);
cgltf_size cgltf_num_components(cgltf_type type); cgltf_size cgltf_num_components(cgltf_type type);
cgltf_size cgltf_component_size(cgltf_component_type component_type);
cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type);
cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count);
@ -1488,8 +1491,6 @@ cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data,
return cgltf_result_success; return cgltf_result_success;
} }
static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type);
static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count) static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count)
{ {
char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset; char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset;
@ -2217,7 +2218,7 @@ static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_typ
case cgltf_component_type_r_32u: case cgltf_component_type_r_32u:
return *((const uint32_t*) in); return *((const uint32_t*) in);
case cgltf_component_type_r_32f: case cgltf_component_type_r_32f:
return (cgltf_size)*((const float*) in); return (cgltf_size)((cgltf_ssize)*((const float*) in));
case cgltf_component_type_r_8u: case cgltf_component_type_r_8u:
return *((const uint8_t*) in); return *((const uint8_t*) in);
default: default:
@ -2253,8 +2254,6 @@ static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_ty
return (cgltf_float)cgltf_component_read_integer(in, component_type); return (cgltf_float)cgltf_component_read_integer(in, component_type);
} }
static cgltf_size cgltf_component_size(cgltf_component_type component_type);
static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size) static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size)
{ {
cgltf_size num_components = cgltf_num_components(type); cgltf_size num_components = cgltf_num_components(type);
@ -5965,7 +5964,7 @@ cgltf_size cgltf_num_components(cgltf_type type) {
} }
} }
static cgltf_size cgltf_component_size(cgltf_component_type component_type) { cgltf_size cgltf_component_size(cgltf_component_type component_type) {
switch (component_type) switch (component_type)
{ {
case cgltf_component_type_r_8: case cgltf_component_type_r_8:
@ -5983,7 +5982,7 @@ static cgltf_size cgltf_component_size(cgltf_component_type component_type) {
} }
} }
static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type)
{ {
cgltf_size component_size = cgltf_component_size(component_type); cgltf_size component_size = cgltf_component_size(component_type);
if (type == cgltf_type_mat2 && component_size == 1) if (type == cgltf_type_mat2 && component_size == 1)

View file

@ -518,6 +518,10 @@ int jar_xm_create_context(jar_xm_context_t** ctxp, const char* moddata, uint32_t
return jar_xm_create_context_safe(ctxp, moddata, SIZE_MAX, rate); return jar_xm_create_context_safe(ctxp, moddata, SIZE_MAX, rate);
} }
#ifdef ALIGN
#undef ALIGN
#endif
#define ALIGN(x, b) (((x) + ((b) - 1)) & ~((b) - 1)) #define ALIGN(x, b) (((x) + ((b) - 1)) & ~((b) - 1))
#define ALIGN_PTR(x, b) (void*)(((uintptr_t)(x) + ((b) - 1)) & ~((b) - 1)) #define ALIGN_PTR(x, b) (void*)(((uintptr_t)(x) + ((b) - 1)) & ~((b) - 1))
int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, size_t moddata_length, uint32_t rate) { int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, size_t moddata_length, uint32_t rate) {

File diff suppressed because it is too large Load diff

View file

@ -256,16 +256,16 @@ static void msf_cook_frame(MsfCookedFrame * frame, uint8_t * raw, uint8_t * used
int width, int height, int pitch, int depth) int width, int height, int pitch, int depth)
{ MsfTimeFunc { MsfTimeFunc
//bit depth for each channel //bit depth for each channel
const static int rdepthsArray[17] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 }; static const int rdepthsArray[17] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 };
const static int gdepthsArray[17] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6 }; static const int gdepthsArray[17] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6 };
const static int bdepthsArray[17] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 }; static const int bdepthsArray[17] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 };
//this extra level of indirection looks unnecessary but we need to explicitly decay the arrays to pointers //this extra level of indirection looks unnecessary but we need to explicitly decay the arrays to pointers
//in order to be able to swap them because of C's annoying not-quite-pointers, not-quite-value-types stack arrays. //in order to be able to swap them because of C's annoying not-quite-pointers, not-quite-value-types stack arrays.
const int * rdepths = msf_gif_bgra_flag? bdepthsArray : rdepthsArray; const int * rdepths = msf_gif_bgra_flag? bdepthsArray : rdepthsArray;
const int * gdepths = gdepthsArray; const int * gdepths = gdepthsArray;
const int * bdepths = msf_gif_bgra_flag? rdepthsArray : bdepthsArray; const int * bdepths = msf_gif_bgra_flag? rdepthsArray : bdepthsArray;
const static int ditherKernel[16] = { static const int ditherKernel[16] = {
0 << 12, 8 << 12, 2 << 12, 10 << 12, 0 << 12, 8 << 12, 2 << 12, 10 << 12,
12 << 12, 4 << 12, 14 << 12, 6 << 12, 12 << 12, 4 << 12, 14 << 12, 6 << 12,
3 << 12, 11 << 12, 1 << 12, 9 << 12, 3 << 12, 11 << 12, 1 << 12, 9 << 12,
@ -404,7 +404,7 @@ static MsfGifBuffer * msf_compress_frame(void * allocContext, int width, int hei
MsfGifBuffer * buffer = (MsfGifBuffer *) MSF_GIF_MALLOC(allocContext, maxBufSize); MsfGifBuffer * buffer = (MsfGifBuffer *) MSF_GIF_MALLOC(allocContext, maxBufSize);
if (!buffer) { return NULL; } if (!buffer) { return NULL; }
uint8_t * writeHead = buffer->data; uint8_t * writeHead = buffer->data;
MsfStridedList lzw = { lzwMem }; MsfStridedList lzw = { lzwMem, 0, 0 };
//allocate tlb //allocate tlb
int totalBits = frame.rbits + frame.gbits + frame.bbits; int totalBits = frame.rbits + frame.gbits + frame.bbits;

3053
raylib/external/nanosvg.h vendored Normal file

File diff suppressed because it is too large Load diff

1458
raylib/external/nanosvgrast.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -261,11 +261,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
} }
else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed
{ {
int data_size = 0; // NOTE: This forces only 1 mipmap to be loaded which is not really correct but it works
int data_size = (header->pitch_or_linear_size < file_size - 0x80) ? header->pitch_or_linear_size : file_size - 0x80;
// Calculate data size, including all mipmaps *mips = 1;
if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size*2;
else data_size = header->pitch_or_linear_size;
image_data = RL_MALLOC(data_size*sizeof(unsigned char)); image_data = RL_MALLOC(data_size*sizeof(unsigned char));
@ -814,5 +812,4 @@ static int get_pixel_data_size(int width, int height, int format)
return data_size; return data_size;
} }
#endif // RL_GPUTEX_IMPLEMENTATION #endif // RL_GPUTEX_IMPLEMENTATION

305
raylib/external/rprand.h vendored Normal file
View file

@ -0,0 +1,305 @@
/**********************************************************************************************
*
* rprand v1.0 - A simple and easy-to-use pseudo-random numbers generator (PRNG)
*
* FEATURES:
* - Pseudo-random values generation, 32 bits: [0..4294967295]
* - Sequence generation avoiding duplicate values
* - Using standard and proven prng algorithm (Xoshiro128**)
* - State initialized with a separate generator (SplitMix64)
*
* LIMITATIONS:
* - No negative numbers, up to the user to manage them
*
* POSSIBLE IMPROVEMENTS:
* - Support 64 bits generation
*
* ADDITIONAL NOTES:
* This library implements two pseudo-random number generation algorithms:
*
* - Xoshiro128** : https://prng.di.unimi.it/xoshiro128starstar.c
* - SplitMix64 : https://prng.di.unimi.it/splitmix64.c
*
* SplitMix64 is used to initialize the Xoshiro128** state, from a provided seed
*
* It's suggested to use SplitMix64 to initialize the state of the generators starting from
* a 64-bit seed, as research has shown that initialization must be performed with a generator
* radically different in nature from the one initialized to avoid correlation on similar seeds.
*
* CONFIGURATION:
* #define RPRAND_IMPLEMENTATION
* Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation.
*
* DEPENDENCIES: none
*
* VERSIONS HISTORY:
* 1.0 (01-Jun-2023) First version
*
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2023 Ramon Santamaria (@raysan5)
*
* 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.
*
* Permission is granted to anyone to use this software for any purpose, including commercial
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim that you
* wrote the original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
* as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
#ifndef RPRAND_H
#define RPRAND_H
#define RPRAND_VERSION "1.0"
// 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
#if defined(_WIN32)
#if defined(BUILD_LIBTYPE_SHARED)
#define RPRAND __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
#elif defined(USE_LIBTYPE_SHARED)
#define RPRAND __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
#endif
#endif
// Function specifiers definition
#ifndef RPRANDAPI
#define RPRANDAPI // Functions defined as 'extern' by default (implicit specifiers)
#endif
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
// Allow custom memory allocators
#ifndef RPRAND_CALLOC
#define RPRAND_CALLOC(ptr,sz) calloc(ptr,sz)
#endif
#ifndef RPRAND_FREE
#define RPRAND_FREE(ptr) free(ptr)
#endif
// Simple log system to avoid RPNG_LOG() calls if required
// NOTE: Avoiding those calls, also avoids const strings memory usage
#define RPRAND_SHOW_LOG_INFO
#if defined(RPNG_SHOW_LOG_INFO)
#define RPRAND_LOG(...) printf(__VA_ARGS__)
#else
#define RPRAND_LOG(...)
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
//...
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
//...
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
RPRANDAPI void rprand_set_seed(unsigned long long seed); // Set rprand_state for Xoshiro128**, seed is 64bit
RPRANDAPI int rprand_get_value(int min, int max); // Get random value within a range, min and max included
RPRANDAPI int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates
RPRANDAPI void rprand_unload_sequence(int *sequence); // Unload pseudo-random numbers sequence
#ifdef __cplusplus
}
#endif
#endif // RPRAND_H
/***********************************************************************************
*
* RPRAND IMPLEMENTATION
*
************************************************************************************/
#if defined(RPRAND_IMPLEMENTATION)
#include <stdlib.h> // Required for: calloc(), free(), abs()
#include <stdint.h> // Required for data types: uint32_t, uint64_t
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static uint64_t rprand_seed = 0; // SplitMix64 actual seed
static uint32_t rprand_state[4] = { 0 }; // Xoshiro128** state, nitialized by SplitMix64
//----------------------------------------------------------------------------------
// Module internal functions declaration
//----------------------------------------------------------------------------------
static uint32_t rprand_xoshiro(void); // Xoshiro128** generator (uses global rprand_state)
static uint64_t rprand_splitmix64(void); // SplitMix64 generator (uses seed to generate rprand_state)
//----------------------------------------------------------------------------------
// Module functions definition
//----------------------------------------------------------------------------------
// Set rprand_state for Xoshiro128**
// NOTE: We use a custom generation algorithm using SplitMix64
void rprand_set_seed(unsigned long long seed)
{
rprand_seed = (uint64_t)seed; // Set SplitMix64 seed for further use
// To generate the Xoshiro128** state, we use SplitMix64 generator first
// We generate 4 pseudo-random 64bit numbers that we combine using their LSB|MSB
rprand_state[0] = (uint32_t)(rprand_splitmix64() & 0xffffffff);
rprand_state[1] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32);
rprand_state[2] = (uint32_t)(rprand_splitmix64() & 0xffffffff);
rprand_state[3] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32);
}
// Get random value within a range, min and max included
int rprand_get_value(int min, int max)
{
int value = rprand_xoshiro()%(abs(max - min) + 1) + min;
return value;
}
// Load pseudo-random numbers sequence with no duplicates, min and max included
int *rprand_load_sequence(unsigned int count, int min, int max)
{
int *sequence = NULL;
if (count > (abs(max - min) + 1))
{
RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n");
//count = (max - min);
return sequence;
}
sequence = (int *)RPRAND_CALLOC(count, sizeof(int));
int value = 0;
bool value_is_dup = false;
for (int i = 0; i < count;)
{
value = (rprand_xoshiro()%(abs(max - min) + 1)) + min;
value_is_dup = false;
for (int j = 0; j < i; j++)
{
if (sequence[j] == value)
{
value_is_dup = true;
break;
}
}
if (!value_is_dup)
{
sequence[i] = value;
i++;
}
}
return sequence;
}
// Unload pseudo-random numbers sequence
void rprand_unload_sequence(int *sequence)
{
RPRAND_FREE(sequence);
sequence = NULL;
}
//----------------------------------------------------------------------------------
// Module internal functions definition
//----------------------------------------------------------------------------------
static inline uint32_t rprand_rotate_left(const uint32_t x, int k)
{
return (x << k) | (x >> (32 - k));
}
// Xoshiro128** generator info:
//
// Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
//
// To the extent possible under law, the author has dedicated all copyright
// and related and neighboring rights to this software to the public domain
// worldwide. This software is distributed without any warranty.
//
// See <http://creativecommons.org/publicdomain/zero/1.0/>.
//
// This is xoshiro128** 1.1, one of our 32-bit all-purpose, rock-solid
// generators. It has excellent speed, a state size (128 bits) that is
// large enough for mild parallelism, and it passes all tests we are aware
// of.
//
// Note that version 1.0 had mistakenly s[0] instead of s[1] as state
// word passed to the scrambler.
//
// For generating just single-precision (i.e., 32-bit) floating-point
// numbers, xoshiro128+ is even faster.
//
// The state must be seeded so that it is not everywhere zero.
//
uint32_t rprand_xoshiro(void)
{
const uint32_t result = rprand_rotate_left(rprand_state[1]*5, 7)*9;
const uint32_t t = rprand_state[1] << 9;
rprand_state[2] ^= rprand_state[0];
rprand_state[3] ^= rprand_state[1];
rprand_state[1] ^= rprand_state[2];
rprand_state[0] ^= rprand_state[3];
rprand_state[2] ^= t;
rprand_state[3] = rprand_rotate_left(rprand_state[3], 11);
return result;
}
// SplitMix64 generator info:
//
// Written in 2015 by Sebastiano Vigna (vigna@acm.org)
//
// To the extent possible under law, the author has dedicated all copyright
// and related and neighboring rights to this software to the public domain
// worldwide. This software is distributed without any warranty.
//
// See <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//
// This is a fixed-increment version of Java 8's SplittableRandom generator
// See http://dx.doi.org/10.1145/2714064.2660195 and
// http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
//
// It is a very fast generator passing BigCrush, and it can be useful if
// for some reason you absolutely want 64 bits of state.
uint64_t rprand_splitmix64()
{
uint64_t z = (rprand_seed += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30))*0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27))*0x94d049bb133111eb;
return z ^ (z >> 31);
}
#endif // RPRAND_IMPLEMENTATION

View file

@ -38,10 +38,10 @@ this file implementation in *one* C or C++ file to prevent collisions.
| zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 | | zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 |
| zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 | | zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 |
| zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 | | zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 |
| sdefl 1.0 -0 | 127 MB/s | 371 MB/s | 40004116 | 39.88 | | sdefl 1.0 -0 | 127 MB/s | 355 MB/s | 40004116 | 39.88 |
| sdefl 1.0 -1 | 111 MB/s | 398 MB/s | 38940674 | 38.82 | | sdefl 1.0 -1 | 111 MB/s | 413 MB/s | 38940674 | 38.82 |
| sdefl 1.0 -5 | 45 MB/s | 420 MB/s | 36577183 | 36.46 | | sdefl 1.0 -5 | 45 MB/s | 436 MB/s | 36577183 | 36.46 |
| sdefl 1.0 -7 | 38 MB/s | 423 MB/s | 36523781 | 36.41 | | sdefl 1.0 -7 | 38 MB/s | 432 MB/s | 36523781 | 36.41 |
| libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 | | libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 |
| libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 | | libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 |
| libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 | | libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 |
@ -51,7 +51,7 @@ this file implementation in *one* C or C++ file to prevent collisions.
Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia): Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia):
| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | | File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` |
| :------ | ---------: | -----------------: | ---------: | ----------: | | --------| -----------| -------------| ---------- | ------------|
| dickens | 10.192.446 | 4,260,187 | 3,845,261 | 3,833,657 | | dickens | 10.192.446 | 4,260,187 | 3,845,261 | 3,833,657 |
| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | | mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 |
| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | | mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 |
@ -71,7 +71,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index
This software is available under 2 licenses -- choose whichever you prefer. This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
ALTERNATIVE A - MIT License ALTERNATIVE A - MIT License
Copyright (c) 2020 Micha Mettke Copyright (c) 2020-2023 Micha Mettke
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
@ -125,7 +125,7 @@ extern "C" {
#define SDEFL_MIN_MATCH 4 #define SDEFL_MIN_MATCH 4
#define SDEFL_BLK_MAX (256*1024) #define SDEFL_BLK_MAX (256*1024)
#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX + SDEFL_MIN_MATCH)/SDEFL_MIN_MATCH) #define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX+2)/3)
#define SDEFL_SYM_MAX (288) #define SDEFL_SYM_MAX (288)
#define SDEFL_OFF_MAX (32) #define SDEFL_OFF_MAX (32)
@ -185,6 +185,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl);
#define SDEFL_MAX_CODE_LEN (15) #define SDEFL_MAX_CODE_LEN (15)
#define SDEFL_SYM_BITS (10u) #define SDEFL_SYM_BITS (10u)
#define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u) #define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u)
#define SDEFL_RAW_BLK_SIZE (65535)
#define SDEFL_LIT_LEN_CODES (14) #define SDEFL_LIT_LEN_CODES (14)
#define SDEFL_OFF_CODES (15) #define SDEFL_OFF_CODES (15)
#define SDEFL_PRE_CODES (7) #define SDEFL_PRE_CODES (7)
@ -192,6 +193,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl);
#define SDEFL_EOB (256) #define SDEFL_EOB (256)
#define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1)) #define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1))
#define sdefl_div_round_up(n,d) (((n)+((d)-1))/(d))
static int static int
sdefl_ilog2(int n) { sdefl_ilog2(int n) {
@ -438,12 +440,12 @@ sdefl_precode(struct sdefl_symcnt *cnt, unsigned *freqs, unsigned *items,
} while (run_start != total); } while (run_start != total);
cnt->items = (int)(at - items); cnt->items = (int)(at - items);
} }
struct sdefl_match_codes { struct sdefl_match_codest {
int ls, lc; int ls, lc;
int dc, dx; int dc, dx;
}; };
static void static void
sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) {
static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576}; static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576};
static const unsigned char lslot[258+1] = { static const unsigned char lslot[258+1] = {
0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12,
@ -462,11 +464,53 @@ sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) {
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 28 27, 27, 28
}; };
assert(len <= 258);
assert(dist <= 32768);
cod->ls = lslot[len]; cod->ls = lslot[len];
cod->lc = 257 + cod->ls; cod->lc = 257 + cod->ls;
assert(cod->lc <= 285);
cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2);
cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1;
} }
enum sdefl_blk_type {
SDEFL_BLK_UCOMPR,
SDEFL_BLK_DYN
};
static enum sdefl_blk_type
sdefl_blk_type(const struct sdefl *s, int blk_len, int pre_item_len,
const unsigned *pre_freq, const unsigned char *pre_len) {
static const unsigned char x_pre_bits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
static const unsigned char x_len_bits[] = {0,0,0,0,0,0,0,0, 1,1,1,1,2,2,2,2,
3,3,3,3,4,4,4,4, 5,5,5,5,0};
static const unsigned char x_off_bits[] = {0,0,0,0,1,1,2,2, 3,3,4,4,5,5,6,6,
7,7,8,8,9,9,10,10, 11,11,12,12,13,13};
int dyn_cost = 0;
int fix_cost = 0;
int sym = 0;
dyn_cost += 5 + 5 + 4 + (3 * pre_item_len);
for (sym = 0; sym < SDEFL_PRE_MAX; sym++)
dyn_cost += pre_freq[sym] * (x_pre_bits[sym] + pre_len[sym]);
for (sym = 0; sym < 256; sym++)
dyn_cost += s->freq.lit[sym] * s->cod.len.lit[sym];
dyn_cost += s->cod.len.lit[SDEFL_EOB];
for (sym = 257; sym < 286; sym++)
dyn_cost += s->freq.lit[sym] * (x_len_bits[sym - 257] + s->cod.len.lit[sym]);
for (sym = 0; sym < 30; sym++)
dyn_cost += s->freq.off[sym] * (x_off_bits[sym] + s->cod.len.off[sym]);
fix_cost += 8*(5 * sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE) + blk_len + 1 + 2);
return (dyn_cost < fix_cost) ? SDEFL_BLK_DYN : SDEFL_BLK_UCOMPR;
}
static void
sdefl_put16(unsigned char **dst, unsigned short x) {
unsigned char *val = *dst;
val[0] = (unsigned char)(x & 0xff);
val[1] = (unsigned char)(x >> 8);
*dst = val + 2;
}
static void static void
sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) {
static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
@ -475,7 +519,7 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) {
static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257, static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,
385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; 385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577};
struct sdefl_match_codes cod; struct sdefl_match_codest cod;
sdefl_match_codes(&cod, dist, len); sdefl_match_codes(&cod, dist, len);
sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]); sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]);
sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]); sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]);
@ -484,7 +528,8 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) {
} }
static void static void
sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last,
const unsigned char *in) { const unsigned char *in, int blk_begin, int blk_end) {
int blk_len = blk_end - blk_begin;
int j, i = 0, item_cnt = 0; int j, i = 0, item_cnt = 0;
struct sdefl_symcnt symcnt = {0}; struct sdefl_symcnt symcnt = {0};
unsigned codes[SDEFL_PRE_MAX]; unsigned codes[SDEFL_PRE_MAX];
@ -494,23 +539,48 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last,
static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11, static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11,
4,12,3,13,2,14,1,15}; 4,12,3,13,2,14,1,15};
/* huffman codes */ /* calculate huffman codes */
s->freq.lit[SDEFL_EOB]++; s->freq.lit[SDEFL_EOB]++;
sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES); sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES);
sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES); sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES);
sdefl_precode(&symcnt, freqs, items, s->cod.len.lit, s->cod.len.off); sdefl_precode(&symcnt, freqs, items, s->cod.len.lit, s->cod.len.off);
sdefl_huff(lens, codes, freqs, SDEFL_PRE_MAX, SDEFL_PRE_CODES); sdefl_huff(lens, codes, freqs, SDEFL_PRE_MAX, SDEFL_PRE_CODES);
for (item_cnt = SDEFL_PRE_MAX; item_cnt > 4; item_cnt--) { for (item_cnt = SDEFL_PRE_MAX; item_cnt > 4; item_cnt--) {
if (lens[perm[item_cnt - 1]]) break; if (lens[perm[item_cnt - 1]]){
break;
} }
/* block header */ }
sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ /* write block */
switch (sdefl_blk_type(s, blk_len, item_cnt, freqs, lens)) {
case SDEFL_BLK_UCOMPR: {
/* uncompressed blocks */
int n = sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE);
for (i = 0; i < n; ++i) {
int fin = is_last && (i + 1 == n);
int amount = blk_len < SDEFL_RAW_BLK_SIZE ? blk_len : SDEFL_RAW_BLK_SIZE;
sdefl_put(dst, s, !!fin, 1); /* block */
sdefl_put(dst, s, 0x00, 2); /* stored block */
if (s->bitcnt) {
sdefl_put(dst, s, 0x00, 8 - s->bitcnt);
}
assert(s->bitcnt == 0);
sdefl_put16(dst, (unsigned short)amount);
sdefl_put16(dst, ~(unsigned short)amount);
memcpy(*dst, in + blk_begin + i * SDEFL_RAW_BLK_SIZE, amount);
*dst = *dst + amount;
blk_len -= amount;
}
} break;
case SDEFL_BLK_DYN: {
/* dynamic huffman block */
sdefl_put(dst, s, !!is_last, 1); /* block */
sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */
sdefl_put(dst, s, symcnt.lit - 257, 5); sdefl_put(dst, s, symcnt.lit - 257, 5);
sdefl_put(dst, s, symcnt.off - 1, 5); sdefl_put(dst, s, symcnt.off - 1, 5);
sdefl_put(dst, s, item_cnt - 4, 4); sdefl_put(dst, s, item_cnt - 4, 4);
for (i = 0; i < item_cnt; ++i) for (i = 0; i < item_cnt; ++i) {
sdefl_put(dst, s, lens[perm[i]], 3); sdefl_put(dst, s, lens[perm[i]], 3);
}
for (i = 0; i < symcnt.items; ++i) { for (i = 0; i < symcnt.items; ++i) {
unsigned sym = items[i] & 0x1F; unsigned sym = items[i] & 0x1F;
sdefl_put(dst, s, (int)codes[sym], lens[sym]); sdefl_put(dst, s, (int)codes[sym], lens[sym]);
@ -521,14 +591,17 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last,
} }
/* block sequences */ /* block sequences */
for (i = 0; i < s->seq_cnt; ++i) { for (i = 0; i < s->seq_cnt; ++i) {
if (s->seq[i].off >= 0) if (s->seq[i].off >= 0) {
for (j = 0; j < s->seq[i].len; ++j) { for (j = 0; j < s->seq[i].len; ++j) {
int c = in[s->seq[i].off + j]; int c = in[s->seq[i].off + j];
sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]);
} }
else sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); } else {
sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len);
}
} }
sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]);
} break;}
memset(&s->freq, 0, sizeof(s->freq)); memset(&s->freq, 0, sizeof(s->freq));
s->seq_cnt = 0; s->seq_cnt = 0;
} }
@ -541,8 +614,12 @@ sdefl_seq(struct sdefl *s, int off, int len) {
} }
static void static void
sdefl_reg_match(struct sdefl *s, int off, int len) { sdefl_reg_match(struct sdefl *s, int off, int len) {
struct sdefl_match_codes cod; struct sdefl_match_codest cod;
sdefl_match_codes(&cod, off, len); sdefl_match_codes(&cod, off, len);
assert(cod.lc < SDEFL_SYM_MAX);
assert(cod.dc < SDEFL_OFF_MAX);
s->freq.lit[cod.lc]++; s->freq.lit[cod.lc]++;
s->freq.off[cod.dc]++; s->freq.off[cod.dc]++;
} }
@ -551,18 +628,31 @@ struct sdefl_match {
int len; int len;
}; };
static void static void
sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, int chain_len,
int chain_len, int max_match, const unsigned char *in, int p) { int max_match, const unsigned char *in, int p, int e) {
int i = s->tbl[sdefl_hash32(&in[p])]; int i = s->tbl[sdefl_hash32(in + p)];
int limit = ((p - SDEFL_WIN_SIZ) < SDEFL_NIL) ? SDEFL_NIL : (p-SDEFL_WIN_SIZ); int limit = ((p - SDEFL_WIN_SIZ) < SDEFL_NIL) ? SDEFL_NIL : (p-SDEFL_WIN_SIZ);
assert(p < e);
assert(p + max_match <= e);
while (i > limit) { while (i > limit) {
assert(i + m->len < e);
assert(p + m->len < e);
assert(i + SDEFL_MIN_MATCH < e);
assert(p + SDEFL_MIN_MATCH < e);
if (in[i + m->len] == in[p + m->len] && if (in[i + m->len] == in[p + m->len] &&
(sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))) { (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))) {
int n = SDEFL_MIN_MATCH; int n = SDEFL_MIN_MATCH;
while (n < max_match && in[i+n] == in[p+n]) n++; while (n < max_match && in[i + n] == in[p + n]) {
assert(i + n < e);
assert(p + n < e);
n++;
}
if (n > m->len) { if (n > m->len) {
m->len = n, m->off = p - i; m->len = n, m->off = p - i;
if (n == max_match) break; if (n == max_match)
break;
} }
} }
if (!(--chain_len)) break; if (!(--chain_len)) break;
@ -579,18 +669,20 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in,
for (n = 0; n < SDEFL_HASH_SIZ; ++n) { for (n = 0; n < SDEFL_HASH_SIZ; ++n) {
s->tbl[n] = SDEFL_NIL; s->tbl[n] = SDEFL_NIL;
} }
do {int blk_end = i + SDEFL_BLK_MAX < in_len ? i + SDEFL_BLK_MAX : in_len; do {int blk_begin = i;
int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len;
while (i < blk_end) { while (i < blk_end) {
struct sdefl_match m = {0}; struct sdefl_match m = {0};
int max_match = ((in_len-i)>SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH:(in_len-i); int left = blk_end - i;
int max_match = (left > SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left;
int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match;
int run = 1, inc = 1, run_inc; int run = 1, inc = 1, run_inc = 0;
if (max_match > SDEFL_MIN_MATCH) { if (max_match > SDEFL_MIN_MATCH) {
sdefl_fnd(&m, s, max_chain, max_match, in, i); sdefl_fnd(&m, s, max_chain, max_match, in, i, in_len);
} }
if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len < nice_match){ if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len + 1 < nice_match){
struct sdefl_match m2 = {0}; struct sdefl_match m2 = {0};
sdefl_fnd(&m2, s, max_chain, m.len+1, in, i+1); sdefl_fnd(&m2, s, max_chain, m.len + 1, in, i + 1, in_len);
m.len = (m2.len > m.len) ? 0 : m.len; m.len = (m2.len > m.len) ? 0 : m.len;
} }
if (m.len >= SDEFL_MIN_MATCH) { if (m.len >= SDEFL_MIN_MATCH) {
@ -615,20 +707,23 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in,
unsigned h = sdefl_hash32(&in[i]); unsigned h = sdefl_hash32(&in[i]);
s->prv[i&SDEFL_WIN_MSK] = s->tbl[h]; s->prv[i&SDEFL_WIN_MSK] = s->tbl[h];
s->tbl[h] = i, i += inc; s->tbl[h] = i, i += inc;
assert(i <= blk_end);
} }
} else { } else {
i += run_inc; i += run_inc;
assert(i <= blk_end);
} }
} }
if (litlen) { if (litlen) {
sdefl_seq(s, i - litlen, litlen); sdefl_seq(s, i - litlen, litlen);
litlen = 0; litlen = 0;
} }
sdefl_flush(&q, s, blk_end == in_len, in); sdefl_flush(&q, s, blk_end == in_len, in, blk_begin, blk_end);
} while (i < in_len); } while (i < in_len);
if (s->bitcnt) {
if (s->bitcnt)
sdefl_put(&q, s, 0x00, 8 - s->bitcnt); sdefl_put(&q, s, 0x00, 8 - s->bitcnt);
}
assert(s->bitcnt == 0);
return (int)(q - out); return (int)(q - out);
} }
extern int extern int
@ -688,9 +783,8 @@ zsdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) {
} }
extern int extern int
sdefl_bound(int len) { sdefl_bound(int len) {
int a = 128 + (len * 110) / 100; int max_blocks = 1 + sdefl_div_round_up(len, SDEFL_RAW_BLK_SIZE);
int b = 128 + len + ((len / (31 * 1024)) + 1) * 5; int bound = 5 * max_blocks + len + 1 + 4 + 8;
return (a > b) ? a : b; return bound;
} }
#endif /* SDEFL_IMPLEMENTATION */ #endif /* SDEFL_IMPLEMENTATION */

View file

@ -10,7 +10,7 @@ as needed to keep the implementation as concise as possible.
- Dual license with either MIT or public domain - Dual license with either MIT or public domain
- Small implementation - Small implementation
- Deflate: 525 LoC - Deflate: 525 LoC
- Inflate: 320 LoC - Inflate: 500 LoC
- Webassembly: - Webassembly:
- Deflate ~3.7 KB (~2.2KB compressed) - Deflate ~3.7 KB (~2.2KB compressed)
- Inflate ~3.6 KB (~2.2KB compressed) - Inflate ~3.6 KB (~2.2KB compressed)
@ -39,10 +39,10 @@ this file implementation in *one* C or C++ file to prevent collisions.
| zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 | | zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 |
| zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 | | zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 |
| zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 | | zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 |
| sdefl 1.0 -0 | 127 MB/s | 371 MB/s | 40004116 | 39.88 | | sdefl 1.0 -0 | 127 MB/s | 355 MB/s | 40004116 | 39.88 |
| sdefl 1.0 -1 | 111 MB/s | 398 MB/s | 38940674 | 38.82 | | sdefl 1.0 -1 | 111 MB/s | 413 MB/s | 38940674 | 38.82 |
| sdefl 1.0 -5 | 45 MB/s | 420 MB/s | 36577183 | 36.46 | | sdefl 1.0 -5 | 45 MB/s | 436 MB/s | 36577183 | 36.46 |
| sdefl 1.0 -7 | 38 MB/s | 423 MB/s | 36523781 | 36.41 | | sdefl 1.0 -7 | 38 MB/s | 432 MB/s | 36523781 | 36.41 |
| libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 | | libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 |
| libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 | | libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 |
| libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 | | libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 |
@ -52,7 +52,7 @@ this file implementation in *one* C or C++ file to prevent collisions.
Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia): Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia):
| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | | File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` |
| :------ | ---------: | -----------------: | ---------: | ----------: | | --------| -----------| -------------| ---------- | ------------|
| dickens | 10.192.446 | 4,260,187 | 3,845,261 | 3,833,657 | | dickens | 10.192.446 | 4,260,187 | 3,845,261 | 3,833,657 |
| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | | mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 |
| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | | mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 |
@ -72,7 +72,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index
This software is available under 2 licenses -- choose whichever you prefer. This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
ALTERNATIVE A - MIT License ALTERNATIVE A - MIT License
Copyright (c) 2020 Micha Mettke Copyright (c) 2020-2023 Micha Mettke
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
@ -151,7 +151,7 @@ extern int zsinflate(void *out, int cap, const void *in, int size);
#endif #endif
#ifndef SINFL_NO_SIMD #ifndef SINFL_NO_SIMD
#if __x86_64__ || defined(_WIN32) || defined(_WIN64) #if defined(__x86_64__) || defined(_WIN32) || defined(_WIN64)
#include <emmintrin.h> #include <emmintrin.h>
#define sinfl_char16 __m128i #define sinfl_char16 __m128i
#define sinfl_char16_ld(p) _mm_loadu_si128((const __m128i *)(void*)(p)) #define sinfl_char16_ld(p) _mm_loadu_si128((const __m128i *)(void*)(p))
@ -183,6 +183,18 @@ sinfl_read64(const void *p) {
memcpy(&n, p, 8); memcpy(&n, p, 8);
return n; return n;
} }
static void
sinfl_copy64(unsigned char **dst, unsigned char **src) {
unsigned long long n;
memcpy(&n, *src, 8);
memcpy(*dst, &n, 8);
*dst += 8, *src += 8;
}
static unsigned char*
sinfl_write64(unsigned char *dst, unsigned long long w) {
memcpy(dst, &w, 8);
return dst + 8;
}
#ifndef SINFL_NO_SIMD #ifndef SINFL_NO_SIMD
static unsigned char* static unsigned char*
sinfl_write128(unsigned char *dst, sinfl_char16 w) { sinfl_write128(unsigned char *dst, sinfl_char16 w) {
@ -195,25 +207,12 @@ sinfl_copy128(unsigned char **dst, unsigned char **src) {
sinfl_char16_str(*dst, n); sinfl_char16_str(*dst, n);
*dst += 16, *src += 16; *dst += 16, *src += 16;
} }
#else
static unsigned char*
sinfl_write64(unsigned char *dst, unsigned long long w) {
memcpy(dst, &w, 8);
return dst + 8;
}
static void
sinfl_copy64(unsigned char **dst, unsigned char **src) {
unsigned long long n;
memcpy(&n, *src, 8);
memcpy(*dst, &n, 8);
*dst += 8, *src += 8;
}
#endif #endif
static void static void
sinfl_refill(struct sinfl *s) { sinfl_refill(struct sinfl *s) {
s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt;
s->bitptr += (63 - s->bitcnt) >> 3; s->bitptr += (63 - s->bitcnt) >> 3;
s->bitcnt |= 56; /* bitcount is in range [56,63] */ s->bitcnt |= 56; /* bitcount in range [56,63] */
} }
static int static int
sinfl_peek(struct sinfl *s, int cnt) { sinfl_peek(struct sinfl *s, int cnt) {
@ -222,7 +221,7 @@ sinfl_peek(struct sinfl *s, int cnt) {
return s->bitbuf & ((1ull << cnt) - 1); return s->bitbuf & ((1ull << cnt) - 1);
} }
static void static void
sinfl_consume(struct sinfl *s, int cnt) { sinfl_eat(struct sinfl *s, int cnt) {
assert(cnt <= s->bitcnt); assert(cnt <= s->bitcnt);
s->bitbuf >>= cnt; s->bitbuf >>= cnt;
s->bitcnt -= cnt; s->bitcnt -= cnt;
@ -230,7 +229,7 @@ sinfl_consume(struct sinfl *s, int cnt) {
static int static int
sinfl__get(struct sinfl *s, int cnt) { sinfl__get(struct sinfl *s, int cnt) {
int res = sinfl_peek(s, cnt); int res = sinfl_peek(s, cnt);
sinfl_consume(s, cnt); sinfl_eat(s, cnt);
return res; return res;
} }
static int static int
@ -285,7 +284,7 @@ sinfl_build_subtbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits,
while (1) { while (1) {
unsigned entry; unsigned entry;
int bit, stride, i; int bit, stride, i;
/* start new subtable */ /* start new sub-table */
if ((gen->word & ((1 << tbl_bits)-1)) != sub_prefix) { if ((gen->word & ((1 << tbl_bits)-1)) != sub_prefix) {
int used = 0; int used = 0;
sub_prefix = gen->word & ((1 << tbl_bits)-1); sub_prefix = gen->word & ((1 << tbl_bits)-1);
@ -299,7 +298,7 @@ sinfl_build_subtbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits,
tbl_end = sub_start + (1 << sub_bits); tbl_end = sub_start + (1 << sub_bits);
tbl[sub_prefix] = (sub_start << 16) | 0x10 | (sub_bits & 0xf); tbl[sub_prefix] = (sub_start << 16) | 0x10 | (sub_bits & 0xf);
} }
/* fill subtable */ /* fill sub-table */
entry = (*gen->sorted << 16) | ((gen->len - tbl_bits) & 0xf); entry = (*gen->sorted << 16) | ((gen->len - tbl_bits) & 0xf);
gen->sorted++; gen->sorted++;
i = sub_start + (gen->word >> tbl_bits); i = sub_start + (gen->word >> tbl_bits);
@ -353,18 +352,17 @@ sinfl_build(unsigned *tbl, unsigned char *lens, int tbl_bits, int maxlen,
} }
static int static int
sinfl_decode(struct sinfl *s, const unsigned *tbl, int bit_len) { sinfl_decode(struct sinfl *s, const unsigned *tbl, int bit_len) {
sinfl_refill(s); int idx = sinfl_peek(s, bit_len);
{int idx = sinfl_peek(s, bit_len);
unsigned key = tbl[idx]; unsigned key = tbl[idx];
if (key & 0x10) { if (key & 0x10) {
/* sub-table lookup */ /* sub-table lookup */
int len = key & 0x0f; int len = key & 0x0f;
sinfl_consume(s, bit_len); sinfl_eat(s, bit_len);
idx = sinfl_peek(s, len); idx = sinfl_peek(s, len);
key = tbl[((key >> 16) & 0xffff) + (unsigned)idx]; key = tbl[((key >> 16) & 0xffff) + (unsigned)idx];
} }
sinfl_consume(s, key & 0x0f); sinfl_eat(s, key & 0x0f);
return (key >> 16) & 0x0fff;} return (key >> 16) & 0x0fff;
} }
static int static int
sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) { sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) {
@ -402,17 +400,21 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size)
} break; } break;
case stored: { case stored: {
/* uncompressed block */ /* uncompressed block */
int len; unsigned len, nlen;
sinfl_refill(&s);
sinfl__get(&s,s.bitcnt & 7); sinfl__get(&s,s.bitcnt & 7);
len = sinfl__get(&s,16); len = (unsigned short)sinfl__get(&s,16);
//int nlen = sinfl__get(&s,16); // @raysan5: Unused variable? nlen = (unsigned short)sinfl__get(&s,16);
in -= 2; s.bitcnt = 0; s.bitptr -= s.bitcnt / 8;
s.bitbuf = s.bitcnt = 0;
if (len > (e-in) || !len) if ((unsigned short)len != (unsigned short)~nlen)
return (int)(out-o); return (int)(out-o);
memcpy(out, in, (size_t)len); if (len > (e - s.bitptr) || !len)
in += len, out += len; return (int)(out-o);
memcpy(out, s.bitptr, (size_t)len);
s.bitptr += len, out += len;
if (last) return (int)(out-o);
state = hdr; state = hdr;
} break; } break;
case fixed: { case fixed: {
@ -445,7 +447,9 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size)
/* decode code lengths */ /* decode code lengths */
for (n = 0; n < nlit + ndist;) { for (n = 0; n < nlit + ndist;) {
int sym = sinfl_decode(&s, hlens, 7); int sym = 0;
sinfl_refill(&s);
sym = sinfl_decode(&s, hlens, 7);
switch (sym) {default: lens[n++] = (unsigned char)sym; break; switch (sym) {default: lens[n++] = (unsigned char)sym; break;
case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break;
case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break;
@ -458,12 +462,34 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size)
} break; } break;
case blk: { case blk: {
/* decompress block */ /* decompress block */
int sym = sinfl_decode(&s, s.lits, 10); while (1) {
int sym;
sinfl_refill(&s);
sym = sinfl_decode(&s, s.lits, 10);
if (sym < 256) { if (sym < 256) {
/* literal */ /* literal */
if (sinfl_unlikely(out >= oe)) {
return (int)(out-o);
}
*out++ = (unsigned char)sym; *out++ = (unsigned char)sym;
} else if (sym > 256) {sym -= 257; /* match symbol */ sym = sinfl_decode(&s, s.lits, 10);
sinfl_refill(&s); if (sym < 256) {
*out++ = (unsigned char)sym;
continue;
}
}
if (sinfl_unlikely(sym == 256)) {
/* end of block */
if (last) return (int)(out-o);
state = hdr;
break;
}
/* match */
if (sym >= 286) {
/* length codes 286 and 287 must not appear in compressed data */
return (int)(out-o);
}
sym -= 257;
{int len = sinfl__get(&s, lbits[sym]) + lbase[sym]; {int len = sinfl__get(&s, lbits[sym]) + lbase[sym];
int dsym = sinfl_decode(&s, s.dsts, 8); int dsym = sinfl_decode(&s, s.dsts, 8);
int offs = sinfl__get(&s, dbits[dsym]) + dbase[dsym]; int offs = sinfl__get(&s, dbits[dsym]) + dbase[dsym];
@ -476,11 +502,17 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size)
#ifndef SINFL_NO_SIMD #ifndef SINFL_NO_SIMD
if (sinfl_likely(oe - out >= 16 * 3)) { if (sinfl_likely(oe - out >= 16 * 3)) {
if (offs >= 16) { if (offs >= 16) {
/* copy match */ /* simd copy match */
sinfl_copy128(&dst, &src); sinfl_copy128(&dst, &src);
sinfl_copy128(&dst, &src); sinfl_copy128(&dst, &src);
do sinfl_copy128(&dst, &src); do sinfl_copy128(&dst, &src);
while (dst < out); while (dst < out);
} else if (offs >= 8) {
/* word copy match */
sinfl_copy64(&dst, &src);
sinfl_copy64(&dst, &src);
do sinfl_copy64(&dst, &src);
while (dst < out);
} else if (offs == 1) { } else if (offs == 1) {
/* rle match copying */ /* rle match copying */
sinfl_char16 w = sinfl_char16_char(src[0]); sinfl_char16 w = sinfl_char16_char(src[0]);
@ -489,6 +521,7 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size)
do dst = sinfl_write128(dst, w); do dst = sinfl_write128(dst, w);
while (dst < out); while (dst < out);
} else { } else {
/* byte copy match */
*dst++ = *src++; *dst++ = *src++;
*dst++ = *src++; *dst++ = *src++;
do *dst++ = *src++; do *dst++ = *src++;
@ -498,7 +531,7 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size)
#else #else
if (sinfl_likely(oe - out >= 3 * 8 - 3)) { if (sinfl_likely(oe - out >= 3 * 8 - 3)) {
if (offs >= 8) { if (offs >= 8) {
/* copy match */ /* word copy match */
sinfl_copy64(&dst, &src); sinfl_copy64(&dst, &src);
sinfl_copy64(&dst, &src); sinfl_copy64(&dst, &src);
do sinfl_copy64(&dst, &src); do sinfl_copy64(&dst, &src);
@ -513,6 +546,7 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size)
do dst = sinfl_write64(dst, w); do dst = sinfl_write64(dst, w);
while (dst < out); while (dst < out);
} else { } else {
/* byte copy match */
*dst++ = *src++; *dst++ = *src++;
*dst++ = *src++; *dst++ = *src++;
do *dst++ = *src++; do *dst++ = *src++;
@ -524,13 +558,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size)
*dst++ = *src++; *dst++ = *src++;
*dst++ = *src++; *dst++ = *src++;
do *dst++ = *src++; do *dst++ = *src++;
while (dst < out);} while (dst < out);
} }}
} else {
/* end of block */
if (last) return (int)(out-o);
state = hdr;
break;
} }
} break;} } break;}
} }

File diff suppressed because it is too large Load diff

10303
raylib/external/stb_image_resize2.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -746,7 +746,7 @@ static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out,
(*materials_out) = NULL; (*materials_out) = NULL;
(*num_materials_out) = 0; (*num_materials_out) = 0;
fp = fopen(filename, "r"); fp = fopen(filename, "rt");
if (!fp) { if (!fp) {
fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno); fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno);
return TINYOBJ_ERROR_FILE_OPERATION; return TINYOBJ_ERROR_FILE_OPERATION;
@ -1269,6 +1269,11 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
if (is_line_ending(buf, i, end_idx)) { if (is_line_ending(buf, i, end_idx)) {
line_infos[line_no].pos = prev_pos; line_infos[line_no].pos = prev_pos;
line_infos[line_no].len = i - prev_pos; line_infos[line_no].len = i - prev_pos;
// ---- QUICK BUG FIX : https://github.com/raysan5/raylib/issues/3473
if ( i > 0 && buf[i-1] == '\r' ) line_infos[line_no].len--;
// --------
prev_pos = i + 1; prev_pos = i + 1;
line_no++; line_no++;
} }

View file

@ -555,7 +555,7 @@ int Vox_LoadFromMemory(unsigned char* pvoxData, unsigned int voxDataSize, VoxArr
version = ((unsigned int*)fileDataPtr)[0]; version = ((unsigned int*)fileDataPtr)[0];
fileDataPtr += 4; fileDataPtr += 4;
if (version != 150) if (version != 150 && version != 200)
{ {
return VOX_ERROR_FILE_VERSION_NOT_MATCH; //"MagicaVoxel version doesn't match" return VOX_ERROR_FILE_VERSION_NOT_MATCH; //"MagicaVoxel version doesn't match"
} }

View file

@ -11,8 +11,8 @@ package rl
extern void android_init(); extern void android_init();
AAssetManager* asset_manager; extern AAssetManager* asset_manager;
const char* internal_storage_path; extern const char* internal_storage_path;
*/ */
import "C" import "C"

View file

@ -1,5 +1,5 @@
//go:build !rpi && !drm && !android //go:build !sdl && !drm && !android
// +build !rpi,!drm,!android // +build !sdl,!drm,!android
package rl package rl

View file

@ -1,5 +1,5 @@
//go:build linux && rpi && !drm && !android //go:build sdl && !drm && !android
// +build linux,rpi,!drm,!android // +build sdl,!drm,!android
package rl package rl
@ -27,7 +27,6 @@ func InitWindow(width int32, height int32, title string) {
// SetCallbackFunc - Sets callback function // SetCallbackFunc - Sets callback function
func SetCallbackFunc(func()) { func SetCallbackFunc(func()) {
return
} }
// ShowCursor - Shows cursor // ShowCursor - Shows cursor
@ -66,17 +65,27 @@ func DisableCursor() {
// IsFileDropped - Check if a file have been dropped into window // IsFileDropped - Check if a file have been dropped into window
func IsFileDropped() bool { func IsFileDropped() bool {
return false ret := C.IsFileDropped()
v := bool(ret)
return v
} }
// LoadDroppedFiles - Load dropped filepaths // LoadDroppedFiles - Load dropped filepaths
func LoadDroppedFiles() (files []string) { func LoadDroppedFiles() []string {
return ret := C.LoadDroppedFiles()
defer C.UnloadDroppedFiles(ret)
tmpslice := (*[1 << 24]*C.char)(unsafe.Pointer(ret.paths))[:ret.count:ret.count]
gostrings := make([]string, ret.count)
for i, s := range tmpslice {
gostrings[i] = C.GoString(s)
}
return gostrings
} }
// UnloadDroppedFiles - Unload dropped filepaths // UnloadDroppedFiles - Unload dropped filepaths
func UnloadDroppedFiles() { func UnloadDroppedFiles() {
return
} }
// OpenAsset - Open asset // OpenAsset - Open asset

View file

@ -1,5 +1,5 @@
//go:build linux && drm && !rpi && !android //go:build linux && drm && !sdl && !android
// +build linux,drm,!rpi,!android // +build linux,drm,!sdl,!android
package rl package rl

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1942
raylib/platforms/rcore_drm.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,6 @@
* - Play/Stop/Pause/Resume loaded audio * - Play/Stop/Pause/Resume loaded audio
* *
* CONFIGURATION: * CONFIGURATION:
*
* #define SUPPORT_MODULE_RAUDIO * #define SUPPORT_MODULE_RAUDIO
* raudio module is included in the build * raudio module is included in the build
* *
@ -45,7 +44,7 @@
* David Reid (github: @mackron) (Nov. 2017): * David Reid (github: @mackron) (Nov. 2017):
* - Complete port to miniaudio library * - Complete port to miniaudio library
* *
* Joshua Reisenauer (github: @kd7tck) (2015) * Joshua Reisenauer (github: @kd7tck) (2015):
* - XM audio module support (jar_xm) * - XM audio module support (jar_xm)
* - MOD audio module support (jar_mod) * - MOD audio module support (jar_mod)
* - Mixing channels support * - Mixing channels support
@ -231,9 +230,20 @@ typedef struct tagBITMAPINFOHEADER {
#define QOA_MALLOC RL_MALLOC #define QOA_MALLOC RL_MALLOC
#define QOA_FREE RL_FREE #define QOA_FREE RL_FREE
#if defined(_MSC_VER) // Disable some MSVC warning
#pragma warning(push)
#pragma warning(disable : 4018)
#pragma warning(disable : 4267)
#pragma warning(disable : 4244)
#endif
#define QOA_IMPLEMENTATION #define QOA_IMPLEMENTATION
#include "external/qoa.h" // QOA loading and saving functions #include "external/qoa.h" // QOA loading and saving functions
#include "external/qoaplay.c" // QOA stream playing helper functions #include "external/qoaplay.c" // QOA stream playing helper functions
#if defined(_MSC_VER)
#pragma warning(pop) // Disable MSVC warning suppression
#endif
#endif #endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
@ -250,7 +260,7 @@ typedef struct tagBITMAPINFOHEADER {
#define JARXM_MALLOC RL_MALLOC #define JARXM_MALLOC RL_MALLOC
#define JARXM_FREE RL_FREE #define JARXM_FREE RL_FREE
#if defined(_MSC_VER ) // jar_xm has warnings on windows, so disable them just for this file #if defined(_MSC_VER) // Disable some MSVC warning
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4244) #pragma warning(disable : 4244)
#endif #endif
@ -259,7 +269,7 @@ typedef struct tagBITMAPINFOHEADER {
#include "external/jar_xm.h" // XM loading functions #include "external/jar_xm.h" // XM loading functions
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma warning( pop ) #pragma warning(pop) // Disable MSVC warning suppression
#endif #endif
#endif #endif
@ -406,8 +416,8 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr
static bool IsFileExtension(const char *fileName, const char *ext); // Check file extension static bool IsFileExtension(const char *fileName, const char *ext); // Check file extension
static const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes the dot: .png) static const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes the dot: .png)
static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) static unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read)
static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write) static bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write)
static bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated static bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated
#endif #endif
@ -467,6 +477,16 @@ void InitAudioDevice(void)
return; return;
} }
// Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may
// want to look at something a bit smarter later on to keep everything real-time, if that's necessary.
if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS)
{
TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing");
ma_device_uninit(&AUDIO.System.device);
ma_context_uninit(&AUDIO.System.context);
return;
}
// Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running
// while there's at least one sound being played. // while there's at least one sound being played.
result = ma_device_start(&AUDIO.System.device); result = ma_device_start(&AUDIO.System.device);
@ -478,16 +498,6 @@ void InitAudioDevice(void)
return; return;
} }
// Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may
// want to look at something a bit smarter later on to keep everything real-time, if that's necessary.
if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS)
{
TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing");
ma_device_uninit(&AUDIO.System.device);
ma_context_uninit(&AUDIO.System.context);
return;
}
TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully"); TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully");
TRACELOG(LOG_INFO, " > Backend: miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend)); TRACELOG(LOG_INFO, " > Backend: miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend));
TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat)); TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat));
@ -529,6 +539,14 @@ void SetMasterVolume(float volume)
ma_device_set_master_volume(&AUDIO.System.device, volume); ma_device_set_master_volume(&AUDIO.System.device, volume);
} }
// Get master volume (listener)
float GetMasterVolume(void)
{
float volume = 0.0f;
ma_device_get_master_volume(&AUDIO.System.device, &volume);
return volume;
}
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definition - Audio Buffer management // Module Functions Definition - Audio Buffer management
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -725,11 +743,11 @@ Wave LoadWave(const char *fileName)
Wave wave = { 0 }; Wave wave = { 0 };
// Loading file to memory // Loading file to memory
unsigned int fileSize = 0; int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &fileSize); unsigned char *fileData = LoadFileData(fileName, &dataSize);
// Loading wave from memory data // Loading wave from memory data
if (fileData != NULL) wave = LoadWaveFromMemory(GetFileExtension(fileName), fileData, fileSize); if (fileData != NULL) wave = LoadWaveFromMemory(GetFileExtension(fileName), fileData, dataSize);
RL_FREE(fileData); RL_FREE(fileData);
@ -744,7 +762,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
if (false) { } if (false) { }
#if defined(SUPPORT_FILEFORMAT_WAV) #if defined(SUPPORT_FILEFORMAT_WAV)
else if (strcmp(fileType, ".wav") == 0) else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0))
{ {
drwav wav = { 0 }; drwav wav = { 0 };
bool success = drwav_init_memory(&wav, fileData, dataSize, NULL); bool success = drwav_init_memory(&wav, fileData, dataSize, NULL);
@ -766,7 +784,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_OGG) #if defined(SUPPORT_FILEFORMAT_OGG)
else if (strcmp(fileType, ".ogg") == 0) else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0))
{ {
stb_vorbis *oggData = stb_vorbis_open_memory((unsigned char *)fileData, dataSize, NULL, NULL); stb_vorbis *oggData = stb_vorbis_open_memory((unsigned char *)fileData, dataSize, NULL, NULL);
@ -788,7 +806,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_MP3) #if defined(SUPPORT_FILEFORMAT_MP3)
else if (strcmp(fileType, ".mp3") == 0) else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0))
{ {
drmp3_config config = { 0 }; drmp3_config config = { 0 };
unsigned long long int totalFrameCount = 0; unsigned long long int totalFrameCount = 0;
@ -808,7 +826,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_QOA) #if defined(SUPPORT_FILEFORMAT_QOA)
else if (strcmp(fileType, ".qoa") == 0) else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0))
{ {
qoa_desc qoa = { 0 }; qoa_desc qoa = { 0 };
@ -827,7 +845,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
else if (strcmp(fileType, ".flac") == 0) else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0))
{ {
unsigned long long int totalFrameCount = 0; unsigned long long int totalFrameCount = 0;
@ -912,6 +930,35 @@ Sound LoadSoundFromWave(Wave wave)
return sound; return sound;
} }
// Clone sound from existing sound data, clone does not own wave data
// NOTE: Wave data must be unallocated manually and will be shared across all clones
Sound LoadSoundAlias(Sound source)
{
Sound sound = { 0 };
if (source.stream.buffer->data != NULL)
{
AudioBuffer* audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC);
if (audioBuffer == NULL)
{
TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer");
return sound; // early return to avoid dereferencing the audioBuffer null pointer
}
audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames;
audioBuffer->volume = source.stream.buffer->volume;
audioBuffer->data = source.stream.buffer->data;
sound.frameCount = source.frameCount;
sound.stream.sampleRate = AUDIO.System.device.sampleRate;
sound.stream.sampleSize = 32;
sound.stream.channels = AUDIO_DEVICE_CHANNELS;
sound.stream.buffer = audioBuffer;
}
return sound;
}
// Checks if a sound is ready // Checks if a sound is ready
bool IsSoundReady(Sound sound) bool IsSoundReady(Sound sound)
{ {
@ -936,15 +983,26 @@ void UnloadSound(Sound sound)
//TRACELOG(LOG_INFO, "SOUND: Unloaded sound data from RAM"); //TRACELOG(LOG_INFO, "SOUND: Unloaded sound data from RAM");
} }
void UnloadSoundAlias(Sound alias)
{
// untrack and unload just the sound buffer, not the sample data, it is shared with the source for the alias
if (alias.stream.buffer != NULL)
{
ma_data_converter_uninit(&alias.stream.buffer->converter, NULL);
UntrackAudioBuffer(alias.stream.buffer);
RL_FREE(alias.stream.buffer);
}
}
// Update sound buffer with new data // Update sound buffer with new data
void UpdateSound(Sound sound, const void *data, int sampleCount) void UpdateSound(Sound sound, const void *data, int frameCount)
{ {
if (sound.stream.buffer != NULL) if (sound.stream.buffer != NULL)
{ {
StopAudioBuffer(sound.stream.buffer); StopAudioBuffer(sound.stream.buffer);
// TODO: May want to lock/unlock this since this data buffer is read at mixing time // TODO: May want to lock/unlock this since this data buffer is read at mixing time
memcpy(sound.stream.buffer->data, data, sampleCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn)); memcpy(sound.stream.buffer->data, data, frameCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn));
} }
} }
@ -1033,29 +1091,30 @@ bool ExportWaveAsCode(Wave wave, const char *fileName)
byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n"); byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n");
char fileNameLower[256] = { 0 }; // Get file name from path and convert variable name to uppercase
char fileNameUpper[256] = { 0 }; char varFileName[256] = { 0 };
for (int i = 0; fileName[i] != '.'; i++) { fileNameLower[i] = fileName[i]; } // Get filename without extension strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; fileNameLower[i] != '\0'; i++) if (fileNameLower[i] >= 'a' && fileNameLower[i] <= 'z') { fileNameUpper[i] = fileNameLower[i] - 32; } for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
//Add wave information
byteCount += sprintf(txtData + byteCount, "// Wave data information\n"); byteCount += sprintf(txtData + byteCount, "// Wave data information\n");
byteCount += sprintf(txtData + byteCount, "#define %s_FRAME_COUNT %u\n", fileNameUpper, wave.frameCount); byteCount += sprintf(txtData + byteCount, "#define %s_FRAME_COUNT %u\n", varFileName, wave.frameCount);
byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_RATE %u\n", fileNameUpper, wave.sampleRate); byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_RATE %u\n", varFileName, wave.sampleRate);
byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_SIZE %u\n", fileNameUpper, wave.sampleSize); byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_SIZE %u\n", varFileName, wave.sampleSize);
byteCount += sprintf(txtData + byteCount, "#define %s_CHANNELS %u\n\n", fileNameUpper, wave.channels); byteCount += sprintf(txtData + byteCount, "#define %s_CHANNELS %u\n\n", varFileName, wave.channels);
// Write wave data as an array of values // Write wave data as an array of values
// Wave data is exported as byte array for 8/16bit and float array for 32bit float data // Wave data is exported as byte array for 8/16bit and float array for 32bit float data
// NOTE: Frame data exported is channel-interlaced: frame01[sampleChannel1, sampleChannel2, ...], frame02[], frame03[] // NOTE: Frame data exported is channel-interlaced: frame01[sampleChannel1, sampleChannel2, ...], frame02[], frame03[]
if (wave.sampleSize == 32) if (wave.sampleSize == 32)
{ {
byteCount += sprintf(txtData + byteCount, "static float %sData[%i] = {\n", fileNameLower, waveDataSize/4); byteCount += sprintf(txtData + byteCount, "static float %s_DATA[%i] = {\n", varFileName, waveDataSize/4);
for (int i = 1; i < waveDataSize/4; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.4ff,\n " : "%.4ff, "), ((float *)wave.data)[i - 1]); for (int i = 1; i < waveDataSize/4; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.4ff,\n " : "%.4ff, "), ((float *)wave.data)[i - 1]);
byteCount += sprintf(txtData + byteCount, "%.4ff };\n", ((float *)wave.data)[waveDataSize/4 - 1]); byteCount += sprintf(txtData + byteCount, "%.4ff };\n", ((float *)wave.data)[waveDataSize/4 - 1]);
} }
else else
{ {
byteCount += sprintf(txtData + byteCount, "static unsigned char %sData[%i] = { ", fileNameLower, waveDataSize); byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, waveDataSize);
for (int i = 1; i < waveDataSize; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n " : "0x%x, "), ((unsigned char *)wave.data)[i - 1]); for (int i = 1; i < waveDataSize; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n " : "0x%x, "), ((unsigned char *)wave.data)[i - 1]);
byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)wave.data)[waveDataSize - 1]); byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)wave.data)[waveDataSize - 1]);
} }
@ -1383,7 +1442,7 @@ Music LoadMusicStream(const char *fileName)
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_QOA) #if defined(SUPPORT_FILEFORMAT_QOA)
else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context data*/ RL_FREE(music.ctxData); } else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData);
#endif #endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
@ -1420,7 +1479,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
if (false) { } if (false) { }
#if defined(SUPPORT_FILEFORMAT_WAV) #if defined(SUPPORT_FILEFORMAT_WAV)
else if (strcmp(fileType, ".wav") == 0) else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0))
{ {
drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); drwav *ctxWav = RL_CALLOC(1, sizeof(drwav));
@ -1442,7 +1501,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_OGG) #if defined(SUPPORT_FILEFORMAT_OGG)
else if (strcmp(fileType, ".ogg") == 0) else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0))
{ {
// Open ogg audio stream // Open ogg audio stream
music.ctxType = MUSIC_AUDIO_OGG; music.ctxType = MUSIC_AUDIO_OGG;
@ -1464,7 +1523,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_MP3) #if defined(SUPPORT_FILEFORMAT_MP3)
else if (strcmp(fileType, ".mp3") == 0) else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0))
{ {
drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3));
int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL); int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL);
@ -1482,7 +1541,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_QOA) #if defined(SUPPORT_FILEFORMAT_QOA)
else if (strcmp(fileType, ".qoa") == 0) else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0))
{ {
qoaplay_desc *ctxQoa = qoaplay_open_memory(data, dataSize); qoaplay_desc *ctxQoa = qoaplay_open_memory(data, dataSize);
music.ctxType = MUSIC_AUDIO_QOA; music.ctxType = MUSIC_AUDIO_QOA;
@ -1500,7 +1559,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
else if (strcmp(fileType, ".flac") == 0) else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0))
{ {
music.ctxType = MUSIC_AUDIO_FLAC; music.ctxType = MUSIC_AUDIO_FLAC;
music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL); music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL);
@ -1517,7 +1576,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_XM) #if defined(SUPPORT_FILEFORMAT_XM)
else if (strcmp(fileType, ".xm") == 0) else if ((strcmp(fileType, ".xm") == 0) || (strcmp(fileType, ".XM") == 0))
{ {
jar_xm_context_t *ctxXm = NULL; jar_xm_context_t *ctxXm = NULL;
int result = jar_xm_create_context_safe(&ctxXm, (const char *)data, dataSize, AUDIO.System.device.sampleRate); int result = jar_xm_create_context_safe(&ctxXm, (const char *)data, dataSize, AUDIO.System.device.sampleRate);
@ -1542,7 +1601,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_MOD) #if defined(SUPPORT_FILEFORMAT_MOD)
else if (strcmp(fileType, ".mod") == 0) else if ((strcmp(fileType, ".mod") == 0) || (strcmp(fileType, ".MOD") == 0))
{ {
jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_MALLOC(sizeof(jar_mod_context_t)); jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_MALLOC(sizeof(jar_mod_context_t));
int result = 0; int result = 0;
@ -1741,7 +1800,14 @@ void SeekMusicStream(Music music, float position)
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break;
#endif #endif
#if defined(SUPPORT_FILEFORMAT_QOA) #if defined(SUPPORT_FILEFORMAT_QOA)
case MUSIC_AUDIO_QOA: qoaplay_seek_frame((qoaplay_desc *)music.ctxData, positionInFrames); break; case MUSIC_AUDIO_QOA:
{
int qoaFrame = positionInFrames/QOA_FRAME_LEN;
qoaplay_seek_frame((qoaplay_desc *)music.ctxData, qoaFrame); // Seeks to QOA frame, not PCM frame
// We need to compute QOA frame number and update positionInFrames
positionInFrames = ((qoaplay_desc *)music.ctxData)->sample_position;
} break;
#endif #endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break;
@ -1861,7 +1927,7 @@ void UpdateMusicStream(Music music)
{ {
while (true) while (true)
{ {
int frameCountRead = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); int frameCountRead = (int)drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize));
frameCountReadTotal += frameCountRead; frameCountReadTotal += frameCountRead;
frameCountStillNeeded -= frameCountRead; frameCountStillNeeded -= frameCountRead;
if (frameCountStillNeeded == 0) break; if (frameCountStillNeeded == 0) break;
@ -2574,10 +2640,10 @@ static const char *GetFileExtension(const char *fileName)
} }
// Load data from file into a buffer // Load data from file into a buffer
static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) static unsigned char *LoadFileData(const char *fileName, int *dataSize)
{ {
unsigned char *data = NULL; unsigned char *data = NULL;
*bytesRead = 0; *dataSize = 0;
if (fileName != NULL) if (fileName != NULL)
{ {
@ -2597,7 +2663,7 @@ static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead
// NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements]
unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file);
*bytesRead = count; *dataSize = count;
if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName);
else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName);
@ -2614,7 +2680,7 @@ static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead
} }
// Save data to file from buffer // Save data to file from buffer
static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) static bool SaveFileData(const char *fileName, void *data, int dataSize)
{ {
if (fileName != NULL) if (fileName != NULL)
{ {
@ -2622,10 +2688,10 @@ static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToW
if (file != NULL) if (file != NULL)
{ {
unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), bytesToWrite, file); unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), dataSize, file);
if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName);
else if (count != bytesToWrite) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName);
else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName);
fclose(file); fclose(file);

View file

@ -1,6 +1,6 @@
/********************************************************************************************** /**********************************************************************************************
* *
* raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * raylib v4.6-dev - 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
@ -81,10 +81,10 @@
#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_MAJOR 4 #define RAYLIB_VERSION_MAJOR 5
#define RAYLIB_VERSION_MINOR 5 #define RAYLIB_VERSION_MINOR 0
#define RAYLIB_VERSION_PATCH 0 #define RAYLIB_VERSION_PATCH 0
#define RAYLIB_VERSION "4.5" #define RAYLIB_VERSION "5.0"
// 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
@ -133,12 +133,20 @@
// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) // NOTE: MSVC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized with { } // Plain structures in C++ (without constructors) can be initialized with { }
// This is called aggregate initialization (C++11 feature)
#if defined(__cplusplus) #if defined(__cplusplus)
#define CLITERAL(type) type #define CLITERAL(type) type
#else #else
#define CLITERAL(type) (type) #define CLITERAL(type) (type)
#endif #endif
// Some compilers (mostly macos clang) default to C++98,
// where aggregate initialization can't be used
// So, give a more clear error stating how to fix this
#if !defined(_MSC_VER) && (defined(__cplusplus) && __cplusplus < 201103L)
#error "C++11 or later is required. Add -std=c++11"
#endif
// NOTE: We set some defines with some data types declared by raylib // NOTE: We set some defines with some data types declared by raylib
// Other modules (raymath, rlgl) also require some of those types, so, // Other modules (raymath, rlgl) also require some of those types, so,
// to be able to use those other modules as standalone (not depending on raylib) // to be able to use those other modules as standalone (not depending on raylib)
@ -402,6 +410,7 @@ typedef struct ModelAnimation {
int frameCount; // Number of animation frames int frameCount; // Number of animation frames
BoneInfo *bones; // Bones information (skeleton) BoneInfo *bones; // Bones information (skeleton)
Transform **framePoses; // Poses array by frame Transform **framePoses; // Poses array by frame
char name[32]; // Animation name
} ModelAnimation; } ModelAnimation;
// Ray, ray for raycasting // Ray, ray for raycasting
@ -497,6 +506,20 @@ typedef struct FilePathList {
char **paths; // Filepaths entries char **paths; // Filepaths entries
} FilePathList; } FilePathList;
// Automation event
typedef struct AutomationEvent {
unsigned int frame; // Event frame
unsigned int type; // Event type (AutomationEventType)
int params[4]; // Event parameters (if required)
} AutomationEvent;
// Automation event list
typedef struct AutomationEventList {
unsigned int capacity; // Events max entries (MAX_AUTOMATION_EVENTS)
unsigned int count; // Events entries count
AutomationEvent *events; // Events entries
} AutomationEventList;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Enumerators Definition // Enumerators Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -517,6 +540,7 @@ typedef enum {
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_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED
FLAG_BORDERLESS_WINDOWED_MODE = 0x00008000, // Set to run program in borderless windowed mode
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;
@ -802,6 +826,9 @@ typedef enum {
PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float)
PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float)
PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float)
PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float)
PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float)
PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float)
PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha)
PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha)
PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp
@ -905,8 +932,8 @@ typedef enum {
// Callbacks to hook some internal functions // Callbacks to hook some internal functions
// WARNING: These callbacks are intended for advance users // WARNING: These callbacks are intended for advance users
typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages
typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load binary data
typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize); // FileIO: Save binary data
typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data
typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data
@ -925,8 +952,8 @@ extern "C" { // Prevents name mangling of functions
// Window-related functions // Window-related functions
RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context
RLAPI bool WindowShouldClose(void); // Check if KEY_ESCAPE pressed or Close icon pressed
RLAPI void CloseWindow(void); // Close window and unload OpenGL context RLAPI void CloseWindow(void); // Close window and unload OpenGL context
RLAPI bool WindowShouldClose(void); // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked)
RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully
RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen
RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP) RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP)
@ -938,17 +965,20 @@ RLAPI bool IsWindowState(unsigned int flag); // Check if on
RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP) 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 ToggleBorderlessWindowed(void); // Toggle window state: borderless 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)
RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP)
RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP)
RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP)
RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP)
RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP) RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB)
RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP)
RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window
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 SetWindowMaxSize(int width, int height); // Set window maximum 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 SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP)
RLAPI void SetWindowFocused(void); // Set window focused (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
@ -964,20 +994,12 @@ RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specifi
RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate
RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor
RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor
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 specified 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 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 RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling
// Custom frame control functions
// NOTE: Those functions are intended for advance users that want full control over the frame processing
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
// 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 PollInputEvents(void); // Register all input events
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
RLAPI void HideCursor(void); // Hides cursor RLAPI void HideCursor(void); // Hides cursor
@ -1033,24 +1055,37 @@ RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the
// Timing-related functions // Timing-related functions
RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum)
RLAPI int GetFPS(void); // Get current FPS
RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time)
RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow()
RLAPI int GetFPS(void); // Get current FPS
// Custom frame control functions
// NOTE: Those functions are intended for advance users that want full control over the frame processing
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
// 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 PollInputEvents(void); // Register all input events
RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution)
// Random values generation functions
RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator
RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included)
RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated
RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence
// Misc. functions // Misc. functions
RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included)
RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator
RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format)
RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS)
RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available)
// NOTE: Following functions implemented in module [utils]
//------------------------------------------------------------------
RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level
RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator
RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator RLAPI void *MemRealloc(void *ptr, unsigned 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
@ -1060,13 +1095,16 @@ RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom
RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver
// Files management functions // Files management functions
RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) RLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // 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, int dataSize); // Save data to file from byte array (write), returns true on success
RLAPI bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, 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
//------------------------------------------------------------------
// File system functions
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)
@ -1077,7 +1115,7 @@ RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filenam
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 const char *GetApplicationDirectory(void); // Get the directory if the running application (uses static string) RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string)
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 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 LoadDirectoryFiles(const char *dirPath); // Load directory filepaths
@ -1094,18 +1132,29 @@ RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataS
RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree()
RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree()
// Automation events functionality
RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file
RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file
RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to
RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording
RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set)
RLAPI void StopAutomationEventRecording(void); // Stop recording automation events
RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Input Handling Functions (Module: core) // Input Handling Functions (Module: core)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Input-related functions: keyboard // Input-related functions: keyboard
RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once
RLAPI bool IsKeyPressedRepeat(int key); // Check if a key has been pressed again (Only PLATFORM_DESKTOP)
RLAPI bool IsKeyDown(int key); // Check if a key is being pressed RLAPI bool IsKeyDown(int key); // Check if a key is being pressed
RLAPI bool IsKeyReleased(int key); // Check if a key has been released once RLAPI bool IsKeyReleased(int key); // Check if a key has been released once
RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed
RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty
RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty
RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
// Input-related functions: gamepads // Input-related functions: gamepads
RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available
@ -1146,7 +1195,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t
// Gestures and Touch Handling Functions (Module: rgestures) // Gestures and Touch Handling Functions (Module: rgestures)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags
RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected
RLAPI int GetGestureDetected(void); // Get latest detected gesture RLAPI int GetGestureDetected(void); // Get latest detected gesture
RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds
RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector
@ -1177,6 +1226,8 @@ RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color
RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out
RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point
RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points
RLAPI void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color); // Draw a B-Spline line, minimum 4 points
RLAPI void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color); // Draw a Catmull Rom spline line, minimum 4 points
RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence
RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle
RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle
@ -1184,6 +1235,7 @@ RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle,
RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle
RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version)
RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline
RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version)
RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse
RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline
RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring
@ -1227,6 +1279,7 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2);
// NOTE: These functions do not require GPU access // NOTE: These functions do not require GPU access
RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM)
RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data
RLAPI Image LoadImageSvg(const char *fileNameOrString, int width, int height); // Load image from SVG file data or string with specified size
RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data)
RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png'
RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data
@ -1234,13 +1287,14 @@ RLAPI Image LoadImageFromScreen(void);
RLAPI bool IsImageReady(Image image); // Check if an image is ready RLAPI bool IsImageReady(Image image); // Check if an image is ready
RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM)
RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success
RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer
RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success
// Image generation functions // Image generation functions
RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color
RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient
RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient
RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient
RLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer); // Generate image: square gradient
RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked
RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise
RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise
@ -1267,6 +1321,7 @@ RLAPI void ImageMipmaps(Image *image);
RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
RLAPI void ImageFlipVertical(Image *image); // Flip image vertically RLAPI void ImageFlipVertical(Image *image); // Flip image vertically
RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally
RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359)
RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg
RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg
RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint
@ -1351,13 +1406,13 @@ 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, use NULL for fontChars and 0 for glyphCount to load the default character set RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount 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 *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'
RLAPI bool IsFontReady(Font font); // Check if a font is ready RLAPI bool IsFontReady(Font font); // Check if a font is ready
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 *codepoints, int codepointCount, 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 *glyphs, Rectangle **glyphRecs, 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 *glyphs, 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 RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success
@ -1367,9 +1422,10 @@ 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) RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint)
// Text font info functions // Text font info functions
RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks
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
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found
@ -1485,10 +1541,10 @@ RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture
RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh
// Model animations loading/unloading functions // Model animations loading/unloading functions
RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount); // Load model animations from file RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file
RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose
RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data
RLAPI void UnloadModelAnimations(ModelAnimation *animations, unsigned int count); // Unload animation array data RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data
RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match
// Collision detection functions // Collision detection functions
@ -1511,6 +1567,7 @@ RLAPI void InitAudioDevice(void); // Initial
RLAPI void CloseAudioDevice(void); // Close the audio device and context RLAPI void CloseAudioDevice(void); // Close the audio device and context
RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully
RLAPI void SetMasterVolume(float volume); // Set master volume (listener) RLAPI void SetMasterVolume(float volume); // Set master volume (listener)
RLAPI float GetMasterVolume(void); // Get master volume (listener)
// Wave/Sound loading/unloading functions // Wave/Sound loading/unloading functions
RLAPI Wave LoadWave(const char *fileName); // Load wave data from file RLAPI Wave LoadWave(const char *fileName); // Load wave data from file
@ -1518,10 +1575,12 @@ RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileDat
RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready
RLAPI Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSound(const char *fileName); // Load sound from file
RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data
RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data
RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready
RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data
RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadWave(Wave wave); // Unload wave data
RLAPI void UnloadSound(Sound sound); // Unload sound RLAPI void UnloadSound(Sound sound); // Unload sound
RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data)
RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success
RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success
@ -1575,10 +1634,10 @@ RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan
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 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 AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as <float>s
RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream
RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as <float>s
RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline
#if defined(__cplusplus) #if defined(__cplusplus)

View file

@ -2,8 +2,22 @@
* *
* raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions * raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions
* *
* CONFIGURATION: * CONVENTIONS:
* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all
* math operations performed by the library consider the structure as it was column-major
* It is like transposed versions of the matrices are used for all the maths
* It benefits some functions making them cache-friendly and also avoids matrix
* transpositions sometimes required by OpenGL
* Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3]
* - Functions are always self-contained, no function use another raymath function inside,
* required code is directly re-implemented inside
* - Functions input parameters are always received by value (2 unavoidable exceptions)
* - Functions use always a "result" variable for return
* - Functions are always defined inline
* - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience)
* - No compound literals used to make sure libray is compatible with C++
* *
* CONFIGURATION:
* #define RAYMATH_IMPLEMENTATION * #define RAYMATH_IMPLEMENTATION
* Generates the implementation of the library into the included file. * Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers * If not defined, the library is in header only mode and can be included in other headers
@ -13,15 +27,6 @@
* Define static inline functions code, so #include header suffices for use. * Define static inline functions code, so #include header suffices for use.
* This may use up lots of memory. * This may use up lots of memory.
* *
* CONVENTIONS:
*
* - Functions are always self-contained, no function use another raymath function inside,
* required code is directly re-implemented inside
* - Functions input parameters are always received by value (2 unavoidable exceptions)
* - Functions use always a "result" variable for return
* - Functions are always defined inline
* - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience)
*
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
* *
@ -209,6 +214,10 @@ RMAPI float Wrap(float value, float min, float max)
// Check whether two given floats are almost equal // Check whether two given floats are almost equal
RMAPI int FloatEquals(float x, float y) RMAPI int FloatEquals(float x, float y)
{ {
#if !defined(EPSILON)
#define EPSILON 0.000001f
#endif
int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y))));
return result; return result;
@ -310,7 +319,12 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2)
// NOTE: Angle is calculated from origin point (0, 0) // NOTE: Angle is calculated from origin point (0, 0)
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); float result = 0.0f;
float dot = v1.x*v2.x + v1.y*v2.y;
float det = v1.x*v2.y - v1.y*v2.x;
result = atan2f(det, dot);
return result; return result;
} }
@ -322,17 +336,8 @@ RMAPI float Vector2LineAngle(Vector2 start, Vector2 end)
{ {
float result = 0.0f; float result = 0.0f;
float dot = start.x*end.x + start.y*end.y; // Dot product // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior
result = -atan2f(end.y - start.y, end.x - start.x);
float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp
if (dotClamp > 1.0f) dotClamp = 1.0f;
result = acosf(dotClamp);
// Alternative implementation, more costly
//float v1Length = sqrtf((start.x*start.x) + (start.y*start.y));
//float v2Length = sqrtf((end.x*end.x) + (end.y*end.y));
//float result = -acosf((start.x*end.x + start.y*end.y)/(v1Length*v2Length));
return result; return result;
} }
@ -507,6 +512,10 @@ RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max)
// Check whether two given vectors are almost equal // Check whether two given vectors are almost equal
RMAPI int Vector2Equals(Vector2 p, Vector2 q) RMAPI int Vector2Equals(Vector2 p, Vector2 q)
{ {
#if !defined(EPSILON)
#define EPSILON 0.000001f
#endif
int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 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.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y)))));
@ -703,12 +712,48 @@ RMAPI Vector3 Vector3Normalize(Vector3 v)
Vector3 result = v; Vector3 result = v;
float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
if (length == 0.0f) length = 1.0f; if (length != 0.0f)
{
float ilength = 1.0f/length; float ilength = 1.0f/length;
result.x *= ilength; result.x *= ilength;
result.y *= ilength; result.y *= ilength;
result.z *= ilength; result.z *= ilength;
}
return result;
}
//Calculate the projection of the vector v1 on to v2
RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2)
{
Vector3 result = { 0 };
float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z);
float mag = v1dv2/v2dv2;
result.x = v2.x*mag;
result.y = v2.y*mag;
result.z = v2.z*mag;
return result;
}
//Calculate the rejection of the vector v1 on to v2
RMAPI Vector3 Vector3Reject(Vector3 v1, Vector3 v2)
{
Vector3 result = { 0 };
float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z);
float mag = v1dv2/v2dv2;
result.x = v1.x - (v2.x*mag);
result.y = v1.y - (v2.y*mag);
result.z = v1.z - (v2.z*mag);
return result; return result;
} }
@ -1055,6 +1100,10 @@ RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max)
// Check whether two given vectors are almost equal // Check whether two given vectors are almost equal
RMAPI int Vector3Equals(Vector3 p, Vector3 q) RMAPI int Vector3Equals(Vector3 p, Vector3 q)
{ {
#if !defined(EPSILON)
#define EPSILON 0.000001f
#endif
int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 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.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.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z)))));
@ -1062,12 +1111,11 @@ RMAPI int Vector3Equals(Vector3 p, Vector3 q)
return result; return result;
} }
// Compute the direction of a refracted ray where v specifies the // Compute the direction of a refracted ray
// normalized direction of the incoming ray, n specifies the // v: normalized direction of the incoming ray
// normalized normal vector of the interface of two optical media, // n: normalized normal vector of the interface of two optical media
// and r specifies the ratio of the refractive index of the medium // r: ratio of the refractive index of the medium from where the ray comes
// from where the ray comes to the refractive index of the medium // to the refractive index of the medium on the other side of the surface
// on the other side of the surface
RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r)
{ {
Vector3 result = { 0 }; Vector3 result = { 0 };
@ -1509,11 +1557,11 @@ RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top,
// Get perspective projection matrix // Get perspective projection matrix
// NOTE: Fovy angle must 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 nearPlane, double farPlane)
{ {
Matrix result = { 0 }; Matrix result = { 0 };
double top = near*tan(fovy*0.5); double top = nearPlane*tan(fovY*0.5);
double bottom = -top; double bottom = -top;
double right = top*aspect; double right = top*aspect;
double left = -right; double left = -right;
@ -1521,27 +1569,27 @@ RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double f
// MatrixFrustum(-right, right, -top, top, near, far); // MatrixFrustum(-right, right, -top, top, near, far);
float rl = (float)(right - left); float rl = (float)(right - left);
float tb = (float)(top - bottom); float tb = (float)(top - bottom);
float fn = (float)(far - near); float fn = (float)(farPlane - nearPlane);
result.m0 = ((float)near*2.0f)/rl; result.m0 = ((float)nearPlane*2.0f)/rl;
result.m5 = ((float)near*2.0f)/tb; result.m5 = ((float)nearPlane*2.0f)/tb;
result.m8 = ((float)right + (float)left)/rl; result.m8 = ((float)right + (float)left)/rl;
result.m9 = ((float)top + (float)bottom)/tb; result.m9 = ((float)top + (float)bottom)/tb;
result.m10 = -((float)far + (float)near)/fn; result.m10 = -((float)farPlane + (float)nearPlane)/fn;
result.m11 = -1.0f; result.m11 = -1.0f;
result.m14 = -((float)far*(float)near*2.0f)/fn; result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn;
return result; return result;
} }
// Get orthographic projection matrix // Get orthographic projection matrix
RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far) RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane)
{ {
Matrix result = { 0 }; Matrix result = { 0 };
float rl = (float)(right - left); float rl = (float)(right - left);
float tb = (float)(top - bottom); float tb = (float)(top - bottom);
float fn = (float)(far - near); float fn = (float)(farPlane - nearPlane);
result.m0 = 2.0f/rl; result.m0 = 2.0f/rl;
result.m1 = 0.0f; result.m1 = 0.0f;
@ -1557,7 +1605,7 @@ RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, d
result.m11 = 0.0f; result.m11 = 0.0f;
result.m12 = -((float)left + (float)right)/rl; result.m12 = -((float)left + (float)right)/rl;
result.m13 = -((float)top + (float)bottom)/tb; result.m13 = -((float)top + (float)bottom)/tb;
result.m14 = -((float)far + (float)near)/fn; result.m14 = -((float)farPlane + (float)nearPlane)/fn;
result.m15 = 1.0f; result.m15 = 1.0f;
return result; return result;
@ -1812,6 +1860,10 @@ RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount)
{ {
Quaternion result = { 0 }; Quaternion result = { 0 };
#if !defined(EPSILON)
#define EPSILON 0.000001f
#endif
float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w;
if (cosHalfTheta < 0) if (cosHalfTheta < 0)
@ -1827,7 +1879,7 @@ RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount)
float halfTheta = acosf(cosHalfTheta); float halfTheta = acosf(cosHalfTheta);
float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta);
if (fabsf(sinHalfTheta) < 0.001f) if (fabsf(sinHalfTheta) < EPSILON)
{ {
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);
@ -2040,7 +2092,7 @@ RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle
float resAngle = 2.0f*acosf(q.w); float resAngle = 2.0f*acosf(q.w);
float den = sqrtf(1.0f - q.w*q.w); float den = sqrtf(1.0f - q.w*q.w);
if (den > 0.0001f) if (den > EPSILON)
{ {
resAxis.x = q.x/den; resAxis.x = q.x/den;
resAxis.y = q.y/den; resAxis.y = q.y/den;
@ -2119,6 +2171,10 @@ RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat)
// Check whether two given quaternions are almost equal // Check whether two given quaternions are almost equal
RMAPI int QuaternionEquals(Quaternion p, Quaternion q) RMAPI int QuaternionEquals(Quaternion p, Quaternion q)
{ {
#if !defined(EPSILON)
#define EPSILON 0.000001f
#endif
int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 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.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.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) &&

View file

@ -3,13 +3,12 @@
* rcamera - Basic camera system with support for multiple camera modes * rcamera - Basic camera system with support for multiple camera modes
* *
* CONFIGURATION: * CONFIGURATION:
* * #define RCAMERA_IMPLEMENTATION
* #define CAMERA_IMPLEMENTATION
* Generates the implementation of the library into the included file. * Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers * If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation. * or source files without problems. But only ONE file should hold the implementation.
* *
* #define CAMERA_STANDALONE * #define RCAMERA_STANDALONE
* If defined, the library can be used as standalone as a camera system but some * If defined, the library can be used as standalone as a camera system but some
* functions must be redefined to manage inputs accordingly. * functions must be redefined to manage inputs accordingly.
* *
@ -47,11 +46,25 @@
// Defines and Macros // Defines and Macros
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Function specifiers definition // Function specifiers definition
// 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
#if defined(_WIN32)
#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)
#elif defined(USE_LIBTYPE_SHARED)
#define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
#endif
#endif
#ifndef RLAPI #ifndef RLAPI
#define RLAPI // Functions defined as 'extern' by default (implicit specifiers) #define RLAPI // Functions defined as 'extern' by default (implicit specifiers)
#endif #endif
#if defined(CAMERA_STANDALONE) #if defined(RCAMERA_STANDALONE)
#define CAMERA_CULL_DISTANCE_NEAR 0.01 #define CAMERA_CULL_DISTANCE_NEAR 0.01
#define CAMERA_CULL_DISTANCE_FAR 1000.0 #define CAMERA_CULL_DISTANCE_FAR 1000.0
#else #else
@ -61,9 +74,9 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Types and Structures Definition // Types and Structures Definition
// NOTE: Below types are required for CAMERA_STANDALONE usage // NOTE: Below types are required for standalone usage
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if defined(CAMERA_STANDALONE) #if defined(RCAMERA_STANDALONE)
// Vector2, 2 components // Vector2, 2 components
typedef struct Vector2 { typedef struct Vector2 {
float x; // Vector x component float x; // Vector x component
@ -77,6 +90,14 @@
float z; // Vector z component float z; // Vector z component
} Vector3; } Vector3;
// Matrix, 4x4 components, column major, OpenGL style, right-handed
typedef struct Matrix {
float m0, m4, m8, m12; // Matrix first row (4 components)
float m1, m5, m9, m13; // Matrix second row (4 components)
float m2, m6, m10, m14; // Matrix third row (4 components)
float m3, m7, m11, m15; // Matrix fourth row (4 components)
} Matrix;
// Camera type, defines a camera position/orientation in 3d space // Camera type, defines a camera position/orientation in 3d space
typedef struct Camera3D { typedef struct Camera3D {
Vector3 position; // Camera position Vector3 position; // Camera position
@ -139,7 +160,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect);
} }
#endif #endif
#endif // CAMERA_H #endif // RCAMERA_H
/*********************************************************************************** /***********************************************************************************
@ -148,7 +169,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect);
* *
************************************************************************************/ ************************************************************************************/
#if defined(CAMERA_IMPLEMENTATION) #if defined(RCAMERA_IMPLEMENTATION)
#include "raymath.h" // Required for vector maths: #include "raymath.h" // Required for vector maths:
// Vector3Add() // Vector3Add()
@ -177,6 +198,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect);
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#define CAMERA_MOVE_SPEED 0.09f #define CAMERA_MOVE_SPEED 0.09f
#define CAMERA_ROTATION_SPEED 0.03f #define CAMERA_ROTATION_SPEED 0.03f
#define CAMERA_PAN_SPEED 0.2f
// Camera mouse movement sensitivity // Camera mouse movement sensitivity
#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate #define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate
@ -294,7 +316,7 @@ void CameraMoveToTarget(Camera *camera, float delta)
distance += delta; distance += delta;
// Distance must be greater than 0 // Distance must be greater than 0
if (distance < 0) distance = 0.001f; if (distance <= 0) distance = 0.001f;
// Set new distance by moving the position along the forward vector // Set new distance by moving the position along the forward vector
Vector3 forward = GetCameraForward(camera); Vector3 forward = GetCameraForward(camera);
@ -418,7 +440,7 @@ Matrix GetCameraProjectionMatrix(Camera *camera, float aspect)
return MatrixIdentity(); return MatrixIdentity();
} }
#ifndef CAMERA_STANDALONE #if !defined(RCAMERA_STANDALONE)
// Update camera position for selected mode // Update camera position for selected mode
// Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM // Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM
void UpdateCamera(Camera *camera, int mode) void UpdateCamera(Camera *camera, int mode)
@ -428,7 +450,7 @@ void UpdateCamera(Camera *camera, int mode)
bool moveInWorldPlane = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)); bool moveInWorldPlane = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON));
bool rotateAroundTarget = ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); bool rotateAroundTarget = ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL));
bool lockView = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); bool lockView = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL));
bool rotateUp = (mode == CAMERA_FREE); bool rotateUp = false;
if (mode == CAMERA_ORBITAL) if (mode == CAMERA_ORBITAL)
{ {
@ -448,19 +470,51 @@ void UpdateCamera(Camera *camera, int mode)
if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED);
if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED);
// Camera movement
if (!IsGamepadAvailable(0))
{
// Camera pan (for CAMERA_FREE)
if ((mode == CAMERA_FREE) && (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE)))
{
const Vector2 mouseDelta = GetMouseDelta();
if (mouseDelta.x > 0.0f) CameraMoveRight(camera, CAMERA_PAN_SPEED, moveInWorldPlane);
if (mouseDelta.x < 0.0f) CameraMoveRight(camera, -CAMERA_PAN_SPEED, moveInWorldPlane);
if (mouseDelta.y > 0.0f) CameraMoveUp(camera, -CAMERA_PAN_SPEED);
if (mouseDelta.y < 0.0f) CameraMoveUp(camera, CAMERA_PAN_SPEED);
}
else
{
// Mouse support
CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget);
CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp);
}
// Camera movement // Keyboard support
if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
//if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); }
//if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); else
{
// Gamepad controller support
CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget);
CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp);
if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.25f) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
} }
if ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)) if (mode == CAMERA_FREE)
{
if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED);
if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED);
}
}
if ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL) || (mode == CAMERA_FREE))
{ {
// Zoom target distance // Zoom target distance
CameraMoveToTarget(camera, -GetMouseWheelMove()); CameraMoveToTarget(camera, -GetMouseWheelMove());
@ -468,7 +522,7 @@ void UpdateCamera(Camera *camera, int mode)
if (IsKeyPressed(KEY_KP_ADD)) CameraMoveToTarget(camera, -2.0f); if (IsKeyPressed(KEY_KP_ADD)) CameraMoveToTarget(camera, -2.0f);
} }
} }
#endif // !CAMERA_STANDALONE #endif // !RCAMERA_STANDALONE
// Update camera movement, movement/rotation values should be provided by user // Update camera movement, movement/rotation values should be provided by user
void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom) void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom)
@ -501,4 +555,4 @@ void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float z
CameraMoveToTarget(camera, zoom); CameraMoveToTarget(camera, zoom);
} }
#endif // CAMERA_IMPLEMENTATION #endif // RCAMERA_IMPLEMENTATION

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,7 @@ func SetGesturesEnabled(gestureFlags uint32) {
// IsGestureDetected - Check if a gesture have been detected // IsGestureDetected - Check if a gesture have been detected
func IsGestureDetected(gesture Gestures) bool { func IsGestureDetected(gesture Gestures) bool {
cgesture := (C.int)(gesture) cgesture := (C.uint)(gesture)
ret := C.IsGestureDetected(cgesture) ret := C.IsGestureDetected(cgesture)
v := bool(ret) v := bool(ret)
return v return v

View file

@ -2,16 +2,13 @@
* *
* rgestures - Gestures system, gestures processing based on input events (touch/mouse) * rgestures - Gestures system, gestures processing based on input events (touch/mouse)
* *
* NOTE: Memory footprint of this library is aproximately 128 bytes (global variables)
*
* CONFIGURATION: * CONFIGURATION:
* * #define RGESTURES_IMPLEMENTATION
* #define GESTURES_IMPLEMENTATION
* Generates the implementation of the library into the included file. * Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers * If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation. * or source files without problems. But only ONE file should hold the implementation.
* *
* #define GESTURES_STANDALONE * #define RGESTURES_STANDALONE
* If defined, the library can be used as standalone to process gesture events with * If defined, the library can be used as standalone to process gesture events with
* no external dependencies. * no external dependencies.
* *
@ -59,7 +56,7 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Types and Structures Definition // Types and Structures Definition
// NOTE: Below types are required for GESTURES_STANDALONE usage // NOTE: Below types are required for standalone usage
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Boolean type // Boolean type
#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) #if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
@ -76,7 +73,7 @@ typedef struct Vector2 {
} Vector2; } Vector2;
#endif #endif
#if defined(GESTURES_STANDALONE) #if defined(RGESTURES_STANDALONE)
// Gestures type // Gestures type
// NOTE: It could be used as flags to enable only some gestures // NOTE: It could be used as flags to enable only some gestures
typedef enum { typedef enum {
@ -125,12 +122,12 @@ extern "C" { // Prevents name mangling of functions
void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures
void UpdateGestures(void); // Update gestures detected (must be called every frame) void UpdateGestures(void); // Update gestures detected (must be called every frame)
#if defined(GESTURES_STANDALONE) #if defined(RGESTURES_STANDALONE)
void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags
bool IsGestureDetected(int gesture); // Check if a gesture have been detected bool IsGestureDetected(int gesture); // Check if a gesture have been detected
int GetGestureDetected(void); // Get latest detected gesture int GetGestureDetected(void); // Get latest detected gesture
float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds float GetGestureHoldDuration(void); // Get gesture hold time in seconds
Vector2 GetGestureDragVector(void); // Get gesture drag vector Vector2 GetGestureDragVector(void); // Get gesture drag vector
float GetGestureDragAngle(void); // Get gesture drag angle float GetGestureDragAngle(void); // Get gesture drag angle
Vector2 GetGesturePinchVector(void); // Get gesture pinch delta Vector2 GetGesturePinchVector(void); // Get gesture pinch delta
@ -141,17 +138,17 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang
} }
#endif #endif
#endif // GESTURES_H #endif // RGESTURES_H
/*********************************************************************************** /***********************************************************************************
* *
* GESTURES IMPLEMENTATION * RGESTURES IMPLEMENTATION
* *
************************************************************************************/ ************************************************************************************/
#if defined(GESTURES_IMPLEMENTATION) #if defined(RGESTURES_IMPLEMENTATION)
#if defined(GESTURES_STANDALONE) #if defined(RGESTURES_STANDALONE)
#if defined(_WIN32) #if defined(_WIN32)
#if defined(__cplusplus) #if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions extern "C" { // Prevents name mangling of functions
@ -181,11 +178,12 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines and Macros // Defines and Macros
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#define FORCE_TO_SWIPE 0.0005f // Swipe force, measured in normalized screen units/time #define FORCE_TO_SWIPE 0.2f // Swipe force, measured in normalized screen units/time
#define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f) #define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f)
#define DRAG_TIMEOUT 0.3f // Drag minimum time for web, measured in seconds
#define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f) #define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f)
#define TAP_TIMEOUT 300 // Tap minimum time, measured in milliseconds #define TAP_TIMEOUT 0.3f // Tap minimum time, measured in seconds
#define PINCH_TIMEOUT 300 // Pinch minimum time, measured in milliseconds #define PINCH_TIMEOUT 0.3f // Pinch minimum time, measured in seconds
#define DOUBLETAP_RANGE 0.03f // DoubleTap range, measured in normalized screen units (0.0f to 1.0f) #define DOUBLETAP_RANGE 0.03f // DoubleTap range, measured in normalized screen units (0.0f to 1.0f)
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -206,11 +204,13 @@ typedef struct {
Vector2 downDragPosition; // Touch drag position Vector2 downDragPosition; // Touch drag position
Vector2 moveDownPositionA; // First touch down position on move Vector2 moveDownPositionA; // First touch down position on move
Vector2 moveDownPositionB; // Second touch down position on move Vector2 moveDownPositionB; // Second touch down position on move
Vector2 previousPositionA; // Previous position A to compare for pinch gestures
Vector2 previousPositionB; // Previous position B to compare for pinch gestures
int tapCounter; // TAP counter (one tap implies TOUCH_ACTION_DOWN and TOUCH_ACTION_UP actions) int tapCounter; // TAP counter (one tap implies TOUCH_ACTION_DOWN and TOUCH_ACTION_UP actions)
} Touch; } Touch;
struct { struct {
bool resetRequired; // HOLD reset to get first touch point again bool resetRequired; // HOLD reset to get first touch point again
double timeDuration; // HOLD duration in milliseconds double timeDuration; // HOLD duration in seconds
} Hold; } Hold;
struct { struct {
Vector2 vector; // DRAG vector (between initial and current position) Vector2 vector; // DRAG vector (between initial and current position)
@ -219,8 +219,7 @@ typedef struct {
float intensity; // DRAG intensity, how far why did the DRAG (pixels per frame) float intensity; // DRAG intensity, how far why did the DRAG (pixels per frame)
} Drag; } Drag;
struct { struct {
bool start; // SWIPE used to define when start measuring GESTURES.Swipe.timeDuration double startTime; // SWIPE start time to calculate drag intensity
double timeDuration; // SWIPE time to calculate drag intensity
} Swipe; } Swipe;
struct { struct {
Vector2 vector; // PINCH vector (between first and second touch points) Vector2 vector; // PINCH vector (between first and second touch points)
@ -256,7 +255,7 @@ void SetGesturesEnabled(unsigned int flags)
} }
// Check if a gesture have been detected // Check if a gesture have been detected
bool IsGestureDetected(int gesture) bool IsGestureDetected(unsigned int gesture)
{ {
if ((GESTURES.enabledFlags & GESTURES.current) == gesture) return true; if ((GESTURES.enabledFlags & GESTURES.current) == gesture) return true;
else return false; else return false;
@ -292,30 +291,29 @@ void ProcessGestureEvent(GestureEvent event)
GESTURES.Touch.upPosition = GESTURES.Touch.downPositionA; GESTURES.Touch.upPosition = GESTURES.Touch.downPositionA;
GESTURES.Touch.eventTime = rgGetCurrentTime(); GESTURES.Touch.eventTime = rgGetCurrentTime();
GESTURES.Touch.firstId = event.pointId[0]; GESTURES.Swipe.startTime = rgGetCurrentTime();
GESTURES.Drag.vector = (Vector2){ 0.0f, 0.0f }; GESTURES.Drag.vector = (Vector2){ 0.0f, 0.0f };
} }
else if (event.touchAction == TOUCH_ACTION_UP) else if (event.touchAction == TOUCH_ACTION_UP)
{ {
if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.upPosition = event.position[0]; // A swipe can happen while the current gesture is drag, but (specially for web) also hold, so set upPosition for both cases
if (GESTURES.current == GESTURE_DRAG || GESTURES.current == GESTURE_HOLD) GESTURES.Touch.upPosition = event.position[0];
// NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen // NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen
GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.timeDuration)); GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.startTime));
GESTURES.Swipe.start = false;
// Detect GESTURE_SWIPE // Detect GESTURE_SWIPE
if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.Touch.firstId == event.pointId[0])) if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.current != GESTURE_DRAG))
{ {
// NOTE: Angle should be inverted in Y // NOTE: Angle should be inverted in Y
GESTURES.Drag.angle = 360.0f - rgVector2Angle(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); GESTURES.Drag.angle = 360.0f - rgVector2Angle(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
if ((GESTURES.Drag.angle < 30) || (GESTURES.Drag.angle > 330)) GESTURES.current = GESTURE_SWIPE_RIGHT; // Right if ((GESTURES.Drag.angle < 30) || (GESTURES.Drag.angle > 330)) GESTURES.current = GESTURE_SWIPE_RIGHT; // Right
else if ((GESTURES.Drag.angle > 30) && (GESTURES.Drag.angle < 120)) GESTURES.current = GESTURE_SWIPE_UP; // Up else if ((GESTURES.Drag.angle >= 30) && (GESTURES.Drag.angle <= 150)) GESTURES.current = GESTURE_SWIPE_UP; // Up
else if ((GESTURES.Drag.angle > 120) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT; // Left else if ((GESTURES.Drag.angle > 150) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT; // Left
else if ((GESTURES.Drag.angle > 210) && (GESTURES.Drag.angle < 300)) GESTURES.current = GESTURE_SWIPE_DOWN; // Down else if ((GESTURES.Drag.angle >= 210) && (GESTURES.Drag.angle <= 330)) GESTURES.current = GESTURE_SWIPE_DOWN; // Down
else GESTURES.current = GESTURE_NONE; else GESTURES.current = GESTURE_NONE;
} }
else else
@ -332,14 +330,6 @@ void ProcessGestureEvent(GestureEvent event)
} }
else if (event.touchAction == TOUCH_ACTION_MOVE) else if (event.touchAction == TOUCH_ACTION_MOVE)
{ {
if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.eventTime = rgGetCurrentTime();
if (!GESTURES.Swipe.start)
{
GESTURES.Swipe.timeDuration = rgGetCurrentTime();
GESTURES.Swipe.start = true;
}
GESTURES.Touch.moveDownPositionA = event.position[0]; GESTURES.Touch.moveDownPositionA = event.position[0];
if (GESTURES.current == GESTURE_HOLD) if (GESTURES.current == GESTURE_HOLD)
@ -349,7 +339,7 @@ void ProcessGestureEvent(GestureEvent event)
GESTURES.Hold.resetRequired = false; GESTURES.Hold.resetRequired = false;
// Detect GESTURE_DRAG // Detect GESTURE_DRAG
if (rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_DRAG) if ((rgGetCurrentTime() - GESTURES.Touch.eventTime) > DRAG_TIMEOUT)
{ {
GESTURES.Touch.eventTime = rgGetCurrentTime(); GESTURES.Touch.eventTime = rgGetCurrentTime();
GESTURES.current = GESTURE_DRAG; GESTURES.current = GESTURE_DRAG;
@ -367,6 +357,9 @@ void ProcessGestureEvent(GestureEvent event)
GESTURES.Touch.downPositionA = event.position[0]; GESTURES.Touch.downPositionA = event.position[0];
GESTURES.Touch.downPositionB = event.position[1]; GESTURES.Touch.downPositionB = event.position[1];
GESTURES.Touch.previousPositionA = GESTURES.Touch.downPositionA;
GESTURES.Touch.previousPositionB = GESTURES.Touch.downPositionB;
//GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.downPositionB); //GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.downPositionB);
GESTURES.Pinch.vector.x = GESTURES.Touch.downPositionB.x - GESTURES.Touch.downPositionA.x; GESTURES.Pinch.vector.x = GESTURES.Touch.downPositionB.x - GESTURES.Touch.downPositionA.x;
@ -379,18 +372,15 @@ void ProcessGestureEvent(GestureEvent event)
{ {
GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB); GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB);
GESTURES.Touch.downPositionA = GESTURES.Touch.moveDownPositionA;
GESTURES.Touch.downPositionB = GESTURES.Touch.moveDownPositionB;
GESTURES.Touch.moveDownPositionA = event.position[0]; GESTURES.Touch.moveDownPositionA = event.position[0];
GESTURES.Touch.moveDownPositionB = event.position[1]; GESTURES.Touch.moveDownPositionB = event.position[1];
GESTURES.Pinch.vector.x = GESTURES.Touch.moveDownPositionB.x - GESTURES.Touch.moveDownPositionA.x; GESTURES.Pinch.vector.x = GESTURES.Touch.moveDownPositionB.x - GESTURES.Touch.moveDownPositionA.x;
GESTURES.Pinch.vector.y = GESTURES.Touch.moveDownPositionB.y - GESTURES.Touch.moveDownPositionA.y; GESTURES.Pinch.vector.y = GESTURES.Touch.moveDownPositionB.y - GESTURES.Touch.moveDownPositionA.y;
if ((rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (rgVector2Distance(GESTURES.Touch.downPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH)) if ((rgVector2Distance(GESTURES.Touch.previousPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (rgVector2Distance(GESTURES.Touch.previousPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH))
{ {
if ((rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) - GESTURES.Pinch.distance) < 0) GESTURES.current = GESTURE_PINCH_IN; if ( rgVector2Distance(GESTURES.Touch.previousPositionA, GESTURES.Touch.previousPositionB) > rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) ) GESTURES.current = GESTURE_PINCH_IN;
else GESTURES.current = GESTURE_PINCH_OUT; else GESTURES.current = GESTURE_PINCH_OUT;
} }
else else
@ -430,13 +420,6 @@ void UpdateGestures(void)
GESTURES.Hold.timeDuration = rgGetCurrentTime(); GESTURES.Hold.timeDuration = rgGetCurrentTime();
} }
if (((rgGetCurrentTime() - GESTURES.Touch.eventTime) > TAP_TIMEOUT) && (GESTURES.current == GESTURE_DRAG) && (GESTURES.Touch.pointCount < 2))
{
GESTURES.current = GESTURE_HOLD;
GESTURES.Hold.timeDuration = rgGetCurrentTime();
GESTURES.Hold.resetRequired = true;
}
// Detect GESTURE_NONE // Detect GESTURE_NONE
if ((GESTURES.current == GESTURE_SWIPE_RIGHT) || (GESTURES.current == GESTURE_SWIPE_UP) || (GESTURES.current == GESTURE_SWIPE_LEFT) || (GESTURES.current == GESTURE_SWIPE_DOWN)) if ((GESTURES.current == GESTURE_SWIPE_RIGHT) || (GESTURES.current == GESTURE_SWIPE_UP) || (GESTURES.current == GESTURE_SWIPE_LEFT) || (GESTURES.current == GESTURE_SWIPE_DOWN))
{ {
@ -523,12 +506,12 @@ static float rgVector2Distance(Vector2 v1, Vector2 v2)
return result; return result;
} }
// Time measure returned are milliseconds // Time measure returned are seconds
static double rgGetCurrentTime(void) static double rgGetCurrentTime(void)
{ {
double time = 0; double time = 0;
#if !defined(GESTURES_STANDALONE) #if !defined(RGESTURES_STANDALONE)
time = GetTime(); time = GetTime();
#else #else
#if defined(_WIN32) #if defined(_WIN32)
@ -537,7 +520,7 @@ static double rgGetCurrentTime(void)
QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation! QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation!
QueryPerformanceCounter(&currentTime); QueryPerformanceCounter(&currentTime);
time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds time = (double)currentTime/clockFrequency; // Time in seconds
#endif #endif
#if defined(__linux__) #if defined(__linux__)
@ -546,7 +529,7 @@ static double rgGetCurrentTime(void)
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds
time = ((double)nowTime/1000000.0); // Time in miliseconds time = ((double)nowTime*1e-9); // Time in seconds
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__)
@ -562,11 +545,11 @@ static double rgGetCurrentTime(void)
mach_port_deallocate(mach_task_self(), cclock); mach_port_deallocate(mach_task_self(), cclock);
unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds
time = ((double)nowTime/1000000.0); // Time in miliseconds time = ((double)nowTime*1e-9); // Time in seconds
#endif #endif
#endif #endif
return time; return time;
} }
#endif // GESTURES_IMPLEMENTATION #endif // RGESTURES_IMPLEMENTATION

View file

@ -2,30 +2,31 @@
* *
* rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API * rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API
* *
* DESCRIPTION:
* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) * An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0)
* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) * that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...)
* *
* ADDITIONAL NOTES:
* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are * When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are
* initialized on rlglInit() to accumulate vertex data. * initialized on rlglInit() to accumulate vertex data.
* *
* When an internal state change is required all the stored vertex data is renderer in batch, * When an internal state change is required all the stored vertex data is renderer in batch,
* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. * additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch.
* *
* Some additional resources are also loaded for convenience, here the complete list: * Some resources are also loaded for convenience, here the complete list:
* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data * - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data
* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8 * - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8
* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs) * - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs)
* *
* Internal buffer (and additional resources) must be manually unloaded calling rlglClose(). * Internal buffer (and resources) must be manually unloaded calling rlglClose().
*
* *
* CONFIGURATION: * CONFIGURATION:
*
* #define GRAPHICS_API_OPENGL_11 * #define GRAPHICS_API_OPENGL_11
* #define GRAPHICS_API_OPENGL_21 * #define GRAPHICS_API_OPENGL_21
* #define GRAPHICS_API_OPENGL_33 * #define GRAPHICS_API_OPENGL_33
* #define GRAPHICS_API_OPENGL_43 * #define GRAPHICS_API_OPENGL_43
* #define GRAPHICS_API_OPENGL_ES2 * #define GRAPHICS_API_OPENGL_ES2
* #define GRAPHICS_API_OPENGL_ES3
* Use selected OpenGL graphics backend, should be supported by platform * Use selected OpenGL graphics backend, should be supported by platform
* Those preprocessor defines are only used on rlgl module, if OpenGL version is * Those preprocessor defines are only used on rlgl module, if OpenGL version is
* required by any other module, use rlGetVersion() to check it * required by any other module, use rlGetVersion() to check it
@ -77,7 +78,6 @@
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) * #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
* *
* DEPENDENCIES: * DEPENDENCIES:
*
* - OpenGL libraries (depending on platform and OpenGL version selected) * - OpenGL libraries (depending on platform and OpenGL version selected)
* - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core) * - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core)
* *
@ -148,7 +148,8 @@
!defined(GRAPHICS_API_OPENGL_21) && \ !defined(GRAPHICS_API_OPENGL_21) && \
!defined(GRAPHICS_API_OPENGL_33) && \ !defined(GRAPHICS_API_OPENGL_33) && \
!defined(GRAPHICS_API_OPENGL_43) && \ !defined(GRAPHICS_API_OPENGL_43) && \
!defined(GRAPHICS_API_OPENGL_ES2) !defined(GRAPHICS_API_OPENGL_ES2) && \
!defined(GRAPHICS_API_OPENGL_ES3)
#define GRAPHICS_API_OPENGL_33 #define GRAPHICS_API_OPENGL_33
#endif #endif
@ -179,6 +180,11 @@
#define GRAPHICS_API_OPENGL_33 #define GRAPHICS_API_OPENGL_33
#endif #endif
// OpenGL ES 3.0 uses OpenGL ES 2.0 functionality (and more)
#if defined(GRAPHICS_API_OPENGL_ES3)
#define GRAPHICS_API_OPENGL_ES2
#endif
// Support framebuffer objects by default // Support framebuffer objects by default
// NOTE: Some driver implementation do not support it, despite they should // NOTE: Some driver implementation do not support it, despite they should
#define RLGL_RENDER_TEXTURES_HINT #define RLGL_RENDER_TEXTURES_HINT
@ -383,7 +389,8 @@ typedef enum {
RL_OPENGL_21, // OpenGL 2.1 (GLSL 120) RL_OPENGL_21, // OpenGL 2.1 (GLSL 120)
RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) RL_OPENGL_33, // OpenGL 3.3 (GLSL 330)
RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330) RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330)
RL_OPENGL_ES_20 // OpenGL ES 2.0 (GLSL 100) RL_OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100)
RL_OPENGL_ES_30 // OpenGL ES 3.0 (GLSL 300 es)
} rlGlVersion; } rlGlVersion;
// Trace log level // Trace log level
@ -412,6 +419,9 @@ typedef enum {
RL_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) RL_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float)
RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float)
RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float)
RL_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float)
RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float)
RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float)
RL_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) RL_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha)
RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha)
RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp
@ -507,13 +517,13 @@ typedef enum {
// NOTE: By default up to 8 color channels defined, but it can be more // NOTE: By default up to 8 color channels defined, but it can be more
typedef enum { typedef enum {
RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0
RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1 RL_ATTACHMENT_COLOR_CHANNEL1 = 1, // Framebuffer attachment type: color 1
RL_ATTACHMENT_COLOR_CHANNEL2, // Framebuffer attachment type: color 2 RL_ATTACHMENT_COLOR_CHANNEL2 = 2, // Framebuffer attachment type: color 2
RL_ATTACHMENT_COLOR_CHANNEL3, // Framebuffer attachment type: color 3 RL_ATTACHMENT_COLOR_CHANNEL3 = 3, // Framebuffer attachment type: color 3
RL_ATTACHMENT_COLOR_CHANNEL4, // Framebuffer attachment type: color 4 RL_ATTACHMENT_COLOR_CHANNEL4 = 4, // Framebuffer attachment type: color 4
RL_ATTACHMENT_COLOR_CHANNEL5, // Framebuffer attachment type: color 5 RL_ATTACHMENT_COLOR_CHANNEL5 = 5, // Framebuffer attachment type: color 5
RL_ATTACHMENT_COLOR_CHANNEL6, // Framebuffer attachment type: color 6 RL_ATTACHMENT_COLOR_CHANNEL6 = 6, // Framebuffer attachment type: color 6
RL_ATTACHMENT_COLOR_CHANNEL7, // Framebuffer attachment type: color 7 RL_ATTACHMENT_COLOR_CHANNEL7 = 7, // Framebuffer attachment type: color 7
RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth
RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil
} rlFramebufferAttachType; } rlFramebufferAttachType;
@ -521,11 +531,11 @@ typedef enum {
// Framebuffer texture attachment type // Framebuffer texture attachment type
typedef enum { typedef enum {
RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side
RL_ATTACHMENT_CUBEMAP_NEGATIVE_X, // Framebuffer texture attachment type: cubemap, -X side RL_ATTACHMENT_CUBEMAP_NEGATIVE_X = 1, // Framebuffer texture attachment type: cubemap, -X side
RL_ATTACHMENT_CUBEMAP_POSITIVE_Y, // Framebuffer texture attachment type: cubemap, +Y side RL_ATTACHMENT_CUBEMAP_POSITIVE_Y = 2, // Framebuffer texture attachment type: cubemap, +Y side
RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y, // Framebuffer texture attachment type: cubemap, -Y side RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y = 3, // Framebuffer texture attachment type: cubemap, -Y side
RL_ATTACHMENT_CUBEMAP_POSITIVE_Z, // Framebuffer texture attachment type: cubemap, +Z side RL_ATTACHMENT_CUBEMAP_POSITIVE_Z = 4, // Framebuffer texture attachment type: cubemap, +Z side
RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z, // Framebuffer texture attachment type: cubemap, -Z side RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z = 5, // Framebuffer texture attachment type: cubemap, -Z side
RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d
RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer
} rlFramebufferAttachTextureType; } rlFramebufferAttachTextureType;
@ -607,6 +617,7 @@ RLAPI void rlDisableShader(void); // Disable shader progra
RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo) RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo)
RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer
RLAPI void rlActiveDrawBuffers(int count); // Activate multiple draw color buffers RLAPI void rlActiveDrawBuffers(int count); // Activate multiple draw color buffers
RLAPI void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask); // Blit active framebuffer to main framebuffer
// General render state // General render state
RLAPI void rlEnableColorBlend(void); // Enable color blending RLAPI void rlEnableColorBlend(void); // Enable color blending
@ -622,7 +633,8 @@ RLAPI void rlEnableScissorTest(void); // Enable scissor test
RLAPI void rlDisableScissorTest(void); // Disable scissor test RLAPI void rlDisableScissorTest(void); // Disable scissor test
RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test
RLAPI void rlEnableWireMode(void); // Enable wire mode RLAPI void rlEnableWireMode(void); // Enable wire mode
RLAPI void rlDisableWireMode(void); // Disable wire mode RLAPI void rlEnablePointMode(void); // Enable point mode
RLAPI void rlDisableWireMode(void); // Disable wire mode ( and point ) maybe rename
RLAPI void rlSetLineWidth(float width); // Set the line drawing width RLAPI void rlSetLineWidth(float width); // Set the line drawing width
RLAPI float rlGetLineWidth(void); // Get the line drawing width RLAPI float rlGetLineWidth(void); // Get the line drawing width
RLAPI void rlEnableSmoothLines(void); // Enable line aliasing RLAPI void rlEnableSmoothLines(void); // Enable line aliasing
@ -790,10 +802,14 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers
#endif #endif
#if defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_ES3)
#include <GLES3/gl3.h> // OpenGL ES 3.0 library
#define GL_GLEXT_PROTOTYPES
#include <GLES2/gl2ext.h> // OpenGL ES 2.0 extensions library
#elif defined(GRAPHICS_API_OPENGL_ES2)
// NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP,
// in that case, functions are loaded from a custom glad for OpenGL ES 2.0 // in that case, functions are loaded from a custom glad for OpenGL ES 2.0
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL)
#define GLAD_GLES2_IMPLEMENTATION #define GLAD_GLES2_IMPLEMENTATION
#include "external/glad_gles2.h" #include "external/glad_gles2.h"
#else #else
@ -805,7 +821,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
// It seems OpenGL ES 2.0 instancing entry points are not defined on Raspberry Pi // It seems OpenGL ES 2.0 instancing entry points are not defined on Raspberry Pi
// provided headers (despite being defined in official Khronos GLES2 headers) // provided headers (despite being defined in official Khronos GLES2 headers)
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) #if defined(PLATFORM_DRM)
typedef void (GL_APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); typedef void (GL_APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount);
typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor);
@ -887,9 +903,11 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#if defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_ES2)
#define glClearDepth glClearDepthf #define glClearDepth glClearDepthf
#if !defined(GRAPHICS_API_OPENGL_ES3)
#define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER
#define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER
#endif #endif
#endif
// Default shader vertex attribute names to set location points // Default shader vertex attribute names to set location points
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION
@ -999,6 +1017,7 @@ typedef struct rlglData {
bool texDepth; // Depth textures supported (GL_ARB_depth_texture, GL_OES_depth_texture) bool texDepth; // Depth textures supported (GL_ARB_depth_texture, GL_OES_depth_texture)
bool texDepthWebGL; // Depth textures supported WebGL specific (GL_WEBGL_depth_texture) bool texDepthWebGL; // Depth textures supported WebGL specific (GL_WEBGL_depth_texture)
bool texFloat32; // float textures support (32 bit per channel) (GL_OES_texture_float) bool texFloat32; // float textures support (32 bit per channel) (GL_OES_texture_float)
bool texFloat16; // half float textures support (16 bit per channel) (GL_OES_texture_half_float)
bool texCompDXT; // DDS texture compression support (GL_EXT_texture_compression_s3tc, GL_WEBGL_compressed_texture_s3tc, GL_WEBKIT_WEBGL_compressed_texture_s3tc) bool texCompDXT; // DDS texture compression support (GL_EXT_texture_compression_s3tc, GL_WEBGL_compressed_texture_s3tc, GL_WEBKIT_WEBGL_compressed_texture_s3tc)
bool texCompETC1; // ETC1 texture compression support (GL_OES_compressed_ETC1_RGB8_texture, GL_WEBGL_compressed_texture_etc1) bool texCompETC1; // ETC1 texture compression support (GL_OES_compressed_ETC1_RGB8_texture, GL_WEBGL_compressed_texture_etc1)
bool texCompETC2; // ETC2/EAC texture compression support (GL_ARB_ES3_compatibility) bool texCompETC2; // ETC2/EAC texture compression support (GL_ARB_ES3_compatibility)
@ -1026,7 +1045,7 @@ typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions
static rlglData RLGL = { 0 }; static rlglData RLGL = { 0 };
#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
#if defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3)
// NOTE: VAO functionality is exposed through extensions (OES) // NOTE: VAO functionality is exposed through extensions (OES)
static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL; static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL; static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
@ -1416,8 +1435,7 @@ void rlVertex3f(float x, float y, float z)
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter] = RLGL.State.texcoordx; RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter] = RLGL.State.texcoordx;
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter + 1] = RLGL.State.texcoordy; RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter + 1] = RLGL.State.texcoordy;
// TODO: Add current normal // WARNING: By default rlVertexBuffer struct does not store normals
// By default rlVertexBuffer type does not store normals
// Add current color // Add current color
RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter] = RLGL.State.colorr; RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter] = RLGL.State.colorr;
@ -1702,11 +1720,19 @@ void rlDisableFramebuffer(void)
#endif #endif
} }
// Blit active framebuffer to main framebuffer
void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask)
{
#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT)
glBlitFramebuffer(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask, GL_NEAREST);
#endif
}
// Activate multiple draw color buffers // Activate multiple draw color buffers
// NOTE: One color buffer is always active by default // NOTE: One color buffer is always active by default
void rlActiveDrawBuffers(int count) void rlActiveDrawBuffers(int count)
{ {
#if (defined(GRAPHICS_API_OPENGL_33) && defined(RLGL_RENDER_TEXTURES_HINT)) #if ((defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT))
// NOTE: Maximum number of draw buffers supported is implementation dependant, // NOTE: Maximum number of draw buffers supported is implementation dependant,
// it can be queried with glGet*() but it must be at least 8 // it can be queried with glGet*() but it must be at least 8
//GLint maxDrawBuffers = 0; //GLint maxDrawBuffers = 0;
@ -1718,6 +1744,16 @@ void rlActiveDrawBuffers(int count)
else else
{ {
unsigned int buffers[8] = { unsigned int buffers[8] = {
#if defined(GRAPHICS_API_OPENGL_ES3)
GL_COLOR_ATTACHMENT0_EXT,
GL_COLOR_ATTACHMENT1_EXT,
GL_COLOR_ATTACHMENT2_EXT,
GL_COLOR_ATTACHMENT3_EXT,
GL_COLOR_ATTACHMENT4_EXT,
GL_COLOR_ATTACHMENT5_EXT,
GL_COLOR_ATTACHMENT6_EXT,
GL_COLOR_ATTACHMENT7_EXT,
#else
GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT2,
@ -1726,9 +1762,14 @@ void rlActiveDrawBuffers(int count)
GL_COLOR_ATTACHMENT5, GL_COLOR_ATTACHMENT5,
GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT6,
GL_COLOR_ATTACHMENT7, GL_COLOR_ATTACHMENT7,
#endif
}; };
#if defined(GRAPHICS_API_OPENGL_ES3)
glDrawBuffersEXT(count, buffers);
#else
glDrawBuffers(count, buffers); glDrawBuffers(count, buffers);
#endif
} }
} }
else TRACELOG(LOG_WARNING, "GL: One color buffer active by default"); else TRACELOG(LOG_WARNING, "GL: One color buffer active by default");
@ -1792,6 +1833,14 @@ void rlEnableWireMode(void)
#endif #endif
} }
void rlEnablePointMode(void)
{
#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
// NOTE: glPolygonMode() not available on OpenGL ES
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
glEnable(GL_PROGRAM_POINT_SIZE);
#endif
}
// Disable wire mode // Disable wire mode
void rlDisableWireMode(void) void rlDisableWireMode(void)
{ {
@ -2047,7 +2096,7 @@ void rlglInit(int width, int height)
if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL)) if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL))
{ {
glDebugMessageCallback(rlDebugMessageCallback, 0); glDebugMessageCallback(rlDebugMessageCallback, 0);
// glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_HIGH, 0, 0, GL_TRUE); // TODO: Filter message // glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_HIGH, 0, 0, GL_TRUE);
// Debug context options: // Debug context options:
// - GL_DEBUG_OUTPUT - Faster version but not useful for breakpoints // - GL_DEBUG_OUTPUT - Faster version but not useful for breakpoints
@ -2168,6 +2217,7 @@ void rlLoadExtensions(void *loader)
RLGL.ExtSupported.instancing = (GLAD_GL_EXT_draw_instanced && GLAD_GL_ARB_instanced_arrays); RLGL.ExtSupported.instancing = (GLAD_GL_EXT_draw_instanced && GLAD_GL_ARB_instanced_arrays);
RLGL.ExtSupported.texNPOT = GLAD_GL_ARB_texture_non_power_of_two; RLGL.ExtSupported.texNPOT = GLAD_GL_ARB_texture_non_power_of_two;
RLGL.ExtSupported.texFloat32 = GLAD_GL_ARB_texture_float; RLGL.ExtSupported.texFloat32 = GLAD_GL_ARB_texture_float;
RLGL.ExtSupported.texFloat16 = GLAD_GL_ARB_texture_float;
RLGL.ExtSupported.texDepth = GLAD_GL_ARB_depth_texture; RLGL.ExtSupported.texDepth = GLAD_GL_ARB_depth_texture;
RLGL.ExtSupported.maxDepthBits = 32; RLGL.ExtSupported.maxDepthBits = 32;
RLGL.ExtSupported.texAnisoFilter = GLAD_GL_EXT_texture_filter_anisotropic; RLGL.ExtSupported.texAnisoFilter = GLAD_GL_EXT_texture_filter_anisotropic;
@ -2179,6 +2229,7 @@ void rlLoadExtensions(void *loader)
RLGL.ExtSupported.instancing = true; RLGL.ExtSupported.instancing = true;
RLGL.ExtSupported.texNPOT = true; RLGL.ExtSupported.texNPOT = true;
RLGL.ExtSupported.texFloat32 = true; RLGL.ExtSupported.texFloat32 = true;
RLGL.ExtSupported.texFloat16 = true;
RLGL.ExtSupported.texDepth = true; RLGL.ExtSupported.texDepth = true;
RLGL.ExtSupported.maxDepthBits = 32; RLGL.ExtSupported.maxDepthBits = 32;
RLGL.ExtSupported.texAnisoFilter = true; RLGL.ExtSupported.texAnisoFilter = true;
@ -2196,9 +2247,33 @@ void rlLoadExtensions(void *loader)
#endif // GRAPHICS_API_OPENGL_33 #endif // GRAPHICS_API_OPENGL_33
#if defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_ES3)
// Register supported extensions flags
// OpenGL ES 3.0 extensions supported by default (or it should be)
RLGL.ExtSupported.vao = true;
RLGL.ExtSupported.instancing = true;
RLGL.ExtSupported.texNPOT = true;
RLGL.ExtSupported.texFloat32 = true;
RLGL.ExtSupported.texFloat16 = true;
RLGL.ExtSupported.texDepth = true;
RLGL.ExtSupported.texDepthWebGL = true;
RLGL.ExtSupported.maxDepthBits = 24;
RLGL.ExtSupported.texAnisoFilter = true;
RLGL.ExtSupported.texMirrorClamp = true;
// TODO: Check for additional OpenGL ES 3.0 supported extensions:
//RLGL.ExtSupported.texCompDXT = true;
//RLGL.ExtSupported.texCompETC1 = true;
//RLGL.ExtSupported.texCompETC2 = true;
//RLGL.ExtSupported.texCompPVRT = true;
//RLGL.ExtSupported.texCompASTC = true;
//RLGL.ExtSupported.maxAnisotropyLevel = true;
//RLGL.ExtSupported.computeShader = true;
//RLGL.ExtSupported.ssbo = true;
#if defined(PLATFORM_DESKTOP) #elif defined(GRAPHICS_API_OPENGL_ES2)
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL)
// TODO: Support GLAD loader for OpenGL ES 3.0
if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions");
else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully");
#endif #endif
@ -2276,6 +2351,7 @@ void rlLoadExtensions(void *loader)
// Check texture float support // Check texture float support
if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) RLGL.ExtSupported.texFloat32 = true; if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) RLGL.ExtSupported.texFloat32 = true;
if (strcmp(extList[i], (const char *)"GL_OES_texture_half_float") == 0) RLGL.ExtSupported.texFloat16 = true;
// Check depth texture support // Check depth texture support
if (strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) RLGL.ExtSupported.texDepth = true; if (strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) RLGL.ExtSupported.texDepth = true;
@ -2391,15 +2467,17 @@ int rlGetVersion(void)
#endif #endif
#if defined(GRAPHICS_API_OPENGL_21) #if defined(GRAPHICS_API_OPENGL_21)
glVersion = RL_OPENGL_21; glVersion = RL_OPENGL_21;
#elif defined(GRAPHICS_API_OPENGL_43)
glVersion = RL_OPENGL_43;
#elif defined(GRAPHICS_API_OPENGL_33) #elif defined(GRAPHICS_API_OPENGL_33)
glVersion = RL_OPENGL_33; glVersion = RL_OPENGL_33;
#endif #endif
#if defined(GRAPHICS_API_OPENGL_43) #if defined(GRAPHICS_API_OPENGL_ES3)
glVersion = RL_OPENGL_43; glVersion = RL_OPENGL_ES_30;
#endif #elif defined(GRAPHICS_API_OPENGL_ES2)
#if defined(GRAPHICS_API_OPENGL_ES2)
glVersion = RL_OPENGL_ES_20; glVersion = RL_OPENGL_ES_20;
#endif #endif
return glVersion; return glVersion;
} }
@ -2648,7 +2726,7 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
// Update batch vertex buffers // Update batch vertex buffers
//------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------
// NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0)
// TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (use a change detector flag?)
if (RLGL.State.vertexCounter > 0) if (RLGL.State.vertexCounter > 0)
{ {
// Activate elements VAO // Activate elements VAO
@ -2943,7 +3021,11 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
int mipWidth = width; int mipWidth = width;
int mipHeight = height; int mipHeight = height;
int mipOffset = 0; // Mipmap data offset int mipOffset = 0; // Mipmap data offset, only used for tracelog
// NOTE: Added pointer math separately from function to avoid UBSAN complaining
unsigned char *dataPtr = NULL;
if (data != NULL) dataPtr = (unsigned char *)data;
// Load the different mipmap levels // Load the different mipmap levels
for (int i = 0; i < mipmapCount; i++) for (int i = 0; i < mipmapCount; i++)
@ -2955,11 +3037,11 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
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);
if (glInternalFormat != -1) if (glInternalFormat != 0)
{ {
if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, (unsigned char *)data + mipOffset); if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, dataPtr);
#if !defined(GRAPHICS_API_OPENGL_11) #if !defined(GRAPHICS_API_OPENGL_11)
else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, (unsigned char *)data + mipOffset); else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, dataPtr);
#endif #endif
#if defined(GRAPHICS_API_OPENGL_33) #if defined(GRAPHICS_API_OPENGL_33)
@ -2982,7 +3064,8 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
mipWidth /= 2; mipWidth /= 2;
mipHeight /= 2; mipHeight /= 2;
mipOffset += mipSize; mipOffset += mipSize; // Increment offset position to next mipmap
if (data != NULL) dataPtr += mipSize; // Increment data pointer to next mipmap
// Security check for NPOT textures // Security check for NPOT textures
if (mipWidth < 1) mipWidth = 1; if (mipWidth < 1) mipWidth = 1;
@ -3049,7 +3132,7 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer)
// Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F
unsigned int glInternalFormat = GL_DEPTH_COMPONENT; unsigned int glInternalFormat = GL_DEPTH_COMPONENT;
#if defined(GRAPHICS_API_OPENGL_ES2) #if (defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_ES3))
// WARNING: WebGL platform requires unsized internal format definition (GL_DEPTH_COMPONENT) // WARNING: WebGL platform requires unsized internal format definition (GL_DEPTH_COMPONENT)
// while other platforms using OpenGL ES 2.0 require/support sized internal formats depending on the GPU capabilities // while other platforms using OpenGL ES 2.0 require/support sized internal formats depending on the GPU capabilities
if (!RLGL.ExtSupported.texDepthWebGL || useRenderBuffer) if (!RLGL.ExtSupported.texDepthWebGL || useRenderBuffer)
@ -3108,7 +3191,7 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format)
unsigned int glInternalFormat, glFormat, glType; unsigned int glInternalFormat, glFormat, glType;
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
if (glInternalFormat != -1) if (glInternalFormat != 0)
{ {
// Load cubemap faces // Load cubemap faces
for (unsigned int i = 0; i < 6; i++) for (unsigned int i = 0; i < 6; i++)
@ -3117,13 +3200,9 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format)
{ {
if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)
{ {
if (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32) if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)
{ || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16))
// Instead of using a sized internal texture format (GL_RGB16F, GL_RGB32F), we let the driver to choose the better format for us (GL_RGB) TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported");
if (RLGL.ExtSupported.texFloat32) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL);
else TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported");
}
else if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported");
else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, NULL); else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, NULL);
} }
else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format"); else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format");
@ -3180,7 +3259,7 @@ void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int h
unsigned 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 != 0) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB))
{ {
glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, data); glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, data);
} }
@ -3206,9 +3285,27 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned
case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break;
#if !defined(GRAPHICS_API_OPENGL_11) #if !defined(GRAPHICS_API_OPENGL_11)
#if defined(GRAPHICS_API_OPENGL_ES3)
case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F_EXT; *glFormat = GL_RED_EXT; *glType = GL_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F_EXT; *glFormat = GL_RGB; *glType = GL_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F_EXT; *glFormat = GL_RGBA; *glType = GL_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_R16F_EXT; *glFormat = GL_RED_EXT; *glType = GL_HALF_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB16F_EXT; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA16F_EXT; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT; break;
#else
case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
#if defined(GRAPHICS_API_OPENGL_21)
case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_HALF_FLOAT_ARB; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT_ARB; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT_ARB; break;
#else // defined(GRAPHICS_API_OPENGL_ES2)
case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float
#endif
#endif
#endif #endif
#elif defined(GRAPHICS_API_OPENGL_33) #elif defined(GRAPHICS_API_OPENGL_33)
case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break;
@ -3221,6 +3318,9 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned
case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_R16F; *glFormat = GL_RED; *glType = GL_HALF_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB16F; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA16F; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT; break;
#endif #endif
#if !defined(GRAPHICS_API_OPENGL_11) #if !defined(GRAPHICS_API_OPENGL_11)
case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break;
@ -3303,7 +3403,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format)
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
unsigned int size = rlGetPixelDataSize(width, height, format); unsigned int size = rlGetPixelDataSize(width, height, format);
if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) if ((glInternalFormat != 0) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB))
{ {
pixels = RL_MALLOC(size); pixels = RL_MALLOC(size);
glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels);
@ -3466,11 +3566,14 @@ bool rlFramebufferComplete(unsigned int id)
void rlUnloadFramebuffer(unsigned int id) void rlUnloadFramebuffer(unsigned int id)
{ {
#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) #if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT)
// Query depth attachment to automatically delete texture/renderbuffer // Query depth attachment to automatically delete texture/renderbuffer
int depthType = 0, depthId = 0; int depthType = 0, depthId = 0;
glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType); glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType);
// TODO: Review warning retrieving object name in WebGL
// WARNING: WebGL: INVALID_ENUM: getFramebufferAttachmentParameter: invalid parameter name
// https://registry.khronos.org/webgl/specs/latest/1.0/
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId); glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId);
unsigned int depthIdU = (unsigned int)depthId; unsigned int depthIdU = (unsigned int)depthId;
@ -3616,7 +3719,11 @@ void rlDrawVertexArray(int offset, int count)
// Draw vertex array elements // Draw vertex array elements
void rlDrawVertexArrayElements(int offset, int count, const void *buffer) void rlDrawVertexArrayElements(int offset, int count, const void *buffer)
{ {
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset); // NOTE: Added pointer math separately from function to avoid UBSAN complaining
unsigned short *bufferPtr = (unsigned short *)buffer;
if (offset > 0) bufferPtr += offset;
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr);
} }
// Draw vertex array instanced // Draw vertex array instanced
@ -3631,7 +3738,11 @@ void rlDrawVertexArrayInstanced(int offset, int count, int instances)
void rlDrawVertexArrayElementsInstanced(int offset, int count, const 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, (const unsigned short *)buffer + offset, instances); // NOTE: Added pointer math separately from function to avoid UBSAN complaining
unsigned short *bufferPtr = (unsigned short *)buffer;
if (offset > 0) bufferPtr += offset;
glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr, instances);
#endif #endif
} }
@ -3923,8 +4034,8 @@ int rlGetLocationUniform(unsigned int shaderId, const char *uniformName)
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
location = glGetUniformLocation(shaderId, uniformName); location = glGetUniformLocation(shaderId, uniformName);
if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName); //if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName);
else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location); //else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location);
#endif #endif
return location; return location;
} }
@ -3936,8 +4047,8 @@ int rlGetLocationAttrib(unsigned int shaderId, const char *attribName)
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
location = glGetAttribLocation(shaderId, attribName); location = glGetAttribLocation(shaderId, attribName);
if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName); //if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName);
else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location); //else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location);
#endif #endif
return location; return location;
} }
@ -4091,7 +4202,7 @@ unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHi
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); if (data == NULL) glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL); // Clear buffer data to 0
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
#endif #endif
@ -4428,6 +4539,9 @@ const char *rlGetPixelFormatName(unsigned int format)
case RL_PIXELFORMAT_UNCOMPRESSED_R32: return "R32"; break; // 32 bpp (1 channel - float) case RL_PIXELFORMAT_UNCOMPRESSED_R32: return "R32"; break; // 32 bpp (1 channel - float)
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: return "R32G32B32"; break; // 32*3 bpp (3 channels - float) case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: return "R32G32B32"; break; // 32*3 bpp (3 channels - float)
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: return "R32G32B32A32"; break; // 32*4 bpp (4 channels - float) case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: return "R32G32B32A32"; break; // 32*4 bpp (4 channels - float)
case RL_PIXELFORMAT_UNCOMPRESSED_R16: return "R16"; break; // 16 bpp (1 channel - half float)
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: return "R16G16B16"; break; // 16*3 bpp (3 channels - half float)
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: return "R16G16B16A16"; break; // 16*4 bpp (4 channels - half float)
case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: return "DXT1_RGB"; break; // 4 bpp (no alpha) case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: return "DXT1_RGB"; break; // 4 bpp (no alpha)
case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: return "DXT1_RGBA"; break; // 4 bpp (1 bit alpha) case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: return "DXT1_RGBA"; break; // 4 bpp (1 bit alpha)
case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: return "DXT3_RGBA"; break; // 8 bpp case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: return "DXT3_RGBA"; break; // 8 bpp
@ -4476,6 +4590,7 @@ static void rlLoadShaderDefault(void)
#endif #endif
#if defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_ES2)
"#version 100 \n" "#version 100 \n"
"precision mediump float; \n" // Precision required for OpenGL ES2 (WebGL) (on some browsers)
"attribute vec3 vertexPosition; \n" "attribute vec3 vertexPosition; \n"
"attribute vec2 vertexTexCoord; \n" "attribute vec2 vertexTexCoord; \n"
"attribute vec4 vertexColor; \n" "attribute vec4 vertexColor; \n"
@ -4669,6 +4784,9 @@ static int rlGetPixelDataSize(int width, int height, int format)
case RL_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16: bpp = 16; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 16*3; break;
case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 16*4; break;
case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB:
case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA:
case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB:

View file

@ -3,7 +3,6 @@
* rmodels - Basic functions to draw 3d shapes and load and draw 3d models * rmodels - Basic functions to draw 3d shapes and load and draw 3d models
* *
* CONFIGURATION: * CONFIGURATION:
*
* #define SUPPORT_MODULE_RMODELS * #define SUPPORT_MODULE_RMODELS
* rmodels module is included in the build * rmodels module is included in the build
* *
@ -102,7 +101,7 @@
#define PAR_REALLOC(T, BUF, N) ((T*)RL_REALLOC(BUF, sizeof(T)*(N))) #define PAR_REALLOC(T, BUF, N) ((T*)RL_REALLOC(BUF, sizeof(T)*(N)))
#define PAR_FREE RL_FREE #define PAR_FREE RL_FREE
#if defined(_MSC_VER ) // par shapes has 2 warnings on windows, so disable them just fof this file #if defined(_MSC_VER) // Disable some MSVC warning
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4244) #pragma warning(disable : 4244)
#pragma warning(disable : 4305) #pragma warning(disable : 4305)
@ -111,10 +110,9 @@
#define PAR_SHAPES_IMPLEMENTATION #define PAR_SHAPES_IMPLEMENTATION
#include "external/par_shapes.h" // Shapes 3d parametric generation #include "external/par_shapes.h" // Shapes 3d parametric generation
#if defined(_MSC_VER ) // disable MSVC warning suppression for par shapes #if defined(_MSC_VER)
#pragma warning( pop ) #pragma warning(pop) // Disable MSVC warning suppression
#endif #endif
#endif #endif
#if defined(_WIN32) #if defined(_WIN32)
@ -153,18 +151,18 @@ static Model LoadOBJ(const char *fileName); // Load OBJ mesh data
#endif #endif
#if defined(SUPPORT_FILEFORMAT_IQM) #if defined(SUPPORT_FILEFORMAT_IQM)
static Model LoadIQM(const char *fileName); // Load IQM mesh data static Model LoadIQM(const char *fileName); // Load IQM mesh data
static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int *animCount); // Load IQM animation data static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount); // Load IQM animation data
#endif #endif
#if defined(SUPPORT_FILEFORMAT_GLTF) #if defined(SUPPORT_FILEFORMAT_GLTF)
static Model LoadGLTF(const char *fileName); // Load GLTF mesh data static Model LoadGLTF(const char *fileName); // Load GLTF mesh data
static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned int *animCount); // Load GLTF animation data static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount); // Load GLTF animation data
#endif #endif
#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) #if defined(SUPPORT_FILEFORMAT_M3D)
static Model LoadM3D(const char *filename); // Load M3D mesh data static Model LoadM3D(const char *filename); // Load M3D mesh data
static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount); // Load M3D animation data static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount); // Load M3D animation data
#endif #endif
#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) #if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount); // Process obj materials static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount); // Process obj materials
@ -1054,26 +1052,16 @@ Model LoadModel(const char *fileName)
// Make sure model transform is set to identity matrix! // Make sure model transform is set to identity matrix!
model.transform = MatrixIdentity(); model.transform = MatrixIdentity();
if (model.meshCount == 0) if ((model.meshCount != 0) && (model.meshes != NULL))
{ {
model.meshCount = 1; // Upload vertex data to GPU (static meshes)
model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
#if defined(SUPPORT_MESH_GENERATION)
TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data, default to cube mesh", fileName);
model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f);
#else
TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data", fileName);
#endif
}
else
{
// Upload vertex data to GPU (static mesh)
for (int i = 0; i < model.meshCount; i++) UploadMesh(&model.meshes[i], false); for (int i = 0; i < model.meshCount; i++) UploadMesh(&model.meshes[i], false);
} }
else TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load model mesh(es) data", fileName);
if (model.materialCount == 0) if (model.materialCount == 0)
{ {
TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load material data, default to white material", fileName); TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load model material data, default to white material", fileName);
model.materialCount = 1; model.materialCount = 1;
model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
@ -1173,6 +1161,12 @@ BoundingBox GetModelBoundingBox(Model model)
} }
} }
// Apply model.transform to bounding box
// WARNING: Current BoundingBox structure design does not support rotation transformations,
// in those cases is up to the user to calculate the proper box bounds (8 vertices transformed)
bounds.min = Vector3Transform(bounds.min, model.transform);
bounds.max = Vector3Transform(bounds.max, model.transform);
return bounds; return bounds;
} }
@ -1204,7 +1198,7 @@ void UploadMesh(Mesh *mesh, bool dynamic)
// NOTE: Vertex attributes must be uploaded considering default locations points and available vertex data // 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;
mesh->vboId[0] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic); mesh->vboId[0] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic);
rlSetVertexAttribute(0, 3, RL_FLOAT, 0, 0, 0); rlSetVertexAttribute(0, 3, RL_FLOAT, 0, 0, 0);
rlEnableVertexAttribute(0); rlEnableVertexAttribute(0);
@ -1220,7 +1214,7 @@ void UploadMesh(Mesh *mesh, bool dynamic)
if (mesh->normals != NULL) if (mesh->normals != NULL)
{ {
// Enable vertex attributes: normals (shader-location = 2) // Enable vertex attributes: normals (shader-location = 2)
void *normals = mesh->animNormals != NULL ? mesh->animNormals : mesh->normals; void *normals = (mesh->animNormals != NULL)? mesh->animNormals : mesh->normals;
mesh->vboId[2] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic); mesh->vboId[2] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic);
rlSetVertexAttribute(2, 3, RL_FLOAT, 0, 0, 0); rlSetVertexAttribute(2, 3, RL_FLOAT, 0, 0, 0);
rlEnableVertexAttribute(2); rlEnableVertexAttribute(2);
@ -1858,35 +1852,34 @@ bool ExportMesh(Mesh mesh, const char *fileName)
#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) #if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
// Process obj materials // Process obj materials
static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount) static void ProcessMaterialsOBJ(Material *materials, tinyobj_material_t *mats, int materialCount)
{ {
// Init model materials // Init model mats
for (int m = 0; m < materialCount; m++) for (int m = 0; m < materialCount; m++)
{ {
// Init material to default // Init material to default
// NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE
rayMaterials[m] = LoadMaterialDefault(); materials[m] = LoadMaterialDefault();
// Get default texture, in case no texture is defined // Get default texture, in case no texture is defined
// NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
if (materials[m].diffuse_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd if (mats[m].diffuse_texname != NULL) materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(mats[m].diffuse_texname); //char *diffuse_texname; // map_Kd
else materials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(mats[m].diffuse[0]*255.0f), (unsigned char)(mats[m].diffuse[1]*255.0f), (unsigned char)(mats[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3];
materials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f;
rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3]; if (mats[m].specular_texname != NULL) materials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(mats[m].specular_texname); //char *specular_texname; // map_Ks
rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; materials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(mats[m].specular[0]*255.0f), (unsigned char)(mats[m].specular[1]*255.0f), (unsigned char)(mats[m].specular[2] * 255.0f), 255 }; //float specular[3];
materials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f;
if (materials[m].specular_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks if (mats[m].bump_texname != NULL) materials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(mats[m].bump_texname); //char *bump_texname; // map_bump, bump
rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3]; materials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE;
rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; materials[m].maps[MATERIAL_MAP_NORMAL].value = mats[m].shininess;
if (materials[m].bump_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump materials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(mats[m].emission[0]*255.0f), (unsigned char)(mats[m].emission[1]*255.0f), (unsigned char)(mats[m].emission[2] * 255.0f), 255 }; //float emission[3];
rayMaterials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE;
rayMaterials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess;
rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3]; if (mats[m].displacement_texname != NULL) materials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(mats[m].displacement_texname); //char *displacement_texname; // disp
if (materials[m].displacement_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp
} }
} }
#endif #endif
@ -1907,7 +1900,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount)
int result = tinyobj_parse_mtl_file(&mats, &count, fileName); int result = tinyobj_parse_mtl_file(&mats, &count, fileName);
if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName); if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName);
materials = MemAlloc(count*sizeof(Material)); materials = RL_MALLOC(count*sizeof(Material));
ProcessMaterialsOBJ(materials, mats, count); ProcessMaterialsOBJ(materials, mats, count);
tinyobj_materials_free(mats, count); tinyobj_materials_free(mats, count);
@ -1982,7 +1975,7 @@ void SetModelMeshMaterial(Model *model, int meshId, int materialId)
} }
// Load model animations from file // Load model animations from file
ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount) ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount)
{ {
ModelAnimation *animations = NULL; ModelAnimation *animations = NULL;
@ -2102,9 +2095,9 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
} }
// Unload animation array data // Unload animation array data
void UnloadModelAnimations(ModelAnimation *animations, unsigned int count) void UnloadModelAnimations(ModelAnimation *animations, int animCount)
{ {
for (unsigned int i = 0; i < count; i++) UnloadModelAnimation(animations[i]); for (int i = 0; i < animCount; i++) UnloadModelAnimation(animations[i]);
RL_FREE(animations); RL_FREE(animations);
} }
@ -2149,7 +2142,7 @@ Mesh GenMeshPoly(int sides, float radius)
Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
float d = 0.0f, dStep = 360.0f/sides; float d = 0.0f, dStep = 360.0f/sides;
for (int v = 0; v < vertexCount; v += 3) for (int v = 0; v < vertexCount - 2; v += 3)
{ {
vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f }; vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f };
vertices[v + 1] = (Vector3){ sinf(DEG2RAD*d)*radius, 0.0f, cosf(DEG2RAD*d)*radius }; vertices[v + 1] = (Vector3){ sinf(DEG2RAD*d)*radius, 0.0f, cosf(DEG2RAD*d)*radius };
@ -2252,7 +2245,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
for (int face = 0; face < numFaces; face++) for (int face = 0; face < numFaces; face++)
{ {
// Retrieve lower left corner from face ind // Retrieve lower left corner from face ind
int i = face % (resX - 1) + (face/(resZ - 1)*resX); int i = face + face / (resX - 1);
triangles[t++] = i + resX; triangles[t++] = i + resX;
triangles[t++] = i + 1; triangles[t++] = i + 1;
@ -2460,7 +2453,7 @@ Mesh GenMeshCube(float width, float height, float length)
// Platonic solids: // Platonic solids:
par_shapes_mesh* par_shapes_create_tetrahedron(); // 4 sides polyhedron (pyramid) par_shapes_mesh* par_shapes_create_tetrahedron(); // 4 sides polyhedron (pyramid)
par_shapes_mesh* par_shapes_create_cube(); // 6 sides polyhedron (cube) par_shapes_mesh* par_shapes_create_cube(); // 6 sides polyhedron (cube)
par_shapes_mesh* par_shapes_create_octahedron(); // 8 sides polyhedron (dyamond) par_shapes_mesh* par_shapes_create_octahedron(); // 8 sides polyhedron (diamond)
par_shapes_mesh* par_shapes_create_dodecahedron(); // 12 sides polyhedron par_shapes_mesh* par_shapes_create_dodecahedron(); // 12 sides polyhedron
par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron
*/ */
@ -2940,9 +2933,6 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
Color *pixels = LoadImageColors(cubicmap); Color *pixels = LoadImageColors(cubicmap);
int mapWidth = cubicmap.width;
int mapHeight = cubicmap.height;
// NOTE: Max possible number of triangles numCubes*(12 triangles by cube) // NOTE: Max possible number of triangles numCubes*(12 triangles by cube)
int maxTriangles = cubicmap.width * cubicmap.height * 12; int maxTriangles = cubicmap.width * cubicmap.height * 12;
@ -2981,9 +2971,9 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f }; RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f };
RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f }; RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f };
for (int z = 0; z < mapHeight; ++z) for (int z = 0; z < cubicmap.height; ++z)
{ {
for (int x = 0; x < mapWidth; ++x) for (int x = 0; x < cubicmap.width; ++x)
{ {
// Define the 8 vertex of the cube, we will combine them accordingly later... // Define the 8 vertex of the cube, we will combine them accordingly later...
Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) }; Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) };
@ -3400,7 +3390,7 @@ void GenMeshTangents(Mesh *mesh)
{ {
if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0) if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0)
{ {
// Upate existing vertex buffer // Update existing vertex buffer
rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0); rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0);
} }
else else
@ -3497,7 +3487,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.x*(float)source.width/source.height, size.y }; Vector2 sizeRatio = { size.x*fabsf((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);
@ -3563,6 +3553,8 @@ void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a); rlColor4ub(tint.r, tint.g, tint.b, tint.a);
if (sizeRatio.x * sizeRatio.y >= 0.0f)
{
// Bottom-left corner for texture and quad // Bottom-left corner for texture and quad
rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height); rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height);
rlVertex3f(topLeft.x, topLeft.y, topLeft.z); rlVertex3f(topLeft.x, topLeft.y, topLeft.z);
@ -3578,6 +3570,23 @@ void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector
// Bottom-right corner for texture and quad // Bottom-right corner for texture and quad
rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height); rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height);
rlVertex3f(topRight.x, topRight.y, topRight.z); rlVertex3f(topRight.x, topRight.y, topRight.z);
}
else
{
// Reverse vertex order if the size has only one negative dimension
rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height);
rlVertex3f(topRight.x, topRight.y, topRight.z);
rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height);
rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z);
rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height);
rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z);
rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height);
rlVertex3f(topLeft.x, topLeft.y, topLeft.z);
}
rlEnd(); rlEnd();
rlSetTexture(0); rlSetTexture(0);
@ -3922,9 +3931,10 @@ static Model LoadOBJ(const char *fileName)
if (fileText != NULL) if (fileText != NULL)
{ {
unsigned int dataSize = (unsigned int)strlen(fileText); unsigned int dataSize = (unsigned int)strlen(fileText);
char currentDir[1024] = { 0 }; char currentDir[1024] = { 0 };
strcpy(currentDir, GetWorkingDirectory()); strcpy(currentDir, GetWorkingDirectory()); // Save current working directory
const char *workingDir = GetDirectoryPath(fileName); const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness
if (CHDIR(workingDir) != 0) if (CHDIR(workingDir) != 0)
{ {
TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir); TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir);
@ -3936,117 +3946,91 @@ static Model LoadOBJ(const char *fileName)
if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName); if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName);
else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes/%i materials", fileName, meshCount, materialCount); else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes/%i materials", fileName, meshCount, materialCount);
model.meshCount = materialCount; // WARNING: We are not splitting meshes by materials (previous implementation)
// Depending on the provided OBJ that was not the best option and it just crashed
// so, implementation was simplified to prioritize parsed meshes
model.meshCount = meshCount;
// Init model materials array // Set number of materials available
if (materialCount > 0) // NOTE: There could be more materials available than meshes but it will be resolved at
{ // model.meshMaterial, just assigning the right material to corresponding mesh
model.materialCount = materialCount; model.materialCount = materialCount;
model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); if (model.materialCount == 0)
TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", materialCount);
}
else
{ {
model.meshCount = 1; model.materialCount = 1;
TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); TRACELOG(LOG_INFO, "MODEL: No materials provided, setting one default material for all meshes");
} }
// Init model meshes and materials
model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); // Material index assigned to each mesh
model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
// Count the faces for each material // Process each provided mesh
int *matFaces = RL_CALLOC(model.meshCount, sizeof(int)); for (int i = 0; i < model.meshCount; i++)
// if no materials are present use all faces on one mesh
if (materialCount > 0)
{ {
for (unsigned int fi = 0; fi < attrib.num_faces; fi++) // WARNING: We need to calculate the mesh triangles manually using meshes[i].face_offset
// because in case of triangulated quads, meshes[i].length actually report quads,
// despite the triangulation that is efectively considered on attrib.num_faces
unsigned int tris = 0;
if (i == model.meshCount - 1) tris = attrib.num_faces - meshes[i].face_offset;
else tris = meshes[i + 1].face_offset;
model.meshes[i].vertexCount = tris*3;
model.meshes[i].triangleCount = tris; // Face count (triangulated)
model.meshes[i].vertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
model.meshes[i].texcoords = (float *)RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float));
model.meshes[i].normals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
model.meshMaterial[i] = 0; // By default, assign material 0 to each mesh
// Process all mesh faces
for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 3, vn += 3)
{ {
//tinyobj_vertex_index_t face = attrib.faces[fi];
int idx = attrib.material_ids[fi];
matFaces[idx]++;
}
}
else
{
matFaces[0] = attrib.num_faces;
}
//--------------------------------------
// Create the material meshes
// Running counts/indexes for each material mesh as we are
// building them at the same time
int *vCount = RL_CALLOC(model.meshCount, sizeof(int));
int *vtCount = RL_CALLOC(model.meshCount, sizeof(int));
int *vnCount = RL_CALLOC(model.meshCount, sizeof(int));
int *faceCount = RL_CALLOC(model.meshCount, sizeof(int));
// Allocate space for each of the material meshes
for (int mi = 0; mi < model.meshCount; mi++)
{
model.meshes[mi].vertexCount = matFaces[mi]*3;
model.meshes[mi].triangleCount = matFaces[mi];
model.meshes[mi].vertices = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float));
model.meshes[mi].texcoords = (float *)RL_CALLOC(model.meshes[mi].vertexCount*2, sizeof(float));
model.meshes[mi].normals = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float));
model.meshMaterial[mi] = mi;
}
// Scan through the combined sub meshes and pick out each material mesh
for (unsigned int af = 0; af < attrib.num_faces; af++)
{
int mm = attrib.material_ids[af]; // mesh material for this face
if (mm == -1) { mm = 0; } // no material object..
// Get indices for the face // Get indices for the face
tinyobj_vertex_index_t idx0 = attrib.faces[3*af + 0]; tinyobj_vertex_index_t idx0 = attrib.faces[f*3 + 0];
tinyobj_vertex_index_t idx1 = attrib.faces[3*af + 1]; tinyobj_vertex_index_t idx1 = attrib.faces[f*3 + 1];
tinyobj_vertex_index_t idx2 = attrib.faces[3*af + 2]; tinyobj_vertex_index_t idx2 = attrib.faces[f*3 + 2];
// Fill vertices buffer (float) using vertex index of the face // Fill vertices buffer (float) using vertex index of the face
for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount[mm] +=3; for (int n = 0; n < 3; n++) { model.meshes[i].vertices[v*3 + n] = attrib.vertices[idx0.v_idx*3 + n]; }
for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount[mm] +=3; for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 1)*3 + n] = attrib.vertices[idx1.v_idx*3 + n]; }
for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount[mm] +=3; for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 2)*3 + n] = attrib.vertices[idx2.v_idx*3 + n]; }
if (attrib.num_texcoords > 0) if (attrib.num_texcoords > 0)
{ {
// Fill texcoords buffer (float) using vertex index of the face // Fill texcoords buffer (float) using vertex index of the face
// NOTE: Y-coordinate must be flipped upside-down to account for // NOTE: Y-coordinate must be flipped upside-down
// raylib's upside down textures... model.meshes[i].texcoords[vt*2 + 0] = attrib.texcoords[idx0.vt_idx*2 + 0];
model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; model.meshes[i].texcoords[vt*2 + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1];
model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount[mm] += 2;
model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; model.meshes[i].texcoords[(vt + 1)*2 + 0] = attrib.texcoords[idx1.vt_idx*2 + 0];
model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount[mm] += 2; model.meshes[i].texcoords[(vt + 1)*2 + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1];
model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx2.vt_idx*2 + 0];
model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount[mm] += 2; model.meshes[i].texcoords[(vt + 2)*2 + 0] = attrib.texcoords[idx2.vt_idx*2 + 0];
model.meshes[i].texcoords[(vt + 2)*2 + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1];
} }
if (attrib.num_normals > 0) if (attrib.num_normals > 0)
{ {
// Fill normals buffer (float) using vertex index of the face // Fill normals buffer (float) using vertex index of the face
for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount[mm] +=3; for (int n = 0; n < 3; n++) { model.meshes[i].normals[vn*3 + n] = attrib.normals[idx0.vn_idx*3 + n]; }
for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount[mm] +=3; for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 1)*3 + n] = attrib.normals[idx1.vn_idx*3 + n]; }
for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount[mm] +=3; for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 2)*3 + n] = attrib.normals[idx2.vn_idx*3 + n]; }
}
} }
} }
// Init model materials // Init model materials
ProcessMaterialsOBJ(model.materials, materials, materialCount); if (materialCount > 0) ProcessMaterialsOBJ(model.materials, materials, materialCount);
else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh
tinyobj_attrib_free(&attrib); tinyobj_attrib_free(&attrib);
tinyobj_shapes_free(meshes, meshCount); tinyobj_shapes_free(meshes, model.meshCount);
tinyobj_materials_free(materials, materialCount); tinyobj_materials_free(materials, materialCount);
UnloadFileText(fileText); UnloadFileText(fileText);
RL_FREE(matFaces); // Restore current working directory
RL_FREE(vCount);
RL_FREE(vtCount);
RL_FREE(vnCount);
RL_FREE(faceCount);
if (CHDIR(currentDir) != 0) if (CHDIR(currentDir) != 0)
{ {
TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir); TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir);
@ -4068,8 +4052,8 @@ static Model LoadIQM(const char *fileName)
#define MESH_NAME_LENGTH 32 // Mesh name string length #define MESH_NAME_LENGTH 32 // Mesh name string length
#define MATERIAL_NAME_LENGTH 32 // Material name string length #define MATERIAL_NAME_LENGTH 32 // Material name string length
unsigned int fileSize = 0; int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &fileSize); unsigned char *fileData = LoadFileData(fileName, &dataSize);
unsigned char *fileDataPtr = fileData; unsigned char *fileDataPtr = fileData;
// IQM file structs // IQM file structs
@ -4077,7 +4061,7 @@ static Model LoadIQM(const char *fileName)
typedef struct IQMHeader { typedef struct IQMHeader {
char magic[16]; char magic[16];
unsigned int version; unsigned int version;
unsigned int filesize; unsigned int dataSize;
unsigned int flags; unsigned int flags;
unsigned int num_text, ofs_text; unsigned int num_text, ofs_text;
unsigned int num_meshes, ofs_meshes; unsigned int num_meshes, ofs_meshes;
@ -4231,7 +4215,7 @@ static Model LoadIQM(const char *fileName)
model.meshes[i].triangleCount = imesh[i].num_triangles; model.meshes[i].triangleCount = imesh[i].num_triangles;
model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short));
// Animated verted data, what we actually process for rendering // Animated vertex data, what we actually process for rendering
// NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning)
model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
@ -4430,19 +4414,19 @@ static Model LoadIQM(const char *fileName)
} }
// Load IQM animation data // Load IQM animation data
static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int *animCount) static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount)
{ {
#define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
#define IQM_VERSION 2 // only IQM version 2 supported #define IQM_VERSION 2 // only IQM version 2 supported
unsigned int fileSize = 0; int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &fileSize); unsigned char *fileData = LoadFileData(fileName, &dataSize);
unsigned char *fileDataPtr = fileData; unsigned char *fileDataPtr = fileData;
typedef struct IQMHeader { typedef struct IQMHeader {
char magic[16]; char magic[16];
unsigned int version; unsigned int version;
unsigned int filesize; unsigned int dataSize;
unsigned int flags; unsigned int flags;
unsigned int num_text, ofs_text; unsigned int num_text, ofs_text;
unsigned int num_meshes, ofs_meshes; unsigned int num_meshes, ofs_meshes;
@ -4525,7 +4509,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int
animations[a].boneCount = iqmHeader->num_poses; animations[a].boneCount = iqmHeader->num_poses;
animations[a].bones = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo)); animations[a].bones = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo));
animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *));
// animations[a].framerate = anim.framerate; // TODO: Use framerate? // animations[a].framerate = anim.framerate; // TODO: Use animation framerate data?
for (unsigned int j = 0; j < iqmHeader->num_poses; j++) for (unsigned int j = 0; j < iqmHeader->num_poses; j++)
{ {
@ -4691,7 +4675,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat
if (result == cgltf_result_success) if (result == cgltf_result_success)
{ {
image = LoadImageFromMemory(".png", (unsigned char *)data, outSize); image = LoadImageFromMemory(".png", (unsigned char *)data, outSize);
MemFree(data); RL_FREE(data);
} }
} }
} }
@ -4774,7 +4758,7 @@ static Model LoadGLTF(const char *fileName)
RESTRICTIONS: RESTRICTIONS:
- Only triangle meshes supported - Only triangle meshes supported
- Vertex attibute types and formats supported: - Vertex attribute types and formats supported:
> Vertices (position): vec3: float > Vertices (position): vec3: float
> Normals: vec3: float > Normals: vec3: float
> Texcoords: vec2: float > Texcoords: vec2: float
@ -4802,7 +4786,7 @@ static Model LoadGLTF(const char *fileName)
Model model = { 0 }; Model model = { 0 };
// glTF file loading // glTF file loading
unsigned int dataSize = 0; int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &dataSize); unsigned char *fileData = LoadFileData(fileName, &dataSize);
if (fileData == NULL) return model; if (fileData == NULL) return model;
@ -4825,7 +4809,7 @@ static Model LoadGLTF(const char *fileName)
TRACELOG(LOG_DEBUG, " > Textures count: %i", data->textures_count); TRACELOG(LOG_DEBUG, " > Textures count: %i", data->textures_count);
// Force reading data buffers (fills buffer_view->buffer->data) // Force reading data buffers (fills buffer_view->buffer->data)
// NOTE: If an uri is defined to base64 data or external path, it's automatically loaded -> TODO: Verify this assumption // NOTE: If an uri is defined to base64 data or external path, it's automatically loaded
result = cgltf_load_buffers(&options, data, fileName); result = cgltf_load_buffers(&options, data, fileName);
if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName); if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName);
@ -5205,7 +5189,9 @@ static Model LoadGLTF(const char *fileName)
model.meshes[meshIndex].animVertices = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); model.meshes[meshIndex].animVertices = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
memcpy(model.meshes[meshIndex].animVertices, model.meshes[meshIndex].vertices, model.meshes[meshIndex].vertexCount*3*sizeof(float)); memcpy(model.meshes[meshIndex].animVertices, model.meshes[meshIndex].vertices, model.meshes[meshIndex].vertexCount*3*sizeof(float));
model.meshes[meshIndex].animNormals = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); model.meshes[meshIndex].animNormals = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
if (model.meshes[meshIndex].normals != NULL) {
memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float)); memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float));
}
meshIndex++; // Move to next mesh meshIndex++; // Move to next mesh
} }
@ -5231,7 +5217,7 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo
float tend = 0.0f; float tend = 0.0f;
int keyframe = 0; // Defaults to first pose int keyframe = 0; // Defaults to first pose
for (int i = 0; i < input->count - 1; i++) for (int i = 0; i < (int)input->count - 1; i++)
{ {
cgltf_bool r1 = cgltf_accessor_read_float(input, i, &tstart, 1); cgltf_bool r1 = cgltf_accessor_read_float(input, i, &tstart, 1);
if (!r1) return false; if (!r1) return false;
@ -5271,7 +5257,7 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo
Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]}; Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]};
Vector4 *r = data; Vector4 *r = data;
// Only v4 is for rotations, so we know it's a quat // Only v4 is for rotations, so we know it's a quaternion
*r = QuaternionSlerp(v1, v2, t); *r = QuaternionSlerp(v1, v2, t);
} }
@ -5280,10 +5266,10 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo
#define GLTF_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) #define GLTF_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms)
static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned int *animCount) static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount)
{ {
// glTF file loading // glTF file loading
unsigned int dataSize = 0; int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &dataSize); unsigned char *fileData = LoadFileData(fileName, &dataSize);
ModelAnimation *animations = NULL; ModelAnimation *animations = NULL;
@ -5379,6 +5365,9 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in
animDuration = (t > animDuration)? t : animDuration; animDuration = (t > animDuration)? t : animDuration;
} }
strncpy(animations[i].name, animData.name, sizeof(animations[i].name));
animations[i].name[sizeof(animations[i].name) - 1] = '\0';
animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY); animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY);
animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *));
@ -5435,7 +5424,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in
cgltf_free(data); cgltf_free(data);
} }
UnloadFileData(fileData);
return animations; return animations;
} }
#endif #endif
@ -5448,11 +5437,11 @@ static Model LoadVOX(const char *fileName)
int nbvertices = 0; int nbvertices = 0;
int meshescount = 0; int meshescount = 0;
unsigned int fileSize = 0;
unsigned char *fileData = NULL;
// Read vox file into buffer // Read vox file into buffer
fileData = LoadFileData(fileName, &fileSize); int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &dataSize);
if (fileData == 0) if (fileData == 0)
{ {
TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX file", fileName); TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX file", fileName);
@ -5461,7 +5450,7 @@ static Model LoadVOX(const char *fileName)
// Read and build voxarray description // Read and build voxarray description
VoxArray3D voxarray = { 0 }; VoxArray3D voxarray = { 0 };
int ret = Vox_LoadFromMemory(fileData, fileSize, &voxarray); int ret = Vox_LoadFromMemory(fileData, dataSize, &voxarray);
if (ret != VOX_SUCCESS) if (ret != VOX_SUCCESS)
{ {
@ -5517,7 +5506,6 @@ static Model LoadVOX(const char *fileName)
memcpy(pmesh->vertices, pvertices, size); memcpy(pmesh->vertices, pvertices, size);
// Copy indices // Copy indices
// TODO: Compute globals indices array
size = voxarray.indices.used*sizeof(unsigned short); size = voxarray.indices.used*sizeof(unsigned short);
pmesh->indices = RL_MALLOC(size); pmesh->indices = RL_MALLOC(size);
memcpy(pmesh->indices, pindices, size); memcpy(pmesh->indices, pindices, size);
@ -5547,7 +5535,7 @@ static Model LoadVOX(const char *fileName)
#if defined(SUPPORT_FILEFORMAT_M3D) #if defined(SUPPORT_FILEFORMAT_M3D)
// Hook LoadFileData()/UnloadFileData() calls to M3D loaders // Hook LoadFileData()/UnloadFileData() calls to M3D loaders
unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, len); } unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, (int *)len); }
void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); } void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); }
// Load M3D mesh data // Load M3D mesh data
@ -5557,9 +5545,10 @@ static Model LoadM3D(const char *fileName)
m3d_t *m3d = NULL; m3d_t *m3d = NULL;
m3dp_t *prop = NULL; m3dp_t *prop = NULL;
unsigned int bytesRead = 0; int i, j, k, l, n, mi = -2, vcolor = 0;
unsigned char *fileData = LoadFileData(fileName, &bytesRead);
int i, j, k, l, n, mi = -2; int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &dataSize);
if (fileData != NULL) if (fileData != NULL)
{ {
@ -5589,10 +5578,36 @@ static Model LoadM3D(const char *fileName)
} }
else else
{ {
model.meshCount = model.materialCount = 1; model.meshCount = 1; model.materialCount = 0;
TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material");
} }
// We always need a default material, so we add +1
model.materialCount++;
// Faces must be in non-decreasing materialid order. Verify that quickly, sorting them otherwise.
// WARNING: Sorting is not needed, valid M3D model files should already be sorted
// Just keeping the sorting function for reference (Check PR #3363 #3385)
/*
for (i = 1; i < m3d->numface; i++)
{
if (m3d->face[i-1].materialid <= m3d->face[i].materialid) continue;
// face[i-1] > face[i]. slide face[i] lower.
m3df_t slider = m3d->face[i];
j = i-1;
do
{ // face[j] > slider, face[j+1] is svailable vacant gap.
m3d->face[j+1] = m3d->face[j];
j = j-1;
}
while (j >= 0 && m3d->face[j].materialid > slider.materialid);
m3d->face[j+1] = slider;
}
*/
model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
model.materials = (Material *)RL_CALLOC(model.materialCount + 1, sizeof(Material)); model.materials = (Material *)RL_CALLOC(model.materialCount + 1, sizeof(Material));
@ -5617,7 +5632,14 @@ static Model LoadM3D(const char *fileName)
k++; k++;
mi = m3d->face[i].materialid; mi = m3d->face[i].materialid;
for (j = i, l = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++); // Only allocate colors VertexBuffer if there's a color vertex in the model for this material batch
// if all colors are fully transparent black for all verteces of this materal, then we assume no vertex colors
for (j = i, l = vcolor = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++)
{
if (!m3d->vertex[m3d->face[j].vertex[0]].color ||
!m3d->vertex[m3d->face[j].vertex[1]].color ||
!m3d->vertex[m3d->face[j].vertex[2]].color) vcolor = 1;
}
model.meshes[k].vertexCount = l*3; model.meshes[k].vertexCount = l*3;
model.meshes[k].triangleCount = l; model.meshes[k].triangleCount = l;
@ -5625,9 +5647,9 @@ static Model LoadM3D(const char *fileName)
model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, 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)); model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
// If no map is provided, we allocate storage for vertex colors // If no map is provided, or we have colors defined, we allocate storage for vertex colors
// M3D specs only consider vertex colors if no material is provided // M3D specs only consider vertex colors if no material is provided, however raylib uses both and mixes the colors
if (mi != M3D_UNDEF) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
if (m3d->numbone && m3d->numskin) if (m3d->numbone && m3d->numskin)
{ {
@ -5652,7 +5674,7 @@ static Model LoadM3D(const char *fileName)
model.meshes[k].vertices[l*9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y*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; model.meshes[k].vertices[l*9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z*m3d->scale;
// without vertex color (full transparency), we use the default color // Without vertex color (full transparency), we use the default color
if (model.meshes[k].colors != NULL) if (model.meshes[k].colors != NULL)
{ {
if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xFF000000) if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xFF000000)
@ -5694,7 +5716,7 @@ static Model LoadM3D(const char *fileName)
int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid; int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid;
// Check if there is a skin for this mesh, should be, just failsafe // Check if there is a skin for this mesh, should be, just failsafe
if (skinid != M3D_UNDEF && skinid < (int)m3d->numskin) if ((skinid != M3D_UNDEF) && (skinid < (int)m3d->numskin))
{ {
for (j = 0; j < 4; j++) for (j = 0; j < 4; j++)
{ {
@ -5803,7 +5825,7 @@ static Model LoadM3D(const char *fileName)
model.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z; model.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z;
model.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w; model.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w;
// TODO: if the orientation quaternion not normalized, then that's encoding scaling // TODO: If the orientation quaternion is not normalized, then that's encoding scaling
model.bindPose[i].rotation = QuaternionNormalize(model.bindPose[i].rotation); 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; model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f;
@ -5851,16 +5873,17 @@ static Model LoadM3D(const char *fileName)
#define M3D_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) #define M3D_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms)
// Load M3D animation data // Load M3D animation data
static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount) static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount)
{ {
m3d_t *m3d = NULL;
unsigned int bytesRead = 0;
unsigned char *fileData = LoadFileData(fileName, &bytesRead);
ModelAnimation *animations = NULL; ModelAnimation *animations = NULL;
int i = 0, j = 0;
m3d_t *m3d = NULL;
int i = 0, j = 0;
*animCount = 0; *animCount = 0;
int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &dataSize);
if (fileData != NULL) if (fileData != NULL)
{ {
m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL); m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL);

View file

@ -438,7 +438,7 @@ func SetModelMeshMaterial(model *Model, meshId int32, materialId int32) {
func LoadModelAnimations(fileName string) []ModelAnimation { func LoadModelAnimations(fileName string) []ModelAnimation {
cfileName := C.CString(fileName) cfileName := C.CString(fileName)
defer C.free(unsafe.Pointer(cfileName)) defer C.free(unsafe.Pointer(cfileName))
ccount := C.uint(0) ccount := C.int(0)
ret := C.LoadModelAnimations(cfileName, &ccount) ret := C.LoadModelAnimations(cfileName, &ccount)
v := (*[1 << 24]ModelAnimation)(unsafe.Pointer(ret))[:int(ccount)] v := (*[1 << 24]ModelAnimation)(unsafe.Pointer(ret))[:int(ccount)]
return v return v
@ -460,7 +460,7 @@ func UnloadModelAnimation(anim ModelAnimation) {
// UnloadModelAnimations - Unload animation array data // UnloadModelAnimations - Unload animation array data
func UnloadModelAnimations(animations []ModelAnimation) { func UnloadModelAnimations(animations []ModelAnimation) {
C.UnloadModelAnimations((*C.ModelAnimation)(unsafe.Pointer(&animations[0])), (C.uint)(len(animations))) C.UnloadModelAnimations((*C.ModelAnimation)(unsafe.Pointer(&animations[0])), (C.int)(len(animations)))
} }
// IsModelAnimationValid - Check model animation skeleton match // IsModelAnimationValid - Check model animation skeleton match

View file

@ -2,7 +2,7 @@
* *
* rshapes - Basic functions to draw 2d shapes and check collisions * rshapes - Basic functions to draw 2d shapes and check collisions
* *
* NOTES: * ADDITIONAL NOTES:
* Shapes can be draw using 3 types of primitives: LINES, TRIANGLES and QUADS. * Shapes can be draw using 3 types of primitives: LINES, TRIANGLES and QUADS.
* Some functions implement two drawing options: TRIANGLES and QUADS, by default TRIANGLES * Some functions implement two drawing options: TRIANGLES and QUADS, by default TRIANGLES
* are used but QUADS implementation can be selected with SUPPORT_QUADS_DRAW_MODE define * are used but QUADS implementation can be selected with SUPPORT_QUADS_DRAW_MODE define
@ -16,7 +16,6 @@
* a single draw call and it also allows users to configure it the same way with their own fonts. * a single draw call and it also allows users to configure it the same way with their own fonts.
* *
* CONFIGURATION: * CONFIGURATION:
*
* #define SUPPORT_MODULE_RSHAPES * #define SUPPORT_MODULE_RSHAPES
* rshapes module is included in the build * rshapes module is included in the build
* *
@ -68,8 +67,8 @@
#ifndef SMOOTH_CIRCLE_ERROR_RATE #ifndef SMOOTH_CIRCLE_ERROR_RATE
#define SMOOTH_CIRCLE_ERROR_RATE 0.5f // Circle error rate #define SMOOTH_CIRCLE_ERROR_RATE 0.5f // Circle error rate
#endif #endif
#ifndef BEZIER_LINE_DIVISIONS #ifndef SPLINE_LINE_DIVISIONS
#define BEZIER_LINE_DIVISIONS 24 // Bezier line divisions #define SPLINE_LINE_DIVISIONS 24 // Spline lines segment divisions
#endif #endif
@ -81,7 +80,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 (white pixel loaded by rlgl)
Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -97,10 +96,21 @@ static float EaseCubicInOut(float t, float b, float c, float d); // Cubic eas
// NOTE: It can be useful when using basic shapes and one single font, // NOTE: It can be useful when using basic shapes and one single font,
// defining a font char white rectangle would allow drawing everything in a single draw call // defining a font char white rectangle would allow drawing everything in a single draw call
void SetShapesTexture(Texture2D texture, Rectangle source) void SetShapesTexture(Texture2D texture, Rectangle source)
{
// Reset texture to default pixel if required
// WARNING: Shapes texture should be probably better validated,
// it can break the rendering of all shapes if misused
if ((texture.id == 0) || (source.width == 0) || (source.height == 0))
{
texShapes = (Texture2D){ 1, 1, 1, 1, 7 };
texShapesRec = (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f };
}
else
{ {
texShapes = texture; texShapes = texture;
texShapesRec = source; texShapesRec = source;
} }
}
// Draw a pixel // Draw a pixel
void DrawPixel(int posX, int posY, Color color) void DrawPixel(int posX, int posY, Color color)
@ -180,6 +190,7 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
if ((length > 0) && (thick > 0)) if ((length > 0) && (thick > 0))
{ {
float scale = thick/(2*length); float scale = thick/(2*length);
Vector2 radius = { -scale*delta.y, scale*delta.x }; Vector2 radius = { -scale*delta.y, scale*delta.x };
Vector2 strip[4] = { Vector2 strip[4] = {
{ startPos.x - radius.x, startPos.y - radius.y }, { startPos.x - radius.x, startPos.y - radius.y },
@ -198,14 +209,14 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
Vector2 previous = startPos; Vector2 previous = startPos;
Vector2 current = { 0 }; Vector2 current = { 0 };
Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
{ {
// Cubic easing in-out // Cubic easing in-out
// NOTE: Easing is calculated only for y position value // NOTE: Easing is calculated only for y position value
current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)BEZIER_LINE_DIVISIONS); current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)SPLINE_LINE_DIVISIONS);
current.x = previous.x + (endPos.x - startPos.x)/ (float)BEZIER_LINE_DIVISIONS; current.x = previous.x + (endPos.x - startPos.x)/(float)SPLINE_LINE_DIVISIONS;
float dy = current.y - previous.y; float dy = current.y - previous.y;
float dx = current.x - previous.x; float dx = current.x - previous.x;
@ -227,25 +238,26 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
previous = current; previous = current;
} }
DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
} }
// Draw line using quadratic bezier curves with a control point // Draw line using quadratic bezier curves with a control point
void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color) void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color)
{ {
const float step = 1.0f/BEZIER_LINE_DIVISIONS; const float step = 1.0f/SPLINE_LINE_DIVISIONS;
Vector2 previous = startPos; Vector2 previous = startPos;
Vector2 current = { 0 }; Vector2 current = { 0 };
float t = 0.0f; float t = 0.0f;
Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
for (int i = 0; i <= BEZIER_LINE_DIVISIONS; i++) for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
{ {
t = step*i; t = step*i;
float a = powf(1 - t, 2);
float b = 2*(1 - t)*t; float a = powf(1.0f - t, 2);
float b = 2.0f*(1.0f - t)*t;
float c = powf(t, 2); float c = powf(t, 2);
// NOTE: The easing functions aren't suitable here because they don't take a control point // NOTE: The easing functions aren't suitable here because they don't take a control point
@ -272,26 +284,27 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl
previous = current; previous = current;
} }
DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
} }
// Draw line using cubic bezier curves with 2 control points // Draw line using cubic bezier curves with 2 control points
void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color) void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color)
{ {
const float step = 1.0f/BEZIER_LINE_DIVISIONS; const float step = 1.0f/SPLINE_LINE_DIVISIONS;
Vector2 previous = startPos; Vector2 previous = startPos;
Vector2 current = { 0 }; Vector2 current = { 0 };
float t = 0.0f; float t = 0.0f;
Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
for (int i = 0; i <= BEZIER_LINE_DIVISIONS; i++) for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
{ {
t = step*i; t = step*i;
float a = powf(1 - t, 3);
float b = 3*powf(1 - t, 2)*t; float a = powf(1.0f - t, 3);
float c = 3*(1-t)*powf(t, 2); float b = 3.0f*powf(1.0f - t, 2)*t;
float c = 3.0f*(1.0f - t)*powf(t, 2);
float d = powf(t, 3); float d = powf(t, 3);
current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y; current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y;
@ -317,7 +330,149 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP
previous = current; previous = current;
} }
DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
}
// Draw a B-Spline line, minimum 4 points
void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color)
{
if (pointCount < 4) return;
float a[4] = { 0 };
float b[4] = { 0 };
float dy = 0.0f;
float dx = 0.0f;
float size = 0.0f;
Vector2 currentPoint = { 0 };
Vector2 nextPoint = { 0 };
Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
for (int i = 0; i < (pointCount - 3); i++)
{
float t = 0.0f;
Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3];
a[0] = (-p1.x + 3.0f*p2.x - 3.0f*p3.x + p4.x)/6.0f;
a[1] = (3.0f*p1.x - 6.0f*p2.x + 3.0f*p3.x)/6.0f;
a[2] = (-3.0f*p1.x + 3.0f*p3.x)/6.0f;
a[3] = (p1.x + 4.0f*p2.x + p3.x)/6.0f;
b[0] = (-p1.y + 3.0f*p2.y - 3.0f*p3.y + p4.y)/6.0f;
b[1] = (3.0f*p1.y - 6.0f*p2.y + 3.0f*p3.y)/6.0f;
b[2] = (-3.0f*p1.y + 3.0f*p3.y)/6.0f;
b[3] = (p1.y + 4.0f*p2.y + p3.y)/6.0f;
currentPoint.x = a[3];
currentPoint.y = b[3];
if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color); // Draw init line circle-cap
if (i > 0)
{
vertices[0].x = currentPoint.x + dy*size;
vertices[0].y = currentPoint.y - dx*size;
vertices[1].x = currentPoint.x - dy*size;
vertices[1].y = currentPoint.y + dx*size;
}
for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++)
{
t = ((float)j)/((float)SPLINE_LINE_DIVISIONS);
nextPoint.x = a[3] + t*(a[2] + t*(a[1] + t*a[0]));
nextPoint.y = b[3] + t*(b[2] + t*(b[1] + t*b[0]));
dy = nextPoint.y - currentPoint.y;
dx = nextPoint.x - currentPoint.x;
size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
if ((i == 0) && (j == 1))
{
vertices[0].x = currentPoint.x + dy*size;
vertices[0].y = currentPoint.y - dx*size;
vertices[1].x = currentPoint.x - dy*size;
vertices[1].y = currentPoint.y + dx*size;
}
vertices[2*j + 1].x = nextPoint.x - dy*size;
vertices[2*j + 1].y = nextPoint.y + dx*size;
vertices[2*j].x = nextPoint.x + dy*size;
vertices[2*j].y = nextPoint.y - dx*size;
currentPoint = nextPoint;
}
DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color);
}
DrawCircleV(currentPoint, thick/2.0f, color); // Draw end line circle-cap
}
// Draw a Catmull Rom spline line, minimum 4 points
void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color)
{
if (pointCount < 4) return;
float dy = 0.0f;
float dx = 0.0f;
float size = 0.0f;
Vector2 currentPoint = points[1];
Vector2 nextPoint = { 0 };
Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
DrawCircleV(currentPoint, thick/2.0f, color); // Draw init line circle-cap
for (int i = 0; i < (pointCount - 3); i++)
{
float t = 0.0f;
Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3];
if (i > 0)
{
vertices[0].x = currentPoint.x + dy*size;
vertices[0].y = currentPoint.y - dx*size;
vertices[1].x = currentPoint.x - dy*size;
vertices[1].y = currentPoint.y + dx*size;
}
for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++)
{
t = ((float)j)/((float)SPLINE_LINE_DIVISIONS);
float q0 = (-1.0f*t*t*t) + (2.0f*t*t) + (-1.0f*t);
float q1 = (3.0f*t*t*t) + (-5.0f*t*t) + 2.0f;
float q2 = (-3.0f*t*t*t) + (4.0f*t*t) + t;
float q3 = t*t*t - t*t;
nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3));
nextPoint.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3));
dy = nextPoint.y - currentPoint.y;
dx = nextPoint.x - currentPoint.x;
size = (0.5f*thick)/sqrtf(dx*dx + dy*dy);
if ((i == 0) && (j == 1))
{
vertices[0].x = currentPoint.x + dy*size;
vertices[0].y = currentPoint.y - dx*size;
vertices[1].x = currentPoint.x - dy*size;
vertices[1].y = currentPoint.y + dx*size;
}
vertices[2*j + 1].x = nextPoint.x - dy*size;
vertices[2*j + 1].y = nextPoint.y + dx*size;
vertices[2*j].x = nextPoint.x + dy*size;
vertices[2*j].y = nextPoint.y - dx*size;
currentPoint = nextPoint;
}
DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color);
}
DrawCircleV(currentPoint, thick/2.0f, color); // Draw end line circle-cap
} }
// Draw lines sequence // Draw lines sequence
@ -343,6 +498,13 @@ void DrawCircle(int centerX, int centerY, float radius, Color color)
DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color); DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color);
} }
// Draw a color-filled circle (Vector version)
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues
void DrawCircleV(Vector2 center, float radius, Color color)
{
DrawCircleSector(center, radius, 0, 360, 36, color);
}
// Draw a piece of a circle // Draw a piece of a circle
void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color)
{ {
@ -375,6 +537,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA
rlSetTexture(texShapes.id); rlSetTexture(texShapes.id);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
// NOTE: Every QUAD actually represents two segments // NOTE: Every QUAD actually represents two segments
for (int i = 0; i < segments/2; i++) for (int i = 0; i < segments/2; i++)
{ {
@ -383,35 +546,36 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
angle += (stepLength*2); angle += (stepLength*2.0f);
} }
// NOTE: In case number of segments is odd, we add one last piece to the cake // NOTE: In case number of segments is odd, we add one last piece to the cake
if (segments%2) if ((segments%2) == 1)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
} }
rlEnd(); rlEnd();
rlSetTexture(0); rlSetTexture(0);
@ -422,8 +586,8 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
angle += stepLength; angle += stepLength;
} }
@ -465,15 +629,15 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
} }
for (int i = 0; i < segments; i++) for (int i = 0; i < segments; i++)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
angle += stepLength; angle += stepLength;
} }
@ -482,7 +646,7 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
} }
rlEnd(); rlEnd();
} }
@ -497,22 +661,21 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co
rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlColor4ub(color1.r, color1.g, color1.b, color1.a);
rlVertex2f((float)centerX, (float)centerY); rlVertex2f((float)centerX, (float)centerY);
rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlColor4ub(color2.r, color2.g, color2.b, color2.a);
rlVertex2f((float)centerX + sinf(DEG2RAD*i)*radius, (float)centerY + cosf(DEG2RAD*i)*radius); rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radius, (float)centerY + sinf(DEG2RAD*(i + 10))*radius);
rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlColor4ub(color2.r, color2.g, color2.b, color2.a);
rlVertex2f((float)centerX + sinf(DEG2RAD*(i + 10))*radius, (float)centerY + cosf(DEG2RAD*(i + 10))*radius); rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radius, (float)centerY + sinf(DEG2RAD*i)*radius);
} }
rlEnd(); rlEnd();
} }
// Draw a color-filled circle (Vector version)
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues
void DrawCircleV(Vector2 center, float radius, Color color)
{
DrawCircleSector(center, radius, 0, 360, 36, color);
}
// Draw circle outline // Draw circle outline
void DrawCircleLines(int centerX, int centerY, float radius, Color color) void DrawCircleLines(int centerX, int centerY, float radius, Color color)
{
DrawCircleLinesV((Vector2){ (float)centerX, (float)centerY }, radius, color);
}
// Draw circle outline (Vector version)
void DrawCircleLinesV(Vector2 center, float radius, Color color)
{ {
rlBegin(RL_LINES); rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
@ -520,8 +683,8 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color)
// NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360)
for (int i = 0; i < 360; i += 10) for (int i = 0; i < 360; i += 10)
{ {
rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius); rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius);
rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radius, centerY + cosf(DEG2RAD*(i + 10))*radius); rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius);
} }
rlEnd(); rlEnd();
} }
@ -534,8 +697,8 @@ void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color c
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f((float)centerX, (float)centerY); rlVertex2f((float)centerX, (float)centerY);
rlVertex2f((float)centerX + sinf(DEG2RAD*i)*radiusH, (float)centerY + cosf(DEG2RAD*i)*radiusV); rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radiusH, (float)centerY + sinf(DEG2RAD*(i + 10))*radiusV);
rlVertex2f((float)centerX + sinf(DEG2RAD*(i + 10))*radiusH, (float)centerY + cosf(DEG2RAD*(i + 10))*radiusV); rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radiusH, (float)centerY + sinf(DEG2RAD*i)*radiusV);
} }
rlEnd(); rlEnd();
} }
@ -547,8 +710,8 @@ void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Co
for (int i = 0; i < 360; i += 10) for (int i = 0; i < 360; i += 10)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(centerX + sinf(DEG2RAD*i)*radiusH, centerY + cosf(DEG2RAD*i)*radiusV); rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radiusH, centerY + sinf(DEG2RAD*(i + 10))*radiusV);
rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radiusH, centerY + cosf(DEG2RAD*(i + 10))*radiusV); rlVertex2f(centerX + cosf(DEG2RAD*i)*radiusH, centerY + sinf(DEG2RAD*i)*radiusV);
} }
rlEnd(); rlEnd();
} }
@ -606,17 +769,17 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
angle += stepLength; angle += stepLength;
} }
@ -629,13 +792,13 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
angle += stepLength; angle += stepLength;
} }
@ -692,19 +855,19 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float s
if (showCapLines) if (showCapLines)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
} }
for (int i = 0; i < segments; i++) for (int i = 0; i < segments; i++)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
angle += stepLength; angle += stepLength;
} }
@ -712,8 +875,8 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float s
if (showCapLines) if (showCapLines)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
} }
rlEnd(); rlEnd();
} }
@ -972,7 +1135,7 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
}; };
const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; const Vector2 centers[4] = { point[8], point[9], point[10], point[11] };
const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id); rlSetTexture(texShapes.id);
@ -990,12 +1153,16 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
angle += (stepLength*2); angle += (stepLength*2);
} }
@ -1005,10 +1172,13 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
} }
@ -1083,8 +1253,8 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
angle += stepLength; angle += stepLength;
} }
} }
@ -1198,7 +1368,7 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
{(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19 {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19
}; };
const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
if (lineThick > 1) if (lineThick > 1)
{ {
@ -1215,14 +1385,18 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
for (int i = 0; i < segments; i++) for (int i = 0; i < segments; i++)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
angle += stepLength; angle += stepLength;
} }
@ -1287,13 +1461,13 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
angle += stepLength; angle += stepLength;
} }
@ -1351,8 +1525,8 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
for (int i = 0; i < segments; i++) for (int i = 0; i < segments; i++)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
angle += stepLength; angle += stepLength;
} }
} }
@ -1482,7 +1656,8 @@ void DrawTriangleStrip(Vector2 *points, int pointCount, Color color)
void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color)
{ {
if (sides < 3) sides = 3; if (sides < 3) sides = 3;
float centralAngle = rotation; float centralAngle = rotation*DEG2RAD;
float angleStep = 360.0f/(float)sides*DEG2RAD;
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
rlSetTexture(texShapes.id); rlSetTexture(texShapes.id);
@ -1491,19 +1666,21 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
for (int i = 0; i < sides; i++) for (int i = 0; i < sides; i++)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
float nextAngle = centralAngle + angleStep;
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
centralAngle += 360.0f/(float)sides; centralAngle = nextAngle;
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius);
} }
rlEnd(); rlEnd();
rlSetTexture(0); rlSetTexture(0);
@ -1514,10 +1691,10 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); rlVertex2f(center.x + cosf(centralAngle + angleStep)*radius, center.y + sinf(centralAngle + angleStep)*radius);
rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
centralAngle += 360.0f/(float)sides; centralAngle += angleStep;
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius);
} }
rlEnd(); rlEnd();
#endif #endif
@ -1527,16 +1704,18 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color) void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color)
{ {
if (sides < 3) sides = 3; if (sides < 3) sides = 3;
float centralAngle = rotation; float centralAngle = rotation*DEG2RAD;
float angleStep = 360.0f/(float)sides*DEG2RAD;
rlBegin(RL_LINES); rlBegin(RL_LINES);
for (int i = 0; i < sides; i++) for (int i = 0; i < sides; i++)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
centralAngle += 360.0f/(float)sides; rlVertex2f(center.x + cosf(centralAngle + angleStep)*radius, center.y + sinf(centralAngle + angleStep)*radius);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius);
centralAngle += angleStep;
} }
rlEnd(); rlEnd();
} }
@ -1544,8 +1723,8 @@ void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Colo
void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color) void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color)
{ {
if (sides < 3) sides = 3; if (sides < 3) sides = 3;
float centralAngle = rotation; float centralAngle = rotation*DEG2RAD;
float exteriorAngle = 360.0f/(float)sides; float exteriorAngle = 360.0f/(float)sides*DEG2RAD;
float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f)); float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f));
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
@ -1555,19 +1734,21 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl
for (int i = 0; i < sides; i++) for (int i = 0; i < sides; i++)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
float nextAngle = centralAngle + exteriorAngle;
rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius);
rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
centralAngle += exteriorAngle; rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); rlVertex2f(center.x + cosf(nextAngle)*innerRadius, center.y + sinf(nextAngle)*innerRadius);
rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
centralAngle = nextAngle;
} }
rlEnd(); rlEnd();
rlSetTexture(0); rlSetTexture(0);
@ -1578,13 +1759,13 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
float nextAngle = centralAngle + exteriorAngle; float nextAngle = centralAngle + exteriorAngle;
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*radius, center.y + cosf(DEG2RAD*nextAngle)*radius); rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*radius, center.y + cosf(DEG2RAD*nextAngle)*radius); rlVertex2f(center.x + cosf(nextAngle)*innerRadius, center.y + sinf(nextAngle)*innerRadius);
rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*innerRadius, center.y + cosf(DEG2RAD*nextAngle)*innerRadius); rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
centralAngle = nextAngle; centralAngle = nextAngle;
} }
@ -1601,7 +1782,7 @@ bool CheckCollisionPointRec(Vector2 point, Rectangle rec)
{ {
bool collision = false; bool collision = false;
if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) && (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true; if ((point.x >= rec.x) && (point.x < (rec.x + rec.width)) && (point.y >= rec.y) && (point.y < (rec.y + rec.height))) collision = true;
return collision; return collision;
} }
@ -1759,68 +1940,26 @@ bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshol
// Get collision rectangle for two rectangles collision // Get collision rectangle for two rectangles collision
Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
{ {
Rectangle rec = { 0, 0, 0, 0 }; Rectangle overlap = { 0 };
if (CheckCollisionRecs(rec1, rec2)) float left = (rec1.x > rec2.x)? rec1.x : rec2.x;
{ float right1 = rec1.x + rec1.width;
float dxx = fabsf(rec1.x - rec2.x); float right2 = rec2.x + rec2.width;
float dyy = fabsf(rec1.y - rec2.y); float right = (right1 < right2)? right1 : right2;
float top = (rec1.y > rec2.y)? rec1.y : rec2.y;
float bottom1 = rec1.y + rec1.height;
float bottom2 = rec2.y + rec2.height;
float bottom = (bottom1 < bottom2)? bottom1 : bottom2;
if (rec1.x <= rec2.x) if ((left < right) && (top < bottom))
{ {
if (rec1.y <= rec2.y) overlap.x = left;
{ overlap.y = top;
rec.x = rec2.x; overlap.width = right - left;
rec.y = rec2.y; overlap.height = bottom - top;
rec.width = rec1.width - dxx;
rec.height = rec1.height - dyy;
}
else
{
rec.x = rec2.x;
rec.y = rec1.y;
rec.width = rec1.width - dxx;
rec.height = rec2.height - dyy;
}
}
else
{
if (rec1.y <= rec2.y)
{
rec.x = rec1.x;
rec.y = rec2.y;
rec.width = rec2.width - dxx;
rec.height = rec1.height - dyy;
}
else
{
rec.x = rec1.x;
rec.y = rec1.y;
rec.width = rec2.width - dxx;
rec.height = rec2.height - dyy;
}
} }
if (rec1.width > rec2.width) return overlap;
{
if (rec.width >= rec2.width) rec.width = rec2.width;
}
else
{
if (rec.width >= rec1.width) rec.width = rec1.width;
}
if (rec1.height > rec2.height)
{
if (rec.height >= rec2.height) rec.height = rec2.height;
}
else
{
if (rec.height >= rec1.height) rec.height = rec1.height;
}
}
return rec;
} }
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------

View file

@ -3,18 +3,22 @@
* rtext - Basic functions to load fonts and draw text * rtext - Basic functions to load fonts and draw text
* *
* CONFIGURATION: * CONFIGURATION:
*
* #define SUPPORT_MODULE_RTEXT * #define SUPPORT_MODULE_RTEXT
* rtext module is included in the build * rtext module is included in the build
* *
* #define SUPPORT_DEFAULT_FONT
* Load default raylib font on initialization to be used by DrawText() and MeasureText().
* If no default font loaded, DrawTextEx() and MeasureTextEx() are required.
*
* #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
* supported by default, to remove support, just comment unrequired #define in this module * supported by default, to remove support, just comment unrequired #define in this module
* *
* #define SUPPORT_DEFAULT_FONT * #define SUPPORT_FONT_ATLAS_WHITE_REC
* Load default raylib font on initialization to be used by DrawText() and MeasureText(). * On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
* If no default font loaded, DrawTextEx() and MeasureTextEx() are required. * at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
* drawing text and shapes with a single draw call [SetShapesTexture()].
* *
* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH * #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
* TextSplit() function static buffer max size * TextSplit() function static buffer max size
@ -22,7 +26,6 @@
* #define MAX_TEXTSPLIT_COUNT * #define MAX_TEXTSPLIT_COUNT
* TextSplit() function static substrings pointers array (pointing to static buffer) * TextSplit() function static substrings pointers array (pointing to static buffer)
* *
*
* 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 algorithms, required for font atlas generation * stb_rect_pack - Rectangles packing algorithms, required for font atlas generation
@ -68,12 +71,21 @@
#include <ctype.h> // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()] #include <ctype.h> // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
#if defined(SUPPORT_FILEFORMAT_TTF) #if defined(SUPPORT_FILEFORMAT_TTF)
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#define STB_RECT_PACK_IMPLEMENTATION #define STB_RECT_PACK_IMPLEMENTATION
#include "external/stb_rect_pack.h" // Required for: ttf font rectangles packaging #include "external/stb_rect_pack.h" // Required for: ttf font rectangles packaging
#define STBTT_STATIC #define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION
#include "external/stb_truetype.h" // Required for: ttf font data reading #include "external/stb_truetype.h" // Required for: ttf font data reading
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic pop
#endif
#endif #endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -115,6 +127,7 @@ static Font defaultFont = { 0 };
#if defined(SUPPORT_FILEFORMAT_FNT) #if defined(SUPPORT_FILEFORMAT_FNT)
static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
#endif #endif
static int textLineSpacing = 15; // Text vertical line spacing in pixels
#if defined(SUPPORT_DEFAULT_FONT) #if defined(SUPPORT_DEFAULT_FONT)
extern void LoadFontDefault(void); extern void LoadFontDefault(void);
@ -125,7 +138,6 @@ extern void UnloadFontDefault(void);
// Module Functions Definition // Module Functions Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if defined(SUPPORT_DEFAULT_FONT) #if defined(SUPPORT_DEFAULT_FONT)
// Load raylib default font // Load raylib default font
extern void LoadFontDefault(void) extern void LoadFontDefault(void)
{ {
@ -346,18 +358,18 @@ Font LoadFont(const char *fileName)
// Load Font from TTF font file with generation parameters // Load Font from TTF font file with generation parameters
// NOTE: You can pass an array with desired characters, those characters should be available in the font // NOTE: You can pass an array with desired characters, those characters should be available in the font
// if array is NULL, default char set is selected 32..126 // if array is NULL, default char set is selected 32..126
Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount) Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount)
{ {
Font font = { 0 }; Font font = { 0 };
// Loading file to memory // Loading file to memory
unsigned int fileSize = 0; int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &fileSize); unsigned char *fileData = LoadFileData(fileName, &dataSize);
if (fileData != NULL) if (fileData != NULL)
{ {
// Loading font from memory data // Loading font from memory data
font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, fontChars, glyphCount); font = LoadFontFromMemory(GetFileExtension(fileName), fileData, dataSize, fontSize, codepoints, codepointCount);
UnloadFileData(fileData); UnloadFileData(fileData);
} }
@ -492,7 +504,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
} }
// Load font from memory buffer, fileType refers to extension: i.e. ".ttf" // Load font from memory buffer, fileType refers to extension: i.e. ".ttf"
Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount) Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount)
{ {
Font font = { 0 }; Font font = { 0 };
@ -504,9 +516,9 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
TextIsEqual(fileExtLower, ".otf")) TextIsEqual(fileExtLower, ".otf"))
{ {
font.baseSize = fontSize; font.baseSize = fontSize;
font.glyphCount = (glyphCount > 0)? glyphCount : 95; font.glyphCount = (codepointCount > 0)? codepointCount : 95;
font.glyphPadding = 0; font.glyphPadding = 0;
font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, fontChars, font.glyphCount, FONT_DEFAULT); font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT);
if (font.glyphs != NULL) if (font.glyphs != NULL)
{ {
@ -550,7 +562,7 @@ bool IsFontReady(Font font)
// Load font data for further use // Load font data for further use
// NOTE: Requires TTF font memory data and can generate SDF data // NOTE: Requires TTF font memory data and can generate SDF data
GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type) GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type)
{ {
// NOTE: Using some SDF generation default values, // NOTE: Using some SDF generation default values,
// trades off precision with ability to handle *smaller* sizes // trades off precision with ability to handle *smaller* sizes
@ -588,25 +600,25 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap); stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
// In case no chars count provided, default to 95 // In case no chars count provided, default to 95
glyphCount = (glyphCount > 0)? glyphCount : 95; codepointCount = (codepointCount > 0)? codepointCount : 95;
// Fill fontChars in case not provided externally // Fill fontChars in case not provided externally
// NOTE: By default we fill glyphCount consecutively, starting at 32 (Space) // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
if (fontChars == NULL) if (codepoints == NULL)
{ {
fontChars = (int *)RL_MALLOC(glyphCount*sizeof(int)); codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
for (int i = 0; i < glyphCount; i++) fontChars[i] = i + 32; for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32;
genFontChars = true; genFontChars = true;
} }
chars = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo)); chars = (GlyphInfo *)RL_MALLOC(codepointCount*sizeof(GlyphInfo));
// NOTE: Using simple packaging, one char after another // NOTE: Using simple packaging, one char after another
for (int i = 0; i < glyphCount; i++) for (int i = 0; i < codepointCount; i++)
{ {
int chw = 0, chh = 0; // Character width and height (on generation) int chw = 0, chh = 0; // Character width and height (on generation)
int ch = fontChars[i]; // Character value to get info for int ch = codepoints[i]; // Character value to get info for
chars[i].value = ch; chars[i].value = ch;
// Render a unicode codepoint to a bitmap // Render a unicode codepoint to a bitmap
@ -666,7 +678,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
} }
else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data"); else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data");
if (genFontChars) RL_FREE(fontChars); if (genFontChars) RL_FREE(codepoints);
} }
#endif #endif
@ -676,17 +688,17 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
// Generate image font atlas using chars info // Generate image font atlas using chars info
// NOTE: Packing method: 0-Default, 1-Skyline // NOTE: Packing method: 0-Default, 1-Skyline
#if defined(SUPPORT_FILEFORMAT_TTF) #if defined(SUPPORT_FILEFORMAT_TTF)
Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphCount, int fontSize, int padding, int packMethod) Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod)
{ {
Image atlas = { 0 }; Image atlas = { 0 };
if (chars == NULL) if (glyphs == NULL)
{ {
TRACELOG(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas"); TRACELOG(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas");
return atlas; return atlas;
} }
*charRecs = NULL; *glyphRecs = NULL;
// In case no chars count provided we suppose default of 95 // In case no chars count provided we suppose default of 95
glyphCount = (glyphCount > 0)? glyphCount : 95; glyphCount = (glyphCount > 0)? glyphCount : 95;
@ -694,17 +706,48 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// NOTE: Rectangles memory is loaded here! // NOTE: Rectangles memory is loaded here!
Rectangle *recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle)); Rectangle *recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle));
// Calculate image size based on required pixel area // Calculate image size based on total glyph width and glyph row count
// NOTE 1: Image is forced to be squared and POT... very conservative! int totalWidth = 0;
// NOTE 2: SDF font characters already contain an internal padding, int maxGlyphWidth = 0;
// so image size would result bigger than default font type
float requiredArea = 0; for (int i = 0; i < glyphCount; i++)
for (int i = 0; i < glyphCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(fontSize + 2*padding)); {
float guessSize = sqrtf(requiredArea)*1.4f; if (glyphs[i].image.width > maxGlyphWidth) maxGlyphWidth = glyphs[i].image.width;
int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT totalWidth += glyphs[i].image.width + 4*padding;
}
//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE
#if defined(SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE)
int rowCount = 0;
int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images
// NOTE: maxGlyphWidth is maximum possible space left at the end of row
while (totalWidth > (imageSize - maxGlyphWidth)*rowCount)
{
imageSize *= 2; // Double the size of image (to keep POT)
rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size
}
atlas.width = imageSize; // Atlas bitmap width atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize; // Atlas bitmap height atlas.height = imageSize; // Atlas bitmap height
#else
// No need for a so-conservative atlas generation
float totalArea = totalWidth*fontSize*1.2f;
float imageMinSize = sqrtf(totalArea);
int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2)));
if (totalArea < ((imageSize*imageSize)/2))
{
atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize/2; // Atlas bitmap height
}
else
{
atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize; // Atlas bitmap height
}
#endif
atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp)
atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
atlas.mipmaps = 1; atlas.mipmaps = 1;
@ -720,25 +763,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// NOTE: Using simple packaging, one char after another // NOTE: Using simple packaging, one char after another
for (int i = 0; i < glyphCount; i++) for (int i = 0; i < glyphCount; i++)
{ {
// Copy pixel data from fc.data to atlas // Check remaining space for glyph
for (int y = 0; y < chars[i].image.height; y++) if (offsetX >= (atlas.width - glyphs[i].image.width - 2*padding))
{
for (int x = 0; x < chars[i].image.width; x++)
{
((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x];
}
}
// Fill chars rectangles in atlas info
recs[i].x = (float)offsetX;
recs[i].y = (float)offsetY;
recs[i].width = (float)chars[i].image.width;
recs[i].height = (float)chars[i].image.height;
// Move atlas position X for next character drawing
offsetX += (chars[i].image.width + 2*padding);
if (offsetX >= (atlas.width - chars[i].image.width - 2*padding))
{ {
offsetX = padding; offsetX = padding;
@ -761,6 +787,24 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
break; break;
} }
} }
// Copy pixel data from glyph image to atlas
for (int y = 0; y < glyphs[i].image.height; y++)
{
for (int x = 0; x < glyphs[i].image.width; x++)
{
((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x];
}
}
// Fill chars rectangles in atlas info
recs[i].x = (float)offsetX;
recs[i].y = (float)offsetY;
recs[i].width = (float)glyphs[i].image.width;
recs[i].height = (float)glyphs[i].image.height;
// Move atlas position X for next character drawing
offsetX += (glyphs[i].image.width + 2*padding);
} }
} }
else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect) else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect)
@ -775,8 +819,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
for (int i = 0; i < glyphCount; i++) for (int i = 0; i < glyphCount; i++)
{ {
rects[i].id = i; rects[i].id = i;
rects[i].w = chars[i].image.width + 2*padding; rects[i].w = glyphs[i].image.width + 2*padding;
rects[i].h = chars[i].image.height + 2*padding; rects[i].h = glyphs[i].image.height + 2*padding;
} }
// Package rectangles into atlas // Package rectangles into atlas
@ -787,17 +831,17 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// It returns char rectangles in atlas // It returns char rectangles in atlas
recs[i].x = rects[i].x + (float)padding; recs[i].x = rects[i].x + (float)padding;
recs[i].y = rects[i].y + (float)padding; recs[i].y = rects[i].y + (float)padding;
recs[i].width = (float)chars[i].image.width; recs[i].width = (float)glyphs[i].image.width;
recs[i].height = (float)chars[i].image.height; recs[i].height = (float)glyphs[i].image.height;
if (rects[i].was_packed) if (rects[i].was_packed)
{ {
// Copy pixel data from fc.data to atlas // Copy pixel data from fc.data to atlas
for (int y = 0; y < chars[i].image.height; y++) for (int y = 0; y < glyphs[i].image.height; y++)
{ {
for (int x = 0; x < chars[i].image.width; x++) for (int x = 0; x < glyphs[i].image.width; x++)
{ {
((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x]; ((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x];
} }
} }
} }
@ -809,6 +853,19 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
RL_FREE(context); RL_FREE(context);
} }
#if defined(SUPPORT_FONT_ATLAS_WHITE_REC)
// Add a 3x3 white rectangle at the bottom-right corner of the generated atlas,
// useful to use as the white texture to draw shapes with raylib, using this rectangle
// shapes and text can be backed into a single draw call: SetShapesTexture()
for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++)
{
((unsigned char *)atlas.data)[k - 0] = 255;
((unsigned char *)atlas.data)[k - 1] = 255;
((unsigned char *)atlas.data)[k - 2] = 255;
k -= atlas.width;
}
#endif
// Convert image data from GRAYSCALE to GRAY_ALPHA // Convert image data from GRAYSCALE to GRAY_ALPHA
unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels
@ -822,7 +879,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
atlas.data = dataGrayAlpha; atlas.data = dataGrayAlpha;
atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
*charRecs = recs; *glyphRecs = recs;
return atlas; return atlas;
} }
@ -918,7 +975,7 @@ bool ExportFontAsCode(Font font, const char *fileName)
byteCount += sprintf(txtData + byteCount, "static unsigned char fontData_%s[COMPRESSED_DATA_SIZE_FONT_%s] = { ", fileNamePascal, TextToUpper(fileNamePascal)); 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]); 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]); byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", compData[compDataSize - 1]);
MemFree(compData); RL_FREE(compData);
#else #else
// Save font image data (uncompressed) // Save font image data (uncompressed)
byteCount += sprintf(txtData + byteCount, "// Font image pixels data\n"); byteCount += sprintf(txtData + byteCount, "// Font image pixels data\n");
@ -1018,7 +1075,7 @@ void DrawFPS(int posX, int posY)
if ((fps < 30) && (fps >= 15)) color = ORANGE; // Warning FPS if ((fps < 30) && (fps >= 15)) color = ORANGE; // Warning FPS
else if (fps < 15) color = RED; // Low FPS else if (fps < 15) color = RED; // Low FPS
DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, color); DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, color);
} }
// Draw text (using default font) // Draw text (using default font)
@ -1059,15 +1116,10 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
int codepoint = GetCodepointNext(&text[i], &codepointByteCount); int codepoint = GetCodepointNext(&text[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint); int index = GetGlyphIndex(font, codepoint);
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all the bad bytes using the '?' symbol moving one byte
if (codepoint == 0x3f) codepointByteCount = 1;
if (codepoint == '\n') if (codepoint == '\n')
{ {
// NOTE: Fixed line spacing of 1.5 line-height // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
// TODO: Support custom line spacing defined by user textOffsetY += textLineSpacing;
textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor);
textOffsetX = 0.0f; textOffsetX = 0.0f;
} }
else else
@ -1124,22 +1176,21 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz
} }
// Draw multiple character (codepoints) // Draw multiple character (codepoints)
void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint) void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint)
{ {
int textOffsetY = 0; // Offset between lines (on linebreak '\n') int textOffsetY = 0; // Offset between lines (on linebreak '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw float textOffsetX = 0.0f; // Offset X to next character to draw
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
for (int i = 0; i < count; i++) for (int i = 0; i < codepointCount; i++)
{ {
int index = GetGlyphIndex(font, codepoints[i]); int index = GetGlyphIndex(font, codepoints[i]);
if (codepoints[i] == '\n') if (codepoints[i] == '\n')
{ {
// NOTE: Fixed line spacing of 1.5 line-height // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
// TODO: Support custom line spacing defined by user textOffsetY += textLineSpacing;
textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor);
textOffsetX = 0.0f; textOffsetX = 0.0f;
} }
else else
@ -1155,6 +1206,12 @@ void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 pos
} }
} }
// Set vertical line spacing when drawing with line-breaks
void SetTextLineSpacing(int spacing)
{
textLineSpacing = 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)
{ {
@ -1193,7 +1250,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
int letter = 0; // Current character int letter = 0; // Current character
int index = 0; // Index position in sprite font int index = 0; // Index position in sprite font
for (int i = 0; i < size; i++) for (int i = 0; i < size;)
{ {
byteCounter++; byteCounter++;
@ -1201,10 +1258,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
letter = GetCodepointNext(&text[i], &next); letter = GetCodepointNext(&text[i], &next);
index = GetGlyphIndex(font, letter); index = GetGlyphIndex(font, letter);
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) i += next;
// but we need to draw all the bad bytes using the '?' symbol so to not skip any we set next = 1
if (letter == 0x3f) next = 1;
i += next - 1;
if (letter != '\n') if (letter != '\n')
{ {
@ -1216,7 +1270,9 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
if (tempTextWidth < textWidth) tempTextWidth = textWidth; if (tempTextWidth < textWidth) tempTextWidth = textWidth;
byteCounter = 0; byteCounter = 0;
textWidth = 0; textWidth = 0;
textHeight += ((float)font.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines
// NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
textHeight += (float)textLineSpacing;
} }
if (tempByteCounter < byteCounter) tempByteCounter = byteCounter; if (tempByteCounter < byteCounter) tempByteCounter = byteCounter;
@ -1224,7 +1280,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
if (tempTextWidth < textWidth) tempTextWidth = textWidth; if (tempTextWidth < textWidth) tempTextWidth = textWidth;
textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing);
textSize.y = textHeight*scaleFactor; textSize.y = textHeight*scaleFactor;
return textSize; return textSize;
@ -1234,17 +1290,17 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
// NOTE: If codepoint is not found in the font it fallbacks to '?' // NOTE: If codepoint is not found in the font it fallbacks to '?'
int GetGlyphIndex(Font font, int codepoint) int GetGlyphIndex(Font font, int codepoint)
{ {
#ifndef GLYPH_NOTFOUND_CHAR_FALLBACK int index = 0;
#define GLYPH_NOTFOUND_CHAR_FALLBACK 63 // Character used if requested codepoint is not found: '?'
#endif
// Support charsets with any characters order
#define SUPPORT_UNORDERED_CHARSET #define SUPPORT_UNORDERED_CHARSET
#if defined(SUPPORT_UNORDERED_CHARSET) #if defined(SUPPORT_UNORDERED_CHARSET)
int index = GLYPH_NOTFOUND_CHAR_FALLBACK; int fallbackIndex = 0; // Get index of fallback glyph '?'
// Look for character index in the unordered charset
for (int i = 0; i < font.glyphCount; i++) for (int i = 0; i < font.glyphCount; i++)
{ {
if (font.glyphs[i].value == 63) fallbackIndex = i;
if (font.glyphs[i].value == codepoint) if (font.glyphs[i].value == codepoint)
{ {
index = i; index = i;
@ -1252,10 +1308,12 @@ int GetGlyphIndex(Font font, int codepoint)
} }
} }
return index; if ((index == 0) && (font.glyphs[0].value != codepoint)) index = fallbackIndex;
#else #else
return (codepoint - 32); index = codepoint - 32;
#endif #endif
return index;
} }
// Get glyph font info data for a codepoint (unicode character) // Get glyph font info data for a codepoint (unicode character)
@ -1286,10 +1344,12 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint)
// Get text length in bytes, check for \0 character // Get text length in bytes, check for \0 character
unsigned int TextLength(const char *text) unsigned int TextLength(const char *text)
{ {
unsigned int length = 0; //strlen(text) unsigned int length = 0;
if (text != NULL) if (text != NULL)
{ {
// NOTE: Alternative: use strlen(text)
while (*text++) length++; while (*text++) length++;
} }
@ -1313,15 +1373,24 @@ const char *TextFormat(const char *text, ...)
va_list args; va_list args;
va_start(args, text); va_start(args, text);
vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
va_end(args); va_end(args);
// If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured
if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
{
// Inserting "..." at the end of the string to mark as truncated
char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
sprintf(truncBuffer, "...");
}
index += 1; // Move to next buffer for next function call index += 1; // Move to next buffer for next function call
if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
return currentBuffer; return currentBuffer;
} }
// Get integer value from text // Get integer value from text
// NOTE: This function replaces atoi() [stdlib.h] // NOTE: This function replaces atoi() [stdlib.h]
int TextToInteger(const char *text) int TextToInteger(const char *text)
@ -1335,7 +1404,7 @@ int TextToInteger(const char *text)
text++; text++;
} }
for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0'); for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
return value*sign; return value*sign;
} }
@ -1348,6 +1417,8 @@ int TextCopy(char *dst, const char *src)
if ((src != NULL) && (dst != NULL)) if ((src != NULL) && (dst != NULL))
{ {
// NOTE: Alternative: use strcpy(dst, src)
while (*src != '\0') while (*src != '\0')
{ {
*dst = *src; *dst = *src;
@ -1393,6 +1464,8 @@ const char *TextSubtext(const char *text, int position, int length)
if (length >= textLength) length = textLength; if (length >= textLength) length = textLength;
// NOTE: Alternative: memcpy(buffer, text + position, length)
for (int c = 0 ; c < length ; c++) for (int c = 0 ; c < length ; c++)
{ {
*(buffer + c) = *(text + position); *(buffer + c) = *(text + position);
@ -1569,7 +1642,8 @@ int TextFindIndex(const char *text, const char *find)
} }
// Get upper case version of provided string // Get upper case version of provided string
// REQUIRES: toupper() // WARNING: Limited functionality, only basic characters set
// TODO: Support UTF-8 diacritics to upper-case, check codepoints
const char *TextToUpper(const char *text) const char *TextToUpper(const char *text)
{ {
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
@ -1577,17 +1651,10 @@ const char *TextToUpper(const char *text)
if (text != NULL) if (text != NULL)
{ {
for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++)
{ {
if (text[i] != '\0') if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32;
{ else buffer[i] = text[i];
buffer[i] = (char)toupper(text[i]);
//if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32;
// TODO: Support UTF-8 diacritics to upper-case
//if ((text[i] >= 'à') && (text[i] <= 'ý')) buffer[i] = text[i] - 32;
}
else { buffer[i] = '\0'; break; }
} }
} }
@ -1595,7 +1662,7 @@ const char *TextToUpper(const char *text)
} }
// Get lower case version of provided string // Get lower case version of provided string
// REQUIRES: tolower() // WARNING: Limited functionality, only basic characters set
const char *TextToLower(const char *text) const char *TextToLower(const char *text)
{ {
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
@ -1603,14 +1670,10 @@ const char *TextToLower(const char *text)
if (text != NULL) if (text != NULL)
{ {
for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++)
{ {
if (text[i] != '\0') if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32;
{ else buffer[i] = text[i];
buffer[i] = (char)tolower(text[i]);
//if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32;
}
else { buffer[i] = '\0'; break; }
} }
} }
@ -1618,7 +1681,7 @@ const char *TextToLower(const char *text)
} }
// Get Pascal case notation version of provided string // Get Pascal case notation version of provided string
// REQUIRES: toupper() // WARNING: Limited functionality, only basic characters set
const char *TextToPascal(const char *text) const char *TextToPascal(const char *text)
{ {
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
@ -1626,21 +1689,20 @@ const char *TextToPascal(const char *text)
if (text != NULL) if (text != NULL)
{ {
buffer[0] = (char)toupper(text[0]); // Upper case first character
if ((text[0] >= 'a') && (text[0] <= 'z')) buffer[0] = text[0] - 32;
else buffer[0] = text[0];
for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++) // Check for next separator to upper case another character
{ for (int i = 1, j = 1; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++, j++)
if (text[j] != '\0')
{ {
if (text[j] != '_') buffer[i] = text[j]; if (text[j] != '_') buffer[i] = text[j];
else else
{ {
j++; j++;
buffer[i] = (char)toupper(text[j]); if ((text[j] >= 'a') && (text[j] <= 'z')) buffer[i] = text[j] - 32;
} }
} }
else { buffer[i] = '\0'; break; }
}
} }
return buffer; return buffer;
@ -1651,7 +1713,7 @@ const char *TextToPascal(const char *text)
// WARNING: Allocated memory must be manually freed // WARNING: Allocated memory must be manually freed
char *LoadUTF8(const int *codepoints, int length) char *LoadUTF8(const int *codepoints, int length)
{ {
// We allocate enough memory fo fit all possible codepoints // We allocate enough memory to fit all possible codepoints
// NOTE: 5 bytes for every codepoint should be enough // NOTE: 5 bytes for every codepoint should be enough
char *text = (char *)RL_CALLOC(length*5, 1); char *text = (char *)RL_CALLOC(length*5, 1);
const char *utf8 = NULL; const char *utf8 = NULL;
@ -1720,10 +1782,9 @@ int GetCodepointCount(const char *text)
while (*ptr != '\0') while (*ptr != '\0')
{ {
int next = 0; int next = 0;
int letter = GetCodepointNext(ptr, &next); GetCodepointNext(ptr, &next);
if (letter == 0x3f) ptr += 1; ptr += next;
else ptr += next;
length++; length++;
} }
@ -1884,28 +1945,31 @@ int GetCodepointNext(const char *text, int *codepointSize)
{ {
const char *ptr = text; const char *ptr = text;
int codepoint = 0x3f; // Codepoint (defaults to '?') int codepoint = 0x3f; // Codepoint (defaults to '?')
*codepointSize = 0; *codepointSize = 1;
// Get current codepoint and bytes processed // Get current codepoint and bytes processed
if (0xf0 == (0xf8 & ptr[0])) if (0xf0 == (0xf8 & ptr[0]))
{ {
// 4 byte UTF-8 codepoint // 4 byte UTF-8 codepoint
if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]); codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
*codepointSize = 4; *codepointSize = 4;
} }
else if (0xe0 == (0xf0 & ptr[0])) else if (0xe0 == (0xf0 & ptr[0]))
{ {
// 3 byte UTF-8 codepoint */ // 3 byte UTF-8 codepoint */
if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]); codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
*codepointSize = 3; *codepointSize = 3;
} }
else if (0xc0 == (0xe0 & ptr[0])) else if (0xc0 == (0xe0 & ptr[0]))
{ {
// 2 byte UTF-8 codepoint // 2 byte UTF-8 codepoint
if((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks
codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
*codepointSize = 2; *codepointSize = 2;
} }
else else if (0x00 == (0x80 & ptr[0]))
{ {
// 1 byte UTF-8 codepoint // 1 byte UTF-8 codepoint
codepoint = ptr[0]; codepoint = ptr[0];
@ -1938,7 +2002,6 @@ int GetCodepointPrevious(const char *text, int *codepointSize)
// Module specific Functions Definition // Module specific Functions Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if defined(SUPPORT_FILEFORMAT_FNT) #if defined(SUPPORT_FILEFORMAT_FNT)
// Read a line from memory // Read a line from memory
// REQUIRES: memcpy() // REQUIRES: memcpy()
// NOTE: Returns the number of bytes read // NOTE: Returns the number of bytes read
@ -1969,6 +2032,8 @@ static Font LoadBMFont(const char *fileName)
char imFileName[129] = { 0 }; char imFileName[129] = { 0 };
int base = 0; // Useless data int base = 0; // Useless data
int readBytes = 0; // Data bytes read
int readVars = 0; // Variables filled by sscanf()
char *fileText = LoadFileText(fileName); char *fileText = LoadFileText(fileName);
@ -1977,32 +2042,30 @@ static Font LoadBMFont(const char *fileName)
char *fileTextPtr = fileText; char *fileTextPtr = fileText;
// NOTE: We skip first line, it contains no useful information // NOTE: We skip first line, it contains no useful information
int lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
fileTextPtr += (lineBytes + 1); fileTextPtr += (readBytes + 1);
// Read line data // Read line data
lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "lineHeight"); searchPoint = strstr(buffer, "lineHeight");
sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight); readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight);
fileTextPtr += (lineBytes + 1); fileTextPtr += (readBytes + 1);
TRACELOGD("FONT: [%s] Loaded font info:", fileName); if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed
TRACELOGD(" > Base size: %i", fontSize);
TRACELOGD(" > Texture scale: %ix%i", imWidth, imHeight);
lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "file"); searchPoint = strstr(buffer, "file");
sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName); readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
fileTextPtr += (lineBytes + 1); fileTextPtr += (readBytes + 1);
TRACELOGD(" > Texture filename: %s", imFileName); if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read
lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "count"); searchPoint = strstr(buffer, "count");
sscanf(searchPoint, "count=%i", &glyphCount); readVars = sscanf(searchPoint, "count=%i", &glyphCount);
fileTextPtr += (lineBytes + 1); fileTextPtr += (readBytes + 1);
TRACELOGD(" > Chars count: %i", glyphCount); if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read
// Compose correct path using route of .fnt file (fileName) and imFileName // Compose correct path using route of .fnt file (fileName) and imFileName
char *imPath = NULL; char *imPath = NULL;
@ -2060,11 +2123,13 @@ static Font LoadBMFont(const char *fileName)
for (int i = 0; i < glyphCount; i++) for (int i = 0; i < glyphCount; i++)
{ {
lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i",
&charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
fileTextPtr += (lineBytes + 1); fileTextPtr += (readBytes + 1);
if (readVars == 8) // Make sure all char data has been properly read
{
// Get character rectangle in the font atlas texture // Get character rectangle in the font atlas texture
font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight };
@ -2077,6 +2142,8 @@ static Font LoadBMFont(const char *fileName)
// Fill character image data from imFont data // Fill character image data from imFont data
font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]); font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]);
} }
else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName);
}
UnloadImage(imFont); UnloadImage(imFont);
UnloadFileText(fileText); UnloadFileText(fileText);

File diff suppressed because it is too large Load diff

View file

@ -575,30 +575,6 @@ func GenImageColor(width, height int, col color.RGBA) *Image {
return v return v
} }
// GenImageGradientV - Generate image: vertical gradient
func GenImageGradientV(width, height int, top, bottom color.RGBA) *Image {
cwidth := (C.int)(width)
cheight := (C.int)(height)
ctop := colorCptr(top)
cbottom := colorCptr(bottom)
ret := C.GenImageGradientV(cwidth, cheight, *ctop, *cbottom)
v := newImageFromPointer(unsafe.Pointer(&ret))
return v
}
// GenImageGradientH - Generate image: horizontal gradient
func GenImageGradientH(width, height int, left, right color.RGBA) *Image {
cwidth := (C.int)(width)
cheight := (C.int)(height)
cleft := colorCptr(left)
cright := colorCptr(right)
ret := C.GenImageGradientH(cwidth, cheight, *cleft, *cright)
v := newImageFromPointer(unsafe.Pointer(&ret))
return v
}
// GenImageGradientRadial - Generate image: radial gradient // GenImageGradientRadial - Generate image: radial gradient
func GenImageGradientRadial(width, height int, density float32, inner, outer color.RGBA) *Image { func GenImageGradientRadial(width, height int, density float32, inner, outer color.RGBA) *Image {
cwidth := (C.int)(width) cwidth := (C.int)(width)

View file

@ -3,7 +3,6 @@
* raylib.utils - Some common utility functions * raylib.utils - Some common utility functions
* *
* CONFIGURATION: * CONFIGURATION:
*
* #define SUPPORT_TRACELOG * #define SUPPORT_TRACELOG
* Show TraceLog() output messages * Show TraceLog() output messages
* NOTE: By default LOG_DEBUG traces not shown * NOTE: By default LOG_DEBUG traces not shown
@ -145,7 +144,7 @@ void TraceLog(int logType, const char *text, ...)
default: break; default: break;
} }
unsigned int textSize = strlen(text); unsigned int textSize = (unsigned int)strlen(text);
memcpy(buffer + strlen(buffer), text, (textSize < (MAX_TRACELOG_MSG_LENGTH - 12))? textSize : (MAX_TRACELOG_MSG_LENGTH - 12)); memcpy(buffer + strlen(buffer), text, (textSize < (MAX_TRACELOG_MSG_LENGTH - 12))? textSize : (MAX_TRACELOG_MSG_LENGTH - 12));
strcat(buffer, "\n"); strcat(buffer, "\n");
vprintf(buffer, args); vprintf(buffer, args);
@ -181,16 +180,16 @@ void MemFree(void *ptr)
} }
// Load data from file into a buffer // Load data from file into a buffer
unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) unsigned char *LoadFileData(const char *fileName, int *dataSize)
{ {
unsigned char *data = NULL; unsigned char *data = NULL;
*bytesRead = 0; *dataSize = 0;
if (fileName != NULL) if (fileName != NULL)
{ {
if (loadFileData) if (loadFileData)
{ {
data = loadFileData(fileName, bytesRead); data = loadFileData(fileName, dataSize);
return data; return data;
} }
#if defined(SUPPORT_STANDARD_FILEIO) #if defined(SUPPORT_STANDARD_FILEIO)
@ -201,20 +200,37 @@ unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead)
// WARNING: On binary streams SEEK_END could not be found, // WARNING: On binary streams SEEK_END could not be found,
// using fseek() and ftell() could not work in some (rare) cases // using fseek() and ftell() could not work in some (rare) cases
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
int size = ftell(file); int size = ftell(file); // WARNING: ftell() returns 'long int', maximum size returned is INT_MAX (2147483647 bytes)
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
if (size > 0) if (size > 0)
{ {
data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char)); data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char));
if (data != NULL)
{
// NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements]
unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); size_t count = fread(data, sizeof(unsigned char), size, file);
*bytesRead = count;
if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); // WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation)
// dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation
if (count > 2147483647)
{
TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName);
RL_FREE(data);
data = NULL;
}
else
{
*dataSize = (int)count;
if ((*dataSize) != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded (%i bytes out of %i)", fileName, dataSize, count);
else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName);
} }
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName);
fclose(file); fclose(file);
@ -236,7 +252,7 @@ void UnloadFileData(unsigned char *data)
} }
// Save data to file from buffer // Save data to file from buffer
bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) bool SaveFileData(const char *fileName, void *data, int dataSize)
{ {
bool success = false; bool success = false;
@ -244,17 +260,19 @@ bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite)
{ {
if (saveFileData) if (saveFileData)
{ {
return saveFileData(fileName, data, bytesToWrite); return saveFileData(fileName, data, dataSize);
} }
#if defined(SUPPORT_STANDARD_FILEIO) #if defined(SUPPORT_STANDARD_FILEIO)
FILE *file = fopen(fileName, "wb"); FILE *file = fopen(fileName, "wb");
if (file != NULL) if (file != NULL)
{ {
unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), bytesToWrite, file); // WARNING: fwrite() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation)
// and expects a size_t input value but as dataSize is limited to INT_MAX (2147483647 bytes), there shouldn't be a problem
int count = (int)fwrite(data, sizeof(unsigned char), dataSize, file);
if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName);
else if (count != bytesToWrite) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName);
else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName);
int result = fclose(file); int result = fclose(file);
@ -271,7 +289,7 @@ bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite)
} }
// Export data to code (.h), returns true on success // Export data to code (.h), returns true on success
bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName) bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName)
{ {
bool success = false; bool success = false;
@ -281,7 +299,7 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *
// NOTE: Text data buffer size is estimated considering raw data size in bytes // NOTE: Text data buffer size is estimated considering raw data size in bytes
// and requiring 6 char bytes for every byte: "0x00, " // and requiring 6 char bytes for every byte: "0x00, "
char *txtData = (char *)RL_CALLOC(size*6 + 2000, sizeof(char)); char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char));
int byteCount = 0; int byteCount = 0;
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n"); byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
@ -300,9 +318,11 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *
strcpy(varFileName, GetFileNameWithoutExt(fileName)); strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } 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); byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, dataSize);
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]); byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName);
for (int i = 0; i < (dataSize - 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[dataSize - 1]);
// NOTE: Text data size exported is determined by '\0' (NULL) character // NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData); success = SaveFileText(fileName, txtData);
@ -343,6 +363,9 @@ char *LoadFileText(const char *fileName)
if (size > 0) if (size > 0)
{ {
text = (char *)RL_MALLOC((size + 1)*sizeof(char)); text = (char *)RL_MALLOC((size + 1)*sizeof(char));
if (text != NULL)
{
unsigned int count = (unsigned int)fread(text, sizeof(char), size, file); unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
// WARNING: \r\n is converted to \n on reading, so, // WARNING: \r\n is converted to \n on reading, so,
@ -354,6 +377,8 @@ char *LoadFileText(const char *fileName)
TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName); TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName);
} }
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName);
fclose(file); fclose(file);
@ -455,12 +480,12 @@ FILE *android_fopen(const char *fileName, const char *mode)
// Module specific Functions Definition // Module specific Functions Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if defined(PLATFORM_ANDROID) #if defined(PLATFORM_ANDROID)
static int android_read(void *cookie, char *buf, int size) static int android_read(void *cookie, char *data, int dataSize)
{ {
return AAsset_read((AAsset *)cookie, buf, size); return AAsset_read((AAsset *)cookie, data, dataSize);
} }
static int android_write(void *cookie, const char *buf, int size) static int android_write(void *cookie, const char *data, int dataSize)
{ {
TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK"); TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK");

View file

@ -1,6 +1,6 @@
/********************************************************************************************** /**********************************************************************************************
* *
* raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * raylib v4.6-dev - 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
@ -81,10 +81,10 @@
#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_MAJOR 4 #define RAYLIB_VERSION_MAJOR 5
#define RAYLIB_VERSION_MINOR 5 #define RAYLIB_VERSION_MINOR 0
#define RAYLIB_VERSION_PATCH 0 #define RAYLIB_VERSION_PATCH 0
#define RAYLIB_VERSION "4.5" #define RAYLIB_VERSION "5.0"
// 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
@ -133,12 +133,20 @@
// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) // NOTE: MSVC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized with { } // Plain structures in C++ (without constructors) can be initialized with { }
// This is called aggregate initialization (C++11 feature)
#if defined(__cplusplus) #if defined(__cplusplus)
#define CLITERAL(type) type #define CLITERAL(type) type
#else #else
#define CLITERAL(type) (type) #define CLITERAL(type) (type)
#endif #endif
// Some compilers (mostly macos clang) default to C++98,
// where aggregate initialization can't be used
// So, give a more clear error stating how to fix this
#if !defined(_MSC_VER) && (defined(__cplusplus) && __cplusplus < 201103L)
#error "C++11 or later is required. Add -std=c++11"
#endif
// NOTE: We set some defines with some data types declared by raylib // NOTE: We set some defines with some data types declared by raylib
// Other modules (raymath, rlgl) also require some of those types, so, // Other modules (raymath, rlgl) also require some of those types, so,
// to be able to use those other modules as standalone (not depending on raylib) // to be able to use those other modules as standalone (not depending on raylib)
@ -402,6 +410,7 @@ typedef struct ModelAnimation {
int frameCount; // Number of animation frames int frameCount; // Number of animation frames
BoneInfo *bones; // Bones information (skeleton) BoneInfo *bones; // Bones information (skeleton)
Transform **framePoses; // Poses array by frame Transform **framePoses; // Poses array by frame
char name[32]; // Animation name
} ModelAnimation; } ModelAnimation;
// Ray, ray for raycasting // Ray, ray for raycasting
@ -497,6 +506,20 @@ typedef struct FilePathList {
char **paths; // Filepaths entries char **paths; // Filepaths entries
} FilePathList; } FilePathList;
// Automation event
typedef struct AutomationEvent {
unsigned int frame; // Event frame
unsigned int type; // Event type (AutomationEventType)
int params[4]; // Event parameters (if required)
} AutomationEvent;
// Automation event list
typedef struct AutomationEventList {
unsigned int capacity; // Events max entries (MAX_AUTOMATION_EVENTS)
unsigned int count; // Events entries count
AutomationEvent *events; // Events entries
} AutomationEventList;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Enumerators Definition // Enumerators Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -517,6 +540,7 @@ typedef enum {
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_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED
FLAG_BORDERLESS_WINDOWED_MODE = 0x00008000, // Set to run program in borderless windowed mode
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;
@ -802,6 +826,9 @@ typedef enum {
PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float)
PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float)
PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float)
PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float)
PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float)
PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float)
PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha)
PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha)
PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp
@ -905,8 +932,8 @@ typedef enum {
// Callbacks to hook some internal functions // Callbacks to hook some internal functions
// WARNING: These callbacks are intended for advance users // WARNING: These callbacks are intended for advance users
typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages
typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load binary data
typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize); // FileIO: Save binary data
typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data
typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data
@ -925,8 +952,8 @@ extern "C" { // Prevents name mangling of functions
// Window-related functions // Window-related functions
RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context
RLAPI bool WindowShouldClose(void); // Check if KEY_ESCAPE pressed or Close icon pressed
RLAPI void CloseWindow(void); // Close window and unload OpenGL context RLAPI void CloseWindow(void); // Close window and unload OpenGL context
RLAPI bool WindowShouldClose(void); // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked)
RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully
RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen
RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP) RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP)
@ -938,17 +965,20 @@ RLAPI bool IsWindowState(unsigned int flag); // Check if on
RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP) 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 ToggleBorderlessWindowed(void); // Toggle window state: borderless 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)
RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP)
RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP)
RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP)
RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP)
RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP) RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB)
RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP)
RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window
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 SetWindowMaxSize(int width, int height); // Set window maximum 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 SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP)
RLAPI void SetWindowFocused(void); // Set window focused (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
@ -964,20 +994,12 @@ RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specifi
RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate
RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor
RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor
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 specified 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 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 RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling
// Custom frame control functions
// NOTE: Those functions are intended for advance users that want full control over the frame processing
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
// 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 PollInputEvents(void); // Register all input events
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
RLAPI void HideCursor(void); // Hides cursor RLAPI void HideCursor(void); // Hides cursor
@ -1033,24 +1055,37 @@ RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the
// Timing-related functions // Timing-related functions
RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum)
RLAPI int GetFPS(void); // Get current FPS
RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time)
RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow()
RLAPI int GetFPS(void); // Get current FPS
// Custom frame control functions
// NOTE: Those functions are intended for advance users that want full control over the frame processing
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
// 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 PollInputEvents(void); // Register all input events
RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution)
// Random values generation functions
RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator
RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included)
RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated
RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence
// Misc. functions // Misc. functions
RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included)
RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator
RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format)
RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS)
RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available)
// NOTE: Following functions implemented in module [utils]
//------------------------------------------------------------------
RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level
RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator
RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator RLAPI void *MemRealloc(void *ptr, unsigned 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
@ -1060,13 +1095,16 @@ RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom
RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver
// Files management functions // Files management functions
RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) RLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // 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, int dataSize); // Save data to file from byte array (write), returns true on success
RLAPI bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, 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
//------------------------------------------------------------------
// File system functions
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)
@ -1077,7 +1115,7 @@ RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filenam
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 const char *GetApplicationDirectory(void); // Get the directory if the running application (uses static string) RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string)
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 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 LoadDirectoryFiles(const char *dirPath); // Load directory filepaths
@ -1094,18 +1132,29 @@ RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataS
RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree()
RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree()
// Automation events functionality
RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file
RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file
RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to
RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording
RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set)
RLAPI void StopAutomationEventRecording(void); // Stop recording automation events
RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Input Handling Functions (Module: core) // Input Handling Functions (Module: core)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Input-related functions: keyboard // Input-related functions: keyboard
RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once
RLAPI bool IsKeyPressedRepeat(int key); // Check if a key has been pressed again (Only PLATFORM_DESKTOP)
RLAPI bool IsKeyDown(int key); // Check if a key is being pressed RLAPI bool IsKeyDown(int key); // Check if a key is being pressed
RLAPI bool IsKeyReleased(int key); // Check if a key has been released once RLAPI bool IsKeyReleased(int key); // Check if a key has been released once
RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed
RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty
RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty
RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
// Input-related functions: gamepads // Input-related functions: gamepads
RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available
@ -1146,7 +1195,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t
// Gestures and Touch Handling Functions (Module: rgestures) // Gestures and Touch Handling Functions (Module: rgestures)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags
RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected
RLAPI int GetGestureDetected(void); // Get latest detected gesture RLAPI int GetGestureDetected(void); // Get latest detected gesture
RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds
RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector
@ -1177,6 +1226,8 @@ RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color
RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out
RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point
RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points
RLAPI void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color); // Draw a B-Spline line, minimum 4 points
RLAPI void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color); // Draw a Catmull Rom spline line, minimum 4 points
RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence
RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle
RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle
@ -1184,6 +1235,7 @@ RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle,
RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle
RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version)
RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline
RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version)
RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse
RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline
RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring
@ -1227,6 +1279,7 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2);
// NOTE: These functions do not require GPU access // NOTE: These functions do not require GPU access
RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM)
RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data
RLAPI Image LoadImageSvg(const char *fileNameOrString, int width, int height); // Load image from SVG file data or string with specified size
RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data)
RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png'
RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data
@ -1234,13 +1287,14 @@ RLAPI Image LoadImageFromScreen(void);
RLAPI bool IsImageReady(Image image); // Check if an image is ready RLAPI bool IsImageReady(Image image); // Check if an image is ready
RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM)
RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success
RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer
RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success
// Image generation functions // Image generation functions
RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color
RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient
RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient
RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient
RLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer); // Generate image: square gradient
RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked
RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise
RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise
@ -1267,6 +1321,7 @@ RLAPI void ImageMipmaps(Image *image);
RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
RLAPI void ImageFlipVertical(Image *image); // Flip image vertically RLAPI void ImageFlipVertical(Image *image); // Flip image vertically
RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally
RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359)
RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg
RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg
RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint
@ -1351,13 +1406,13 @@ 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, use NULL for fontChars and 0 for glyphCount to load the default character set RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount 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 *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'
RLAPI bool IsFontReady(Font font); // Check if a font is ready RLAPI bool IsFontReady(Font font); // Check if a font is ready
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 *codepoints, int codepointCount, 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 *glyphs, Rectangle **glyphRecs, 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 *glyphs, 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 RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success
@ -1367,9 +1422,10 @@ 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) RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint)
// Text font info functions // Text font info functions
RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks
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
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found
@ -1485,10 +1541,10 @@ RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture
RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh
// Model animations loading/unloading functions // Model animations loading/unloading functions
RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount); // Load model animations from file RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file
RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose
RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data
RLAPI void UnloadModelAnimations(ModelAnimation *animations, unsigned int count); // Unload animation array data RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data
RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match
// Collision detection functions // Collision detection functions
@ -1511,6 +1567,7 @@ RLAPI void InitAudioDevice(void); // Initial
RLAPI void CloseAudioDevice(void); // Close the audio device and context RLAPI void CloseAudioDevice(void); // Close the audio device and context
RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully
RLAPI void SetMasterVolume(float volume); // Set master volume (listener) RLAPI void SetMasterVolume(float volume); // Set master volume (listener)
RLAPI float GetMasterVolume(void); // Get master volume (listener)
// Wave/Sound loading/unloading functions // Wave/Sound loading/unloading functions
RLAPI Wave LoadWave(const char *fileName); // Load wave data from file RLAPI Wave LoadWave(const char *fileName); // Load wave data from file
@ -1518,10 +1575,12 @@ RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileDat
RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready
RLAPI Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSound(const char *fileName); // Load sound from file
RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data
RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data
RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready
RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data
RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadWave(Wave wave); // Unload wave data
RLAPI void UnloadSound(Sound sound); // Unload sound RLAPI void UnloadSound(Sound sound); // Unload sound
RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data)
RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success
RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success
@ -1575,10 +1634,10 @@ RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan
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 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 AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as <float>s
RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream
RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as <float>s
RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline
#if defined(__cplusplus) #if defined(__cplusplus)