From 92e2fbf7e92e3ccc7beae13e77d98b1b029c3915 Mon Sep 17 00:00:00 2001 From: Kaehvaman Date: Tue, 24 Dec 2024 20:06:13 +0400 Subject: [PATCH] =?UTF-8?q?=D0=B8=D0=B3=D1=80=D0=B0=20=D0=B6=D0=B8=D0=B7?= =?UTF-8?q?=D0=BD=D1=8C=20=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Game of Life/Game of Life.sln | 28 + .../Game of Life/Game of Life.vcxproj | 144 + .../Game of Life/Game of Life.vcxproj.filters | 27 + Game of Life/include/raylib.h | 1708 ++++++ Game of Life/include/raymath.h | 2941 +++++++++ Game of Life/include/rlgl.h | 5262 +++++++++++++++++ Game of Life/lib/raylib.lib | Bin 0 -> 5009534 bytes .../resources/Inconsolata-LGC-Bold.ttf | Bin 0 -> 145852 bytes Game of Life/resources/Inconsolata-LGC.ttf | Bin 0 -> 145420 bytes Game of Life/src/main.c | 35 + lab16 with raylib/src/main.c | 66 +- lab18/lab18/lab18.cpp | 776 +-- 12 files changed, 10588 insertions(+), 399 deletions(-) create mode 100644 Game of Life/Game of Life.sln create mode 100644 Game of Life/Game of Life/Game of Life.vcxproj create mode 100644 Game of Life/Game of Life/Game of Life.vcxproj.filters create mode 100644 Game of Life/include/raylib.h create mode 100644 Game of Life/include/raymath.h create mode 100644 Game of Life/include/rlgl.h create mode 100644 Game of Life/lib/raylib.lib create mode 100644 Game of Life/resources/Inconsolata-LGC-Bold.ttf create mode 100644 Game of Life/resources/Inconsolata-LGC.ttf create mode 100644 Game of Life/src/main.c diff --git a/Game of Life/Game of Life.sln b/Game of Life/Game of Life.sln new file mode 100644 index 0000000..4c12974 --- /dev/null +++ b/Game of Life/Game of Life.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35527.113 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Game of Life", "Game of Life\Game of Life.vcxproj", "{743E4B45-9B39-4BC5-939A-C9CF6C0B295B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {743E4B45-9B39-4BC5-939A-C9CF6C0B295B}.Debug|x64.ActiveCfg = Debug|x64 + {743E4B45-9B39-4BC5-939A-C9CF6C0B295B}.Debug|x64.Build.0 = Debug|x64 + {743E4B45-9B39-4BC5-939A-C9CF6C0B295B}.Debug|x86.ActiveCfg = Debug|Win32 + {743E4B45-9B39-4BC5-939A-C9CF6C0B295B}.Debug|x86.Build.0 = Debug|Win32 + {743E4B45-9B39-4BC5-939A-C9CF6C0B295B}.Release|x64.ActiveCfg = Release|x64 + {743E4B45-9B39-4BC5-939A-C9CF6C0B295B}.Release|x64.Build.0 = Release|x64 + {743E4B45-9B39-4BC5-939A-C9CF6C0B295B}.Release|x86.ActiveCfg = Release|Win32 + {743E4B45-9B39-4BC5-939A-C9CF6C0B295B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Game of Life/Game of Life/Game of Life.vcxproj b/Game of Life/Game of Life/Game of Life.vcxproj new file mode 100644 index 0000000..c627802 --- /dev/null +++ b/Game of Life/Game of Life/Game of Life.vcxproj @@ -0,0 +1,144 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {743e4b45-9b39-4bc5-939a-c9cf6c0b295b} + GameofLife + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)\include + + + Console + true + $(SolutionDir)\lib + raylib.lib;winmm.lib;opengl32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)\include + + + Console + true + true + true + $(SolutionDir)\lib + raylib.lib;winmm.lib;opengl32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + \ No newline at end of file diff --git a/Game of Life/Game of Life/Game of Life.vcxproj.filters b/Game of Life/Game of Life/Game of Life.vcxproj.filters new file mode 100644 index 0000000..8abaa75 --- /dev/null +++ b/Game of Life/Game of Life/Game of Life.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Файлы заголовков + + + + + Исходные файлы + + + \ No newline at end of file diff --git a/Game of Life/include/raylib.h b/Game of Life/include/raylib.h new file mode 100644 index 0000000..576e575 --- /dev/null +++ b/Game of Life/include/raylib.h @@ -0,0 +1,1708 @@ +/********************************************************************************************** +* +* raylib v5.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* +* FEATURES: +* - NO external dependencies, all required libraries included with raylib +* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, +* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5. +* - Written in plain C code (C99) in PascalCase/camelCase notation +* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3, ES2, ES3 - choose at compile) +* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] +* - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts) +* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) +* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! +* - Flexible Materials system, supporting classic maps and PBR maps +* - Animated 3D models supported (skeletal bones animation) (IQM, M3D, GLTF) +* - Shaders support, including Model shaders and Postprocessing shaders +* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] +* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, QOA, XM, MOD) +* - VR stereo rendering with configurable HMD device parameters +* - Bindings to multiple programming languages available! +* +* NOTES: +* - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text] +* - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2) +* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2) +* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2) +* +* DEPENDENCIES (included): +* [rcore][GLFW] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input +* [rcore][RGFW] rgfw (ColleagueRiley - github.com/ColleagueRiley/RGFW) for window/context management and input +* [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading +* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management +* +* OPTIONAL DEPENDENCIES (included): +* [rcore] msf_gif (Miles Fogle) for GIF recording +* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm +* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm +* [rcore] rprand (Ramon Snatamaria) for pseudo-random numbers generation +* [rtextures] qoi (Dominic Szablewski - https://phoboslab.org) for QOI image manage +* [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) +* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) +* [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms +* [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation +* [rtext] stb_truetype (Sean Barret) for ttf fonts loading +* [rtext] stb_rect_pack (Sean Barret) for rectangles packing +* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation +* [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) +* [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF) +* [rmodels] m3d (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d) +* [rmodels] vox_loader (Johann Nadalutti) for models loading (VOX) +* [raudio] dr_wav (David Reid) for WAV audio file loading +* [raudio] dr_flac (David Reid) for FLAC audio file loading +* [raudio] dr_mp3 (David Reid) for MP3 audio file loading +* [raudio] stb_vorbis (Sean Barret) for OGG audio loading +* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading +* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading +* [raudio] qoa (Dominic Szablewski - https://phoboslab.org) for QOA audio manage +* +* +* LICENSE: zlib/libpng +* +* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software: +* +* Copyright (c) 2013-2024 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 RAYLIB_H +#define RAYLIB_H + +#include // Required for: va_list - Only used by TraceLogCallback + +#define RAYLIB_VERSION_MAJOR 5 +#define RAYLIB_VERSION_MINOR 5 +#define RAYLIB_VERSION_PATCH 0 +#define RAYLIB_VERSION "5.5" + +// Function specifiers in case library is build/used as a shared library +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +// NOTE: visibility("default") attribute makes symbols "visible" when compiled with -fvisibility=hidden +#if defined(_WIN32) + #if defined(__TINYC__) + #define __declspec(x) __attribute__((x)) + #endif + #if defined(BUILD_LIBTYPE_SHARED) + #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 +#else + #if defined(BUILD_LIBTYPE_SHARED) + #define RLAPI __attribute__((visibility("default"))) // We are building as a Unix shared library (.so/.dylib) + #endif +#endif + +#ifndef RLAPI + #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Some basic Defines +//---------------------------------------------------------------------------------- +#ifndef PI + #define PI 3.14159265358979323846f +#endif +#ifndef DEG2RAD + #define DEG2RAD (PI/180.0f) +#endif +#ifndef RAD2DEG + #define RAD2DEG (180.0f/PI) +#endif + +// Allow custom memory allocators +// NOTE: Require recompiling raylib sources +#ifndef RL_MALLOC + #define RL_MALLOC(sz) malloc(sz) +#endif +#ifndef RL_CALLOC + #define RL_CALLOC(n,sz) calloc(n,sz) +#endif +#ifndef RL_REALLOC + #define RL_REALLOC(ptr,sz) realloc(ptr,sz) +#endif +#ifndef RL_FREE + #define RL_FREE(ptr) free(ptr) +#endif + +// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) +// Plain structures in C++ (without constructors) can be initialized with { } +// This is called aggregate initialization (C++11 feature) +#if defined(__cplusplus) + #define CLITERAL(type) type +#else + #define CLITERAL(type) (type) +#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 +// 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) +// this defines are very useful for internal check and avoid type (re)definitions +#define RL_COLOR_TYPE +#define RL_RECTANGLE_TYPE +#define RL_VECTOR2_TYPE +#define RL_VECTOR3_TYPE +#define RL_VECTOR4_TYPE +#define RL_QUATERNION_TYPE +#define RL_MATRIX_TYPE + +// Some Basic Colors +// NOTE: Custom raylib color palette for amazing visuals on WHITE background +#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray +#define GRAY CLITERAL(Color){ 130, 130, 130, 255 } // Gray +#define DARKGRAY CLITERAL(Color){ 80, 80, 80, 255 } // Dark Gray +#define YELLOW CLITERAL(Color){ 253, 249, 0, 255 } // Yellow +#define GOLD CLITERAL(Color){ 255, 203, 0, 255 } // Gold +#define ORANGE CLITERAL(Color){ 255, 161, 0, 255 } // Orange +#define PINK CLITERAL(Color){ 255, 109, 194, 255 } // Pink +#define RED CLITERAL(Color){ 230, 41, 55, 255 } // Red +#define MAROON CLITERAL(Color){ 190, 33, 55, 255 } // Maroon +#define GREEN CLITERAL(Color){ 0, 228, 48, 255 } // Green +#define LIME CLITERAL(Color){ 0, 158, 47, 255 } // Lime +#define DARKGREEN CLITERAL(Color){ 0, 117, 44, 255 } // Dark Green +#define SKYBLUE CLITERAL(Color){ 102, 191, 255, 255 } // Sky Blue +#define BLUE CLITERAL(Color){ 0, 121, 241, 255 } // Blue +#define DARKBLUE CLITERAL(Color){ 0, 82, 172, 255 } // Dark Blue +#define PURPLE CLITERAL(Color){ 200, 122, 255, 255 } // Purple +#define VIOLET CLITERAL(Color){ 135, 60, 190, 255 } // Violet +#define DARKPURPLE CLITERAL(Color){ 112, 31, 126, 255 } // Dark Purple +#define BEIGE CLITERAL(Color){ 211, 176, 131, 255 } // Beige +#define BROWN CLITERAL(Color){ 127, 106, 79, 255 } // Brown +#define DARKBROWN CLITERAL(Color){ 76, 63, 47, 255 } // Dark Brown + +#define WHITE CLITERAL(Color){ 255, 255, 255, 255 } // White +#define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black +#define BLANK CLITERAL(Color){ 0, 0, 0, 0 } // Blank (Transparent) +#define MAGENTA CLITERAL(Color){ 255, 0, 255, 255 } // Magenta +#define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 } // My own White (raylib logo) + +//---------------------------------------------------------------------------------- +// Structures Definition +//---------------------------------------------------------------------------------- +// Boolean type +#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum bool { false = 0, true = !false } bool; + #define RL_BOOL_TYPE +#endif + +// Vector2, 2 components +typedef struct Vector2 { + float x; // Vector x component + float y; // Vector y component +} Vector2; + +// Vector3, 3 components +typedef struct Vector3 { + float x; // Vector x component + float y; // Vector y component + float z; // Vector z component +} Vector3; + +// Vector4, 4 components +typedef struct Vector4 { + float x; // Vector x component + float y; // Vector y component + float z; // Vector z component + float w; // Vector w component +} Vector4; + +// Quaternion, 4 components (Vector4 alias) +typedef Vector4 Quaternion; + +// 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; + +// Color, 4 components, R8G8B8A8 (32bit) +typedef struct Color { + unsigned char r; // Color red value + unsigned char g; // Color green value + unsigned char b; // Color blue value + unsigned char a; // Color alpha value +} Color; + +// Rectangle, 4 components +typedef struct Rectangle { + float x; // Rectangle top-left corner position x + float y; // Rectangle top-left corner position y + float width; // Rectangle width + float height; // Rectangle height +} Rectangle; + +// Image, pixel data stored in CPU memory (RAM) +typedef struct Image { + void *data; // Image raw data + int width; // Image base width + int height; // Image base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) +} Image; + +// Texture, tex data stored in GPU memory (VRAM) +typedef struct Texture { + unsigned int id; // OpenGL texture id + int width; // Texture base width + int height; // Texture base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) +} Texture; + +// Texture2D, same as Texture +typedef Texture Texture2D; + +// TextureCubemap, same as Texture +typedef Texture TextureCubemap; + +// RenderTexture, fbo for texture rendering +typedef struct RenderTexture { + unsigned int id; // OpenGL framebuffer object id + Texture texture; // Color buffer attachment texture + Texture depth; // Depth buffer attachment texture +} RenderTexture; + +// RenderTexture2D, same as RenderTexture +typedef RenderTexture RenderTexture2D; + +// NPatchInfo, n-patch layout info +typedef struct NPatchInfo { + Rectangle source; // Texture source rectangle + int left; // Left border offset + int top; // Top border offset + int right; // Right border offset + int bottom; // Bottom border offset + int layout; // Layout of the n-patch: 3x3, 1x3 or 3x1 +} NPatchInfo; + +// GlyphInfo, font characters glyphs info +typedef struct GlyphInfo { + int value; // Character value (Unicode) + int offsetX; // Character offset X when drawing + int offsetY; // Character offset Y when drawing + int advanceX; // Character advance position X + Image image; // Character image data +} GlyphInfo; + +// Font, font texture and GlyphInfo array data +typedef struct Font { + int baseSize; // Base size (default chars height) + int glyphCount; // Number of glyph characters + int glyphPadding; // Padding around the glyph characters + Texture2D texture; // Texture atlas containing the glyphs + Rectangle *recs; // Rectangles in texture for the glyphs + GlyphInfo *glyphs; // Glyphs info data +} Font; + +// Camera, defines position/orientation in 3d space +typedef struct Camera3D { + Vector3 position; // Camera position + Vector3 target; // Camera target it looks-at + Vector3 up; // Camera up vector (rotation over its axis) + float fovy; // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic + int projection; // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC +} Camera3D; + +typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D + +// Camera2D, defines position/orientation in 2d space +typedef struct Camera2D { + Vector2 offset; // Camera offset (displacement from target) + Vector2 target; // Camera target (rotation and zoom origin) + float rotation; // Camera rotation in degrees + float zoom; // Camera zoom (scaling), should be 1.0f by default +} Camera2D; + +// Mesh, vertex data and vao/vbo +typedef struct Mesh { + int vertexCount; // Number of vertices stored in arrays + int triangleCount; // Number of triangles stored (indexed or not) + + // Vertex attributes data + float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + float *texcoords2; // Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5) + float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) + float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) + unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) + unsigned short *indices; // Vertex indices (in case vertex data comes indexed) + + // Animation vertex data + float *animVertices; // Animated vertex positions (after bones transformations) + float *animNormals; // Animated normals (after bones transformations) + unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7) + Matrix *boneMatrices; // Bones animated transformation matrices + int boneCount; // Number of bones + + // OpenGL identifiers + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data) +} Mesh; + +// Shader +typedef struct Shader { + unsigned int id; // Shader program id + int *locs; // Shader locations array (RL_MAX_SHADER_LOCATIONS) +} Shader; + +// MaterialMap +typedef struct MaterialMap { + Texture2D texture; // Material map texture + Color color; // Material map color + float value; // Material map value +} MaterialMap; + +// Material, includes shader and maps +typedef struct Material { + Shader shader; // Material shader + MaterialMap *maps; // Material maps array (MAX_MATERIAL_MAPS) + float params[4]; // Material generic parameters (if required) +} Material; + +// Transform, vertex transformation data +typedef struct Transform { + Vector3 translation; // Translation + Quaternion rotation; // Rotation + Vector3 scale; // Scale +} Transform; + +// Bone, skeletal animation bone +typedef struct BoneInfo { + char name[32]; // Bone name + int parent; // Bone parent +} BoneInfo; + +// Model, meshes, materials and animation data +typedef struct Model { + Matrix transform; // Local transform matrix + + int meshCount; // Number of meshes + int materialCount; // Number of materials + Mesh *meshes; // Meshes array + Material *materials; // Materials array + int *meshMaterial; // Mesh material number + + // Animation data + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + Transform *bindPose; // Bones base transformation (pose) +} Model; + +// ModelAnimation +typedef struct ModelAnimation { + int boneCount; // Number of bones + int frameCount; // Number of animation frames + BoneInfo *bones; // Bones information (skeleton) + Transform **framePoses; // Poses array by frame + char name[32]; // Animation name +} ModelAnimation; + +// Ray, ray for raycasting +typedef struct Ray { + Vector3 position; // Ray position (origin) + Vector3 direction; // Ray direction (normalized) +} Ray; + +// RayCollision, ray hit information +typedef struct RayCollision { + bool hit; // Did the ray hit something? + float distance; // Distance to the nearest hit + Vector3 point; // Point of the nearest hit + Vector3 normal; // Surface normal of hit +} RayCollision; + +// BoundingBox +typedef struct BoundingBox { + Vector3 min; // Minimum vertex box-corner + Vector3 max; // Maximum vertex box-corner +} BoundingBox; + +// Wave, audio wave data +typedef struct Wave { + unsigned int frameCount; // Total number of frames (considering channels) + unsigned int sampleRate; // Frequency (samples per second) + unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + unsigned int channels; // Number of channels (1-mono, 2-stereo, ...) + void *data; // Buffer data pointer +} Wave; + +// Opaque structs declaration +// NOTE: Actual structs are defined internally in raudio module +typedef struct rAudioBuffer rAudioBuffer; +typedef struct rAudioProcessor rAudioProcessor; + +// AudioStream, custom audio stream +typedef struct AudioStream { + rAudioBuffer *buffer; // Pointer to internal data used by the audio system + rAudioProcessor *processor; // Pointer to internal data processor, useful for audio effects + + unsigned int sampleRate; // Frequency (samples per second) + unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + unsigned int channels; // Number of channels (1-mono, 2-stereo, ...) +} AudioStream; + +// Sound +typedef struct Sound { + AudioStream stream; // Audio stream + unsigned int frameCount; // Total number of frames (considering channels) +} Sound; + +// Music, audio stream, anything longer than ~10 seconds should be streamed +typedef struct Music { + AudioStream stream; // Audio stream + unsigned int frameCount; // Total number of frames (considering channels) + bool looping; // Music looping enable + + int ctxType; // Type of music context (audio filetype) + void *ctxData; // Audio context data, depends on type +} Music; + +// VrDeviceInfo, Head-Mounted-Display device parameters +typedef struct VrDeviceInfo { + int hResolution; // Horizontal resolution in pixels + int vResolution; // Vertical resolution in pixels + float hScreenSize; // Horizontal size in meters + float vScreenSize; // Vertical size in meters + float eyeToScreenDistance; // Distance between eye and display in meters + float lensSeparationDistance; // Lens separation distance in meters + float interpupillaryDistance; // IPD (distance between pupils) in meters + float lensDistortionValues[4]; // Lens distortion constant parameters + float chromaAbCorrection[4]; // Chromatic aberration correction parameters +} VrDeviceInfo; + +// VrStereoConfig, VR stereo rendering configuration for simulator +typedef struct VrStereoConfig { + Matrix projection[2]; // VR projection matrices (per eye) + Matrix viewOffset[2]; // VR view offset matrices (per eye) + float leftLensCenter[2]; // VR left lens center + float rightLensCenter[2]; // VR right lens center + float leftScreenCenter[2]; // VR left screen center + float rightScreenCenter[2]; // VR right screen center + float scale[2]; // VR distortion scale + float scaleIn[2]; // VR distortion scale in +} VrStereoConfig; + +// File path list +typedef struct FilePathList { + unsigned int capacity; // Filepaths max entries + unsigned int count; // Filepaths entries count + char **paths; // Filepaths entries +} 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 +//---------------------------------------------------------------------------------- +// System/Window config flags +// NOTE: Every bit registers one state (use it with bit masks) +// By default all flags are set to 0 +typedef enum { + FLAG_VSYNC_HINT = 0x00000040, // Set to try enabling V-Sync on GPU + FLAG_FULLSCREEN_MODE = 0x00000002, // Set to run program in fullscreen + FLAG_WINDOW_RESIZABLE = 0x00000004, // Set to allow resizable window + FLAG_WINDOW_UNDECORATED = 0x00000008, // Set to disable window decoration (frame and buttons) + FLAG_WINDOW_HIDDEN = 0x00000080, // Set to hide window + FLAG_WINDOW_MINIMIZED = 0x00000200, // Set to minimize window (iconify) + FLAG_WINDOW_MAXIMIZED = 0x00000400, // Set to maximize window (expanded to monitor) + FLAG_WINDOW_UNFOCUSED = 0x00000800, // Set to window non focused + FLAG_WINDOW_TOPMOST = 0x00001000, // Set to window always on top + FLAG_WINDOW_ALWAYS_RUN = 0x00000100, // Set to allow windows running while minimized + FLAG_WINDOW_TRANSPARENT = 0x00000010, // Set to allow transparent framebuffer + 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_BORDERLESS_WINDOWED_MODE = 0x00008000, // Set to run program in borderless windowed mode + FLAG_MSAA_4X_HINT = 0x00000020, // Set to try enabling MSAA 4X + FLAG_INTERLACED_HINT = 0x00010000 // Set to try enabling interlaced video format (for V3D) +} ConfigFlags; + +// Trace log level +// NOTE: Organized by priority level +typedef enum { + LOG_ALL = 0, // Display all logs + LOG_TRACE, // Trace logging, intended for internal use only + LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds + LOG_INFO, // Info logging, used for program execution info + LOG_WARNING, // Warning logging, used on recoverable failures + LOG_ERROR, // Error logging, used on unrecoverable failures + LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) + LOG_NONE // Disable logging +} TraceLogLevel; + +// Keyboard keys (US keyboard layout) +// NOTE: Use GetKeyPressed() to allow redefining +// required keys for alternative layouts +typedef enum { + KEY_NULL = 0, // Key: NULL, used for no key pressed + // Alphanumeric keys + KEY_APOSTROPHE = 39, // Key: ' + KEY_COMMA = 44, // Key: , + KEY_MINUS = 45, // Key: - + KEY_PERIOD = 46, // Key: . + KEY_SLASH = 47, // Key: / + KEY_ZERO = 48, // Key: 0 + KEY_ONE = 49, // Key: 1 + KEY_TWO = 50, // Key: 2 + KEY_THREE = 51, // Key: 3 + KEY_FOUR = 52, // Key: 4 + KEY_FIVE = 53, // Key: 5 + KEY_SIX = 54, // Key: 6 + KEY_SEVEN = 55, // Key: 7 + KEY_EIGHT = 56, // Key: 8 + KEY_NINE = 57, // Key: 9 + KEY_SEMICOLON = 59, // Key: ; + KEY_EQUAL = 61, // Key: = + KEY_A = 65, // Key: A | a + KEY_B = 66, // Key: B | b + KEY_C = 67, // Key: C | c + KEY_D = 68, // Key: D | d + KEY_E = 69, // Key: E | e + KEY_F = 70, // Key: F | f + KEY_G = 71, // Key: G | g + KEY_H = 72, // Key: H | h + KEY_I = 73, // Key: I | i + KEY_J = 74, // Key: J | j + KEY_K = 75, // Key: K | k + KEY_L = 76, // Key: L | l + KEY_M = 77, // Key: M | m + KEY_N = 78, // Key: N | n + KEY_O = 79, // Key: O | o + KEY_P = 80, // Key: P | p + KEY_Q = 81, // Key: Q | q + KEY_R = 82, // Key: R | r + KEY_S = 83, // Key: S | s + KEY_T = 84, // Key: T | t + KEY_U = 85, // Key: U | u + KEY_V = 86, // Key: V | v + KEY_W = 87, // Key: W | w + KEY_X = 88, // Key: X | x + KEY_Y = 89, // Key: Y | y + KEY_Z = 90, // Key: Z | z + KEY_LEFT_BRACKET = 91, // Key: [ + KEY_BACKSLASH = 92, // Key: '\' + KEY_RIGHT_BRACKET = 93, // Key: ] + KEY_GRAVE = 96, // Key: ` + // Function keys + KEY_SPACE = 32, // Key: Space + KEY_ESCAPE = 256, // Key: Esc + KEY_ENTER = 257, // Key: Enter + KEY_TAB = 258, // Key: Tab + KEY_BACKSPACE = 259, // Key: Backspace + KEY_INSERT = 260, // Key: Ins + KEY_DELETE = 261, // Key: Del + KEY_RIGHT = 262, // Key: Cursor right + KEY_LEFT = 263, // Key: Cursor left + KEY_DOWN = 264, // Key: Cursor down + KEY_UP = 265, // Key: Cursor up + KEY_PAGE_UP = 266, // Key: Page up + KEY_PAGE_DOWN = 267, // Key: Page down + KEY_HOME = 268, // Key: Home + KEY_END = 269, // Key: End + KEY_CAPS_LOCK = 280, // Key: Caps lock + KEY_SCROLL_LOCK = 281, // Key: Scroll down + KEY_NUM_LOCK = 282, // Key: Num lock + KEY_PRINT_SCREEN = 283, // Key: Print screen + KEY_PAUSE = 284, // Key: Pause + KEY_F1 = 290, // Key: F1 + KEY_F2 = 291, // Key: F2 + KEY_F3 = 292, // Key: F3 + KEY_F4 = 293, // Key: F4 + KEY_F5 = 294, // Key: F5 + KEY_F6 = 295, // Key: F6 + KEY_F7 = 296, // Key: F7 + KEY_F8 = 297, // Key: F8 + KEY_F9 = 298, // Key: F9 + KEY_F10 = 299, // Key: F10 + KEY_F11 = 300, // Key: F11 + KEY_F12 = 301, // Key: F12 + KEY_LEFT_SHIFT = 340, // Key: Shift left + KEY_LEFT_CONTROL = 341, // Key: Control left + KEY_LEFT_ALT = 342, // Key: Alt left + KEY_LEFT_SUPER = 343, // Key: Super left + KEY_RIGHT_SHIFT = 344, // Key: Shift right + KEY_RIGHT_CONTROL = 345, // Key: Control right + KEY_RIGHT_ALT = 346, // Key: Alt right + KEY_RIGHT_SUPER = 347, // Key: Super right + KEY_KB_MENU = 348, // Key: KB menu + // Keypad keys + KEY_KP_0 = 320, // Key: Keypad 0 + KEY_KP_1 = 321, // Key: Keypad 1 + KEY_KP_2 = 322, // Key: Keypad 2 + KEY_KP_3 = 323, // Key: Keypad 3 + KEY_KP_4 = 324, // Key: Keypad 4 + KEY_KP_5 = 325, // Key: Keypad 5 + KEY_KP_6 = 326, // Key: Keypad 6 + KEY_KP_7 = 327, // Key: Keypad 7 + KEY_KP_8 = 328, // Key: Keypad 8 + KEY_KP_9 = 329, // Key: Keypad 9 + KEY_KP_DECIMAL = 330, // Key: Keypad . + KEY_KP_DIVIDE = 331, // Key: Keypad / + KEY_KP_MULTIPLY = 332, // Key: Keypad * + KEY_KP_SUBTRACT = 333, // Key: Keypad - + KEY_KP_ADD = 334, // Key: Keypad + + KEY_KP_ENTER = 335, // Key: Keypad Enter + KEY_KP_EQUAL = 336, // Key: Keypad = + // Android key buttons + KEY_BACK = 4, // Key: Android back button + KEY_MENU = 5, // Key: Android menu button + KEY_VOLUME_UP = 24, // Key: Android volume up button + KEY_VOLUME_DOWN = 25 // Key: Android volume down button +} KeyboardKey; + +// Add backwards compatibility support for deprecated names +#define MOUSE_LEFT_BUTTON MOUSE_BUTTON_LEFT +#define MOUSE_RIGHT_BUTTON MOUSE_BUTTON_RIGHT +#define MOUSE_MIDDLE_BUTTON MOUSE_BUTTON_MIDDLE + +// Mouse buttons +typedef enum { + MOUSE_BUTTON_LEFT = 0, // Mouse button left + MOUSE_BUTTON_RIGHT = 1, // Mouse button right + MOUSE_BUTTON_MIDDLE = 2, // Mouse button middle (pressed wheel) + MOUSE_BUTTON_SIDE = 3, // Mouse button side (advanced mouse device) + MOUSE_BUTTON_EXTRA = 4, // Mouse button extra (advanced mouse device) + MOUSE_BUTTON_FORWARD = 5, // Mouse button forward (advanced mouse device) + MOUSE_BUTTON_BACK = 6, // Mouse button back (advanced mouse device) +} MouseButton; + +// Mouse cursor +typedef enum { + MOUSE_CURSOR_DEFAULT = 0, // Default pointer shape + MOUSE_CURSOR_ARROW = 1, // Arrow shape + MOUSE_CURSOR_IBEAM = 2, // Text writing cursor shape + MOUSE_CURSOR_CROSSHAIR = 3, // Cross shape + MOUSE_CURSOR_POINTING_HAND = 4, // Pointing hand cursor + MOUSE_CURSOR_RESIZE_EW = 5, // Horizontal resize/move arrow shape + MOUSE_CURSOR_RESIZE_NS = 6, // Vertical resize/move arrow shape + MOUSE_CURSOR_RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape + MOUSE_CURSOR_RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape + MOUSE_CURSOR_RESIZE_ALL = 9, // The omnidirectional resize/move cursor shape + MOUSE_CURSOR_NOT_ALLOWED = 10 // The operation-not-allowed shape +} MouseCursor; + +// Gamepad buttons +typedef enum { + GAMEPAD_BUTTON_UNKNOWN = 0, // Unknown button, just for error checking + GAMEPAD_BUTTON_LEFT_FACE_UP, // Gamepad left DPAD up button + GAMEPAD_BUTTON_LEFT_FACE_RIGHT, // Gamepad left DPAD right button + GAMEPAD_BUTTON_LEFT_FACE_DOWN, // Gamepad left DPAD down button + GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Gamepad left DPAD left button + GAMEPAD_BUTTON_RIGHT_FACE_UP, // Gamepad right button up (i.e. PS3: Triangle, Xbox: Y) + GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, // Gamepad right button right (i.e. PS3: Circle, Xbox: B) + GAMEPAD_BUTTON_RIGHT_FACE_DOWN, // Gamepad right button down (i.e. PS3: Cross, Xbox: A) + GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // Gamepad right button left (i.e. PS3: Square, Xbox: X) + GAMEPAD_BUTTON_LEFT_TRIGGER_1, // Gamepad top/back trigger left (first), it could be a trailing button + GAMEPAD_BUTTON_LEFT_TRIGGER_2, // Gamepad top/back trigger left (second), it could be a trailing button + GAMEPAD_BUTTON_RIGHT_TRIGGER_1, // Gamepad top/back trigger right (first), it could be a trailing button + GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // Gamepad top/back trigger right (second), it could be a trailing button + GAMEPAD_BUTTON_MIDDLE_LEFT, // Gamepad center buttons, left one (i.e. PS3: Select) + GAMEPAD_BUTTON_MIDDLE, // Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX) + GAMEPAD_BUTTON_MIDDLE_RIGHT, // Gamepad center buttons, right one (i.e. PS3: Start) + GAMEPAD_BUTTON_LEFT_THUMB, // Gamepad joystick pressed button left + GAMEPAD_BUTTON_RIGHT_THUMB // Gamepad joystick pressed button right +} GamepadButton; + +// Gamepad axis +typedef enum { + GAMEPAD_AXIS_LEFT_X = 0, // Gamepad left stick X axis + GAMEPAD_AXIS_LEFT_Y = 1, // Gamepad left stick Y axis + GAMEPAD_AXIS_RIGHT_X = 2, // Gamepad right stick X axis + GAMEPAD_AXIS_RIGHT_Y = 3, // Gamepad right stick Y axis + GAMEPAD_AXIS_LEFT_TRIGGER = 4, // Gamepad back trigger left, pressure level: [1..-1] + GAMEPAD_AXIS_RIGHT_TRIGGER = 5 // Gamepad back trigger right, pressure level: [1..-1] +} GamepadAxis; + +// Material map index +typedef enum { + MATERIAL_MAP_ALBEDO = 0, // Albedo material (same as: MATERIAL_MAP_DIFFUSE) + MATERIAL_MAP_METALNESS, // Metalness material (same as: MATERIAL_MAP_SPECULAR) + MATERIAL_MAP_NORMAL, // Normal material + MATERIAL_MAP_ROUGHNESS, // Roughness material + MATERIAL_MAP_OCCLUSION, // Ambient occlusion material + MATERIAL_MAP_EMISSION, // Emission material + MATERIAL_MAP_HEIGHT, // Heightmap material + MATERIAL_MAP_CUBEMAP, // Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP) + MATERIAL_MAP_IRRADIANCE, // Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP) + MATERIAL_MAP_PREFILTER, // Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP) + MATERIAL_MAP_BRDF // Brdf material +} MaterialMapIndex; + +#define MATERIAL_MAP_DIFFUSE MATERIAL_MAP_ALBEDO +#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS + +// Shader location index +typedef enum { + SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position + SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 + SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 + SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal + SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent + SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color + SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection + SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) + SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection + SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) + SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal + SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view + SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color + SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color + SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color + SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: SHADER_LOC_MAP_DIFFUSE) + SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: SHADER_LOC_MAP_SPECULAR) + SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal + SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness + SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion + SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission + SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height + SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap + SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance + SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter + SHADER_LOC_MAP_BRDF, // Shader location: sampler2d texture: brdf + SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: boneIds + SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: boneWeights + SHADER_LOC_BONE_MATRICES // Shader location: array of matrices uniform: boneMatrices +} ShaderLocationIndex; + +#define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO +#define SHADER_LOC_MAP_SPECULAR SHADER_LOC_MAP_METALNESS + +// Shader uniform data type +typedef enum { + SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float + SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) + SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) + SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float) + SHADER_UNIFORM_INT, // Shader uniform type: int + SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) + SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) + SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) + SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d +} ShaderUniformDataType; + +// Shader attribute data types +typedef enum { + SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float + SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float) + SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float) + SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) +} ShaderAttributeDataType; + +// Pixel formats +// NOTE: Support depends on OpenGL version and platform +typedef enum { + PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp + PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp + PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp + PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 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_RGBA, // 4 bpp (1 bit alpha) + PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp + PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp + PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp + PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp + PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp + PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp + PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp +} PixelFormat; + +// Texture parameters: filter mode +// NOTE 1: Filtering considers mipmaps if available in the texture +// NOTE 2: Filter is accordingly set for minification and magnification +typedef enum { + TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation + TEXTURE_FILTER_BILINEAR, // Linear filtering + TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) + TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x + TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x + TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x +} TextureFilter; + +// Texture parameters: wrap mode +typedef enum { + TEXTURE_WRAP_REPEAT = 0, // Repeats texture in tiled mode + TEXTURE_WRAP_CLAMP, // Clamps texture to edge pixel in tiled mode + TEXTURE_WRAP_MIRROR_REPEAT, // Mirrors and repeats the texture in tiled mode + TEXTURE_WRAP_MIRROR_CLAMP // Mirrors and clamps to border the texture in tiled mode +} TextureWrap; + +// Cubemap layouts +typedef enum { + CUBEMAP_LAYOUT_AUTO_DETECT = 0, // Automatically detect layout type + CUBEMAP_LAYOUT_LINE_VERTICAL, // Layout is defined by a vertical line with faces + CUBEMAP_LAYOUT_LINE_HORIZONTAL, // Layout is defined by a horizontal line with faces + CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces + CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE // Layout is defined by a 4x3 cross with cubemap faces +} CubemapLayout; + +// Font type, defines generation method +typedef enum { + FONT_DEFAULT = 0, // Default font generation, anti-aliased + FONT_BITMAP, // Bitmap font generation, no anti-aliasing + FONT_SDF // SDF font generation, requires external shader +} FontType; + +// Color blending modes (pre-defined) +typedef enum { + BLEND_ALPHA = 0, // Blend textures considering alpha (default) + BLEND_ADDITIVE, // Blend textures adding colors + BLEND_MULTIPLIED, // Blend textures multiplying colors + BLEND_ADD_COLORS, // Blend textures adding colors (alternative) + BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) + BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha + BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) +} BlendMode; + +// Gesture +// NOTE: Provided as bit-wise flags to enable only desired gestures +typedef enum { + GESTURE_NONE = 0, // No gesture + GESTURE_TAP = 1, // Tap gesture + GESTURE_DOUBLETAP = 2, // Double tap gesture + GESTURE_HOLD = 4, // Hold gesture + GESTURE_DRAG = 8, // Drag gesture + GESTURE_SWIPE_RIGHT = 16, // Swipe right gesture + GESTURE_SWIPE_LEFT = 32, // Swipe left gesture + GESTURE_SWIPE_UP = 64, // Swipe up gesture + GESTURE_SWIPE_DOWN = 128, // Swipe down gesture + GESTURE_PINCH_IN = 256, // Pinch in gesture + GESTURE_PINCH_OUT = 512 // Pinch out gesture +} Gesture; + +// Camera system modes +typedef enum { + CAMERA_CUSTOM = 0, // Camera custom, controlled by user (UpdateCamera() does nothing) + CAMERA_FREE, // Camera free mode + CAMERA_ORBITAL, // Camera orbital, around target, zoom supported + CAMERA_FIRST_PERSON, // Camera first person + CAMERA_THIRD_PERSON // Camera third person +} CameraMode; + +// Camera projection +typedef enum { + CAMERA_PERSPECTIVE = 0, // Perspective projection + CAMERA_ORTHOGRAPHIC // Orthographic projection +} CameraProjection; + +// N-patch layout +typedef enum { + NPATCH_NINE_PATCH = 0, // Npatch layout: 3x3 tiles + NPATCH_THREE_PATCH_VERTICAL, // Npatch layout: 1x3 tiles + NPATCH_THREE_PATCH_HORIZONTAL // Npatch layout: 3x1 tiles +} NPatchLayout; + +// Callbacks to hook some internal functions +// WARNING: These callbacks are intended for advanced users +typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages +typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load 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 bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data + +//------------------------------------------------------------------------------------ +// Global Variables Definition +//------------------------------------------------------------------------------------ +// It's lonely here... + +//------------------------------------------------------------------------------------ +// Window and Graphics Device Functions (Module: core) +//------------------------------------------------------------------------------------ + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +// Window-related functions +RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and 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 IsWindowFullscreen(void); // Check if window is currently fullscreen +RLAPI bool IsWindowHidden(void); // Check if window is currently hidden +RLAPI bool IsWindowMinimized(void); // Check if window is currently minimized +RLAPI bool IsWindowMaximized(void); // Check if window is currently maximized +RLAPI bool IsWindowFocused(void); // Check if window is currently focused +RLAPI bool IsWindowResized(void); // Check if window has been resized last frame +RLAPI bool IsWindowState(unsigned int flag); // Check if one specific window flag is enabled +RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags +RLAPI void ClearWindowState(unsigned int flags); // Clear window configuration state flags +RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed, resizes monitor to match window resolution +RLAPI void ToggleBorderlessWindowed(void); // Toggle window state: borderless windowed, resizes window to match monitor resolution +RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable +RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable +RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized +RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit) +RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit) +RLAPI void SetWindowTitle(const char *title); // Set title for window +RLAPI void SetWindowPosition(int x, int y); // Set window position on screen +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 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 SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] +RLAPI void SetWindowFocused(void); // Set window focused +RLAPI void *GetWindowHandle(void); // Get native window handle +RLAPI int GetScreenWidth(void); // Get current screen width +RLAPI int GetScreenHeight(void); // Get current screen height +RLAPI int GetRenderWidth(void); // Get current render width (it considers HiDPI) +RLAPI int GetRenderHeight(void); // Get current render height (it considers HiDPI) +RLAPI int GetMonitorCount(void); // Get number of connected monitors +RLAPI int GetCurrentMonitor(void); // Get current monitor where window is placed +RLAPI Vector2 GetMonitorPosition(int monitor); // Get specified monitor position +RLAPI int GetMonitorWidth(int monitor); // Get specified monitor width (current video mode used by monitor) +RLAPI int GetMonitorHeight(int monitor); // Get specified monitor height (current video mode used by monitor) +RLAPI int GetMonitorPhysicalWidth(int monitor); // Get specified monitor physical width in millimetres +RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specified monitor physical height in millimetres +RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate +RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor +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 specified monitor +RLAPI void SetClipboardText(const char *text); // Set clipboard text content +RLAPI const char *GetClipboardText(void); // Get clipboard text content +RLAPI Image GetClipboardImage(void); // Get clipboard image content +RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling +RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling + +// Cursor-related functions +RLAPI void ShowCursor(void); // Shows cursor +RLAPI void HideCursor(void); // Hides cursor +RLAPI bool IsCursorHidden(void); // Check if cursor is not visible +RLAPI void EnableCursor(void); // Enables cursor (unlock cursor) +RLAPI void DisableCursor(void); // Disables cursor (lock cursor) +RLAPI bool IsCursorOnScreen(void); // Check if cursor is on the screen + +// Drawing-related functions +RLAPI void ClearBackground(Color color); // Set background color (framebuffer clear color) +RLAPI void BeginDrawing(void); // Setup canvas (framebuffer) to start drawing +RLAPI void EndDrawing(void); // End canvas drawing and swap buffers (double buffering) +RLAPI void BeginMode2D(Camera2D camera); // Begin 2D mode with custom camera (2D) +RLAPI void EndMode2D(void); // Ends 2D mode with custom camera +RLAPI void BeginMode3D(Camera3D camera); // Begin 3D mode with custom camera (3D) +RLAPI void EndMode3D(void); // Ends 3D mode and returns to default 2D orthographic mode +RLAPI void BeginTextureMode(RenderTexture2D target); // Begin drawing to render texture +RLAPI void EndTextureMode(void); // Ends drawing to render texture +RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing +RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader) +RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied, subtract, custom) +RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending) +RLAPI void BeginScissorMode(int x, int y, int width, int height); // Begin scissor mode (define screen area for following drawing) +RLAPI void EndScissorMode(void); // End scissor mode +RLAPI void BeginVrStereoMode(VrStereoConfig config); // Begin stereo rendering (requires VR simulator) +RLAPI void EndVrStereoMode(void); // End stereo rendering (requires VR simulator) + +// VR stereo config functions for VR simulator +RLAPI VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device); // Load VR stereo config for VR simulator device parameters +RLAPI void UnloadVrStereoConfig(VrStereoConfig config); // Unload VR stereo config + +// Shader management functions +// NOTE: Shader functionality is not available on OpenGL 1.1 +RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations +RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations +RLAPI bool IsShaderValid(Shader shader); // Check if a shader is valid (loaded on GPU) +RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location +RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName); // Get shader attribute location +RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value +RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector +RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value for texture (sampler2d) +RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) + +// Screen-space-related functions +#define GetMouseRay GetScreenToWorldRay // Compatibility hack for previous raylib versions +RLAPI Ray GetScreenToWorldRay(Vector2 position, Camera camera); // Get a ray trace from screen position (i.e mouse) +RLAPI Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height); // Get a ray trace from screen position (i.e mouse) in a viewport +RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Get the screen space position for a 3d world space position +RLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position +RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the screen space position for a 2d camera world space position +RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position +RLAPI Matrix GetCameraMatrix(Camera camera); // Get camera transform matrix (view matrix) +RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix + +// Timing-related functions +RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) +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 int GetFPS(void); // Get current FPS + +// Custom frame control functions +// NOTE: Those functions are intended for advanced 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 +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 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 SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level +RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator +RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator +RLAPI void MemFree(void *ptr); // Internal memory free + +// Set custom callbacks +// WARNING: Callbacks setup is intended for advanced users +RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log +RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader +RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver +RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader +RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver + +// Files management functions +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 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, 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 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 +//------------------------------------------------------------------ + +// File system functions +RLAPI bool FileExists(const char *fileName); // Check if file 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 int GetFileLength(const char *fileName); // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) +RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png') +RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string +RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) +RLAPI const char *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 *GetWorkingDirectory(void); // Get current working directory (uses static string) +RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string) +RLAPI int MakeDirectory(const char *dirPath); // Create directories (including full path requested), returns 0 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 IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS +RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths +RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result +RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths +RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window +RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths +RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths +RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) + +// Compression/Encoding functionality +RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree() +RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree() +RLAPI char *EncodeDataBase64(const unsigned char *data, int 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 int ComputeCRC32(unsigned char *data, int dataSize); // Compute CRC32 hash code +RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize); // Compute MD5 hash code, returns static int[4] (16 bytes) +RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize); // Compute SHA1 hash code, returns static int[5] (20 bytes) + + +// 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-related functions: keyboard +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 +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 IsKeyUp(int key); // Check if a key is NOT being pressed +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 void SetExitKey(int key); // Set a custom key to exit program (default is ESC) + +// Input-related functions: gamepads +RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available +RLAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name id +RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once +RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed +RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once +RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed +RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed +RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad +RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis +RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) +RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration); // Set gamepad vibration for both motors (duration in seconds) + +// Input-related functions: mouse +RLAPI bool IsMouseButtonPressed(int button); // Check if a mouse button has been pressed once +RLAPI bool IsMouseButtonDown(int button); // Check if a mouse button is being pressed +RLAPI bool IsMouseButtonReleased(int button); // Check if a mouse button has been released once +RLAPI bool IsMouseButtonUp(int button); // Check if a mouse button is NOT being pressed +RLAPI int GetMouseX(void); // Get mouse position X +RLAPI int GetMouseY(void); // Get mouse position Y +RLAPI Vector2 GetMousePosition(void); // Get mouse position XY +RLAPI Vector2 GetMouseDelta(void); // Get mouse delta between frames +RLAPI void SetMousePosition(int x, int y); // Set mouse position XY +RLAPI void SetMouseOffset(int offsetX, int offsetY); // Set mouse offset +RLAPI void SetMouseScale(float scaleX, float scaleY); // Set mouse scaling +RLAPI float GetMouseWheelMove(void); // Get mouse wheel movement for X or Y, whichever is larger +RLAPI Vector2 GetMouseWheelMoveV(void); // Get mouse wheel movement for both X and Y +RLAPI void SetMouseCursor(int cursor); // Set mouse cursor + +// Input-related functions: touch +RLAPI int GetTouchX(void); // Get touch position X for touch point 0 (relative to screen size) +RLAPI int GetTouchY(void); // Get touch position Y for touch point 0 (relative to screen size) +RLAPI Vector2 GetTouchPosition(int index); // Get touch position XY for a touch point index (relative to screen size) +RLAPI int GetTouchPointId(int index); // Get touch point identifier for given index +RLAPI int GetTouchPointCount(void); // Get number of touch points + +//------------------------------------------------------------------------------------ +// Gestures and Touch Handling Functions (Module: rgestures) +//------------------------------------------------------------------------------------ +RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags +RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected +RLAPI int GetGestureDetected(void); // Get latest detected gesture +RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in seconds +RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector +RLAPI float GetGestureDragAngle(void); // Get gesture drag angle +RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta +RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle + +//------------------------------------------------------------------------------------ +// Camera System Functions (Module: rcamera) +//------------------------------------------------------------------------------------ +RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode +RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom); // Update camera movement/rotation + +//------------------------------------------------------------------------------------ +// Basic Shapes Drawing Functions (Module: shapes) +//------------------------------------------------------------------------------------ +// Set texture and rectangle to be used on shapes drawing +// 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 +RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing +RLAPI Texture2D GetShapesTexture(void); // Get texture that is used for shapes drawing +RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source rectangle that is used for shapes drawing + +// Basic shapes drawing functions +RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel using geometry [Can be slow, use with care] +RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel using geometry (Vector version) [Can be slow, use with care] +RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line +RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (using gl lines) +RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line (using triangles/quads) +RLAPI void DrawLineStrip(const Vector2 *points, int pointCount, Color color); // Draw lines sequence (using gl lines) +RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw line segment cubic-bezier in-out interpolation +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 DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline +RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer); // Draw a gradient-filled circle +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 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 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 DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline +RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle +RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) +RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle +RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters +RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom); // Draw a vertical-gradient-filled rectangle +RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right); // Draw a horizontal-gradient-filled rectangle +RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors +RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline +RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); // Draw rectangle outline with extended parameters +RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges +RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle lines with rounded edges +RLAPI void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline +RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!) +RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline (vertex in counter-clockwise order!) +RLAPI void DrawTriangleFan(const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points (first vertex is the center) +RLAPI void DrawTriangleStrip(const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points +RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) +RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a polygon outline of n sides +RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters + +// Splines drawing functions +RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points +RLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: B-Spline, minimum 4 points +RLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Catmull-Rom, minimum 4 points +RLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] +RLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] +RLAPI void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color); // Draw spline segment: Linear, 2 points +RLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: B-Spline, 4 points +RLAPI void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: Catmull-Rom, 4 points +RLAPI void DrawSplineSegmentBezierQuadratic(Vector2 p1, Vector2 c2, Vector2 p3, float thick, Color color); // Draw spline segment: Quadratic Bezier, 2 points, 1 control point +RLAPI void DrawSplineSegmentBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float thick, Color color); // Draw spline segment: Cubic Bezier, 2 points, 2 control points + +// Spline segment point evaluation functions, for a given t [0.0f .. 1.0f] +RLAPI Vector2 GetSplinePointLinear(Vector2 startPos, Vector2 endPos, float t); // Get (evaluate) spline point: Linear +RLAPI Vector2 GetSplinePointBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t); // Get (evaluate) spline point: B-Spline +RLAPI Vector2 GetSplinePointCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t); // Get (evaluate) spline point: Catmull-Rom +RLAPI Vector2 GetSplinePointBezierQuad(Vector2 p1, Vector2 c2, Vector2 p3, float t); // Get (evaluate) spline point: Quadratic Bezier +RLAPI Vector2 GetSplinePointBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float t); // Get (evaluate) spline point: Cubic Bezier + +// Basic shapes collision detection functions +RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles +RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles +RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle +RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2); // Check if circle collides with a line created betweeen two points [p1] and [p2] +RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle +RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle +RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle +RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold); // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] +RLAPI bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCount); // Check if point is within a polygon described by array of vertices +RLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference +RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision + +//------------------------------------------------------------------------------------ +// Texture Loading and Drawing Functions (Module: textures) +//------------------------------------------------------------------------------------ + +// Image loading functions +// NOTE: These functions do not require GPU access +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 LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) +RLAPI Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames); // Load image sequence from memory buffer +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 LoadImageFromScreen(void); // Load image from screen buffer and (screenshot) +RLAPI bool IsImageValid(Image image); // Check if an image is valid (data and parameters) +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 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 + +// Image generation functions +RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color +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 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 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 GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells +RLAPI Image GenImageText(int width, int height, const char *text); // Generate image: grayscale image from text data + +// Image manipulation functions +RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) +RLAPI Image ImageFromImage(Image image, Rectangle rec); // Create an image from another image piece +RLAPI Image ImageFromChannel(Image image, int selectedChannel); // Create an image from a selected channel of another image (GRAYSCALE) +RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) +RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) +RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format +RLAPI void ImageToPOT(Image *image, Color fill); // Convert image to POT (power-of-two) +RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle +RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value +RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); // Clear alpha channel to desired color +RLAPI void ImageAlphaMask(Image *image, Image alphaMask); // Apply alpha mask to image +RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel +RLAPI void ImageBlurGaussian(Image *image, int blurSize); // Apply Gaussian blur using a box blur approximation +RLAPI void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize); // Apply custom square convolution kernel to image +RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm) +RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) +RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color +RLAPI void ImageMipmaps(Image *image); // Compute all mipmap levels for a provided 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 ImageFlipVertical(Image *image); // Flip image vertically +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 ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg +RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint +RLAPI void ImageColorInvert(Image *image); // Modify image color: invert +RLAPI void ImageColorGrayscale(Image *image); // Modify image color: grayscale +RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100) +RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255) +RLAPI void ImageColorReplace(Image *image, Color color, Color replace); // Modify image color: replace color +RLAPI Color *LoadImageColors(Image image); // Load color data from image as a Color array (RGBA - 32bit) +RLAPI Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount); // Load colors palette from image as a Color array (RGBA - 32bit) +RLAPI void UnloadImageColors(Color *colors); // Unload color data loaded with LoadImageColors() +RLAPI void UnloadImagePalette(Color *colors); // Unload colors palette loaded with LoadImagePalette() +RLAPI Rectangle GetImageAlphaBorder(Image image, float threshold); // Get image alpha border rectangle +RLAPI Color GetImageColor(Image image, int x, int y); // Get image pixel color at (x, y) position + +// Image drawing functions +// NOTE: Image software-rendering functions (CPU) +RLAPI void ImageClearBackground(Image *dst, Color color); // Clear image background with given color +RLAPI void ImageDrawPixel(Image *dst, int posX, int posY, Color color); // Draw pixel within an image +RLAPI void ImageDrawPixelV(Image *dst, Vector2 position, Color color); // Draw pixel within an image (Vector version) +RLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw line within an image +RLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color); // Draw line within an image (Vector version) +RLAPI void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color color); // Draw a line defining thickness within an image +RLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw a filled circle within an image +RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw a filled circle within an image (Vector version) +RLAPI void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle outline within an image +RLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color); // Draw circle outline within an image (Vector version) +RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color); // Draw rectangle within an image +RLAPI void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color); // Draw rectangle within an image (Vector version) +RLAPI void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color); // Draw rectangle within an image +RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color); // Draw rectangle lines within an image +RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle within an image +RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image +RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline within an image +RLAPI void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center) +RLAPI void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image +RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source) +RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination) +RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination) + +// Texture loading functions +// NOTE: These functions require GPU access +RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) +RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data +RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout); // Load cubemap from image, multiple image cubemap layouts supported +RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) +RLAPI bool IsTextureValid(Texture2D texture); // Check if a texture is valid (loaded in GPU) +RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) +RLAPI bool IsRenderTextureValid(RenderTexture2D target); // Check if a render texture is valid (loaded in GPU) +RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) +RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data +RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data + +// Texture configuration functions +RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture +RLAPI void SetTextureFilter(Texture2D texture, int filter); // Set texture scaling filter mode +RLAPI void SetTextureWrap(Texture2D texture, int wrap); // Set texture wrapping mode + +// Texture drawing functions +RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D +RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 +RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters +RLAPI void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle +RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters +RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely + +// Color/pixel related functions +RLAPI bool ColorIsEqual(Color col1, Color col2); // Check if two colors are equal +RLAPI Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f +RLAPI int ColorToInt(Color color); // Get hexadecimal value for a Color (0xRRGGBBAA) +RLAPI Vector4 ColorNormalize(Color color); // Get Color normalized as float [0..1] +RLAPI Color ColorFromNormalized(Vector4 normalized); // Get Color from normalized values [0..1] +RLAPI Vector3 ColorToHSV(Color color); // Get HSV values for a Color, hue [0..360], saturation/value [0..1] +RLAPI Color ColorFromHSV(float hue, float saturation, float value); // Get a Color from HSV values, hue [0..360], saturation/value [0..1] +RLAPI Color ColorTint(Color color, Color tint); // Get color multiplied with another color +RLAPI Color ColorBrightness(Color color, float factor); // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f +RLAPI Color ColorContrast(Color color, float contrast); // Get color with contrast correction, contrast values between -1.0f and 1.0f +RLAPI Color ColorAlpha(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f +RLAPI Color ColorAlphaBlend(Color dst, Color src, Color tint); // Get src alpha-blended into dst color with tint +RLAPI Color ColorLerp(Color color1, Color color2, float factor); // Get color lerp interpolation between two colors, factor [0.0f..1.0f] +RLAPI Color GetColor(unsigned int hexValue); // Get Color structure from hexadecimal value +RLAPI Color GetPixelColor(void *srcPtr, int format); // Get Color from a source pixel pointer of certain format +RLAPI void SetPixelColor(void *dstPtr, Color color, int format); // Set color formatted into destination pixel pointer +RLAPI int GetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes for certain format + +//------------------------------------------------------------------------------------ +// Font Loading and Text Drawing Functions (Module: text) +//------------------------------------------------------------------------------------ + +// Font loading/unloading functions +RLAPI Font GetFontDefault(void); // Get the default Font +RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) +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, font size is provided in pixels height +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 *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' +RLAPI bool IsFontValid(Font font); // Check if a font is valid (font data loaded, WARNING: GPU texture not checked) +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 *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info +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 bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success + +// Text drawing functions +RLAPI void DrawFPS(int posX, int posY); // Draw current FPS +RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) +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 DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one 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 +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 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 GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found +RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found + +// Text codepoints management functions (unicode characters) +RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array +RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array +RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter +RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory +RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string +RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) + +// Text strings management functions (no UTF-8 strings, only byte chars) +// NOTE: Some strings allocate memory internally for returned strings, just be careful! +RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied +RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal +RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending +RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf() style) +RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string +RLAPI char *TextReplace(const char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!) +RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!) +RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter +RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! +RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string +RLAPI const char *TextToUpper(const char *text); // Get upper case version of provided string +RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string +RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string +RLAPI const char *TextToSnake(const char *text); // Get Snake case notation version of provided string +RLAPI const char *TextToCamel(const char *text); // Get Camel case notation version of provided string + +RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) +RLAPI float TextToFloat(const char *text); // Get float value from text (negative values not supported) + +//------------------------------------------------------------------------------------ +// Basic 3d Shapes Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ + +// Basic geometric 3D shapes drawing functions +RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space +RLAPI void DrawPoint3D(Vector3 position, Color color); // Draw a point in 3D space, actually a small line +RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space +RLAPI void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!) +RLAPI void DrawTriangleStrip3D(const Vector3 *points, int pointCount, Color color); // Draw a triangle strip defined by points +RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color); // Draw cube +RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) +RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); // Draw cube wires +RLAPI void DrawCubeWiresV(Vector3 position, Vector3 size, Color color); // Draw cube wires (Vector version) +RLAPI void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere +RLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters +RLAPI void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires +RLAPI void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone +RLAPI void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder with base at startPos and top at endPos +RLAPI void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires +RLAPI void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder wires with base at startPos and top at endPos +RLAPI void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw a capsule with the center of its sphere caps at startPos and endPos +RLAPI void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw capsule wireframe with the center of its sphere caps at startPos and endPos +RLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ +RLAPI void DrawRay(Ray ray, Color color); // Draw a ray line +RLAPI void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) + +//------------------------------------------------------------------------------------ +// Model 3d Loading and Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ + +// Model management functions +RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) +RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh (default material) +RLAPI bool IsModelValid(Model model); // Check if a model is valid (loaded in GPU, VAO/VBOs) +RLAPI void UnloadModel(Model model); // Unload model (including meshes) from memory (RAM and/or VRAM) +RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes) + +// Model drawing functions +RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) +RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters +RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) +RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters +RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points +RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters +RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) +RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture +RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source +RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation + +// Mesh management functions +RLAPI void UploadMesh(Mesh *mesh, bool dynamic); // Upload mesh vertex data in GPU and provide VAO/VBO ids +RLAPI void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset); // Update mesh vertex data in GPU for a specific buffer index +RLAPI void UnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU +RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform +RLAPI void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms +RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits +RLAPI void GenMeshTangents(Mesh *mesh); // Compute mesh tangents +RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success +RLAPI bool ExportMeshAsCode(Mesh mesh, const char *fileName); // Export mesh as code file (.h) defining multiple arrays of vertex attributes + +// Mesh generation functions +RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh +RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ); // Generate plane mesh (with subdivisions) +RLAPI Mesh GenMeshCube(float width, float height, float length); // Generate cuboid mesh +RLAPI Mesh GenMeshSphere(float radius, int rings, int slices); // Generate sphere mesh (standard sphere) +RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices); // Generate half-sphere mesh (no bottom cap) +RLAPI Mesh GenMeshCylinder(float radius, float height, int slices); // Generate cylinder mesh +RLAPI Mesh GenMeshCone(float radius, float height, int slices); // Generate cone/pyramid mesh +RLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides); // Generate torus mesh +RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); // Generate trefoil knot mesh +RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data +RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data + +// Material loading/unloading functions +RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file +RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +RLAPI bool IsMaterialValid(Material material); // Check if a material is valid (shader assigned, map textures loaded in GPU) +RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) +RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture); // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) +RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh + +// Model animations loading/unloading functions +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 (CPU) +RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning) +RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data +RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data +RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match + +// Collision detection functions +RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres +RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Check collision between two bounding boxes +RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere +RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere +RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box +RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh +RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle +RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad + +//------------------------------------------------------------------------------------ +// Audio Loading and Playing Functions (Module: audio) +//------------------------------------------------------------------------------------ +typedef void (*AudioCallback)(void *bufferData, unsigned int frames); + +// Audio device management functions +RLAPI void InitAudioDevice(void); // Initialize 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 void SetMasterVolume(float volume); // Set master volume (listener) +RLAPI float GetMasterVolume(void); // Get master volume (listener) + +// Wave/Sound loading/unloading functions +RLAPI Wave LoadWave(const char *fileName); // Load wave data from file +RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav' +RLAPI bool IsWaveValid(Wave wave); // Checks if wave data is valid (data loaded and parameters) +RLAPI Sound LoadSound(const char *fileName); // Load sound from file +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 IsSoundValid(Sound sound); // Checks if a sound is valid (data loaded and buffers initialized) +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 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 ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success + +// Wave/Sound management functions +RLAPI void PlaySound(Sound sound); // Play a sound +RLAPI void StopSound(Sound sound); // Stop playing a sound +RLAPI void PauseSound(Sound sound); // Pause a sound +RLAPI void ResumeSound(Sound sound); // Resume a paused sound +RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing +RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) +RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) +RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (0.5 is center) +RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave +RLAPI void WaveCrop(Wave *wave, int initFrame, int finalFrame); // Crop a wave to defined frames range +RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format +RLAPI float *LoadWaveSamples(Wave wave); // Load samples data from wave as a 32bit float data array +RLAPI void UnloadWaveSamples(float *samples); // Unload samples data loaded with LoadWaveSamples() + +// Music management functions +RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file +RLAPI Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize); // Load music stream from data +RLAPI bool IsMusicValid(Music music); // Checks if a music stream is valid (context and buffers initialized) +RLAPI void UnloadMusicStream(Music music); // Unload music stream +RLAPI void PlayMusicStream(Music music); // Start music playing +RLAPI bool IsMusicStreamPlaying(Music music); // Check if music is playing +RLAPI void UpdateMusicStream(Music music); // Updates buffers for music streaming +RLAPI void StopMusicStream(Music music); // Stop music playing +RLAPI void PauseMusicStream(Music music); // Pause music playing +RLAPI void ResumeMusicStream(Music music); // Resume playing paused music +RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds) +RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) +RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) +RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (0.5 is center) +RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds) +RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) + +// AudioStream management functions +RLAPI AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Load audio stream (to stream raw audio pcm data) +RLAPI bool IsAudioStreamValid(AudioStream stream); // Checks if an audio stream is valid (buffers initialized) +RLAPI void UnloadAudioStream(AudioStream stream); // Unload audio stream and free memory +RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int frameCount); // Update audio stream buffers with data +RLAPI bool IsAudioStreamProcessed(AudioStream stream); // Check if any audio stream buffers requires refill +RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream +RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream +RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream +RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing +RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream +RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) +RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) +RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered) +RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams +RLAPI void 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, receives the samples as 'float' +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, receives the samples as 'float' +RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline + +#if defined(__cplusplus) +} +#endif + +#endif // RAYLIB_H diff --git a/Game of Life/include/raymath.h b/Game of Life/include/raymath.h new file mode 100644 index 0000000..ed4eda1 --- /dev/null +++ b/Game of Life/include/raymath.h @@ -0,0 +1,2941 @@ +/********************************************************************************************** +* +* raymath v2.0 - Math functions to work with Vector2, Vector3, Matrix and Quaternions +* +* 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 (except C++ operators) +* - 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 +* 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. +* +* #define RAYMATH_STATIC_INLINE +* Define static inline functions code, so #include header suffices for use. +* This may use up lots of memory. +* +* #define RAYMATH_DISABLE_CPP_OPERATORS +* Disables C++ operator overloads for raymath types. +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2015-2024 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 RAYMATH_H +#define RAYMATH_H + +#if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_STATIC_INLINE) + #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_STATIC_INLINE is contradictory" +#endif + +// Function specifiers definition +#if defined(RAYMATH_IMPLEMENTATION) + #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) + #define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll) + #elif defined(BUILD_LIBTYPE_SHARED) + #define RMAPI __attribute__((visibility("default"))) // We are building raylib as a Unix shared library (.so/.dylib) + #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) + #define RMAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) + #else + #define RMAPI extern inline // Provide external definition + #endif +#elif defined(RAYMATH_STATIC_INLINE) + #define RMAPI static inline // Functions may be inlined, no external out-of-line definition +#else + #if defined(__TINYC__) + #define RMAPI static inline // plain inline not supported by tinycc (See issue #435) + #else + #define RMAPI inline // Functions may be inlined or external definition used + #endif +#endif + + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef PI + #define PI 3.14159265358979323846f +#endif + +#ifndef EPSILON + #define EPSILON 0.000001f +#endif + +#ifndef DEG2RAD + #define DEG2RAD (PI/180.0f) +#endif + +#ifndef RAD2DEG + #define RAD2DEG (180.0f/PI) +#endif + +// Get float vector for Matrix +#ifndef MatrixToFloat + #define MatrixToFloat(mat) (MatrixToFloatV(mat).v) +#endif + +// Get float vector for Vector3 +#ifndef Vector3ToFloat + #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +#if !defined(RL_VECTOR2_TYPE) +// Vector2 type +typedef struct Vector2 { + float x; + float y; +} Vector2; +#define RL_VECTOR2_TYPE +#endif + +#if !defined(RL_VECTOR3_TYPE) +// Vector3 type +typedef struct Vector3 { + float x; + float y; + float z; +} Vector3; +#define RL_VECTOR3_TYPE +#endif + +#if !defined(RL_VECTOR4_TYPE) +// Vector4 type +typedef struct Vector4 { + float x; + float y; + float z; + float w; +} Vector4; +#define RL_VECTOR4_TYPE +#endif + +#if !defined(RL_QUATERNION_TYPE) +// Quaternion type +typedef Vector4 Quaternion; +#define RL_QUATERNION_TYPE +#endif + +#if !defined(RL_MATRIX_TYPE) +// Matrix type (OpenGL style 4x4 - right handed, column major) +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; +#define RL_MATRIX_TYPE +#endif + +// NOTE: Helper types to be used instead of array return types for *ToFloat functions +typedef struct float3 { + float v[3]; +} float3; + +typedef struct float16 { + float v[16]; +} float16; + +#include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf() + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Utils math +//---------------------------------------------------------------------------------- + +// Clamp float value +RMAPI float Clamp(float value, float min, float max) +{ + float result = (value < min)? min : value; + + if (result > max) result = max; + + return result; +} + +// Calculate linear interpolation between two floats +RMAPI float Lerp(float start, float end, float amount) +{ + float result = start + amount*(end - start); + + return result; +} + +// Normalize input value within input range +RMAPI float Normalize(float value, float start, float end) +{ + float result = (value - start)/(end - start); + + return result; +} + +// Remap input value within input range to output range +RMAPI float Remap(float value, float inputStart, float inputEnd, float outputStart, float outputEnd) +{ + float result = (value - inputStart)/(inputEnd - inputStart)*(outputEnd - outputStart) + outputStart; + + return result; +} + +// Wrap input value from min to max +RMAPI float Wrap(float value, float min, float max) +{ + float result = value - (max - min)*floorf((value - min)/(max - min)); + + return result; +} + +// Check whether two given floats are almost equal +RMAPI int FloatEquals(float x, float y) +{ +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + + int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); + + return result; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vector2 math +//---------------------------------------------------------------------------------- + +// Vector with components value 0.0f +RMAPI Vector2 Vector2Zero(void) +{ + Vector2 result = { 0.0f, 0.0f }; + + return result; +} + +// Vector with components value 1.0f +RMAPI Vector2 Vector2One(void) +{ + Vector2 result = { 1.0f, 1.0f }; + + return result; +} + +// Add two vectors (v1 + v2) +RMAPI Vector2 Vector2Add(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x + v2.x, v1.y + v2.y }; + + return result; +} + +// Add vector and float value +RMAPI Vector2 Vector2AddValue(Vector2 v, float add) +{ + Vector2 result = { v.x + add, v.y + add }; + + return result; +} + +// Subtract two vectors (v1 - v2) +RMAPI Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x - v2.x, v1.y - v2.y }; + + return result; +} + +// Subtract vector by float value +RMAPI Vector2 Vector2SubtractValue(Vector2 v, float sub) +{ + Vector2 result = { v.x - sub, v.y - sub }; + + return result; +} + +// Calculate vector length +RMAPI float Vector2Length(Vector2 v) +{ + float result = sqrtf((v.x*v.x) + (v.y*v.y)); + + return result; +} + +// Calculate vector square length +RMAPI float Vector2LengthSqr(Vector2 v) +{ + float result = (v.x*v.x) + (v.y*v.y); + + return result; +} + +// Calculate two vectors dot product +RMAPI float Vector2DotProduct(Vector2 v1, Vector2 v2) +{ + float result = (v1.x*v2.x + v1.y*v2.y); + + return result; +} + +// Calculate distance between two vectors +RMAPI float Vector2Distance(Vector2 v1, Vector2 v2) +{ + float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); + + return result; +} + +// Calculate square distance between two vectors +RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) +{ + float result = ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); + + return result; +} + +// Calculate angle between two vectors +// NOTE: Angle is calculated from origin point (0, 0) +RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) +{ + 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; +} + +// Calculate angle defined by a two vectors line +// NOTE: Parameters need to be normalized +// Current implementation should be aligned with glm::angle +RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) +{ + float result = 0.0f; + + // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior + result = -atan2f(end.y - start.y, end.x - start.x); + + return result; +} + +// Scale vector (multiply by value) +RMAPI Vector2 Vector2Scale(Vector2 v, float scale) +{ + Vector2 result = { v.x*scale, v.y*scale }; + + return result; +} + +// Multiply vector by vector +RMAPI Vector2 Vector2Multiply(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x*v2.x, v1.y*v2.y }; + + return result; +} + +// Negate vector +RMAPI Vector2 Vector2Negate(Vector2 v) +{ + Vector2 result = { -v.x, -v.y }; + + return result; +} + +// Divide vector by vector +RMAPI Vector2 Vector2Divide(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x/v2.x, v1.y/v2.y }; + + return result; +} + +// Normalize provided vector +RMAPI Vector2 Vector2Normalize(Vector2 v) +{ + Vector2 result = { 0 }; + float length = sqrtf((v.x*v.x) + (v.y*v.y)); + + if (length > 0) + { + float ilength = 1.0f/length; + result.x = v.x*ilength; + result.y = v.y*ilength; + } + + return result; +} + +// Transforms a Vector2 by a given Matrix +RMAPI Vector2 Vector2Transform(Vector2 v, Matrix mat) +{ + Vector2 result = { 0 }; + + float x = v.x; + float y = v.y; + float z = 0; + + result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; + result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; + + return result; +} + +// Calculate linear interpolation between two vectors +RMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount) +{ + Vector2 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + + return result; +} + +// Calculate reflected vector to normal +RMAPI Vector2 Vector2Reflect(Vector2 v, Vector2 normal) +{ + Vector2 result = { 0 }; + + float dotProduct = (v.x*normal.x + v.y*normal.y); // Dot product + + result.x = v.x - (2.0f*normal.x)*dotProduct; + result.y = v.y - (2.0f*normal.y)*dotProduct; + + return result; +} + +// Get min value for each pair of components +RMAPI Vector2 Vector2Min(Vector2 v1, Vector2 v2) +{ + Vector2 result = { 0 }; + + result.x = fminf(v1.x, v2.x); + result.y = fminf(v1.y, v2.y); + + return result; +} + +// Get max value for each pair of components +RMAPI Vector2 Vector2Max(Vector2 v1, Vector2 v2) +{ + Vector2 result = { 0 }; + + result.x = fmaxf(v1.x, v2.x); + result.y = fmaxf(v1.y, v2.y); + + return result; +} + +// Rotate vector by angle +RMAPI Vector2 Vector2Rotate(Vector2 v, float angle) +{ + Vector2 result = { 0 }; + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.x = v.x*cosres - v.y*sinres; + result.y = v.x*sinres + v.y*cosres; + + return result; +} + +// Move Vector towards target +RMAPI Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance) +{ + Vector2 result = { 0 }; + + float dx = target.x - v.x; + float dy = target.y - v.y; + float value = (dx*dx) + (dy*dy); + + if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; + + float dist = sqrtf(value); + + result.x = v.x + dx/dist*maxDistance; + result.y = v.y + dy/dist*maxDistance; + + return result; +} + +// Invert the given vector +RMAPI Vector2 Vector2Invert(Vector2 v) +{ + Vector2 result = { 1.0f/v.x, 1.0f/v.y }; + + return result; +} + +// Clamp the components of the vector between +// min and max values specified by the given vectors +RMAPI Vector2 Vector2Clamp(Vector2 v, Vector2 min, Vector2 max) +{ + Vector2 result = { 0 }; + + result.x = fminf(max.x, fmaxf(min.x, v.x)); + result.y = fminf(max.y, fmaxf(min.y, v.y)); + + return result; +} + +// Clamp the magnitude of the vector between two min and max values +RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max) +{ + Vector2 result = v; + + float length = (v.x*v.x) + (v.y*v.y); + if (length > 0.0f) + { + length = sqrtf(length); + + float scale = 1; // By default, 1 as the neutral element. + if (length < min) + { + scale = min/length; + } + else if (length > max) + { + scale = max/length; + } + + result.x = v.x*scale; + result.y = v.y*scale; + } + + return result; +} + +// Check whether two given vectors are almost equal +RMAPI int Vector2Equals(Vector2 p, Vector2 q) +{ +#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))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))); + + return result; +} + +// Compute the direction of a refracted ray +// v: normalized direction of the incoming ray +// n: normalized normal vector of the interface of two optical media +// r: ratio of the refractive index of the medium from where the ray comes +// to the refractive index of the medium on the other side of the surface +RMAPI Vector2 Vector2Refract(Vector2 v, Vector2 n, float r) +{ + Vector2 result = { 0 }; + + float dot = v.x*n.x + v.y*n.y; + float d = 1.0f - r*r*(1.0f - dot*dot); + + if (d >= 0.0f) + { + d = sqrtf(d); + v.x = r*v.x - (r*dot + d)*n.x; + v.y = r*v.y - (r*dot + d)*n.y; + + result = v; + } + + return result; +} + + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vector3 math +//---------------------------------------------------------------------------------- + +// Vector with components value 0.0f +RMAPI Vector3 Vector3Zero(void) +{ + Vector3 result = { 0.0f, 0.0f, 0.0f }; + + return result; +} + +// Vector with components value 1.0f +RMAPI Vector3 Vector3One(void) +{ + Vector3 result = { 1.0f, 1.0f, 1.0f }; + + return result; +} + +// Add two vectors +RMAPI Vector3 Vector3Add(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; + + return result; +} + +// Add vector and float value +RMAPI Vector3 Vector3AddValue(Vector3 v, float add) +{ + Vector3 result = { v.x + add, v.y + add, v.z + add }; + + return result; +} + +// Subtract two vectors +RMAPI Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; + + return result; +} + +// Subtract vector by float value +RMAPI Vector3 Vector3SubtractValue(Vector3 v, float sub) +{ + Vector3 result = { v.x - sub, v.y - sub, v.z - sub }; + + return result; +} + +// Multiply vector by scalar +RMAPI Vector3 Vector3Scale(Vector3 v, float scalar) +{ + Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; + + return result; +} + +// Multiply vector by vector +RMAPI Vector3 Vector3Multiply(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; + + return result; +} + +// Calculate two vectors cross product +RMAPI Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; + + return result; +} + +// Calculate one vector perpendicular vector +RMAPI Vector3 Vector3Perpendicular(Vector3 v) +{ + Vector3 result = { 0 }; + + float min = fabsf(v.x); + Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; + + if (fabsf(v.y) < min) + { + min = fabsf(v.y); + Vector3 tmp = {0.0f, 1.0f, 0.0f}; + cardinalAxis = tmp; + } + + if (fabsf(v.z) < min) + { + Vector3 tmp = {0.0f, 0.0f, 1.0f}; + cardinalAxis = tmp; + } + + // Cross product between vectors + result.x = v.y*cardinalAxis.z - v.z*cardinalAxis.y; + result.y = v.z*cardinalAxis.x - v.x*cardinalAxis.z; + result.z = v.x*cardinalAxis.y - v.y*cardinalAxis.x; + + return result; +} + +// Calculate vector length +RMAPI float Vector3Length(const Vector3 v) +{ + float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + + return result; +} + +// Calculate vector square length +RMAPI float Vector3LengthSqr(const Vector3 v) +{ + float result = v.x*v.x + v.y*v.y + v.z*v.z; + + return result; +} + +// Calculate two vectors dot product +RMAPI float Vector3DotProduct(Vector3 v1, Vector3 v2) +{ + float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + + return result; +} + +// Calculate distance between two vectors +RMAPI float Vector3Distance(Vector3 v1, Vector3 v2) +{ + float result = 0.0f; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + float dz = v2.z - v1.z; + result = sqrtf(dx*dx + dy*dy + dz*dz); + + return result; +} + +// Calculate square distance between two vectors +RMAPI float Vector3DistanceSqr(Vector3 v1, Vector3 v2) +{ + float result = 0.0f; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + float dz = v2.z - v1.z; + result = dx*dx + dy*dy + dz*dz; + + return result; +} + +// Calculate angle between two vectors +RMAPI float Vector3Angle(Vector3 v1, Vector3 v2) +{ + float result = 0.0f; + + Vector3 cross = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; + float len = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z); + float dot = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + result = atan2f(len, dot); + + return result; +} + +// Negate provided vector (invert direction) +RMAPI Vector3 Vector3Negate(Vector3 v) +{ + Vector3 result = { -v.x, -v.y, -v.z }; + + return result; +} + +// Divide vector by vector +RMAPI Vector3 Vector3Divide(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; + + return result; +} + +// Normalize provided vector +RMAPI Vector3 Vector3Normalize(Vector3 v) +{ + Vector3 result = v; + + float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length != 0.0f) + { + float ilength = 1.0f/length; + + result.x *= ilength; + result.y *= 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; +} + +// Orthonormalize provided vectors +// Makes vectors normalized and orthogonal to each other +// Gram-Schmidt function implementation +RMAPI void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2) +{ + float length = 0.0f; + float ilength = 0.0f; + + // Vector3Normalize(*v1); + Vector3 v = *v1; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + v1->x *= ilength; + v1->y *= ilength; + v1->z *= ilength; + + // Vector3CrossProduct(*v1, *v2) + Vector3 vn1 = { v1->y*v2->z - v1->z*v2->y, v1->z*v2->x - v1->x*v2->z, v1->x*v2->y - v1->y*v2->x }; + + // Vector3Normalize(vn1); + v = vn1; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + vn1.x *= ilength; + vn1.y *= ilength; + vn1.z *= ilength; + + // Vector3CrossProduct(vn1, *v1) + Vector3 vn2 = { vn1.y*v1->z - vn1.z*v1->y, vn1.z*v1->x - vn1.x*v1->z, vn1.x*v1->y - vn1.y*v1->x }; + + *v2 = vn2; +} + +// Transforms a Vector3 by a given Matrix +RMAPI Vector3 Vector3Transform(Vector3 v, Matrix mat) +{ + Vector3 result = { 0 }; + + float x = v.x; + float y = v.y; + float z = v.z; + + result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; + result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; + result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; + + return result; +} + +// Transform a vector by quaternion rotation +RMAPI Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) +{ + Vector3 result = { 0 }; + + result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); + result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); + result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); + + return result; +} + +// Rotates a vector around an axis +RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) +{ + // Using Euler-Rodrigues Formula + // Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula + + Vector3 result = v; + + // Vector3Normalize(axis); + float length = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + axis.x *= ilength; + axis.y *= ilength; + axis.z *= ilength; + + angle /= 2.0f; + float a = sinf(angle); + float b = axis.x*a; + float c = axis.y*a; + float d = axis.z*a; + a = cosf(angle); + Vector3 w = { b, c, d }; + + // Vector3CrossProduct(w, v) + Vector3 wv = { w.y*v.z - w.z*v.y, w.z*v.x - w.x*v.z, w.x*v.y - w.y*v.x }; + + // Vector3CrossProduct(w, wv) + Vector3 wwv = { w.y*wv.z - w.z*wv.y, w.z*wv.x - w.x*wv.z, w.x*wv.y - w.y*wv.x }; + + // Vector3Scale(wv, 2*a) + a *= 2; + wv.x *= a; + wv.y *= a; + wv.z *= a; + + // Vector3Scale(wwv, 2) + wwv.x *= 2; + wwv.y *= 2; + wwv.z *= 2; + + result.x += wv.x; + result.y += wv.y; + result.z += wv.z; + + result.x += wwv.x; + result.y += wwv.y; + result.z += wwv.z; + + return result; +} + +// Move Vector towards target +RMAPI Vector3 Vector3MoveTowards(Vector3 v, Vector3 target, float maxDistance) +{ + Vector3 result = { 0 }; + + float dx = target.x - v.x; + float dy = target.y - v.y; + float dz = target.z - v.z; + float value = (dx*dx) + (dy*dy) + (dz*dz); + + if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; + + float dist = sqrtf(value); + + result.x = v.x + dx/dist*maxDistance; + result.y = v.y + dy/dist*maxDistance; + result.z = v.z + dz/dist*maxDistance; + + return result; +} + +// Calculate linear interpolation between two vectors +RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) +{ + Vector3 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + result.z = v1.z + amount*(v2.z - v1.z); + + return result; +} + +// Calculate cubic hermite interpolation between two vectors and their tangents +// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic +RMAPI Vector3 Vector3CubicHermite(Vector3 v1, Vector3 tangent1, Vector3 v2, Vector3 tangent2, float amount) +{ + Vector3 result = { 0 }; + + float amountPow2 = amount*amount; + float amountPow3 = amount*amount*amount; + + result.x = (2*amountPow3 - 3*amountPow2 + 1)*v1.x + (amountPow3 - 2*amountPow2 + amount)*tangent1.x + (-2*amountPow3 + 3*amountPow2)*v2.x + (amountPow3 - amountPow2)*tangent2.x; + result.y = (2*amountPow3 - 3*amountPow2 + 1)*v1.y + (amountPow3 - 2*amountPow2 + amount)*tangent1.y + (-2*amountPow3 + 3*amountPow2)*v2.y + (amountPow3 - amountPow2)*tangent2.y; + result.z = (2*amountPow3 - 3*amountPow2 + 1)*v1.z + (amountPow3 - 2*amountPow2 + amount)*tangent1.z + (-2*amountPow3 + 3*amountPow2)*v2.z + (amountPow3 - amountPow2)*tangent2.z; + + return result; +} + +// Calculate reflected vector to normal +RMAPI Vector3 Vector3Reflect(Vector3 v, Vector3 normal) +{ + Vector3 result = { 0 }; + + // I is the original vector + // N is the normal of the incident plane + // R = I - (2*N*(DotProduct[I, N])) + + float dotProduct = (v.x*normal.x + v.y*normal.y + v.z*normal.z); + + result.x = v.x - (2.0f*normal.x)*dotProduct; + result.y = v.y - (2.0f*normal.y)*dotProduct; + result.z = v.z - (2.0f*normal.z)*dotProduct; + + return result; +} + +// Get min value for each pair of components +RMAPI Vector3 Vector3Min(Vector3 v1, Vector3 v2) +{ + Vector3 result = { 0 }; + + result.x = fminf(v1.x, v2.x); + result.y = fminf(v1.y, v2.y); + result.z = fminf(v1.z, v2.z); + + return result; +} + +// Get max value for each pair of components +RMAPI Vector3 Vector3Max(Vector3 v1, Vector3 v2) +{ + Vector3 result = { 0 }; + + result.x = fmaxf(v1.x, v2.x); + result.y = fmaxf(v1.y, v2.y); + result.z = fmaxf(v1.z, v2.z); + + return result; +} + +// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) +// NOTE: Assumes P is on the plane of the triangle +RMAPI Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) +{ + Vector3 result = { 0 }; + + Vector3 v0 = { b.x - a.x, b.y - a.y, b.z - a.z }; // Vector3Subtract(b, a) + Vector3 v1 = { c.x - a.x, c.y - a.y, c.z - a.z }; // Vector3Subtract(c, a) + Vector3 v2 = { p.x - a.x, p.y - a.y, p.z - a.z }; // Vector3Subtract(p, a) + float d00 = (v0.x*v0.x + v0.y*v0.y + v0.z*v0.z); // Vector3DotProduct(v0, v0) + float d01 = (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z); // Vector3DotProduct(v0, v1) + float d11 = (v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); // Vector3DotProduct(v1, v1) + float d20 = (v2.x*v0.x + v2.y*v0.y + v2.z*v0.z); // Vector3DotProduct(v2, v0) + float d21 = (v2.x*v1.x + v2.y*v1.y + v2.z*v1.z); // Vector3DotProduct(v2, v1) + + float denom = d00*d11 - d01*d01; + + result.y = (d11*d20 - d01*d21)/denom; + result.z = (d00*d21 - d01*d20)/denom; + result.x = 1.0f - (result.z + result.y); + + return result; +} + +// Projects a Vector3 from screen space into object space +// NOTE: We are avoiding calling other raymath functions despite available +RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) +{ + Vector3 result = { 0 }; + + // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it + Matrix matViewProj = { // MatrixMultiply(view, projection); + view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, + view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, + view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14, + view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15, + view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12, + view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13, + view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14, + view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15, + view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12, + view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13, + view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14, + view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15, + view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12, + view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13, + view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14, + view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 }; + + // Calculate inverted matrix -> MatrixInvert(matViewProj); + // Cache the matrix values (speed optimization) + float a00 = matViewProj.m0, a01 = matViewProj.m1, a02 = matViewProj.m2, a03 = matViewProj.m3; + float a10 = matViewProj.m4, a11 = matViewProj.m5, a12 = matViewProj.m6, a13 = matViewProj.m7; + float a20 = matViewProj.m8, a21 = matViewProj.m9, a22 = matViewProj.m10, a23 = matViewProj.m11; + float a30 = matViewProj.m12, a31 = matViewProj.m13, a32 = matViewProj.m14, a33 = matViewProj.m15; + + float b00 = a00*a11 - a01*a10; + float b01 = a00*a12 - a02*a10; + float b02 = a00*a13 - a03*a10; + float b03 = a01*a12 - a02*a11; + float b04 = a01*a13 - a03*a11; + float b05 = a02*a13 - a03*a12; + float b06 = a20*a31 - a21*a30; + float b07 = a20*a32 - a22*a30; + float b08 = a20*a33 - a23*a30; + float b09 = a21*a32 - a22*a31; + float b10 = a21*a33 - a23*a31; + float b11 = a22*a33 - a23*a32; + + // Calculate the invert determinant (inlined to avoid double-caching) + float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); + + Matrix matViewProjInv = { + (a11*b11 - a12*b10 + a13*b09)*invDet, + (-a01*b11 + a02*b10 - a03*b09)*invDet, + (a31*b05 - a32*b04 + a33*b03)*invDet, + (-a21*b05 + a22*b04 - a23*b03)*invDet, + (-a10*b11 + a12*b08 - a13*b07)*invDet, + (a00*b11 - a02*b08 + a03*b07)*invDet, + (-a30*b05 + a32*b02 - a33*b01)*invDet, + (a20*b05 - a22*b02 + a23*b01)*invDet, + (a10*b10 - a11*b08 + a13*b06)*invDet, + (-a00*b10 + a01*b08 - a03*b06)*invDet, + (a30*b04 - a31*b02 + a33*b00)*invDet, + (-a20*b04 + a21*b02 - a23*b00)*invDet, + (-a10*b09 + a11*b07 - a12*b06)*invDet, + (a00*b09 - a01*b07 + a02*b06)*invDet, + (-a30*b03 + a31*b01 - a32*b00)*invDet, + (a20*b03 - a21*b01 + a22*b00)*invDet }; + + // Create quaternion from source point + Quaternion quat = { source.x, source.y, source.z, 1.0f }; + + // Multiply quat point by unprojecte matrix + Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) + matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, + matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, + matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w, + matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w }; + + // Normalized world points in vectors + result.x = qtransformed.x/qtransformed.w; + result.y = qtransformed.y/qtransformed.w; + result.z = qtransformed.z/qtransformed.w; + + return result; +} + +// Get Vector3 as float array +RMAPI float3 Vector3ToFloatV(Vector3 v) +{ + float3 buffer = { 0 }; + + buffer.v[0] = v.x; + buffer.v[1] = v.y; + buffer.v[2] = v.z; + + return buffer; +} + +// Invert the given vector +RMAPI Vector3 Vector3Invert(Vector3 v) +{ + Vector3 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z }; + + return result; +} + +// Clamp the components of the vector between +// min and max values specified by the given vectors +RMAPI Vector3 Vector3Clamp(Vector3 v, Vector3 min, Vector3 max) +{ + Vector3 result = { 0 }; + + result.x = fminf(max.x, fmaxf(min.x, v.x)); + result.y = fminf(max.y, fmaxf(min.y, v.y)); + result.z = fminf(max.z, fmaxf(min.z, v.z)); + + return result; +} + +// Clamp the magnitude of the vector between two values +RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) +{ + Vector3 result = v; + + float length = (v.x*v.x) + (v.y*v.y) + (v.z*v.z); + if (length > 0.0f) + { + length = sqrtf(length); + + float scale = 1; // By default, 1 as the neutral element. + if (length < min) + { + scale = min/length; + } + else if (length > max) + { + scale = max/length; + } + + result.x = v.x*scale; + result.y = v.y*scale; + result.z = v.z*scale; + } + + return result; +} + +// Check whether two given vectors are almost equal +RMAPI int Vector3Equals(Vector3 p, Vector3 q) +{ +#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))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); + + return result; +} + +// Compute the direction of a refracted ray +// v: normalized direction of the incoming ray +// n: normalized normal vector of the interface of two optical media +// r: ratio of the refractive index of the medium from where the ray comes +// to the refractive index of the medium on the other side of the surface +RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) +{ + Vector3 result = { 0 }; + + float dot = v.x*n.x + v.y*n.y + v.z*n.z; + float d = 1.0f - r*r*(1.0f - dot*dot); + + if (d >= 0.0f) + { + d = sqrtf(d); + v.x = r*v.x - (r*dot + d)*n.x; + v.y = r*v.y - (r*dot + d)*n.y; + v.z = r*v.z - (r*dot + d)*n.z; + + result = v; + } + + return result; +} + + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vector4 math +//---------------------------------------------------------------------------------- + +RMAPI Vector4 Vector4Zero(void) +{ + Vector4 result = { 0.0f, 0.0f, 0.0f, 0.0f }; + return result; +} + +RMAPI Vector4 Vector4One(void) +{ + Vector4 result = { 1.0f, 1.0f, 1.0f, 1.0f }; + return result; +} + +RMAPI Vector4 Vector4Add(Vector4 v1, Vector4 v2) +{ + Vector4 result = { + v1.x + v2.x, + v1.y + v2.y, + v1.z + v2.z, + v1.w + v2.w + }; + return result; +} + +RMAPI Vector4 Vector4AddValue(Vector4 v, float add) +{ + Vector4 result = { + v.x + add, + v.y + add, + v.z + add, + v.w + add + }; + return result; +} + +RMAPI Vector4 Vector4Subtract(Vector4 v1, Vector4 v2) +{ + Vector4 result = { + v1.x - v2.x, + v1.y - v2.y, + v1.z - v2.z, + v1.w - v2.w + }; + return result; +} + +RMAPI Vector4 Vector4SubtractValue(Vector4 v, float add) +{ + Vector4 result = { + v.x - add, + v.y - add, + v.z - add, + v.w - add + }; + return result; +} + +RMAPI float Vector4Length(Vector4 v) +{ + float result = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w)); + return result; +} + +RMAPI float Vector4LengthSqr(Vector4 v) +{ + float result = (v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w); + return result; +} + +RMAPI float Vector4DotProduct(Vector4 v1, Vector4 v2) +{ + float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w); + return result; +} + +// Calculate distance between two vectors +RMAPI float Vector4Distance(Vector4 v1, Vector4 v2) +{ + float result = sqrtf( + (v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) + + (v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w)); + return result; +} + +// Calculate square distance between two vectors +RMAPI float Vector4DistanceSqr(Vector4 v1, Vector4 v2) +{ + float result = + (v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) + + (v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w); + + return result; +} + +RMAPI Vector4 Vector4Scale(Vector4 v, float scale) +{ + Vector4 result = { v.x*scale, v.y*scale, v.z*scale, v.w*scale }; + return result; +} + +// Multiply vector by vector +RMAPI Vector4 Vector4Multiply(Vector4 v1, Vector4 v2) +{ + Vector4 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z, v1.w*v2.w }; + return result; +} + +// Negate vector +RMAPI Vector4 Vector4Negate(Vector4 v) +{ + Vector4 result = { -v.x, -v.y, -v.z, -v.w }; + return result; +} + +// Divide vector by vector +RMAPI Vector4 Vector4Divide(Vector4 v1, Vector4 v2) +{ + Vector4 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z, v1.w/v2.w }; + return result; +} + +// Normalize provided vector +RMAPI Vector4 Vector4Normalize(Vector4 v) +{ + Vector4 result = { 0 }; + float length = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w)); + + if (length > 0) + { + float ilength = 1.0f/length; + result.x = v.x*ilength; + result.y = v.y*ilength; + result.z = v.z*ilength; + result.w = v.w*ilength; + } + + return result; +} + +// Get min value for each pair of components +RMAPI Vector4 Vector4Min(Vector4 v1, Vector4 v2) +{ + Vector4 result = { 0 }; + + result.x = fminf(v1.x, v2.x); + result.y = fminf(v1.y, v2.y); + result.z = fminf(v1.z, v2.z); + result.w = fminf(v1.w, v2.w); + + return result; +} + +// Get max value for each pair of components +RMAPI Vector4 Vector4Max(Vector4 v1, Vector4 v2) +{ + Vector4 result = { 0 }; + + result.x = fmaxf(v1.x, v2.x); + result.y = fmaxf(v1.y, v2.y); + result.z = fmaxf(v1.z, v2.z); + result.w = fmaxf(v1.w, v2.w); + + return result; +} + +// Calculate linear interpolation between two vectors +RMAPI Vector4 Vector4Lerp(Vector4 v1, Vector4 v2, float amount) +{ + Vector4 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + result.z = v1.z + amount*(v2.z - v1.z); + result.w = v1.w + amount*(v2.w - v1.w); + + return result; +} + +// Move Vector towards target +RMAPI Vector4 Vector4MoveTowards(Vector4 v, Vector4 target, float maxDistance) +{ + Vector4 result = { 0 }; + + float dx = target.x - v.x; + float dy = target.y - v.y; + float dz = target.z - v.z; + float dw = target.w - v.w; + float value = (dx*dx) + (dy*dy) + (dz*dz) + (dw*dw); + + if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; + + float dist = sqrtf(value); + + result.x = v.x + dx/dist*maxDistance; + result.y = v.y + dy/dist*maxDistance; + result.z = v.z + dz/dist*maxDistance; + result.w = v.w + dw/dist*maxDistance; + + return result; +} + +// Invert the given vector +RMAPI Vector4 Vector4Invert(Vector4 v) +{ + Vector4 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z, 1.0f/v.w }; + return result; +} + +// Check whether two given vectors are almost equal +RMAPI int Vector4Equals(Vector4 p, Vector4 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))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && + ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w))))); + return result; +} + + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Matrix math +//---------------------------------------------------------------------------------- + +// Compute matrix determinant +RMAPI float MatrixDeterminant(Matrix mat) +{ + float result = 0.0f; + + // Cache the matrix values (speed optimization) + float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; + float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; + float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; + float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; + + result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + + a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + + a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + + a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + + a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + + a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; + + return result; +} + +// Get the trace of the matrix (sum of the values along the diagonal) +RMAPI float MatrixTrace(Matrix mat) +{ + float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); + + return result; +} + +// Transposes provided matrix +RMAPI Matrix MatrixTranspose(Matrix mat) +{ + Matrix result = { 0 }; + + result.m0 = mat.m0; + result.m1 = mat.m4; + result.m2 = mat.m8; + result.m3 = mat.m12; + result.m4 = mat.m1; + result.m5 = mat.m5; + result.m6 = mat.m9; + result.m7 = mat.m13; + result.m8 = mat.m2; + result.m9 = mat.m6; + result.m10 = mat.m10; + result.m11 = mat.m14; + result.m12 = mat.m3; + result.m13 = mat.m7; + result.m14 = mat.m11; + result.m15 = mat.m15; + + return result; +} + +// Invert provided matrix +RMAPI Matrix MatrixInvert(Matrix mat) +{ + Matrix result = { 0 }; + + // Cache the matrix values (speed optimization) + float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; + float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; + float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; + float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; + + float b00 = a00*a11 - a01*a10; + float b01 = a00*a12 - a02*a10; + float b02 = a00*a13 - a03*a10; + float b03 = a01*a12 - a02*a11; + float b04 = a01*a13 - a03*a11; + float b05 = a02*a13 - a03*a12; + float b06 = a20*a31 - a21*a30; + float b07 = a20*a32 - a22*a30; + float b08 = a20*a33 - a23*a30; + float b09 = a21*a32 - a22*a31; + float b10 = a21*a33 - a23*a31; + float b11 = a22*a33 - a23*a32; + + // Calculate the invert determinant (inlined to avoid double-caching) + float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); + + result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; + result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; + result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; + result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; + result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; + result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; + result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; + result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; + result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; + result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; + result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; + result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; + result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; + result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; + result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; + result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; + + return result; +} + +// Get identity matrix +RMAPI Matrix MatrixIdentity(void) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Add two matrices +RMAPI Matrix MatrixAdd(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0 + right.m0; + result.m1 = left.m1 + right.m1; + result.m2 = left.m2 + right.m2; + result.m3 = left.m3 + right.m3; + result.m4 = left.m4 + right.m4; + result.m5 = left.m5 + right.m5; + result.m6 = left.m6 + right.m6; + result.m7 = left.m7 + right.m7; + result.m8 = left.m8 + right.m8; + result.m9 = left.m9 + right.m9; + result.m10 = left.m10 + right.m10; + result.m11 = left.m11 + right.m11; + result.m12 = left.m12 + right.m12; + result.m13 = left.m13 + right.m13; + result.m14 = left.m14 + right.m14; + result.m15 = left.m15 + right.m15; + + return result; +} + +// Subtract two matrices (left - right) +RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0 - right.m0; + result.m1 = left.m1 - right.m1; + result.m2 = left.m2 - right.m2; + result.m3 = left.m3 - right.m3; + result.m4 = left.m4 - right.m4; + result.m5 = left.m5 - right.m5; + result.m6 = left.m6 - right.m6; + result.m7 = left.m7 - right.m7; + result.m8 = left.m8 - right.m8; + result.m9 = left.m9 - right.m9; + result.m10 = left.m10 - right.m10; + result.m11 = left.m11 - right.m11; + result.m12 = left.m12 - right.m12; + result.m13 = left.m13 - right.m13; + result.m14 = left.m14 - right.m14; + result.m15 = left.m15 - right.m15; + + return result; +} + +// Get two matrix multiplication +// NOTE: When multiplying matrices... the order matters! +RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; + result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; + result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; + result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; + result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; + result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; + result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; + result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; + result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; + result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; + result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; + result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; + result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; + result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; + result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; + result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; + + return result; +} + +// Get translation matrix +RMAPI Matrix MatrixTranslate(float x, float y, float z) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, x, + 0.0f, 1.0f, 0.0f, y, + 0.0f, 0.0f, 1.0f, z, + 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Create rotation matrix from axis and angle +// NOTE: Angle should be provided in radians +RMAPI Matrix MatrixRotate(Vector3 axis, float angle) +{ + Matrix result = { 0 }; + + float x = axis.x, y = axis.y, z = axis.z; + + float lengthSquared = x*x + y*y + z*z; + + if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) + { + float ilength = 1.0f/sqrtf(lengthSquared); + x *= ilength; + y *= ilength; + z *= ilength; + } + + float sinres = sinf(angle); + float cosres = cosf(angle); + float t = 1.0f - cosres; + + result.m0 = x*x*t + cosres; + result.m1 = y*x*t + z*sinres; + result.m2 = z*x*t - y*sinres; + result.m3 = 0.0f; + + result.m4 = x*y*t - z*sinres; + result.m5 = y*y*t + cosres; + result.m6 = z*y*t + x*sinres; + result.m7 = 0.0f; + + result.m8 = x*z*t + y*sinres; + result.m9 = y*z*t - x*sinres; + result.m10 = z*z*t + cosres; + result.m11 = 0.0f; + + result.m12 = 0.0f; + result.m13 = 0.0f; + result.m14 = 0.0f; + result.m15 = 1.0f; + + return result; +} + +// Get x-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateX(float angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.m5 = cosres; + result.m6 = sinres; + result.m9 = -sinres; + result.m10 = cosres; + + return result; +} + +// Get y-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateY(float angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.m0 = cosres; + result.m2 = -sinres; + result.m8 = sinres; + result.m10 = cosres; + + return result; +} + +// Get z-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateZ(float angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.m0 = cosres; + result.m1 = sinres; + result.m4 = -sinres; + result.m5 = cosres; + + return result; +} + + +// Get xyz-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateXYZ(Vector3 angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosz = cosf(-angle.z); + float sinz = sinf(-angle.z); + float cosy = cosf(-angle.y); + float siny = sinf(-angle.y); + float cosx = cosf(-angle.x); + float sinx = sinf(-angle.x); + + result.m0 = cosz*cosy; + result.m1 = (cosz*siny*sinx) - (sinz*cosx); + result.m2 = (cosz*siny*cosx) + (sinz*sinx); + + result.m4 = sinz*cosy; + result.m5 = (sinz*siny*sinx) + (cosz*cosx); + result.m6 = (sinz*siny*cosx) - (cosz*sinx); + + result.m8 = -siny; + result.m9 = cosy*sinx; + result.m10= cosy*cosx; + + return result; +} + +// Get zyx-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateZYX(Vector3 angle) +{ + Matrix result = { 0 }; + + float cz = cosf(angle.z); + float sz = sinf(angle.z); + float cy = cosf(angle.y); + float sy = sinf(angle.y); + float cx = cosf(angle.x); + float sx = sinf(angle.x); + + result.m0 = cz*cy; + result.m4 = cz*sy*sx - cx*sz; + result.m8 = sz*sx + cz*cx*sy; + result.m12 = 0; + + result.m1 = cy*sz; + result.m5 = cz*cx + sz*sy*sx; + result.m9 = cx*sz*sy - cz*sx; + result.m13 = 0; + + result.m2 = -sy; + result.m6 = cy*sx; + result.m10 = cy*cx; + result.m14 = 0; + + result.m3 = 0; + result.m7 = 0; + result.m11 = 0; + result.m15 = 1; + + return result; +} + +// Get scaling matrix +RMAPI Matrix MatrixScale(float x, float y, float z) +{ + Matrix result = { x, 0.0f, 0.0f, 0.0f, + 0.0f, y, 0.0f, 0.0f, + 0.0f, 0.0f, z, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Get perspective projection matrix +RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, double nearPlane, double farPlane) +{ + Matrix result = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(farPlane - nearPlane); + + result.m0 = ((float)nearPlane*2.0f)/rl; + result.m1 = 0.0f; + result.m2 = 0.0f; + result.m3 = 0.0f; + + result.m4 = 0.0f; + result.m5 = ((float)nearPlane*2.0f)/tb; + result.m6 = 0.0f; + result.m7 = 0.0f; + + result.m8 = ((float)right + (float)left)/rl; + result.m9 = ((float)top + (float)bottom)/tb; + result.m10 = -((float)farPlane + (float)nearPlane)/fn; + result.m11 = -1.0f; + + result.m12 = 0.0f; + result.m13 = 0.0f; + result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; + result.m15 = 0.0f; + + return result; +} + +// Get perspective projection matrix +// NOTE: Fovy angle must be provided in radians +RMAPI Matrix MatrixPerspective(double fovY, double aspect, double nearPlane, double farPlane) +{ + Matrix result = { 0 }; + + double top = nearPlane*tan(fovY*0.5); + double bottom = -top; + double right = top*aspect; + double left = -right; + + // MatrixFrustum(-right, right, -top, top, near, far); + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(farPlane - nearPlane); + + result.m0 = ((float)nearPlane*2.0f)/rl; + result.m5 = ((float)nearPlane*2.0f)/tb; + result.m8 = ((float)right + (float)left)/rl; + result.m9 = ((float)top + (float)bottom)/tb; + result.m10 = -((float)farPlane + (float)nearPlane)/fn; + result.m11 = -1.0f; + result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; + + return result; +} + +// Get orthographic projection matrix +RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane) +{ + Matrix result = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(farPlane - nearPlane); + + result.m0 = 2.0f/rl; + result.m1 = 0.0f; + result.m2 = 0.0f; + result.m3 = 0.0f; + result.m4 = 0.0f; + result.m5 = 2.0f/tb; + result.m6 = 0.0f; + result.m7 = 0.0f; + result.m8 = 0.0f; + result.m9 = 0.0f; + result.m10 = -2.0f/fn; + result.m11 = 0.0f; + result.m12 = -((float)left + (float)right)/rl; + result.m13 = -((float)top + (float)bottom)/tb; + result.m14 = -((float)farPlane + (float)nearPlane)/fn; + result.m15 = 1.0f; + + return result; +} + +// Get camera look-at matrix (view matrix) +RMAPI Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) +{ + Matrix result = { 0 }; + + float length = 0.0f; + float ilength = 0.0f; + + // Vector3Subtract(eye, target) + Vector3 vz = { eye.x - target.x, eye.y - target.y, eye.z - target.z }; + + // Vector3Normalize(vz) + Vector3 v = vz; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + vz.x *= ilength; + vz.y *= ilength; + vz.z *= ilength; + + // Vector3CrossProduct(up, vz) + Vector3 vx = { up.y*vz.z - up.z*vz.y, up.z*vz.x - up.x*vz.z, up.x*vz.y - up.y*vz.x }; + + // Vector3Normalize(x) + v = vx; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + vx.x *= ilength; + vx.y *= ilength; + vx.z *= ilength; + + // Vector3CrossProduct(vz, vx) + Vector3 vy = { vz.y*vx.z - vz.z*vx.y, vz.z*vx.x - vz.x*vx.z, vz.x*vx.y - vz.y*vx.x }; + + result.m0 = vx.x; + result.m1 = vy.x; + result.m2 = vz.x; + result.m3 = 0.0f; + result.m4 = vx.y; + result.m5 = vy.y; + result.m6 = vz.y; + result.m7 = 0.0f; + result.m8 = vx.z; + result.m9 = vy.z; + result.m10 = vz.z; + result.m11 = 0.0f; + result.m12 = -(vx.x*eye.x + vx.y*eye.y + vx.z*eye.z); // Vector3DotProduct(vx, eye) + result.m13 = -(vy.x*eye.x + vy.y*eye.y + vy.z*eye.z); // Vector3DotProduct(vy, eye) + result.m14 = -(vz.x*eye.x + vz.y*eye.y + vz.z*eye.z); // Vector3DotProduct(vz, eye) + result.m15 = 1.0f; + + return result; +} + +// Get float array of matrix data +RMAPI float16 MatrixToFloatV(Matrix mat) +{ + float16 result = { 0 }; + + result.v[0] = mat.m0; + result.v[1] = mat.m1; + result.v[2] = mat.m2; + result.v[3] = mat.m3; + result.v[4] = mat.m4; + result.v[5] = mat.m5; + result.v[6] = mat.m6; + result.v[7] = mat.m7; + result.v[8] = mat.m8; + result.v[9] = mat.m9; + result.v[10] = mat.m10; + result.v[11] = mat.m11; + result.v[12] = mat.m12; + result.v[13] = mat.m13; + result.v[14] = mat.m14; + result.v[15] = mat.m15; + + return result; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Quaternion math +//---------------------------------------------------------------------------------- + +// Add two quaternions +RMAPI Quaternion QuaternionAdd(Quaternion q1, Quaternion q2) +{ + Quaternion result = {q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w}; + + return result; +} + +// Add quaternion and float value +RMAPI Quaternion QuaternionAddValue(Quaternion q, float add) +{ + Quaternion result = {q.x + add, q.y + add, q.z + add, q.w + add}; + + return result; +} + +// Subtract two quaternions +RMAPI Quaternion QuaternionSubtract(Quaternion q1, Quaternion q2) +{ + Quaternion result = {q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w}; + + return result; +} + +// Subtract quaternion and float value +RMAPI Quaternion QuaternionSubtractValue(Quaternion q, float sub) +{ + Quaternion result = {q.x - sub, q.y - sub, q.z - sub, q.w - sub}; + + return result; +} + +// Get identity quaternion +RMAPI Quaternion QuaternionIdentity(void) +{ + Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Computes the length of a quaternion +RMAPI float QuaternionLength(Quaternion q) +{ + float result = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + + return result; +} + +// Normalize provided quaternion +RMAPI Quaternion QuaternionNormalize(Quaternion q) +{ + Quaternion result = { 0 }; + + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + + return result; +} + +// Invert provided quaternion +RMAPI Quaternion QuaternionInvert(Quaternion q) +{ + Quaternion result = q; + + float lengthSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; + + if (lengthSq != 0.0f) + { + float invLength = 1.0f/lengthSq; + + result.x *= -invLength; + result.y *= -invLength; + result.z *= -invLength; + result.w *= invLength; + } + + return result; +} + +// Calculate two quaternion multiplication +RMAPI Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) +{ + Quaternion result = { 0 }; + + float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; + float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; + + result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; + result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; + result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; + result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; + + return result; +} + +// Scale quaternion by float value +RMAPI Quaternion QuaternionScale(Quaternion q, float mul) +{ + Quaternion result = { 0 }; + + result.x = q.x*mul; + result.y = q.y*mul; + result.z = q.z*mul; + result.w = q.w*mul; + + return result; +} + +// Divide two quaternions +RMAPI Quaternion QuaternionDivide(Quaternion q1, Quaternion q2) +{ + Quaternion result = { q1.x/q2.x, q1.y/q2.y, q1.z/q2.z, q1.w/q2.w }; + + return result; +} + +// Calculate linear interpolation between two quaternions +RMAPI Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = { 0 }; + + result.x = q1.x + amount*(q2.x - q1.x); + result.y = q1.y + amount*(q2.y - q1.y); + result.z = q1.z + amount*(q2.z - q1.z); + result.w = q1.w + amount*(q2.w - q1.w); + + return result; +} + +// Calculate slerp-optimized interpolation between two quaternions +RMAPI Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = { 0 }; + + // QuaternionLerp(q1, q2, amount) + result.x = q1.x + amount*(q2.x - q1.x); + result.y = q1.y + amount*(q2.y - q1.y); + result.z = q1.z + amount*(q2.z - q1.z); + result.w = q1.w + amount*(q2.w - q1.w); + + // QuaternionNormalize(q); + Quaternion q = result; + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + + return result; +} + +// Calculates spherical linear interpolation between two quaternions +RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) +{ + 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; + + if (cosHalfTheta < 0) + { + q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w; + cosHalfTheta = -cosHalfTheta; + } + + if (fabsf(cosHalfTheta) >= 1.0f) result = q1; + else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); + else + { + float halfTheta = acosf(cosHalfTheta); + float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); + + if (fabsf(sinHalfTheta) < EPSILON) + { + result.x = (q1.x*0.5f + q2.x*0.5f); + result.y = (q1.y*0.5f + q2.y*0.5f); + result.z = (q1.z*0.5f + q2.z*0.5f); + result.w = (q1.w*0.5f + q2.w*0.5f); + } + else + { + float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; + float ratioB = sinf(amount*halfTheta)/sinHalfTheta; + + result.x = (q1.x*ratioA + q2.x*ratioB); + result.y = (q1.y*ratioA + q2.y*ratioB); + result.z = (q1.z*ratioA + q2.z*ratioB); + result.w = (q1.w*ratioA + q2.w*ratioB); + } + } + + return result; +} + +// Calculate quaternion cubic spline interpolation using Cubic Hermite Spline algorithm +// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic +RMAPI Quaternion QuaternionCubicHermiteSpline(Quaternion q1, Quaternion outTangent1, Quaternion q2, Quaternion inTangent2, float t) +{ + float t2 = t*t; + float t3 = t2*t; + float h00 = 2*t3 - 3*t2 + 1; + float h10 = t3 - 2*t2 + t; + float h01 = -2*t3 + 3*t2; + float h11 = t3 - t2; + + Quaternion p0 = QuaternionScale(q1, h00); + Quaternion m0 = QuaternionScale(outTangent1, h10); + Quaternion p1 = QuaternionScale(q2, h01); + Quaternion m1 = QuaternionScale(inTangent2, h11); + + Quaternion result = { 0 }; + + result = QuaternionAdd(p0, m0); + result = QuaternionAdd(result, p1); + result = QuaternionAdd(result, m1); + result = QuaternionNormalize(result); + + return result; +} + +// Calculate quaternion based on the rotation from one vector to another +RMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) +{ + Quaternion result = { 0 }; + + float cos2Theta = (from.x*to.x + from.y*to.y + from.z*to.z); // Vector3DotProduct(from, to) + Vector3 cross = { from.y*to.z - from.z*to.y, from.z*to.x - from.x*to.z, from.x*to.y - from.y*to.x }; // Vector3CrossProduct(from, to) + + result.x = cross.x; + result.y = cross.y; + result.z = cross.z; + result.w = 1.0f + cos2Theta; + + // QuaternionNormalize(q); + // NOTE: Normalize to essentially nlerp the original and identity to 0.5 + Quaternion q = result; + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + + return result; +} + +// Get a quaternion for a given rotation matrix +RMAPI Quaternion QuaternionFromMatrix(Matrix mat) +{ + Quaternion result = { 0 }; + + float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; + float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; + float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; + float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5; + + int biggestIndex = 0; + float fourBiggestSquaredMinus1 = fourWSquaredMinus1; + if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourXSquaredMinus1; + biggestIndex = 1; + } + + if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourYSquaredMinus1; + biggestIndex = 2; + } + + if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourZSquaredMinus1; + biggestIndex = 3; + } + + float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f)*0.5f; + float mult = 0.25f/biggestVal; + + switch (biggestIndex) + { + case 0: + result.w = biggestVal; + result.x = (mat.m6 - mat.m9)*mult; + result.y = (mat.m8 - mat.m2)*mult; + result.z = (mat.m1 - mat.m4)*mult; + break; + case 1: + result.x = biggestVal; + result.w = (mat.m6 - mat.m9)*mult; + result.y = (mat.m1 + mat.m4)*mult; + result.z = (mat.m8 + mat.m2)*mult; + break; + case 2: + result.y = biggestVal; + result.w = (mat.m8 - mat.m2)*mult; + result.x = (mat.m1 + mat.m4)*mult; + result.z = (mat.m6 + mat.m9)*mult; + break; + case 3: + result.z = biggestVal; + result.w = (mat.m1 - mat.m4)*mult; + result.x = (mat.m8 + mat.m2)*mult; + result.y = (mat.m6 + mat.m9)*mult; + break; + } + + return result; +} + +// Get a matrix for a given quaternion +RMAPI Matrix QuaternionToMatrix(Quaternion q) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float a2 = q.x*q.x; + float b2 = q.y*q.y; + float c2 = q.z*q.z; + float ac = q.x*q.z; + float ab = q.x*q.y; + float bc = q.y*q.z; + float ad = q.w*q.x; + float bd = q.w*q.y; + float cd = q.w*q.z; + + result.m0 = 1 - 2*(b2 + c2); + result.m1 = 2*(ab + cd); + result.m2 = 2*(ac - bd); + + result.m4 = 2*(ab - cd); + result.m5 = 1 - 2*(a2 + c2); + result.m6 = 2*(bc + ad); + + result.m8 = 2*(ac + bd); + result.m9 = 2*(bc - ad); + result.m10 = 1 - 2*(a2 + b2); + + return result; +} + +// Get rotation quaternion for an angle and axis +// NOTE: Angle must be provided in radians +RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) +{ + Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; + + float axisLength = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); + + if (axisLength != 0.0f) + { + angle *= 0.5f; + + float length = 0.0f; + float ilength = 0.0f; + + // Vector3Normalize(axis) + length = axisLength; + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + axis.x *= ilength; + axis.y *= ilength; + axis.z *= ilength; + + float sinres = sinf(angle); + float cosres = cosf(angle); + + result.x = axis.x*sinres; + result.y = axis.y*sinres; + result.z = axis.z*sinres; + result.w = cosres; + + // QuaternionNormalize(q); + Quaternion q = result; + length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + } + + return result; +} + +// Get the rotation angle and axis for a given quaternion +RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) +{ + if (fabsf(q.w) > 1.0f) + { + // QuaternionNormalize(q); + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + q.x = q.x*ilength; + q.y = q.y*ilength; + q.z = q.z*ilength; + q.w = q.w*ilength; + } + + Vector3 resAxis = { 0.0f, 0.0f, 0.0f }; + float resAngle = 2.0f*acosf(q.w); + float den = sqrtf(1.0f - q.w*q.w); + + if (den > EPSILON) + { + resAxis.x = q.x/den; + resAxis.y = q.y/den; + resAxis.z = q.z/den; + } + else + { + // This occurs when the angle is zero. + // Not a problem: just set an arbitrary normalized axis. + resAxis.x = 1.0f; + } + + *outAxis = resAxis; + *outAngle = resAngle; +} + +// Get the quaternion equivalent to Euler angles +// NOTE: Rotation order is ZYX +RMAPI Quaternion QuaternionFromEuler(float pitch, float yaw, float roll) +{ + Quaternion result = { 0 }; + + float x0 = cosf(pitch*0.5f); + float x1 = sinf(pitch*0.5f); + float y0 = cosf(yaw*0.5f); + float y1 = sinf(yaw*0.5f); + float z0 = cosf(roll*0.5f); + float z1 = sinf(roll*0.5f); + + result.x = x1*y0*z0 - x0*y1*z1; + result.y = x0*y1*z0 + x1*y0*z1; + result.z = x0*y0*z1 - x1*y1*z0; + result.w = x0*y0*z0 + x1*y1*z1; + + return result; +} + +// Get the Euler angles equivalent to quaternion (roll, pitch, yaw) +// NOTE: Angles are returned in a Vector3 struct in radians +RMAPI Vector3 QuaternionToEuler(Quaternion q) +{ + Vector3 result = { 0 }; + + // Roll (x-axis rotation) + float x0 = 2.0f*(q.w*q.x + q.y*q.z); + float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); + result.x = atan2f(x0, x1); + + // Pitch (y-axis rotation) + float y0 = 2.0f*(q.w*q.y - q.z*q.x); + y0 = y0 > 1.0f ? 1.0f : y0; + y0 = y0 < -1.0f ? -1.0f : y0; + result.y = asinf(y0); + + // Yaw (z-axis rotation) + float z0 = 2.0f*(q.w*q.z + q.x*q.y); + float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); + result.z = atan2f(z0, z1); + + return result; +} + +// Transform a quaternion given a transformation matrix +RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat) +{ + Quaternion result = { 0 }; + + result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; + result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; + result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; + result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; + + return result; +} + +// Check whether two given quaternions are almost equal +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))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && + ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) || + (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && + ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))); + + return result; +} + +// Decompose a transformation matrix into its rotational, translational and scaling components +RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotation, Vector3 *scale) +{ + // Extract translation. + translation->x = mat.m12; + translation->y = mat.m13; + translation->z = mat.m14; + + // Extract upper-left for determinant computation + const float a = mat.m0; + const float b = mat.m4; + const float c = mat.m8; + const float d = mat.m1; + const float e = mat.m5; + const float f = mat.m9; + const float g = mat.m2; + const float h = mat.m6; + const float i = mat.m10; + const float A = e*i - f*h; + const float B = f*g - d*i; + const float C = d*h - e*g; + + // Extract scale + const float det = a*A + b*B + c*C; + Vector3 abc = { a, b, c }; + Vector3 def = { d, e, f }; + Vector3 ghi = { g, h, i }; + + float scalex = Vector3Length(abc); + float scaley = Vector3Length(def); + float scalez = Vector3Length(ghi); + Vector3 s = { scalex, scaley, scalez }; + + if (det < 0) s = Vector3Negate(s); + + *scale = s; + + // Remove scale from the matrix if it is not close to zero + Matrix clone = mat; + if (!FloatEquals(det, 0)) + { + clone.m0 /= s.x; + clone.m4 /= s.x; + clone.m8 /= s.x; + clone.m1 /= s.y; + clone.m5 /= s.y; + clone.m9 /= s.y; + clone.m2 /= s.z; + clone.m6 /= s.z; + clone.m10 /= s.z; + + // Extract rotation + *rotation = QuaternionFromMatrix(clone); + } + else + { + // Set to identity if close to zero + *rotation = QuaternionIdentity(); + } +} + +#if defined(__cplusplus) && !defined(RAYMATH_DISABLE_CPP_OPERATORS) + +// Optional C++ math operators +//------------------------------------------------------------------------------- + +// Vector2 operators +static constexpr Vector2 Vector2Zeros = { 0, 0 }; +static constexpr Vector2 Vector2Ones = { 1, 1 }; +static constexpr Vector2 Vector2UnitX = { 1, 0 }; +static constexpr Vector2 Vector2UnitY = { 0, 1 }; + +inline Vector2 operator + (const Vector2& lhs, const Vector2& rhs) +{ + return Vector2Add(lhs, rhs); +} + +inline const Vector2& operator += (Vector2& lhs, const Vector2& rhs) +{ + lhs = Vector2Add(lhs, rhs); + return lhs; +} + +inline Vector2 operator - (const Vector2& lhs, const Vector2& rhs) +{ + return Vector2Subtract(lhs, rhs); +} + +inline const Vector2& operator -= (Vector2& lhs, const Vector2& rhs) +{ + lhs = Vector2Subtract(lhs, rhs); + return lhs; +} + +inline Vector2 operator * (const Vector2& lhs, const float& rhs) +{ + return Vector2Scale(lhs, rhs); +} + +inline const Vector2& operator *= (Vector2& lhs, const float& rhs) +{ + lhs = Vector2Scale(lhs, rhs); + return lhs; +} + +inline Vector2 operator * (const Vector2& lhs, const Vector2& rhs) +{ + return Vector2Multiply(lhs, rhs); +} + +inline const Vector2& operator *= (Vector2& lhs, const Vector2& rhs) +{ + lhs = Vector2Multiply(lhs, rhs); + return lhs; +} + +inline Vector2 operator * (const Vector2& lhs, const Matrix& rhs) +{ + return Vector2Transform(lhs, rhs); +} + +inline const Vector2& operator -= (Vector2& lhs, const Matrix& rhs) +{ + lhs = Vector2Transform(lhs, rhs); + return lhs; +} + +inline Vector2 operator / (const Vector2& lhs, const float& rhs) +{ + return Vector2Scale(lhs, 1.0f / rhs); +} + +inline const Vector2& operator /= (Vector2& lhs, const float& rhs) +{ + lhs = Vector2Scale(lhs, rhs); + return lhs; +} + +inline Vector2 operator / (const Vector2& lhs, const Vector2& rhs) +{ + return Vector2Divide(lhs, rhs); +} + +inline const Vector2& operator /= (Vector2& lhs, const Vector2& rhs) +{ + lhs = Vector2Divide(lhs, rhs); + return lhs; +} + +inline bool operator == (const Vector2& lhs, const Vector2& rhs) +{ + return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y); +} + +inline bool operator != (const Vector2& lhs, const Vector2& rhs) +{ + return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y); +} + +// Vector3 operators +static constexpr Vector3 Vector3Zeros = { 0, 0, 0 }; +static constexpr Vector3 Vector3Ones = { 1, 1, 1 }; +static constexpr Vector3 Vector3UnitX = { 1, 0, 0 }; +static constexpr Vector3 Vector3UnitY = { 0, 1, 0 }; +static constexpr Vector3 Vector3UnitZ = { 0, 0, 1 }; + +inline Vector3 operator + (const Vector3& lhs, const Vector3& rhs) +{ + return Vector3Add(lhs, rhs); +} + +inline const Vector3& operator += (Vector3& lhs, const Vector3& rhs) +{ + lhs = Vector3Add(lhs, rhs); + return lhs; +} + +inline Vector3 operator - (const Vector3& lhs, const Vector3& rhs) +{ + return Vector3Subtract(lhs, rhs); +} + +inline const Vector3& operator -= (Vector3& lhs, const Vector3& rhs) +{ + lhs = Vector3Subtract(lhs, rhs); + return lhs; +} + +inline Vector3 operator * (const Vector3& lhs, const float& rhs) +{ + return Vector3Scale(lhs, rhs); +} + +inline const Vector3& operator *= (Vector3& lhs, const float& rhs) +{ + lhs = Vector3Scale(lhs, rhs); + return lhs; +} + +inline Vector3 operator * (const Vector3& lhs, const Vector3& rhs) +{ + return Vector3Multiply(lhs, rhs); +} + +inline const Vector3& operator *= (Vector3& lhs, const Vector3& rhs) +{ + lhs = Vector3Multiply(lhs, rhs); + return lhs; +} + +inline Vector3 operator * (const Vector3& lhs, const Matrix& rhs) +{ + return Vector3Transform(lhs, rhs); +} + +inline const Vector3& operator -= (Vector3& lhs, const Matrix& rhs) +{ + lhs = Vector3Transform(lhs, rhs); + return lhs; +} + +inline Vector3 operator / (const Vector3& lhs, const float& rhs) +{ + return Vector3Scale(lhs, 1.0f / rhs); +} + +inline const Vector3& operator /= (Vector3& lhs, const float& rhs) +{ + lhs = Vector3Scale(lhs, rhs); + return lhs; +} + +inline Vector3 operator / (const Vector3& lhs, const Vector3& rhs) +{ + return Vector3Divide(lhs, rhs); +} + +inline const Vector3& operator /= (Vector3& lhs, const Vector3& rhs) +{ + lhs = Vector3Divide(lhs, rhs); + return lhs; +} + +inline bool operator == (const Vector3& lhs, const Vector3& rhs) +{ + return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y) && FloatEquals(lhs.z, rhs.z); +} + +inline bool operator != (const Vector3& lhs, const Vector3& rhs) +{ + return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y) || !FloatEquals(lhs.z, rhs.z); +} + +// Vector4 operators +static constexpr Vector4 Vector4Zeros = { 0, 0, 0, 0 }; +static constexpr Vector4 Vector4Ones = { 1, 1, 1, 1 }; +static constexpr Vector4 Vector4UnitX = { 1, 0, 0, 0 }; +static constexpr Vector4 Vector4UnitY = { 0, 1, 0, 0 }; +static constexpr Vector4 Vector4UnitZ = { 0, 0, 1, 0 }; +static constexpr Vector4 Vector4UnitW = { 0, 0, 0, 1 }; + +inline Vector4 operator + (const Vector4& lhs, const Vector4& rhs) +{ + return Vector4Add(lhs, rhs); +} + +inline const Vector4& operator += (Vector4& lhs, const Vector4& rhs) +{ + lhs = Vector4Add(lhs, rhs); + return lhs; +} + +inline Vector4 operator - (const Vector4& lhs, const Vector4& rhs) +{ + return Vector4Subtract(lhs, rhs); +} + +inline const Vector4& operator -= (Vector4& lhs, const Vector4& rhs) +{ + lhs = Vector4Subtract(lhs, rhs); + return lhs; +} + +inline Vector4 operator * (const Vector4& lhs, const float& rhs) +{ + return Vector4Scale(lhs, rhs); +} + +inline const Vector4& operator *= (Vector4& lhs, const float& rhs) +{ + lhs = Vector4Scale(lhs, rhs); + return lhs; +} + +inline Vector4 operator * (const Vector4& lhs, const Vector4& rhs) +{ + return Vector4Multiply(lhs, rhs); +} + +inline const Vector4& operator *= (Vector4& lhs, const Vector4& rhs) +{ + lhs = Vector4Multiply(lhs, rhs); + return lhs; +} + +inline Vector4 operator / (const Vector4& lhs, const float& rhs) +{ + return Vector4Scale(lhs, 1.0f / rhs); +} + +inline const Vector4& operator /= (Vector4& lhs, const float& rhs) +{ + lhs = Vector4Scale(lhs, rhs); + return lhs; +} + +inline Vector4 operator / (const Vector4& lhs, const Vector4& rhs) +{ + return Vector4Divide(lhs, rhs); +} + +inline const Vector4& operator /= (Vector4& lhs, const Vector4& rhs) +{ + lhs = Vector4Divide(lhs, rhs); + return lhs; +} + +inline bool operator == (const Vector4& lhs, const Vector4& rhs) +{ + return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y) && FloatEquals(lhs.z, rhs.z) && FloatEquals(lhs.w, rhs.w); +} + +inline bool operator != (const Vector4& lhs, const Vector4& rhs) +{ + return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y) || !FloatEquals(lhs.z, rhs.z) || !FloatEquals(lhs.w, rhs.w); +} + +// Quaternion operators +static constexpr Quaternion QuaternionZeros = { 0, 0, 0, 0 }; +static constexpr Quaternion QuaternionOnes = { 1, 1, 1, 1 }; +static constexpr Quaternion QuaternionUnitX = { 0, 0, 0, 1 }; + +inline Quaternion operator + (const Quaternion& lhs, const float& rhs) +{ + return QuaternionAddValue(lhs, rhs); +} + +inline const Quaternion& operator += (Quaternion& lhs, const float& rhs) +{ + lhs = QuaternionAddValue(lhs, rhs); + return lhs; +} + +inline Quaternion operator - (const Quaternion& lhs, const float& rhs) +{ + return QuaternionSubtractValue(lhs, rhs); +} + +inline const Quaternion& operator -= (Quaternion& lhs, const float& rhs) +{ + lhs = QuaternionSubtractValue(lhs, rhs); + return lhs; +} + +inline Quaternion operator * (const Quaternion& lhs, const Matrix& rhs) +{ + return QuaternionTransform(lhs, rhs); +} + +inline const Quaternion& operator *= (Quaternion& lhs, const Matrix& rhs) +{ + lhs = QuaternionTransform(lhs, rhs); + return lhs; +} + +// Matrix operators +inline Matrix operator + (const Matrix& lhs, const Matrix& rhs) +{ + return MatrixAdd(lhs, rhs); +} + +inline const Matrix& operator += (Matrix& lhs, const Matrix& rhs) +{ + lhs = MatrixAdd(lhs, rhs); + return lhs; +} + +inline Matrix operator - (const Matrix& lhs, const Matrix& rhs) +{ + return MatrixSubtract(lhs, rhs); +} + +inline const Matrix& operator -= (Matrix& lhs, const Matrix& rhs) +{ + lhs = MatrixSubtract(lhs, rhs); + return lhs; +} + +inline Matrix operator * (const Matrix& lhs, const Matrix& rhs) +{ + return MatrixMultiply(lhs, rhs); +} + +inline const Matrix& operator *= (Matrix& lhs, const Matrix& rhs) +{ + lhs = MatrixMultiply(lhs, rhs); + return lhs; +} +//------------------------------------------------------------------------------- +#endif // C++ operators + +#endif // RAYMATH_H diff --git a/Game of Life/include/rlgl.h b/Game of Life/include/rlgl.h new file mode 100644 index 0000000..508960d --- /dev/null +++ b/Game of Life/include/rlgl.h @@ -0,0 +1,5262 @@ +/********************************************************************************************** +* +* rlgl v5.0 - 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) +* 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 +* initialized on rlglInit() to accumulate vertex data +* +* 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 +* +* Some resources are also loaded for convenience, here the complete list: +* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data +* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8 +* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs) +* +* Internal buffer (and resources) must be manually unloaded calling rlglClose() +* +* CONFIGURATION: +* #define GRAPHICS_API_OPENGL_11 +* #define GRAPHICS_API_OPENGL_21 +* #define GRAPHICS_API_OPENGL_33 +* #define GRAPHICS_API_OPENGL_43 +* #define GRAPHICS_API_OPENGL_ES2 +* #define GRAPHICS_API_OPENGL_ES3 +* Use selected OpenGL graphics backend, should be supported by platform +* Those preprocessor defines are only used on rlgl module, if OpenGL version is +* required by any other module, use rlGetVersion() to check it +* +* #define RLGL_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 +* +* #define RLGL_RENDER_TEXTURES_HINT +* Enable framebuffer objects (fbo) support (enabled by default) +* Some GPUs could not support them despite the OpenGL version +* +* #define RLGL_SHOW_GL_DETAILS_INFO +* Show OpenGL extensions and capabilities detailed logs on init +* +* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT +* Enable debug context (only available on OpenGL 4.3) +* +* rlgl capabilities could be customized just defining some internal +* values before library inclusion (default values listed): +* +* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits +* #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) +* #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) +* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +* +* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack +* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported +* #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance +* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance +* +* When loading a shader, the following vertex attributes and uniform +* location names are tried to be set automatically: +* +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS "vertexBoneIds" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView))) +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES "boneMatrices" // bone matrices +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) +* +* DEPENDENCIES: +* - OpenGL libraries (depending on platform and OpenGL version selected) +* - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2024 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 RLGL_H +#define RLGL_H + +#define RLGL_VERSION "5.0" + +// Function specifiers in case library is build/used as a shared library +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +// NOTE: visibility(default) attribute makes symbols "visible" when compiled with -fvisibility=hidden +#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) + #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) +#elif defined(BUILD_LIBTYPE_SHARED) + #define RLAPI __attribute__((visibility("default"))) // We are building the library as a Unix shared library (.so/.dylib) +#elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) + #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) +#endif + +// Function specifiers definition +#ifndef RLAPI + #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +// Support TRACELOG macros +#ifndef TRACELOG + #define TRACELOG(level, ...) (void)0 + #define TRACELOGD(...) (void)0 +#endif + +// Allow custom memory allocators +#ifndef RL_MALLOC + #define RL_MALLOC(sz) malloc(sz) +#endif +#ifndef RL_CALLOC + #define RL_CALLOC(n,sz) calloc(n,sz) +#endif +#ifndef RL_REALLOC + #define RL_REALLOC(n,sz) realloc(n,sz) +#endif +#ifndef RL_FREE + #define RL_FREE(p) free(p) +#endif + +// Security check in case no GRAPHICS_API_OPENGL_* defined +#if !defined(GRAPHICS_API_OPENGL_11) && \ + !defined(GRAPHICS_API_OPENGL_21) && \ + !defined(GRAPHICS_API_OPENGL_33) && \ + !defined(GRAPHICS_API_OPENGL_43) && \ + !defined(GRAPHICS_API_OPENGL_ES2) && \ + !defined(GRAPHICS_API_OPENGL_ES3) + #define GRAPHICS_API_OPENGL_33 +#endif + +// Security check in case multiple GRAPHICS_API_OPENGL_* defined +#if defined(GRAPHICS_API_OPENGL_11) + #if defined(GRAPHICS_API_OPENGL_21) + #undef GRAPHICS_API_OPENGL_21 + #endif + #if defined(GRAPHICS_API_OPENGL_33) + #undef GRAPHICS_API_OPENGL_33 + #endif + #if defined(GRAPHICS_API_OPENGL_43) + #undef GRAPHICS_API_OPENGL_43 + #endif + #if defined(GRAPHICS_API_OPENGL_ES2) + #undef GRAPHICS_API_OPENGL_ES2 + #endif +#endif + +// OpenGL 2.1 uses most of OpenGL 3.3 Core functionality +// WARNING: Specific parts are checked with #if defines +#if defined(GRAPHICS_API_OPENGL_21) + #define GRAPHICS_API_OPENGL_33 +#endif + +// OpenGL 4.3 uses OpenGL 3.3 Core functionality +#if defined(GRAPHICS_API_OPENGL_43) + #define GRAPHICS_API_OPENGL_33 +#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 +// NOTE: Some driver implementation do not support it, despite they should +#define RLGL_RENDER_TEXTURES_HINT + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- + +// Default internal render batch elements limits +#ifndef RL_DEFAULT_BATCH_BUFFER_ELEMENTS + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // This is the maximum amount of elements (quads) per batch + // NOTE: Be careful with text, every letter maps to a quad + #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 + #endif + #if defined(GRAPHICS_API_OPENGL_ES2) + // We reduce memory sizes for embedded systems (RPI and HTML5) + // NOTE: On HTML5 (emscripten) this is allocated on heap, + // by default it's only 16MB!...just take care... + #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 2048 + #endif +#endif +#ifndef RL_DEFAULT_BATCH_BUFFERS + #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) +#endif +#ifndef RL_DEFAULT_BATCH_DRAWCALLS + #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) +#endif +#ifndef RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS + #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +#endif + +// Internal Matrix stack +#ifndef RL_MAX_MATRIX_STACK_SIZE + #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of Matrix stack +#endif + +// Shader limits +#ifndef RL_MAX_SHADER_LOCATIONS + #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported +#endif + +// Projection matrix culling +#ifndef RL_CULL_DISTANCE_NEAR + #define RL_CULL_DISTANCE_NEAR 0.01 // Default near cull distance +#endif +#ifndef RL_CULL_DISTANCE_FAR + #define RL_CULL_DISTANCE_FAR 1000.0 // Default far cull distance +#endif + +// Texture parameters (equivalent to OpenGL defines) +#define RL_TEXTURE_WRAP_S 0x2802 // GL_TEXTURE_WRAP_S +#define RL_TEXTURE_WRAP_T 0x2803 // GL_TEXTURE_WRAP_T +#define RL_TEXTURE_MAG_FILTER 0x2800 // GL_TEXTURE_MAG_FILTER +#define RL_TEXTURE_MIN_FILTER 0x2801 // GL_TEXTURE_MIN_FILTER + +#define RL_TEXTURE_FILTER_NEAREST 0x2600 // GL_NEAREST +#define RL_TEXTURE_FILTER_LINEAR 0x2601 // GL_LINEAR +#define RL_TEXTURE_FILTER_MIP_NEAREST 0x2700 // GL_NEAREST_MIPMAP_NEAREST +#define RL_TEXTURE_FILTER_NEAREST_MIP_LINEAR 0x2702 // GL_NEAREST_MIPMAP_LINEAR +#define RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST 0x2701 // GL_LINEAR_MIPMAP_NEAREST +#define RL_TEXTURE_FILTER_MIP_LINEAR 0x2703 // GL_LINEAR_MIPMAP_LINEAR +#define RL_TEXTURE_FILTER_ANISOTROPIC 0x3000 // Anisotropic filter (custom identifier) +#define RL_TEXTURE_MIPMAP_BIAS_RATIO 0x4000 // Texture mipmap bias, percentage ratio (custom identifier) + +#define RL_TEXTURE_WRAP_REPEAT 0x2901 // GL_REPEAT +#define RL_TEXTURE_WRAP_CLAMP 0x812F // GL_CLAMP_TO_EDGE +#define RL_TEXTURE_WRAP_MIRROR_REPEAT 0x8370 // GL_MIRRORED_REPEAT +#define RL_TEXTURE_WRAP_MIRROR_CLAMP 0x8742 // GL_MIRROR_CLAMP_EXT + +// Matrix modes (equivalent to OpenGL) +#define RL_MODELVIEW 0x1700 // GL_MODELVIEW +#define RL_PROJECTION 0x1701 // GL_PROJECTION +#define RL_TEXTURE 0x1702 // GL_TEXTURE + +// Primitive assembly draw modes +#define RL_LINES 0x0001 // GL_LINES +#define RL_TRIANGLES 0x0004 // GL_TRIANGLES +#define RL_QUADS 0x0007 // GL_QUADS + +// GL equivalent data types +#define RL_UNSIGNED_BYTE 0x1401 // GL_UNSIGNED_BYTE +#define RL_FLOAT 0x1406 // GL_FLOAT + +// GL buffer usage hint +#define RL_STREAM_DRAW 0x88E0 // GL_STREAM_DRAW +#define RL_STREAM_READ 0x88E1 // GL_STREAM_READ +#define RL_STREAM_COPY 0x88E2 // GL_STREAM_COPY +#define RL_STATIC_DRAW 0x88E4 // GL_STATIC_DRAW +#define RL_STATIC_READ 0x88E5 // GL_STATIC_READ +#define RL_STATIC_COPY 0x88E6 // GL_STATIC_COPY +#define RL_DYNAMIC_DRAW 0x88E8 // GL_DYNAMIC_DRAW +#define RL_DYNAMIC_READ 0x88E9 // GL_DYNAMIC_READ +#define RL_DYNAMIC_COPY 0x88EA // GL_DYNAMIC_COPY + +// GL Shader type +#define RL_FRAGMENT_SHADER 0x8B30 // GL_FRAGMENT_SHADER +#define RL_VERTEX_SHADER 0x8B31 // GL_VERTEX_SHADER +#define RL_COMPUTE_SHADER 0x91B9 // GL_COMPUTE_SHADER + +// GL blending factors +#define RL_ZERO 0 // GL_ZERO +#define RL_ONE 1 // GL_ONE +#define RL_SRC_COLOR 0x0300 // GL_SRC_COLOR +#define RL_ONE_MINUS_SRC_COLOR 0x0301 // GL_ONE_MINUS_SRC_COLOR +#define RL_SRC_ALPHA 0x0302 // GL_SRC_ALPHA +#define RL_ONE_MINUS_SRC_ALPHA 0x0303 // GL_ONE_MINUS_SRC_ALPHA +#define RL_DST_ALPHA 0x0304 // GL_DST_ALPHA +#define RL_ONE_MINUS_DST_ALPHA 0x0305 // GL_ONE_MINUS_DST_ALPHA +#define RL_DST_COLOR 0x0306 // GL_DST_COLOR +#define RL_ONE_MINUS_DST_COLOR 0x0307 // GL_ONE_MINUS_DST_COLOR +#define RL_SRC_ALPHA_SATURATE 0x0308 // GL_SRC_ALPHA_SATURATE +#define RL_CONSTANT_COLOR 0x8001 // GL_CONSTANT_COLOR +#define RL_ONE_MINUS_CONSTANT_COLOR 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR +#define RL_CONSTANT_ALPHA 0x8003 // GL_CONSTANT_ALPHA +#define RL_ONE_MINUS_CONSTANT_ALPHA 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA + +// GL blending functions/equations +#define RL_FUNC_ADD 0x8006 // GL_FUNC_ADD +#define RL_MIN 0x8007 // GL_MIN +#define RL_MAX 0x8008 // GL_MAX +#define RL_FUNC_SUBTRACT 0x800A // GL_FUNC_SUBTRACT +#define RL_FUNC_REVERSE_SUBTRACT 0x800B // GL_FUNC_REVERSE_SUBTRACT +#define RL_BLEND_EQUATION 0x8009 // GL_BLEND_EQUATION +#define RL_BLEND_EQUATION_RGB 0x8009 // GL_BLEND_EQUATION_RGB // (Same as BLEND_EQUATION) +#define RL_BLEND_EQUATION_ALPHA 0x883D // GL_BLEND_EQUATION_ALPHA +#define RL_BLEND_DST_RGB 0x80C8 // GL_BLEND_DST_RGB +#define RL_BLEND_SRC_RGB 0x80C9 // GL_BLEND_SRC_RGB +#define RL_BLEND_DST_ALPHA 0x80CA // GL_BLEND_DST_ALPHA +#define RL_BLEND_SRC_ALPHA 0x80CB // GL_BLEND_SRC_ALPHA +#define RL_BLEND_COLOR 0x8005 // GL_BLEND_COLOR + +#define RL_READ_FRAMEBUFFER 0x8CA8 // GL_READ_FRAMEBUFFER +#define RL_DRAW_FRAMEBUFFER 0x8CA9 // GL_DRAW_FRAMEBUFFER + +// Default shader vertex attribute locations +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR 3 +#endif + #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT +#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6 +#endif +#ifdef RL_SUPPORT_MESH_GPU_SKINNING +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8 +#endif +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) + #include +#elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE) + // Boolean type +typedef enum bool { false = 0, true = !false } bool; +#endif + +#if !defined(RL_MATRIX_TYPE) +// 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; +#define RL_MATRIX_TYPE +#endif + +// Dynamic vertex buffers (position + texcoords + colors + indices arrays) +typedef struct rlVertexBuffer { + int elementCount; // Number of elements in the buffer (QUADS) + + float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + float *normals; // Vertex normal (XYZ - 3 components per vertex) (shader-location = 2) + unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + unsigned int *indices; // Vertex indices (in case vertex data comes indexed) (6 indices per quad) +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + unsigned short *indices; // Vertex indices (in case vertex data comes indexed) (6 indices per quad) +#endif + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int vboId[5]; // OpenGL Vertex Buffer Objects id (5 types of vertex data) +} rlVertexBuffer; + +// Draw call type +// NOTE: Only texture changes register a new draw, other state-change-related elements are not +// used at this moment (vaoId, shaderId, matrices), raylib just forces a batch draw call if any +// of those state-change happens (this is done in core module) +typedef struct rlDrawCall { + int mode; // Drawing mode: LINES, TRIANGLES, QUADS + int vertexCount; // Number of vertex of the draw + int vertexAlignment; // Number of vertex required for index alignment (LINES, TRIANGLES) + //unsigned int vaoId; // Vertex array id to be used on the draw -> Using RLGL.currentBatch->vertexBuffer.vaoId + //unsigned int shaderId; // Shader id to be used on the draw -> Using RLGL.currentShaderId + unsigned int textureId; // Texture id to be used on the draw -> Use to create new draw call if changes + + //Matrix projection; // Projection matrix for this draw -> Using RLGL.projection by default + //Matrix modelview; // Modelview matrix for this draw -> Using RLGL.modelview by default +} rlDrawCall; + +// rlRenderBatch type +typedef struct rlRenderBatch { + int bufferCount; // Number of vertex buffers (multi-buffering support) + int currentBuffer; // Current buffer tracking in case of multi-buffering + rlVertexBuffer *vertexBuffer; // Dynamic buffer(s) for vertex data + + rlDrawCall *draws; // Draw calls array, depends on textureId + int drawCounter; // Draw calls counter + float currentDepth; // Current depth value for next draw +} rlRenderBatch; + +// OpenGL version +typedef enum { + RL_OPENGL_11 = 1, // OpenGL 1.1 + RL_OPENGL_21, // OpenGL 2.1 (GLSL 120) + RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) + RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330) + RL_OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100) + RL_OPENGL_ES_30 // OpenGL ES 3.0 (GLSL 300 es) +} rlGlVersion; + +// Trace log level +// NOTE: Organized by priority level +typedef enum { + RL_LOG_ALL = 0, // Display all logs + RL_LOG_TRACE, // Trace logging, intended for internal use only + RL_LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds + RL_LOG_INFO, // Info logging, used for program execution info + RL_LOG_WARNING, // Warning logging, used on recoverable failures + RL_LOG_ERROR, // Error logging, used on unrecoverable failures + RL_LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) + RL_LOG_NONE // Disable logging +} rlTraceLogLevel; + +// Texture pixel formats +// NOTE: Support depends on OpenGL version +typedef enum { + RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp + RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp + RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp + RL_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 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_RGBA, // 4 bpp (1 bit alpha) + RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp + RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp + RL_PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp + RL_PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp + RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + RL_PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp + RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp + RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp +} rlPixelFormat; + +// Texture parameters: filter mode +// NOTE 1: Filtering considers mipmaps if available in the texture +// NOTE 2: Filter is accordingly set for minification and magnification +typedef enum { + RL_TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation + RL_TEXTURE_FILTER_BILINEAR, // Linear filtering + RL_TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) + RL_TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x + RL_TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x + RL_TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x +} rlTextureFilter; + +// Color blending modes (pre-defined) +typedef enum { + RL_BLEND_ALPHA = 0, // Blend textures considering alpha (default) + RL_BLEND_ADDITIVE, // Blend textures adding colors + RL_BLEND_MULTIPLIED, // Blend textures multiplying colors + RL_BLEND_ADD_COLORS, // Blend textures adding colors (alternative) + RL_BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) + RL_BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha + RL_BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + RL_BLEND_CUSTOM_SEPARATE // Blend textures using custom src/dst factors (use rlSetBlendFactorsSeparate()) +} rlBlendMode; + +// Shader location point type +typedef enum { + RL_SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position + RL_SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 + RL_SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 + RL_SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal + RL_SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent + RL_SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color + RL_SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection + RL_SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) + RL_SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection + RL_SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) + RL_SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal + RL_SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view + RL_SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color + RL_SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color + RL_SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color + RL_SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: RL_SHADER_LOC_MAP_DIFFUSE) + RL_SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: RL_SHADER_LOC_MAP_SPECULAR) + RL_SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal + RL_SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness + RL_SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion + RL_SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission + RL_SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height + RL_SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap + RL_SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance + RL_SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter + RL_SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf +} rlShaderLocationIndex; + +#define RL_SHADER_LOC_MAP_DIFFUSE RL_SHADER_LOC_MAP_ALBEDO +#define RL_SHADER_LOC_MAP_SPECULAR RL_SHADER_LOC_MAP_METALNESS + +// Shader uniform data type +typedef enum { + RL_SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float + RL_SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) + RL_SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) + RL_SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float) + RL_SHADER_UNIFORM_INT, // Shader uniform type: int + RL_SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) + RL_SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) + RL_SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) + RL_SHADER_UNIFORM_UINT, // Shader uniform type: unsigned int + RL_SHADER_UNIFORM_UIVEC2, // Shader uniform type: uivec2 (2 unsigned int) + RL_SHADER_UNIFORM_UIVEC3, // Shader uniform type: uivec3 (3 unsigned int) + RL_SHADER_UNIFORM_UIVEC4, // Shader uniform type: uivec4 (4 unsigned int) + RL_SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d +} rlShaderUniformDataType; + +// Shader attribute data types +typedef enum { + RL_SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float + RL_SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float) + RL_SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float) + RL_SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) +} rlShaderAttributeDataType; + +// Framebuffer attachment type +// NOTE: By default up to 8 color channels defined, but it can be more +typedef enum { + RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 + RL_ATTACHMENT_COLOR_CHANNEL1 = 1, // Framebuffer attachment type: color 1 + RL_ATTACHMENT_COLOR_CHANNEL2 = 2, // Framebuffer attachment type: color 2 + RL_ATTACHMENT_COLOR_CHANNEL3 = 3, // Framebuffer attachment type: color 3 + RL_ATTACHMENT_COLOR_CHANNEL4 = 4, // Framebuffer attachment type: color 4 + RL_ATTACHMENT_COLOR_CHANNEL5 = 5, // Framebuffer attachment type: color 5 + RL_ATTACHMENT_COLOR_CHANNEL6 = 6, // Framebuffer attachment type: color 6 + RL_ATTACHMENT_COLOR_CHANNEL7 = 7, // Framebuffer attachment type: color 7 + RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth + RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil +} rlFramebufferAttachType; + +// Framebuffer texture attachment type +typedef enum { + RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // 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 = 2, // 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 = 4, // 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_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer +} rlFramebufferAttachTextureType; + +// Face culling mode +typedef enum { + RL_CULL_FACE_FRONT = 0, + RL_CULL_FACE_BACK +} rlCullMode; + +//------------------------------------------------------------------------------------ +// Functions Declaration - Matrix operations +//------------------------------------------------------------------------------------ + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +RLAPI void rlMatrixMode(int mode); // Choose the current matrix to be transformed +RLAPI void rlPushMatrix(void); // Push the current matrix to stack +RLAPI void rlPopMatrix(void); // Pop latest inserted matrix from stack +RLAPI void rlLoadIdentity(void); // Reset current matrix to identity matrix +RLAPI void rlTranslatef(float x, float y, float z); // Multiply the current matrix by a translation matrix +RLAPI void rlRotatef(float angle, float x, float y, float z); // Multiply the current matrix by a rotation matrix +RLAPI void rlScalef(float x, float y, float z); // Multiply the current matrix by a scaling matrix +RLAPI void rlMultMatrixf(const float *matf); // Multiply the current matrix by another matrix +RLAPI void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar); +RLAPI void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar); +RLAPI void rlViewport(int x, int y, int width, int height); // Set the viewport area +RLAPI void rlSetClipPlanes(double nearPlane, double farPlane); // Set clip planes distances +RLAPI double rlGetCullDistanceNear(void); // Get cull plane distance near +RLAPI double rlGetCullDistanceFar(void); // Get cull plane distance far + +//------------------------------------------------------------------------------------ +// Functions Declaration - Vertex level operations +//------------------------------------------------------------------------------------ +RLAPI void rlBegin(int mode); // Initialize drawing mode (how to organize vertex) +RLAPI void rlEnd(void); // Finish vertex providing +RLAPI void rlVertex2i(int x, int y); // Define one vertex (position) - 2 int +RLAPI void rlVertex2f(float x, float y); // Define one vertex (position) - 2 float +RLAPI void rlVertex3f(float x, float y, float z); // Define one vertex (position) - 3 float +RLAPI void rlTexCoord2f(float x, float y); // Define one vertex (texture coordinate) - 2 float +RLAPI void rlNormal3f(float x, float y, float z); // Define one vertex (normal) - 3 float +RLAPI void rlColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a); // Define one vertex (color) - 4 byte +RLAPI void rlColor3f(float x, float y, float z); // Define one vertex (color) - 3 float +RLAPI void rlColor4f(float x, float y, float z, float w); // Define one vertex (color) - 4 float + +//------------------------------------------------------------------------------------ +// Functions Declaration - OpenGL style functions (common to 1.1, 3.3+, ES2) +// NOTE: This functions are used to completely abstract raylib code from OpenGL layer, +// some of them are direct wrappers over OpenGL calls, some others are custom +//------------------------------------------------------------------------------------ + +// Vertex buffers state +RLAPI bool rlEnableVertexArray(unsigned int vaoId); // Enable vertex array (VAO, if supported) +RLAPI void rlDisableVertexArray(void); // Disable vertex array (VAO, if supported) +RLAPI void rlEnableVertexBuffer(unsigned int id); // Enable vertex buffer (VBO) +RLAPI void rlDisableVertexBuffer(void); // Disable vertex buffer (VBO) +RLAPI void rlEnableVertexBufferElement(unsigned int id); // Enable vertex buffer element (VBO element) +RLAPI void rlDisableVertexBufferElement(void); // Disable vertex buffer element (VBO element) +RLAPI void rlEnableVertexAttribute(unsigned int index); // Enable vertex attribute index +RLAPI void rlDisableVertexAttribute(unsigned int index); // Disable vertex attribute index +#if defined(GRAPHICS_API_OPENGL_11) +RLAPI void rlEnableStatePointer(int vertexAttribType, void *buffer); // Enable attribute state pointer +RLAPI void rlDisableStatePointer(int vertexAttribType); // Disable attribute state pointer +#endif + +// Textures state +RLAPI void rlActiveTextureSlot(int slot); // Select and active a texture slot +RLAPI void rlEnableTexture(unsigned int id); // Enable texture +RLAPI void rlDisableTexture(void); // Disable texture +RLAPI void rlEnableTextureCubemap(unsigned int id); // Enable texture cubemap +RLAPI void rlDisableTextureCubemap(void); // Disable texture cubemap +RLAPI void rlTextureParameters(unsigned int id, int param, int value); // Set texture parameters (filter, wrap) +RLAPI void rlCubemapParameters(unsigned int id, int param, int value); // Set cubemap parameters (filter, wrap) + +// Shader state +RLAPI void rlEnableShader(unsigned int id); // Enable shader program +RLAPI void rlDisableShader(void); // Disable shader program + +// Framebuffer state +RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo) +RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer +RLAPI unsigned int rlGetActiveFramebuffer(void); // Get the currently active render texture (fbo), 0 for default framebuffer +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 +RLAPI void rlBindFramebuffer(unsigned int target, unsigned int framebuffer); // Bind framebuffer (FBO) + +// General render state +RLAPI void rlEnableColorBlend(void); // Enable color blending +RLAPI void rlDisableColorBlend(void); // Disable color blending +RLAPI void rlEnableDepthTest(void); // Enable depth test +RLAPI void rlDisableDepthTest(void); // Disable depth test +RLAPI void rlEnableDepthMask(void); // Enable depth write +RLAPI void rlDisableDepthMask(void); // Disable depth write +RLAPI void rlEnableBackfaceCulling(void); // Enable backface culling +RLAPI void rlDisableBackfaceCulling(void); // Disable backface culling +RLAPI void rlColorMask(bool r, bool g, bool b, bool a); // Color mask control +RLAPI void rlSetCullFace(int mode); // Set face culling mode +RLAPI void rlEnableScissorTest(void); // Enable scissor test +RLAPI void rlDisableScissorTest(void); // Disable scissor test +RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test +RLAPI void rlEnableWireMode(void); // Enable wire mode +RLAPI void rlEnablePointMode(void); // Enable point mode +RLAPI void rlDisableWireMode(void); // Disable wire (and point) mode +RLAPI void rlSetLineWidth(float width); // Set the line drawing width +RLAPI float rlGetLineWidth(void); // Get the line drawing width +RLAPI void rlEnableSmoothLines(void); // Enable line aliasing +RLAPI void rlDisableSmoothLines(void); // Disable line aliasing +RLAPI void rlEnableStereoRender(void); // Enable stereo rendering +RLAPI void rlDisableStereoRender(void); // Disable stereo rendering +RLAPI bool rlIsStereoRenderEnabled(void); // Check if stereo render is enabled + +RLAPI void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a); // Clear color buffer with color +RLAPI void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) +RLAPI void rlCheckErrors(void); // Check and log OpenGL error codes +RLAPI void rlSetBlendMode(int mode); // Set blending mode +RLAPI void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation); // Set blending mode factor and equation (using OpenGL factors) +RLAPI void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha); // Set blending mode factors and equations separately (using OpenGL factors) + +//------------------------------------------------------------------------------------ +// Functions Declaration - rlgl functionality +//------------------------------------------------------------------------------------ +// rlgl initialization functions +RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) +RLAPI void rlglClose(void); // De-initialize rlgl (buffers, shaders, textures) +RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required) +RLAPI int rlGetVersion(void); // Get current OpenGL version +RLAPI void rlSetFramebufferWidth(int width); // Set current framebuffer width +RLAPI int rlGetFramebufferWidth(void); // Get default framebuffer width +RLAPI void rlSetFramebufferHeight(int height); // Set current framebuffer height +RLAPI int rlGetFramebufferHeight(void); // Get default framebuffer height + +RLAPI unsigned int rlGetTextureIdDefault(void); // Get default texture id +RLAPI unsigned int rlGetShaderIdDefault(void); // Get default shader id +RLAPI int *rlGetShaderLocsDefault(void); // Get default shader locations + +// Render batch management +// NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode +// but this render batch API is exposed in case of custom batches are required +RLAPI rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements); // Load a render batch system +RLAPI void rlUnloadRenderBatch(rlRenderBatch batch); // Unload render batch system +RLAPI void rlDrawRenderBatch(rlRenderBatch *batch); // Draw render batch data (Update->Draw->Reset) +RLAPI void rlSetRenderBatchActive(rlRenderBatch *batch); // Set the active render batch for rlgl (NULL for default internal) +RLAPI void rlDrawRenderBatchActive(void); // Update and draw internal render batch +RLAPI bool rlCheckRenderBatchLimit(int vCount); // Check internal buffer overflow for a given number of vertex + +RLAPI void rlSetTexture(unsigned int id); // Set current texture for render batch and check buffers limits + +//------------------------------------------------------------------------------------------------------------------------ + +// Vertex buffers management +RLAPI unsigned int rlLoadVertexArray(void); // Load vertex array (vao) if supported +RLAPI unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic); // Load a vertex buffer object +RLAPI unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic); // Load vertex buffer elements object +RLAPI void rlUpdateVertexBuffer(unsigned int bufferId, const void *data, int dataSize, int offset); // Update vertex buffer object data on GPU buffer +RLAPI void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset); // Update vertex buffer elements data on GPU buffer +RLAPI void rlUnloadVertexArray(unsigned int vaoId); // Unload vertex array (vao) +RLAPI void rlUnloadVertexBuffer(unsigned int vboId); // Unload vertex buffer object +RLAPI void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, int offset); // Set vertex attribute data configuration +RLAPI void rlSetVertexAttributeDivisor(unsigned int index, int divisor); // Set vertex attribute data divisor +RLAPI void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count); // Set vertex attribute default value, when attribute to provided +RLAPI void rlDrawVertexArray(int offset, int count); // Draw vertex array (currently active vao) +RLAPI void rlDrawVertexArrayElements(int offset, int count, const void *buffer); // Draw vertex array elements +RLAPI void rlDrawVertexArrayInstanced(int offset, int count, int instances); // Draw vertex array (currently active vao) with instancing +RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances); // Draw vertex array elements with instancing + +// Textures management +RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount); // Load texture data +RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo) +RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mipmapCount); // Load texture cubemap data +RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update texture with new data on GPU +RLAPI void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats +RLAPI const char *rlGetPixelFormatName(unsigned int format); // Get name string for pixel format +RLAPI void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory +RLAPI void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps); // Generate mipmap data for selected texture +RLAPI void *rlReadTexturePixels(unsigned int id, int width, int height, int format); // Read texture pixel data +RLAPI unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) + +// Framebuffer management (fbo) +RLAPI unsigned int rlLoadFramebuffer(void); // Load an empty framebuffer +RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer +RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete +RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU + +// Shaders management +RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings +RLAPI unsigned int rlCompileShader(const char *shaderCode, int type); // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER) +RLAPI unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program +RLAPI void rlUnloadShaderProgram(unsigned int id); // Unload shader program +RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName); // Get shader location uniform +RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName); // Get shader location attribute +RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count); // Set shader value uniform +RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat); // Set shader value matrix +RLAPI void rlSetUniformMatrices(int locIndex, const Matrix *mat, int count); // Set shader value matrices +RLAPI void rlSetUniformSampler(int locIndex, unsigned int textureId); // Set shader value sampler +RLAPI void rlSetShader(unsigned int id, int *locs); // Set shader currently active (id and locations) + +// Compute shader management +RLAPI unsigned int rlLoadComputeShaderProgram(unsigned int shaderId); // Load compute shader program +RLAPI void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ); // Dispatch compute shader (equivalent to *draw* for graphics pipeline) + +// Shader buffer storage object management (ssbo) +RLAPI unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHint); // Load shader storage buffer object (SSBO) +RLAPI void rlUnloadShaderBuffer(unsigned int ssboId); // Unload shader storage buffer object (SSBO) +RLAPI void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSize, unsigned int offset); // Update SSBO buffer data +RLAPI void rlBindShaderBuffer(unsigned int id, unsigned int index); // Bind SSBO buffer +RLAPI void rlReadShaderBuffer(unsigned int id, void *dest, unsigned int count, unsigned int offset); // Read SSBO buffer data (GPU->CPU) +RLAPI void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned int destOffset, unsigned int srcOffset, unsigned int count); // Copy SSBO data between buffers +RLAPI unsigned int rlGetShaderBufferSize(unsigned int id); // Get SSBO buffer size + +// Buffer management +RLAPI void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool readonly); // Bind image texture + +// Matrix state management +RLAPI Matrix rlGetMatrixModelview(void); // Get internal modelview matrix +RLAPI Matrix rlGetMatrixProjection(void); // Get internal projection matrix +RLAPI Matrix rlGetMatrixTransform(void); // Get internal accumulated transform matrix +RLAPI Matrix rlGetMatrixProjectionStereo(int eye); // Get internal projection matrix for stereo render (selected eye) +RLAPI Matrix rlGetMatrixViewOffsetStereo(int eye); // Get internal view offset matrix for stereo render (selected eye) +RLAPI void rlSetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) +RLAPI void rlSetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) +RLAPI void rlSetMatrixProjectionStereo(Matrix right, Matrix left); // Set eyes projection matrices for stereo rendering +RLAPI void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left); // Set eyes view offsets matrices for stereo rendering + +// Quick and dirty cube/quad buffers load->draw->unload +RLAPI void rlLoadDrawCube(void); // Load and draw a cube +RLAPI void rlLoadDrawQuad(void); // Load and draw a quad + +#if defined(__cplusplus) +} +#endif + +#endif // RLGL_H + +/*********************************************************************************** +* +* RLGL IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RLGL_IMPLEMENTATION) + +// Expose OpenGL functions from glad in raylib +#if defined(BUILD_LIBTYPE_SHARED) + #define GLAD_API_CALL_EXPORT + #define GLAD_API_CALL_EXPORT_BUILD +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + #if defined(__APPLE__) + #include // OpenGL 1.1 library for OSX + #include // OpenGL extensions library + #else + // APIENTRY for OpenGL function pointer declarations is required + #if !defined(APIENTRY) + #if defined(_WIN32) + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif + #endif + // WINGDIAPI definition. Some Windows OpenGL headers need it + #if !defined(WINGDIAPI) && defined(_WIN32) + #define WINGDIAPI __declspec(dllimport) + #endif + + #include // OpenGL 1.1 library + #endif +#endif + +#if defined(GRAPHICS_API_OPENGL_33) + #define GLAD_MALLOC RL_MALLOC + #define GLAD_FREE RL_FREE + + #define GLAD_GL_IMPLEMENTATION + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers +#endif + +#if defined(GRAPHICS_API_OPENGL_ES3) + #include // OpenGL ES 3.0 library + #define GL_GLEXT_PROTOTYPES + #include // OpenGL ES 2.0 extensions library +#elif defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: OpenGL ES 2.0 can be enabled on Desktop platforms, + // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 + #if defined(PLATFORM_DESKTOP_GLFW) || defined(PLATFORM_DESKTOP_SDL) + #define GLAD_GLES2_IMPLEMENTATION + #include "external/glad_gles2.h" + #else + #define GL_GLEXT_PROTOTYPES + //#include // EGL library -> not required, platform layer + #include // OpenGL ES 2.0 library + #include // OpenGL ES 2.0 extensions library + #endif + + // 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) + #if defined(PLATFORM_DRM) + 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 PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); + #endif +#endif + +#include // Required for: malloc(), free() +#include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] +#include // Required for: sqrtf(), sinf(), cosf(), floor(), log() + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef PI + #define PI 3.14159265358979323846f +#endif +#ifndef DEG2RAD + #define DEG2RAD (PI/180.0f) +#endif +#ifndef RAD2DEG + #define RAD2DEG (180.0f/PI) +#endif + +#ifndef GL_SHADING_LANGUAGE_VERSION + #define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#endif + +#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif +#ifndef GL_ETC1_RGB8_OES + #define GL_ETC1_RGB8_OES 0x8D64 +#endif +#ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 +#endif +#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif +#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#endif +#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR + #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_8x8_KHR + #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 +#endif + +#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif +#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#endif + +#ifndef GL_PROGRAM_POINT_SIZE + #define GL_PROGRAM_POINT_SIZE 0x8642 +#endif + +#ifndef GL_LINE_WIDTH + #define GL_LINE_WIDTH 0x0B21 +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + #define GL_UNSIGNED_SHORT_5_6_5 0x8363 + #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 + #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#endif + +#if defined(GRAPHICS_API_OPENGL_21) + #define GL_LUMINANCE 0x1909 + #define GL_LUMINANCE_ALPHA 0x190A +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + #define glClearDepth glClearDepthf + #if !defined(GRAPHICS_API_OPENGL_ES3) + #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER + #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER + #endif +#endif + +// Default shader vertex attribute names to set location points +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION + #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL + #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR + #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS + #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS "vertexBoneIds" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS + #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS +#endif + +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP + #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW + #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION + #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL + #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL + #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR + #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES + #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES "boneMatrices" // bone matrices +#endif +#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 + #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +#endif +#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 + #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) +#endif +#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 + #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +typedef struct rlglData { + rlRenderBatch *currentBatch; // Current render batch + rlRenderBatch defaultBatch; // Default internal render batch + + struct { + int vertexCounter; // Current active render batch vertex counter (generic, used for all batches) + float texcoordx, texcoordy; // Current active texture coordinate (added on glVertex*()) + float normalx, normaly, normalz; // Current active normal (added on glVertex*()) + unsigned char colorr, colorg, colorb, colora; // Current active color (added on glVertex*()) + + int currentMatrixMode; // Current matrix mode + Matrix *currentMatrix; // Current matrix pointer + Matrix modelview; // Default modelview matrix + Matrix projection; // Default projection matrix + Matrix transform; // Transform matrix to be used with rlTranslate, rlRotate, rlScale + bool transformRequired; // Require transform matrix application to current draw-call vertex (if required) + Matrix stack[RL_MAX_MATRIX_STACK_SIZE];// Matrix stack for push/pop + int stackCounter; // Matrix stack counter + + unsigned int defaultTextureId; // Default texture used on shapes/poly drawing (required by shader) + unsigned int activeTextureId[RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS]; // Active texture ids to be enabled on batch drawing (0 active by default) + unsigned int defaultVShaderId; // Default vertex shader id (used by default shader program) + unsigned int defaultFShaderId; // Default fragment shader id (used by default shader program) + unsigned int defaultShaderId; // Default shader program id, supports vertex color and diffuse texture + int *defaultShaderLocs; // Default shader locations pointer to be used on rendering + unsigned int currentShaderId; // Current shader id to be used on rendering (by default, defaultShaderId) + int *currentShaderLocs; // Current shader locations pointer to be used on rendering (by default, defaultShaderLocs) + + bool stereoRender; // Stereo rendering flag + Matrix projectionStereo[2]; // VR stereo rendering eyes projection matrices + Matrix viewOffsetStereo[2]; // VR stereo rendering eyes view offset matrices + + // Blending variables + int currentBlendMode; // Blending mode active + int glBlendSrcFactor; // Blending source factor + int glBlendDstFactor; // Blending destination factor + int glBlendEquation; // Blending equation + int glBlendSrcFactorRGB; // Blending source RGB factor + int glBlendDestFactorRGB; // Blending destination RGB factor + int glBlendSrcFactorAlpha; // Blending source alpha factor + int glBlendDestFactorAlpha; // Blending destination alpha factor + int glBlendEquationRGB; // Blending equation for RGB + int glBlendEquationAlpha; // Blending equation for alpha + bool glCustomBlendModeModified; // Custom blending factor and equation modification status + + int framebufferWidth; // Current framebuffer width + int framebufferHeight; // Current framebuffer height + + } State; // Renderer state + struct { + bool vao; // VAO support (OpenGL ES2 could not support VAO extension) (GL_ARB_vertex_array_object) + bool instancing; // Instancing supported (GL_ANGLE_instanced_arrays, GL_EXT_draw_instanced + GL_EXT_instanced_arrays) + bool texNPOT; // NPOT textures full support (GL_ARB_texture_non_power_of_two, GL_OES_texture_npot) + 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 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 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 texCompPVRT; // PVR texture compression support (GL_IMG_texture_compression_pvrtc) + bool texCompASTC; // ASTC texture compression support (GL_KHR_texture_compression_astc_hdr, GL_KHR_texture_compression_astc_ldr) + bool texMirrorClamp; // Clamp mirror wrap mode supported (GL_EXT_texture_mirror_clamp) + bool texAnisoFilter; // Anisotropic texture filtering support (GL_EXT_texture_filter_anisotropic) + bool computeShader; // Compute shaders support (GL_ARB_compute_shader) + bool ssbo; // Shader storage buffer object support (GL_ARB_shader_storage_buffer_object) + + float maxAnisotropyLevel; // Maximum anisotropy level supported (minimum is 2.0f) + int maxDepthBits; // Maximum bits for depth component + + } ExtSupported; // Extensions supported flags +} rlglData; + +typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions loader signature (same as GLADloadproc) + +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static double rlCullDistanceNear = RL_CULL_DISTANCE_NEAR; +static double rlCullDistanceFar = RL_CULL_DISTANCE_FAR; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +static rlglData RLGL = { 0 }; +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3) +// NOTE: VAO functionality is exposed through extensions (OES) +static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL; +static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL; +static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL; + +// NOTE: Instancing functionality could also be available through extension +static PFNGLDRAWARRAYSINSTANCEDEXTPROC glDrawArraysInstanced = NULL; +static PFNGLDRAWELEMENTSINSTANCEDEXTPROC glDrawElementsInstanced = NULL; +static PFNGLVERTEXATTRIBDIVISOREXTPROC glVertexAttribDivisor = NULL; +#endif + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +static void rlLoadShaderDefault(void); // Load default shader +static void rlUnloadShaderDefault(void); // Unload default shader +#if defined(RLGL_SHOW_GL_DETAILS_INFO) +static const char *rlGetCompressedFormatName(int format); // Get compressed format official GL identifier name +#endif // RLGL_SHOW_GL_DETAILS_INFO +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +static int rlGetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) + +// Auxiliar matrix math functions +typedef struct rl_float16 { + float v[16]; +} rl_float16; +static rl_float16 rlMatrixToFloatV(Matrix mat); // Get float array of matrix data +#define rlMatrixToFloat(mat) (rlMatrixToFloatV(mat).v) // Get float vector for Matrix +static Matrix rlMatrixIdentity(void); // Get identity matrix +static Matrix rlMatrixMultiply(Matrix left, Matrix right); // Multiply two matrices +static Matrix rlMatrixTranspose(Matrix mat); // Transposes provided matrix +static Matrix rlMatrixInvert(Matrix mat); // Invert provided matrix + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Matrix operations +//---------------------------------------------------------------------------------- + +#if defined(GRAPHICS_API_OPENGL_11) +// Fallback to OpenGL 1.1 function calls +//--------------------------------------- +void rlMatrixMode(int mode) +{ + switch (mode) + { + case RL_PROJECTION: glMatrixMode(GL_PROJECTION); break; + case RL_MODELVIEW: glMatrixMode(GL_MODELVIEW); break; + case RL_TEXTURE: glMatrixMode(GL_TEXTURE); break; + default: break; + } +} + +void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar) +{ + glFrustum(left, right, bottom, top, znear, zfar); +} + +void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar) +{ + glOrtho(left, right, bottom, top, znear, zfar); +} + +void rlPushMatrix(void) { glPushMatrix(); } +void rlPopMatrix(void) { glPopMatrix(); } +void rlLoadIdentity(void) { glLoadIdentity(); } +void rlTranslatef(float x, float y, float z) { glTranslatef(x, y, z); } +void rlRotatef(float angle, float x, float y, float z) { glRotatef(angle, x, y, z); } +void rlScalef(float x, float y, float z) { glScalef(x, y, z); } +void rlMultMatrixf(const float *matf) { glMultMatrixf(matf); } +#endif +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Choose the current matrix to be transformed +void rlMatrixMode(int mode) +{ + if (mode == RL_PROJECTION) RLGL.State.currentMatrix = &RLGL.State.projection; + else if (mode == RL_MODELVIEW) RLGL.State.currentMatrix = &RLGL.State.modelview; + //else if (mode == RL_TEXTURE) // Not supported + + RLGL.State.currentMatrixMode = mode; +} + +// Push the current matrix into RLGL.State.stack +void rlPushMatrix(void) +{ + if (RLGL.State.stackCounter >= RL_MAX_MATRIX_STACK_SIZE) TRACELOG(RL_LOG_ERROR, "RLGL: Matrix stack overflow (RL_MAX_MATRIX_STACK_SIZE)"); + + if (RLGL.State.currentMatrixMode == RL_MODELVIEW) + { + RLGL.State.transformRequired = true; + RLGL.State.currentMatrix = &RLGL.State.transform; + } + + RLGL.State.stack[RLGL.State.stackCounter] = *RLGL.State.currentMatrix; + RLGL.State.stackCounter++; +} + +// Pop lattest inserted matrix from RLGL.State.stack +void rlPopMatrix(void) +{ + if (RLGL.State.stackCounter > 0) + { + Matrix mat = RLGL.State.stack[RLGL.State.stackCounter - 1]; + *RLGL.State.currentMatrix = mat; + RLGL.State.stackCounter--; + } + + if ((RLGL.State.stackCounter == 0) && (RLGL.State.currentMatrixMode == RL_MODELVIEW)) + { + RLGL.State.currentMatrix = &RLGL.State.modelview; + RLGL.State.transformRequired = false; + } +} + +// Reset current matrix to identity matrix +void rlLoadIdentity(void) +{ + *RLGL.State.currentMatrix = rlMatrixIdentity(); +} + +// Multiply the current matrix by a translation matrix +void rlTranslatef(float x, float y, float z) +{ + Matrix matTranslation = { + 1.0f, 0.0f, 0.0f, x, + 0.0f, 1.0f, 0.0f, y, + 0.0f, 0.0f, 1.0f, z, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + // NOTE: We transpose matrix with multiplication order + *RLGL.State.currentMatrix = rlMatrixMultiply(matTranslation, *RLGL.State.currentMatrix); +} + +// Multiply the current matrix by a rotation matrix +// NOTE: The provided angle must be in degrees +void rlRotatef(float angle, float x, float y, float z) +{ + Matrix matRotation = rlMatrixIdentity(); + + // Axis vector (x, y, z) normalization + float lengthSquared = x*x + y*y + z*z; + if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) + { + float inverseLength = 1.0f/sqrtf(lengthSquared); + x *= inverseLength; + y *= inverseLength; + z *= inverseLength; + } + + // Rotation matrix generation + float sinres = sinf(DEG2RAD*angle); + float cosres = cosf(DEG2RAD*angle); + float t = 1.0f - cosres; + + matRotation.m0 = x*x*t + cosres; + matRotation.m1 = y*x*t + z*sinres; + matRotation.m2 = z*x*t - y*sinres; + matRotation.m3 = 0.0f; + + matRotation.m4 = x*y*t - z*sinres; + matRotation.m5 = y*y*t + cosres; + matRotation.m6 = z*y*t + x*sinres; + matRotation.m7 = 0.0f; + + matRotation.m8 = x*z*t + y*sinres; + matRotation.m9 = y*z*t - x*sinres; + matRotation.m10 = z*z*t + cosres; + matRotation.m11 = 0.0f; + + matRotation.m12 = 0.0f; + matRotation.m13 = 0.0f; + matRotation.m14 = 0.0f; + matRotation.m15 = 1.0f; + + // NOTE: We transpose matrix with multiplication order + *RLGL.State.currentMatrix = rlMatrixMultiply(matRotation, *RLGL.State.currentMatrix); +} + +// Multiply the current matrix by a scaling matrix +void rlScalef(float x, float y, float z) +{ + Matrix matScale = { + x, 0.0f, 0.0f, 0.0f, + 0.0f, y, 0.0f, 0.0f, + 0.0f, 0.0f, z, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + // NOTE: We transpose matrix with multiplication order + *RLGL.State.currentMatrix = rlMatrixMultiply(matScale, *RLGL.State.currentMatrix); +} + +// Multiply the current matrix by another matrix +void rlMultMatrixf(const float *matf) +{ + // Matrix creation from array + Matrix mat = { matf[0], matf[4], matf[8], matf[12], + matf[1], matf[5], matf[9], matf[13], + matf[2], matf[6], matf[10], matf[14], + matf[3], matf[7], matf[11], matf[15] }; + + *RLGL.State.currentMatrix = rlMatrixMultiply(mat, *RLGL.State.currentMatrix); +} + +// Multiply the current matrix by a perspective matrix generated by parameters +void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar) +{ + Matrix matFrustum = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(zfar - znear); + + matFrustum.m0 = ((float) znear*2.0f)/rl; + matFrustum.m1 = 0.0f; + matFrustum.m2 = 0.0f; + matFrustum.m3 = 0.0f; + + matFrustum.m4 = 0.0f; + matFrustum.m5 = ((float) znear*2.0f)/tb; + matFrustum.m6 = 0.0f; + matFrustum.m7 = 0.0f; + + matFrustum.m8 = ((float)right + (float)left)/rl; + matFrustum.m9 = ((float)top + (float)bottom)/tb; + matFrustum.m10 = -((float)zfar + (float)znear)/fn; + matFrustum.m11 = -1.0f; + + matFrustum.m12 = 0.0f; + matFrustum.m13 = 0.0f; + matFrustum.m14 = -((float)zfar*(float)znear*2.0f)/fn; + matFrustum.m15 = 0.0f; + + *RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, matFrustum); +} + +// Multiply the current matrix by an orthographic matrix generated by parameters +void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar) +{ + // NOTE: If left-right and top-botton values are equal it could create a division by zero, + // response to it is platform/compiler dependant + Matrix matOrtho = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(zfar - znear); + + matOrtho.m0 = 2.0f/rl; + matOrtho.m1 = 0.0f; + matOrtho.m2 = 0.0f; + matOrtho.m3 = 0.0f; + matOrtho.m4 = 0.0f; + matOrtho.m5 = 2.0f/tb; + matOrtho.m6 = 0.0f; + matOrtho.m7 = 0.0f; + matOrtho.m8 = 0.0f; + matOrtho.m9 = 0.0f; + matOrtho.m10 = -2.0f/fn; + matOrtho.m11 = 0.0f; + matOrtho.m12 = -((float)left + (float)right)/rl; + matOrtho.m13 = -((float)top + (float)bottom)/tb; + matOrtho.m14 = -((float)zfar + (float)znear)/fn; + matOrtho.m15 = 1.0f; + + *RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, matOrtho); +} +#endif + +// Set the viewport area (transformation from normalized device coordinates to window coordinates) +// NOTE: We store current viewport dimensions +void rlViewport(int x, int y, int width, int height) +{ + glViewport(x, y, width, height); +} + +// Set clip planes distances +void rlSetClipPlanes(double nearPlane, double farPlane) +{ + rlCullDistanceNear = nearPlane; + rlCullDistanceFar = farPlane; +} + +// Get cull plane distance near +double rlGetCullDistanceNear(void) +{ + return rlCullDistanceNear; +} + +// Get cull plane distance far +double rlGetCullDistanceFar(void) +{ + return rlCullDistanceFar; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vertex level operations +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_11) +// Fallback to OpenGL 1.1 function calls +//--------------------------------------- +void rlBegin(int mode) +{ + switch (mode) + { + case RL_LINES: glBegin(GL_LINES); break; + case RL_TRIANGLES: glBegin(GL_TRIANGLES); break; + case RL_QUADS: glBegin(GL_QUADS); break; + default: break; + } +} + +void rlEnd(void) { glEnd(); } +void rlVertex2i(int x, int y) { glVertex2i(x, y); } +void rlVertex2f(float x, float y) { glVertex2f(x, y); } +void rlVertex3f(float x, float y, float z) { glVertex3f(x, y, z); } +void rlTexCoord2f(float x, float y) { glTexCoord2f(x, y); } +void rlNormal3f(float x, float y, float z) { glNormal3f(x, y, z); } +void rlColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { glColor4ub(r, g, b, a); } +void rlColor3f(float x, float y, float z) { glColor3f(x, y, z); } +void rlColor4f(float x, float y, float z, float w) { glColor4f(x, y, z, w); } +#endif +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Initialize drawing mode (how to organize vertex) +void rlBegin(int mode) +{ + // Draw mode can be RL_LINES, RL_TRIANGLES and RL_QUADS + // NOTE: In all three cases, vertex are accumulated over default internal vertex buffer + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode != mode) + { + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount > 0) + { + // Make sure current RLGL.currentBatch->draws[i].vertexCount is aligned a multiple of 4, + // that way, following QUADS drawing will keep aligned with index processing + // It implies adding some extra alignment vertex at the end of the draw, + // those vertex are not processed but they are considered as an additional offset + // for the next set of vertex to be drawn + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount : RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4); + else if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? 1 : (4 - (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4))); + else RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = 0; + + if (!rlCheckRenderBatchLimit(RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment)) + { + RLGL.State.vertexCounter += RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment; + RLGL.currentBatch->drawCounter++; + } + } + + if (RLGL.currentBatch->drawCounter >= RL_DEFAULT_BATCH_DRAWCALLS) rlDrawRenderBatch(RLGL.currentBatch); + + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = mode; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount = 0; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = RLGL.State.defaultTextureId; + } +} + +// Finish vertex providing +void rlEnd(void) +{ + // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, + // as well as depth buffer bit-depth (16bit or 24bit or 32bit) + // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) + RLGL.currentBatch->currentDepth += (1.0f/20000.0f); +} + +// Define one vertex (position) +// NOTE: Vertex position data is the basic information required for drawing +void rlVertex3f(float x, float y, float z) +{ + float tx = x; + float ty = y; + float tz = z; + + // Transform provided vector if required + if (RLGL.State.transformRequired) + { + tx = RLGL.State.transform.m0*x + RLGL.State.transform.m4*y + RLGL.State.transform.m8*z + RLGL.State.transform.m12; + ty = RLGL.State.transform.m1*x + RLGL.State.transform.m5*y + RLGL.State.transform.m9*z + RLGL.State.transform.m13; + tz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z + RLGL.State.transform.m14; + } + + // WARNING: We can't break primitives when launching a new batch + // RL_LINES comes in pairs, RL_TRIANGLES come in groups of 3 vertices and RL_QUADS come in groups of 4 vertices + // We must check current draw.mode when a new vertex is required and finish the batch only if the draw.mode draw.vertexCount is %2, %3 or %4 + if (RLGL.State.vertexCounter > (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4 - 4)) + { + if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) && + (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%2 == 0)) + { + // Reached the maximum number of vertices for RL_LINES drawing + // Launch a draw call but keep current state for next vertices comming + // NOTE: We add +1 vertex to the check for security + rlCheckRenderBatchLimit(2 + 1); + } + else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) && + (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%3 == 0)) + { + rlCheckRenderBatchLimit(3 + 1); + } + else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_QUADS) && + (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4 == 0)) + { + rlCheckRenderBatchLimit(4 + 1); + } + } + + // Add vertices + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter] = tx; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 1] = ty; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 2] = tz; + + // Add current texcoord + 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; + + // Add current normal + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].normals[3*RLGL.State.vertexCounter] = RLGL.State.normalx; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].normals[3*RLGL.State.vertexCounter + 1] = RLGL.State.normaly; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].normals[3*RLGL.State.vertexCounter + 2] = RLGL.State.normalz; + + // 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 + 1] = RLGL.State.colorg; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 2] = RLGL.State.colorb; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 3] = RLGL.State.colora; + + RLGL.State.vertexCounter++; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount++; +} + +// Define one vertex (position) +void rlVertex2f(float x, float y) +{ + rlVertex3f(x, y, RLGL.currentBatch->currentDepth); +} + +// Define one vertex (position) +void rlVertex2i(int x, int y) +{ + rlVertex3f((float)x, (float)y, RLGL.currentBatch->currentDepth); +} + +// Define one vertex (texture coordinate) +// NOTE: Texture coordinates are limited to QUADS only +void rlTexCoord2f(float x, float y) +{ + RLGL.State.texcoordx = x; + RLGL.State.texcoordy = y; +} + +// Define one vertex (normal) +// NOTE: Normals limited to TRIANGLES only? +void rlNormal3f(float x, float y, float z) +{ + float normalx = x; + float normaly = y; + float normalz = z; + if (RLGL.State.transformRequired) + { + normalx = RLGL.State.transform.m0*x + RLGL.State.transform.m4*y + RLGL.State.transform.m8*z; + normaly = RLGL.State.transform.m1*x + RLGL.State.transform.m5*y + RLGL.State.transform.m9*z; + normalz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z; + } + float length = sqrtf(normalx*normalx + normaly*normaly + normalz*normalz); + if (length != 0.0f) + { + float ilength = 1.0f/length; + normalx *= ilength; + normaly *= ilength; + normalz *= ilength; + } + RLGL.State.normalx = normalx; + RLGL.State.normaly = normaly; + RLGL.State.normalz = normalz; +} + +// Define one vertex (color) +void rlColor4ub(unsigned char x, unsigned char y, unsigned char z, unsigned char w) +{ + RLGL.State.colorr = x; + RLGL.State.colorg = y; + RLGL.State.colorb = z; + RLGL.State.colora = w; +} + +// Define one vertex (color) +void rlColor4f(float r, float g, float b, float a) +{ + rlColor4ub((unsigned char)(r*255), (unsigned char)(g*255), (unsigned char)(b*255), (unsigned char)(a*255)); +} + +// Define one vertex (color) +void rlColor3f(float x, float y, float z) +{ + rlColor4ub((unsigned char)(x*255), (unsigned char)(y*255), (unsigned char)(z*255), 255); +} + +#endif + +//-------------------------------------------------------------------------------------- +// Module Functions Definition - OpenGL style functions (common to 1.1, 3.3+, ES2) +//-------------------------------------------------------------------------------------- + +// Set current texture to use +void rlSetTexture(unsigned int id) +{ + if (id == 0) + { +#if defined(GRAPHICS_API_OPENGL_11) + rlDisableTexture(); +#else + // NOTE: If quads batch limit is reached, we force a draw call and next batch starts + if (RLGL.State.vertexCounter >= + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4) + { + rlDrawRenderBatch(RLGL.currentBatch); + } +#endif + } + else + { +#if defined(GRAPHICS_API_OPENGL_11) + rlEnableTexture(id); +#else + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId != id) + { + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount > 0) + { + // Make sure current RLGL.currentBatch->draws[i].vertexCount is aligned a multiple of 4, + // that way, following QUADS drawing will keep aligned with index processing + // It implies adding some extra alignment vertex at the end of the draw, + // those vertex are not processed but they are considered as an additional offset + // for the next set of vertex to be drawn + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount : RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4); + else if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? 1 : (4 - (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4))); + else RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = 0; + + if (!rlCheckRenderBatchLimit(RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment)) + { + RLGL.State.vertexCounter += RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment; + + RLGL.currentBatch->drawCounter++; + } + } + + if (RLGL.currentBatch->drawCounter >= RL_DEFAULT_BATCH_DRAWCALLS) rlDrawRenderBatch(RLGL.currentBatch); + + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = id; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount = 0; + } +#endif + } +} + +// Select and active a texture slot +void rlActiveTextureSlot(int slot) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glActiveTexture(GL_TEXTURE0 + slot); +#endif +} + +// Enable texture +void rlEnableTexture(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_TEXTURE_2D); +#endif + glBindTexture(GL_TEXTURE_2D, id); +} + +// Disable texture +void rlDisableTexture(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glDisable(GL_TEXTURE_2D); +#endif + glBindTexture(GL_TEXTURE_2D, 0); +} + +// Enable texture cubemap +void rlEnableTextureCubemap(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindTexture(GL_TEXTURE_CUBE_MAP, id); +#endif +} + +// Disable texture cubemap +void rlDisableTextureCubemap(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif +} + +// Set texture parameters (wrap mode/filter mode) +void rlTextureParameters(unsigned int id, int param, int value) +{ + glBindTexture(GL_TEXTURE_2D, id); + +#if !defined(GRAPHICS_API_OPENGL_11) + // Reset anisotropy filter, in case it was set + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); +#endif + + switch (param) + { + case RL_TEXTURE_WRAP_S: + case RL_TEXTURE_WRAP_T: + { + if (value == RL_TEXTURE_WRAP_MIRROR_CLAMP) + { +#if !defined(GRAPHICS_API_OPENGL_11) + if (RLGL.ExtSupported.texMirrorClamp) glTexParameteri(GL_TEXTURE_2D, param, value); + else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); +#endif + } + else glTexParameteri(GL_TEXTURE_2D, param, value); + + } break; + case RL_TEXTURE_MAG_FILTER: + case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break; + case RL_TEXTURE_FILTER_ANISOTROPIC: + { +#if !defined(GRAPHICS_API_OPENGL_11) + if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f) + { + TRACELOG(RL_LOG_WARNING, "GL: Maximum anisotropic filter level supported is %iX", id, (int)RLGL.ExtSupported.maxAnisotropyLevel); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + } + else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); +#endif + } break; +#if defined(GRAPHICS_API_OPENGL_33) + case RL_TEXTURE_MIPMAP_BIAS_RATIO: glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, value/100.0f); +#endif + default: break; + } + + glBindTexture(GL_TEXTURE_2D, 0); +} + +// Set cubemap parameters (wrap mode/filter mode) +void rlCubemapParameters(unsigned int id, int param, int value) +{ +#if !defined(GRAPHICS_API_OPENGL_11) + glBindTexture(GL_TEXTURE_CUBE_MAP, id); + + // Reset anisotropy filter, in case it was set + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + + switch (param) + { + case RL_TEXTURE_WRAP_S: + case RL_TEXTURE_WRAP_T: + { + if (value == RL_TEXTURE_WRAP_MIRROR_CLAMP) + { + if (RLGL.ExtSupported.texMirrorClamp) glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); + else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); + } + else glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); + + } break; + case RL_TEXTURE_MAG_FILTER: + case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); break; + case RL_TEXTURE_FILTER_ANISOTROPIC: + { + if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f) + { + TRACELOG(RL_LOG_WARNING, "GL: Maximum anisotropic filter level supported is %iX", id, (int)RLGL.ExtSupported.maxAnisotropyLevel); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + } + else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); + } break; +#if defined(GRAPHICS_API_OPENGL_33) + case RL_TEXTURE_MIPMAP_BIAS_RATIO: glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_LOD_BIAS, value/100.0f); +#endif + default: break; + } + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif +} + +// Enable shader program +void rlEnableShader(unsigned int id) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + glUseProgram(id); +#endif +} + +// Disable shader program +void rlDisableShader(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + glUseProgram(0); +#endif +} + +// Enable rendering to texture (fbo) +void rlEnableFramebuffer(unsigned int id) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, id); +#endif +} + +// return the active render texture (fbo) +unsigned int rlGetActiveFramebuffer(void) +{ + GLint fboId = 0; +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT) + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fboId); +#endif + return fboId; +} + +// Disable rendering to texture +void rlDisableFramebuffer(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#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 +} + +// Bind framebuffer object (fbo) +void rlBindFramebuffer(unsigned int target, unsigned int framebuffer) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBindFramebuffer(target, framebuffer); +#endif +} + +// Activate multiple draw color buffers +// NOTE: One color buffer is always active by default +void rlActiveDrawBuffers(int count) +{ +#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, + // it can be queried with glGet*() but it must be at least 8 + //GLint maxDrawBuffers = 0; + //glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); + + if (count > 0) + { + if (count > 8) TRACELOG(LOG_WARNING, "GL: Max color buffers limited to 8"); + else + { + 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_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3, + GL_COLOR_ATTACHMENT4, + GL_COLOR_ATTACHMENT5, + GL_COLOR_ATTACHMENT6, + GL_COLOR_ATTACHMENT7, +#endif + }; + +#if defined(GRAPHICS_API_OPENGL_ES3) + glDrawBuffersEXT(count, buffers); +#else + glDrawBuffers(count, buffers); +#endif + } + } + else TRACELOG(LOG_WARNING, "GL: One color buffer active by default"); +#endif +} + +//---------------------------------------------------------------------------------- +// General render state configuration +//---------------------------------------------------------------------------------- + +// Enable color blending +void rlEnableColorBlend(void) { glEnable(GL_BLEND); } + +// Disable color blending +void rlDisableColorBlend(void) { glDisable(GL_BLEND); } + +// Enable depth test +void rlEnableDepthTest(void) { glEnable(GL_DEPTH_TEST); } + +// Disable depth test +void rlDisableDepthTest(void) { glDisable(GL_DEPTH_TEST); } + +// Enable depth write +void rlEnableDepthMask(void) { glDepthMask(GL_TRUE); } + +// Disable depth write +void rlDisableDepthMask(void) { glDepthMask(GL_FALSE); } + +// Enable backface culling +void rlEnableBackfaceCulling(void) { glEnable(GL_CULL_FACE); } + +// Disable backface culling +void rlDisableBackfaceCulling(void) { glDisable(GL_CULL_FACE); } + +// Set color mask active for screen read/draw +void rlColorMask(bool r, bool g, bool b, bool a) { glColorMask(r, g, b, a); } + +// Set face culling mode +void rlSetCullFace(int mode) +{ + switch (mode) + { + case RL_CULL_FACE_BACK: glCullFace(GL_BACK); break; + case RL_CULL_FACE_FRONT: glCullFace(GL_FRONT); break; + default: break; + } +} + +// Enable scissor test +void rlEnableScissorTest(void) { glEnable(GL_SCISSOR_TEST); } + +// Disable scissor test +void rlDisableScissorTest(void) { glDisable(GL_SCISSOR_TEST); } + +// Scissor test +void rlScissor(int x, int y, int width, int height) { glScissor(x, y, width, height); } + +// Enable wire mode +void rlEnableWireMode(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_LINE); +#endif +} + +// Enable point mode +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 +void rlDisableWireMode(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_FILL); +#endif +} + +// Set the line drawing width +void rlSetLineWidth(float width) { glLineWidth(width); } + +// Get the line drawing width +float rlGetLineWidth(void) +{ + float width = 0; + glGetFloatv(GL_LINE_WIDTH, &width); + return width; +} + +// Enable line aliasing +void rlEnableSmoothLines(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_LINE_SMOOTH); +#endif +} + +// Disable line aliasing +void rlDisableSmoothLines(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_11) + glDisable(GL_LINE_SMOOTH); +#endif +} + +// Enable stereo rendering +void rlEnableStereoRender(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + RLGL.State.stereoRender = true; +#endif +} + +// Disable stereo rendering +void rlDisableStereoRender(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + RLGL.State.stereoRender = false; +#endif +} + +// Check if stereo render is enabled +bool rlIsStereoRenderEnabled(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + return RLGL.State.stereoRender; +#else + return false; +#endif +} + +// Clear color buffer with color +void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + // Color values clamp to 0.0f(0) and 1.0f(255) + float cr = (float)r/255; + float cg = (float)g/255; + float cb = (float)b/255; + float ca = (float)a/255; + + glClearColor(cr, cg, cb, ca); +} + +// Clear used screen buffers (color and depth) +void rlClearScreenBuffers(void) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers: Color and Depth (Depth is used for 3D) + //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Stencil buffer not used... +} + +// Check and log OpenGL error codes +void rlCheckErrors(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int check = 1; + while (check) + { + const GLenum err = glGetError(); + switch (err) + { + case GL_NO_ERROR: check = 0; break; + case 0x0500: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_ENUM"); break; + case 0x0501: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_VALUE"); break; + case 0x0502: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_OPERATION"); break; + case 0x0503: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_STACK_OVERFLOW"); break; + case 0x0504: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_STACK_UNDERFLOW"); break; + case 0x0505: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_OUT_OF_MEMORY"); break; + case 0x0506: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_FRAMEBUFFER_OPERATION"); break; + default: TRACELOG(RL_LOG_WARNING, "GL: Error detected: Unknown error code: %x", err); break; + } + } +#endif +} + +// Set blend mode +void rlSetBlendMode(int mode) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.State.currentBlendMode != mode) || ((mode == RL_BLEND_CUSTOM || mode == RL_BLEND_CUSTOM_SEPARATE) && RLGL.State.glCustomBlendModeModified)) + { + rlDrawRenderBatch(RLGL.currentBatch); + + switch (mode) + { + case RL_BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_ADD_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_SUBTRACT_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_SUBTRACT); break; + case RL_BLEND_ALPHA_PREMULTIPLY: glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_CUSTOM: + { + // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactors() + glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation); + + } break; + case RL_BLEND_CUSTOM_SEPARATE: + { + // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactorsSeparate() + glBlendFuncSeparate(RLGL.State.glBlendSrcFactorRGB, RLGL.State.glBlendDestFactorRGB, RLGL.State.glBlendSrcFactorAlpha, RLGL.State.glBlendDestFactorAlpha); + glBlendEquationSeparate(RLGL.State.glBlendEquationRGB, RLGL.State.glBlendEquationAlpha); + + } break; + default: break; + } + + RLGL.State.currentBlendMode = mode; + RLGL.State.glCustomBlendModeModified = false; + } +#endif +} + +// Set blending mode factor and equation +void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.State.glBlendSrcFactor != glSrcFactor) || + (RLGL.State.glBlendDstFactor != glDstFactor) || + (RLGL.State.glBlendEquation != glEquation)) + { + RLGL.State.glBlendSrcFactor = glSrcFactor; + RLGL.State.glBlendDstFactor = glDstFactor; + RLGL.State.glBlendEquation = glEquation; + + RLGL.State.glCustomBlendModeModified = true; + } +#endif +} + +// Set blending mode factor and equation separately for RGB and alpha +void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.State.glBlendSrcFactorRGB != glSrcRGB) || + (RLGL.State.glBlendDestFactorRGB != glDstRGB) || + (RLGL.State.glBlendSrcFactorAlpha != glSrcAlpha) || + (RLGL.State.glBlendDestFactorAlpha != glDstAlpha) || + (RLGL.State.glBlendEquationRGB != glEqRGB) || + (RLGL.State.glBlendEquationAlpha != glEqAlpha)) + { + RLGL.State.glBlendSrcFactorRGB = glSrcRGB; + RLGL.State.glBlendDestFactorRGB = glDstRGB; + RLGL.State.glBlendSrcFactorAlpha = glSrcAlpha; + RLGL.State.glBlendDestFactorAlpha = glDstAlpha; + RLGL.State.glBlendEquationRGB = glEqRGB; + RLGL.State.glBlendEquationAlpha = glEqAlpha; + + RLGL.State.glCustomBlendModeModified = true; + } +#endif +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - OpenGL Debug +//---------------------------------------------------------------------------------- +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43) +static void GLAPIENTRY rlDebugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) +{ + // Ignore non-significant error/warning codes (NVidia drivers) + // NOTE: Here there are the details with a sample output: + // - #131169 - Framebuffer detailed info: The driver allocated storage for renderbuffer 2. (severity: low) + // - #131185 - Buffer detailed info: Buffer object 1 (bound to GL_ELEMENT_ARRAY_BUFFER_ARB, usage hint is GL_ENUM_88e4) + // will use VIDEO memory as the source for buffer object operations. (severity: low) + // - #131218 - Program/shader state performance warning: Vertex shader in program 7 is being recompiled based on GL state. (severity: medium) + // - #131204 - Texture state usage warning: The texture object (0) bound to texture image unit 0 does not have + // a defined base level and cannot be used for texture mapping. (severity: low) + if ((id == 131169) || (id == 131185) || (id == 131218) || (id == 131204)) return; + + const char *msgSource = NULL; + switch (source) + { + case GL_DEBUG_SOURCE_API: msgSource = "API"; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: msgSource = "WINDOW_SYSTEM"; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: msgSource = "SHADER_COMPILER"; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: msgSource = "THIRD_PARTY"; break; + case GL_DEBUG_SOURCE_APPLICATION: msgSource = "APPLICATION"; break; + case GL_DEBUG_SOURCE_OTHER: msgSource = "OTHER"; break; + default: break; + } + + const char *msgType = NULL; + switch (type) + { + case GL_DEBUG_TYPE_ERROR: msgType = "ERROR"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: msgType = "DEPRECATED_BEHAVIOR"; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: msgType = "UNDEFINED_BEHAVIOR"; break; + case GL_DEBUG_TYPE_PORTABILITY: msgType = "PORTABILITY"; break; + case GL_DEBUG_TYPE_PERFORMANCE: msgType = "PERFORMANCE"; break; + case GL_DEBUG_TYPE_MARKER: msgType = "MARKER"; break; + case GL_DEBUG_TYPE_PUSH_GROUP: msgType = "PUSH_GROUP"; break; + case GL_DEBUG_TYPE_POP_GROUP: msgType = "POP_GROUP"; break; + case GL_DEBUG_TYPE_OTHER: msgType = "OTHER"; break; + default: break; + } + + const char *msgSeverity = "DEFAULT"; + switch (severity) + { + case GL_DEBUG_SEVERITY_LOW: msgSeverity = "LOW"; break; + case GL_DEBUG_SEVERITY_MEDIUM: msgSeverity = "MEDIUM"; break; + case GL_DEBUG_SEVERITY_HIGH: msgSeverity = "HIGH"; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: msgSeverity = "NOTIFICATION"; break; + default: break; + } + + TRACELOG(LOG_WARNING, "GL: OpenGL debug message: %s", message); + TRACELOG(LOG_WARNING, " > Type: %s", msgType); + TRACELOG(LOG_WARNING, " > Source = %s", msgSource); + TRACELOG(LOG_WARNING, " > Severity = %s", msgSeverity); +} +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition - rlgl functionality +//---------------------------------------------------------------------------------- + +// Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states +void rlglInit(int width, int height) +{ + // Enable OpenGL debug context if required +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43) + if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL)) + { + glDebugMessageCallback(rlDebugMessageCallback, 0); + // glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_HIGH, 0, 0, GL_TRUE); + + // Debug context options: + // - GL_DEBUG_OUTPUT - Faster version but not useful for breakpoints + // - GL_DEBUG_OUTPUT_SYNCHRONUS - Callback is in sync with errors, so a breakpoint can be placed on the callback in order to get a stacktrace for the GL error + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + } +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Init default white texture + unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) + RLGL.State.defaultTextureId = rlLoadTexture(pixels, 1, 1, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1); + + if (RLGL.State.defaultTextureId != 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture loaded successfully", RLGL.State.defaultTextureId); + else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load default texture"); + + // Init default Shader (customized for GL 3.3 and ES2) + // Loaded: RLGL.State.defaultShaderId + RLGL.State.defaultShaderLocs + rlLoadShaderDefault(); + RLGL.State.currentShaderId = RLGL.State.defaultShaderId; + RLGL.State.currentShaderLocs = RLGL.State.defaultShaderLocs; + + // Init default vertex arrays buffers + // Simulate that the default shader has the location RL_SHADER_LOC_VERTEX_NORMAL to bind the normal buffer for the default render batch + RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL] = RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL; + RLGL.defaultBatch = rlLoadRenderBatch(RL_DEFAULT_BATCH_BUFFERS, RL_DEFAULT_BATCH_BUFFER_ELEMENTS); + RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL] = -1; + RLGL.currentBatch = &RLGL.defaultBatch; + + // Init stack matrices (emulating OpenGL 1.1) + for (int i = 0; i < RL_MAX_MATRIX_STACK_SIZE; i++) RLGL.State.stack[i] = rlMatrixIdentity(); + + // Init internal matrices + RLGL.State.transform = rlMatrixIdentity(); + RLGL.State.projection = rlMatrixIdentity(); + RLGL.State.modelview = rlMatrixIdentity(); + RLGL.State.currentMatrix = &RLGL.State.modelview; +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + + // Initialize OpenGL default states + //---------------------------------------------------------- + // Init state: Depth test + glDepthFunc(GL_LEQUAL); // Type of depth testing to apply + glDisable(GL_DEPTH_TEST); // Disable depth testing for 2D (only used for 3D) + + // Init state: Blending mode + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed) + glEnable(GL_BLEND); // Enable color blending (required to work with transparencies) + + // Init state: Culling + // NOTE: All shapes/models triangles are drawn CCW + glCullFace(GL_BACK); // Cull the back face (default) + glFrontFace(GL_CCW); // Front face are defined counter clockwise (default) + glEnable(GL_CULL_FACE); // Enable backface culling + + // Init state: Cubemap seamless +#if defined(GRAPHICS_API_OPENGL_33) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Seamless cubemaps (not supported on OpenGL ES 2.0) +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + // Init state: Color hints (deprecated in OpenGL 3.0+) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation + glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Store screen size into global variables + RLGL.State.framebufferWidth = width; + RLGL.State.framebufferHeight = height; + + TRACELOG(RL_LOG_INFO, "RLGL: Default OpenGL state initialized successfully"); + //---------------------------------------------------------- +#endif + + // Init state: Color/Depth buffers clear + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black) + glClearDepth(1.0f); // Set clear depth value (default) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers (depth buffer required for 3D) +} + +// Vertex Buffer Object deinitialization (memory free) +void rlglClose(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlUnloadRenderBatch(RLGL.defaultBatch); + + rlUnloadShaderDefault(); // Unload default shader + + glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture + TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId); +#endif +} + +// Load OpenGL extensions +// NOTE: External loader function must be provided +void rlLoadExtensions(void *loader) +{ +#if defined(GRAPHICS_API_OPENGL_33) // Also defined for GRAPHICS_API_OPENGL_21 + // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions (and lower versions) + if (gladLoadGL((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); + else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); + + // Get number of supported extensions + GLint numExt = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); + TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt); + +#if defined(RLGL_SHOW_GL_DETAILS_INFO) + // Get supported extensions list + // WARNING: glGetStringi() not available on OpenGL 2.1 + TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:"); + for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", glGetStringi(GL_EXTENSIONS, i)); +#endif + +#if defined(GRAPHICS_API_OPENGL_21) + // Register supported extensions flags + // Optional OpenGL 2.1 extensions + RLGL.ExtSupported.vao = GLAD_GL_ARB_vertex_array_object; + 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.texFloat32 = GLAD_GL_ARB_texture_float; + RLGL.ExtSupported.texFloat16 = GLAD_GL_ARB_texture_float; + RLGL.ExtSupported.texDepth = GLAD_GL_ARB_depth_texture; + RLGL.ExtSupported.maxDepthBits = 32; + RLGL.ExtSupported.texAnisoFilter = GLAD_GL_EXT_texture_filter_anisotropic; + RLGL.ExtSupported.texMirrorClamp = GLAD_GL_EXT_texture_mirror_clamp; +#else + // Register supported extensions flags + // OpenGL 3.3 extensions supported by default (core) + 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.maxDepthBits = 32; + RLGL.ExtSupported.texAnisoFilter = true; + RLGL.ExtSupported.texMirrorClamp = true; +#endif + + // Optional OpenGL 3.3 extensions + RLGL.ExtSupported.texCompASTC = GLAD_GL_KHR_texture_compression_astc_hdr && GLAD_GL_KHR_texture_compression_astc_ldr; + RLGL.ExtSupported.texCompDXT = GLAD_GL_EXT_texture_compression_s3tc; // Texture compression: DXT + RLGL.ExtSupported.texCompETC2 = GLAD_GL_ARB_ES3_compatibility; // Texture compression: ETC2/EAC + #if defined(GRAPHICS_API_OPENGL_43) + RLGL.ExtSupported.computeShader = GLAD_GL_ARB_compute_shader; + RLGL.ExtSupported.ssbo = GLAD_GL_ARB_shader_storage_buffer_object; + #endif + +#endif // GRAPHICS_API_OPENGL_33 + +#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; + +#elif defined(GRAPHICS_API_OPENGL_ES2) + + #if defined(PLATFORM_DESKTOP_GLFW) || 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"); + else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); + #endif + + // Get supported extensions list + GLint numExt = 0; + const char **extList = RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) + const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string + + // NOTE: We have to duplicate string because glGetString() returns a const string + int size = strlen(extensions) + 1; // Get extensions string size in bytes + char *extensionsDup = (char *)RL_CALLOC(size, sizeof(char)); + strcpy(extensionsDup, extensions); + extList[numExt] = extensionsDup; + + for (int i = 0; i < size; i++) + { + if (extensionsDup[i] == ' ') + { + extensionsDup[i] = '\0'; + numExt++; + extList[numExt] = &extensionsDup[i + 1]; + } + } + + TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt); + +#if defined(RLGL_SHOW_GL_DETAILS_INFO) + TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:"); + for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", extList[i]); +#endif + + // Check required extensions + for (int i = 0; i < numExt; i++) + { + // Check VAO support + // NOTE: Only check on OpenGL ES, OpenGL 3.3 has VAO support as core feature + if (strcmp(extList[i], (const char *)"GL_OES_vertex_array_object") == 0) + { + // The extension is supported by our hardware and driver, try to get related functions pointers + // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... + glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)((rlglLoadProc)loader)("glGenVertexArraysOES"); + glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)((rlglLoadProc)loader)("glBindVertexArrayOES"); + glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)((rlglLoadProc)loader)("glDeleteVertexArraysOES"); + //glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)loader("glIsVertexArrayOES"); // NOTE: Fails in WebGL, omitted + + if ((glGenVertexArrays != NULL) && (glBindVertexArray != NULL) && (glDeleteVertexArrays != NULL)) RLGL.ExtSupported.vao = true; + } + + // Check instanced rendering support + if (strstr(extList[i], (const char*)"instanced_arrays") != NULL) // Broad check for instanced_arrays + { + // Specific check + if (strcmp(extList[i], (const char *)"GL_ANGLE_instanced_arrays") == 0) // ANGLE + { + glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedANGLE"); + glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedANGLE"); + glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISOREXTPROC)((rlglLoadProc)loader)("glVertexAttribDivisorANGLE"); + } + else if (strcmp(extList[i], (const char *)"GL_EXT_instanced_arrays") == 0) // EXT + { + glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedEXT"); + glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedEXT"); + glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISOREXTPROC)((rlglLoadProc)loader)("glVertexAttribDivisorEXT"); + } + else if (strcmp(extList[i], (const char *)"GL_NV_instanced_arrays") == 0) // NVIDIA GLES + { + glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedNV"); + glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedNV"); + glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISOREXTPROC)((rlglLoadProc)loader)("glVertexAttribDivisorNV"); + } + + // The feature will only be marked as supported if the elements from GL_XXX_instanced_arrays are present + if ((glDrawArraysInstanced != NULL) && (glDrawElementsInstanced != NULL) && (glVertexAttribDivisor != NULL)) RLGL.ExtSupported.instancing = true; + } + else if (strstr(extList[i], (const char *)"draw_instanced") != NULL) + { + // GL_ANGLE_draw_instanced doesn't exist + if (strcmp(extList[i], (const char *)"GL_EXT_draw_instanced") == 0) + { + glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedEXT"); + glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedEXT"); + } + else if (strcmp(extList[i], (const char*)"GL_NV_draw_instanced") == 0) + { + glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedNV"); + glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedNV"); + } + + // But the functions will at least be loaded if only GL_XX_EXT_draw_instanced exist + if ((glDrawArraysInstanced != NULL) && (glDrawElementsInstanced != NULL) && (glVertexAttribDivisor != NULL)) RLGL.ExtSupported.instancing = true; + } + + // Check NPOT textures support + // NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature + if (strcmp(extList[i], (const char *)"GL_OES_texture_npot") == 0) RLGL.ExtSupported.texNPOT = true; + + // 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_half_float") == 0) RLGL.ExtSupported.texFloat16 = true; + + // 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_WEBGL_depth_texture") == 0) RLGL.ExtSupported.texDepthWebGL = true; // WebGL requires unsized internal format + if (RLGL.ExtSupported.texDepthWebGL) RLGL.ExtSupported.texDepth = true; + + if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) RLGL.ExtSupported.maxDepthBits = 24; // Not available on WebGL + if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) RLGL.ExtSupported.maxDepthBits = 32; // Not available on WebGL + + // Check texture compression support: DXT + if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_s3tc") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBKIT_WEBGL_compressed_texture_s3tc") == 0)) RLGL.ExtSupported.texCompDXT = true; + + // Check texture compression support: ETC1 + if ((strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_etc1") == 0)) RLGL.ExtSupported.texCompETC1 = true; + + // Check texture compression support: ETC2/EAC + if (strcmp(extList[i], (const char *)"GL_ARB_ES3_compatibility") == 0) RLGL.ExtSupported.texCompETC2 = true; + + // Check texture compression support: PVR + if (strcmp(extList[i], (const char *)"GL_IMG_texture_compression_pvrtc") == 0) RLGL.ExtSupported.texCompPVRT = true; + + // Check texture compression support: ASTC + if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) RLGL.ExtSupported.texCompASTC = true; + + // Check anisotropic texture filter support + if (strcmp(extList[i], (const char *)"GL_EXT_texture_filter_anisotropic") == 0) RLGL.ExtSupported.texAnisoFilter = true; + + // Check clamp mirror wrap mode support + if (strcmp(extList[i], (const char *)"GL_EXT_texture_mirror_clamp") == 0) RLGL.ExtSupported.texMirrorClamp = true; + } + + // Free extensions pointers + RL_FREE(extList); + RL_FREE(extensionsDup); // Duplicated string must be deallocated +#endif // GRAPHICS_API_OPENGL_ES2 + + // Check OpenGL information and capabilities + //------------------------------------------------------------------------------ + // Show current OpenGL and GLSL version + TRACELOG(RL_LOG_INFO, "GL: OpenGL device information:"); + TRACELOG(RL_LOG_INFO, " > Vendor: %s", glGetString(GL_VENDOR)); + TRACELOG(RL_LOG_INFO, " > Renderer: %s", glGetString(GL_RENDERER)); + TRACELOG(RL_LOG_INFO, " > Version: %s", glGetString(GL_VERSION)); + TRACELOG(RL_LOG_INFO, " > GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: Anisotropy levels capability is an extension + #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + #endif + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &RLGL.ExtSupported.maxAnisotropyLevel); + +#if defined(RLGL_SHOW_GL_DETAILS_INFO) + // Show some OpenGL GPU capabilities + TRACELOG(RL_LOG_INFO, "GL: OpenGL capabilities:"); + GLint capability = 0; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_TEXTURE_SIZE: %i", capability); + glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_CUBE_MAP_TEXTURE_SIZE: %i", capability); + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_TEXTURE_IMAGE_UNITS: %i", capability); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_VERTEX_ATTRIBS: %i", capability); + #if !defined(GRAPHICS_API_OPENGL_ES2) + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_UNIFORM_BLOCK_SIZE: %i", capability); + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_DRAW_BUFFERS: %i", capability); + if (RLGL.ExtSupported.texAnisoFilter) TRACELOG(RL_LOG_INFO, " GL_MAX_TEXTURE_MAX_ANISOTROPY: %.0f", RLGL.ExtSupported.maxAnisotropyLevel); + #endif + glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &capability); + TRACELOG(RL_LOG_INFO, " GL_NUM_COMPRESSED_TEXTURE_FORMATS: %i", capability); + GLint *compFormats = (GLint *)RL_CALLOC(capability, sizeof(GLint)); + glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, compFormats); + for (int i = 0; i < capability; i++) TRACELOG(RL_LOG_INFO, " %s", rlGetCompressedFormatName(compFormats[i])); + RL_FREE(compFormats); + +#if defined(GRAPHICS_API_OPENGL_43) + glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_VERTEX_ATTRIB_BINDINGS: %i", capability); + glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_UNIFORM_LOCATIONS: %i", capability); +#endif // GRAPHICS_API_OPENGL_43 +#else // RLGL_SHOW_GL_DETAILS_INFO + + // Show some basic info about GL supported features + if (RLGL.ExtSupported.vao) TRACELOG(RL_LOG_INFO, "GL: VAO extension detected, VAO functions loaded successfully"); + else TRACELOG(RL_LOG_WARNING, "GL: VAO extension not found, VAO not supported"); + if (RLGL.ExtSupported.texNPOT) TRACELOG(RL_LOG_INFO, "GL: NPOT textures extension detected, full NPOT textures supported"); + else TRACELOG(RL_LOG_WARNING, "GL: NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); + if (RLGL.ExtSupported.texCompDXT) TRACELOG(RL_LOG_INFO, "GL: DXT compressed textures supported"); + if (RLGL.ExtSupported.texCompETC1) TRACELOG(RL_LOG_INFO, "GL: ETC1 compressed textures supported"); + if (RLGL.ExtSupported.texCompETC2) TRACELOG(RL_LOG_INFO, "GL: ETC2/EAC compressed textures supported"); + if (RLGL.ExtSupported.texCompPVRT) TRACELOG(RL_LOG_INFO, "GL: PVRT compressed textures supported"); + if (RLGL.ExtSupported.texCompASTC) TRACELOG(RL_LOG_INFO, "GL: ASTC compressed textures supported"); + if (RLGL.ExtSupported.computeShader) TRACELOG(RL_LOG_INFO, "GL: Compute shaders supported"); + if (RLGL.ExtSupported.ssbo) TRACELOG(RL_LOG_INFO, "GL: Shader storage buffer objects supported"); +#endif // RLGL_SHOW_GL_DETAILS_INFO + +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 +} + +// Get current OpenGL version +int rlGetVersion(void) +{ + int glVersion = 0; +#if defined(GRAPHICS_API_OPENGL_11) + glVersion = RL_OPENGL_11; +#endif +#if defined(GRAPHICS_API_OPENGL_21) + glVersion = RL_OPENGL_21; +#elif defined(GRAPHICS_API_OPENGL_43) + glVersion = RL_OPENGL_43; +#elif defined(GRAPHICS_API_OPENGL_33) + glVersion = RL_OPENGL_33; +#endif +#if defined(GRAPHICS_API_OPENGL_ES3) + glVersion = RL_OPENGL_ES_30; +#elif defined(GRAPHICS_API_OPENGL_ES2) + glVersion = RL_OPENGL_ES_20; +#endif + + return glVersion; +} + +// Set current framebuffer width +void rlSetFramebufferWidth(int width) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.framebufferWidth = width; +#endif +} + +// Set current framebuffer height +void rlSetFramebufferHeight(int height) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.framebufferHeight = height; +#endif +} + +// Get default framebuffer width +int rlGetFramebufferWidth(void) +{ + int width = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + width = RLGL.State.framebufferWidth; +#endif + return width; +} + +// Get default framebuffer height +int rlGetFramebufferHeight(void) +{ + int height = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + height = RLGL.State.framebufferHeight; +#endif + return height; +} + +// Get default internal texture (white texture) +// NOTE: Default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 +unsigned int rlGetTextureIdDefault(void) +{ + unsigned int id = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + id = RLGL.State.defaultTextureId; +#endif + return id; +} + +// Get default shader id +unsigned int rlGetShaderIdDefault(void) +{ + unsigned int id = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + id = RLGL.State.defaultShaderId; +#endif + return id; +} + +// Get default shader locs +int *rlGetShaderLocsDefault(void) +{ + int *locs = NULL; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + locs = RLGL.State.defaultShaderLocs; +#endif + return locs; +} + +// Render batch management +//------------------------------------------------------------------------------------------------ +// Load render batch +rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements) +{ + rlRenderBatch batch = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Initialize CPU (RAM) vertex buffers (position, texcoord, color data and indexes) + //-------------------------------------------------------------------------------------------- + batch.vertexBuffer = (rlVertexBuffer *)RL_MALLOC(numBuffers*sizeof(rlVertexBuffer)); + + for (int i = 0; i < numBuffers; i++) + { + batch.vertexBuffer[i].elementCount = bufferElements; + + batch.vertexBuffer[i].vertices = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float)); // 3 float by vertex, 4 vertex by quad + batch.vertexBuffer[i].texcoords = (float *)RL_MALLOC(bufferElements*2*4*sizeof(float)); // 2 float by texcoord, 4 texcoord by quad + batch.vertexBuffer[i].normals = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float)); // 3 float by vertex, 4 vertex by quad + batch.vertexBuffer[i].colors = (unsigned char *)RL_MALLOC(bufferElements*4*4*sizeof(unsigned char)); // 4 float by color, 4 colors by quad +#if defined(GRAPHICS_API_OPENGL_33) + batch.vertexBuffer[i].indices = (unsigned int *)RL_MALLOC(bufferElements*6*sizeof(unsigned int)); // 6 int by quad (indices) +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + batch.vertexBuffer[i].indices = (unsigned short *)RL_MALLOC(bufferElements*6*sizeof(unsigned short)); // 6 int by quad (indices) +#endif + + for (int j = 0; j < (3*4*bufferElements); j++) batch.vertexBuffer[i].vertices[j] = 0.0f; + for (int j = 0; j < (2*4*bufferElements); j++) batch.vertexBuffer[i].texcoords[j] = 0.0f; + for (int j = 0; j < (3*4*bufferElements); j++) batch.vertexBuffer[i].normals[j] = 0.0f; + for (int j = 0; j < (4*4*bufferElements); j++) batch.vertexBuffer[i].colors[j] = 0; + + int k = 0; + + // Indices can be initialized right now + for (int j = 0; j < (6*bufferElements); j += 6) + { + batch.vertexBuffer[i].indices[j] = 4*k; + batch.vertexBuffer[i].indices[j + 1] = 4*k + 1; + batch.vertexBuffer[i].indices[j + 2] = 4*k + 2; + batch.vertexBuffer[i].indices[j + 3] = 4*k; + batch.vertexBuffer[i].indices[j + 4] = 4*k + 2; + batch.vertexBuffer[i].indices[j + 5] = 4*k + 3; + + k++; + } + + RLGL.State.vertexCounter = 0; + } + + TRACELOG(RL_LOG_INFO, "RLGL: Render batch vertex buffers loaded successfully in RAM (CPU)"); + //-------------------------------------------------------------------------------------------- + + // Upload to GPU (VRAM) vertex data and initialize VAOs/VBOs + //-------------------------------------------------------------------------------------------- + for (int i = 0; i < numBuffers; i++) + { + if (RLGL.ExtSupported.vao) + { + // Initialize Quads VAO + glGenVertexArrays(1, &batch.vertexBuffer[i].vaoId); + glBindVertexArray(batch.vertexBuffer[i].vaoId); + } + + // Quads - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[0]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*3*4*sizeof(float), batch.vertexBuffer[i].vertices, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + + // Vertex texcoord buffer (shader-location = 1) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[1]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*2*4*sizeof(float), batch.vertexBuffer[i].texcoords, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + + // Vertex normal buffer (shader-location = 2) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[2]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*3*4*sizeof(float), batch.vertexBuffer[i].normals, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0); + + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[3]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[3]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*4*4*sizeof(unsigned char), batch.vertexBuffer[i].colors, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + // Fill index buffer + glGenBuffers(1, &batch.vertexBuffer[i].vboId[4]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[4]); +#if defined(GRAPHICS_API_OPENGL_33) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(int), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(short), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); +#endif + } + + TRACELOG(RL_LOG_INFO, "RLGL: Render batch vertex buffers loaded successfully in VRAM (GPU)"); + + // Unbind the current VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(0); + //-------------------------------------------------------------------------------------------- + + // Init draw calls tracking system + //-------------------------------------------------------------------------------------------- + batch.draws = (rlDrawCall *)RL_MALLOC(RL_DEFAULT_BATCH_DRAWCALLS*sizeof(rlDrawCall)); + + for (int i = 0; i < RL_DEFAULT_BATCH_DRAWCALLS; i++) + { + batch.draws[i].mode = RL_QUADS; + batch.draws[i].vertexCount = 0; + batch.draws[i].vertexAlignment = 0; + //batch.draws[i].vaoId = 0; + //batch.draws[i].shaderId = 0; + batch.draws[i].textureId = RLGL.State.defaultTextureId; + //batch.draws[i].RLGL.State.projection = rlMatrixIdentity(); + //batch.draws[i].RLGL.State.modelview = rlMatrixIdentity(); + } + + batch.bufferCount = numBuffers; // Record buffer count + batch.drawCounter = 1; // Reset draws counter + batch.currentDepth = -1.0f; // Reset depth value + //-------------------------------------------------------------------------------------------- +#endif + + return batch; +} + +// Unload default internal buffers vertex data from CPU and GPU +void rlUnloadRenderBatch(rlRenderBatch batch) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Unbind everything + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // Unload all vertex buffers data + for (int i = 0; i < batch.bufferCount; i++) + { + // Unbind VAO attribs data + if (RLGL.ExtSupported.vao) + { + glBindVertexArray(batch.vertexBuffer[i].vaoId); + glDisableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION); + glDisableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + glDisableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL); + glDisableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR); + glBindVertexArray(0); + } + + // Delete VBOs from GPU (VRAM) + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[0]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[1]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[2]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[3]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[4]); + + // Delete VAOs from GPU (VRAM) + if (RLGL.ExtSupported.vao) glDeleteVertexArrays(1, &batch.vertexBuffer[i].vaoId); + + // Free vertex arrays memory from CPU (RAM) + RL_FREE(batch.vertexBuffer[i].vertices); + RL_FREE(batch.vertexBuffer[i].texcoords); + RL_FREE(batch.vertexBuffer[i].normals); + RL_FREE(batch.vertexBuffer[i].colors); + RL_FREE(batch.vertexBuffer[i].indices); + } + + // Unload arrays + RL_FREE(batch.vertexBuffer); + RL_FREE(batch.draws); +#endif +} + +// Draw render batch +// NOTE: We require a pointer to reset batch and increase current buffer (multi-buffer) +void rlDrawRenderBatch(rlRenderBatch *batch) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Update batch vertex buffers + //------------------------------------------------------------------------------------------------------------ + // 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 (use a change detector flag?) + if (RLGL.State.vertexCounter > 0) + { + // Activate elements VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); + + // Vertex positions buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*3*sizeof(float), batch->vertexBuffer[batch->currentBuffer].vertices); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].vertices, GL_DYNAMIC_DRAW); // Update all buffer + + // Texture coordinates buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); + glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*2*sizeof(float), batch->vertexBuffer[batch->currentBuffer].texcoords); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].texcoords, GL_DYNAMIC_DRAW); // Update all buffer + + // Normals buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); + glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*3*sizeof(float), batch->vertexBuffer[batch->currentBuffer].normals); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].normals, GL_DYNAMIC_DRAW); // Update all buffer + + // Colors buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[3]); + glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*4*sizeof(unsigned char), batch->vertexBuffer[batch->currentBuffer].colors); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].colors, GL_DYNAMIC_DRAW); // Update all buffer + + // NOTE: glMapBuffer() causes sync issue + // If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job + // To avoid waiting (idle), you can call first glBufferData() with NULL pointer before glMapBuffer() + // If you do that, the previous data in PBO will be discarded and glMapBuffer() returns a new + // allocated pointer immediately even if GPU is still working with the previous data + + // Another option: map the buffer object into client's memory + // Probably this code could be moved somewhere else... + // batch->vertexBuffer[batch->currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // if (batch->vertexBuffer[batch->currentBuffer].vertices) + // { + // Update vertex data + // } + // glUnmapBuffer(GL_ARRAY_BUFFER); + + // Unbind the current VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(0); + } + //------------------------------------------------------------------------------------------------------------ + + // Draw batch vertex buffers (considering VR stereo if required) + //------------------------------------------------------------------------------------------------------------ + Matrix matProjection = RLGL.State.projection; + Matrix matModelView = RLGL.State.modelview; + + int eyeCount = 1; + if (RLGL.State.stereoRender) eyeCount = 2; + + for (int eye = 0; eye < eyeCount; eye++) + { + if (eyeCount == 2) + { + // Setup current eye viewport (half screen width) + rlViewport(eye*RLGL.State.framebufferWidth/2, 0, RLGL.State.framebufferWidth/2, RLGL.State.framebufferHeight); + + // Set current eye view offset to modelview matrix + rlSetMatrixModelview(rlMatrixMultiply(matModelView, RLGL.State.viewOffsetStereo[eye])); + // Set current eye projection matrix + rlSetMatrixProjection(RLGL.State.projectionStereo[eye]); + } + + // Draw buffers + if (RLGL.State.vertexCounter > 0) + { + // Set current shader and upload current MVP matrix + glUseProgram(RLGL.State.currentShaderId); + + // Create modelview-projection matrix and upload to shader + Matrix matMVP = rlMatrixMultiply(RLGL.State.modelview, RLGL.State.projection); + glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_MVP], 1, false, rlMatrixToFloat(matMVP)); + + if (RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_PROJECTION] != -1) + { + glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_PROJECTION], 1, false, rlMatrixToFloat(RLGL.State.projection)); + } + + // WARNING: For the following setup of the view, model, and normal matrices, it is expected that + // transformations and rendering occur between rlPushMatrix() and rlPopMatrix() + + if (RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_VIEW] != -1) + { + glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_VIEW], 1, false, rlMatrixToFloat(RLGL.State.modelview)); + } + + if (RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_MODEL] != -1) + { + glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_MODEL], 1, false, rlMatrixToFloat(RLGL.State.transform)); + } + + if (RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_NORMAL] != -1) + { + glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_NORMAL], 1, false, rlMatrixToFloat(rlMatrixTranspose(rlMatrixInvert(RLGL.State.transform)))); + } + + if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION]); + + // Bind vertex attrib: texcoord (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01]); + + // Bind vertex attrib: normal (shader-location = 2) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_NORMAL]); + + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[3]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR]); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[4]); + } + + // Setup some default shader values + glUniform4f(RLGL.State.currentShaderLocs[RL_SHADER_LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1i(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MAP_DIFFUSE], 0); // Active default sampler2D: texture0 + + // Activate additional sampler textures + // Those additional textures will be common for all draw calls of the batch + for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) + { + if (RLGL.State.activeTextureId[i] > 0) + { + glActiveTexture(GL_TEXTURE0 + 1 + i); + glBindTexture(GL_TEXTURE_2D, RLGL.State.activeTextureId[i]); + } + } + + // Activate default sampler2D texture0 (one texture is always active for default batch shader) + // NOTE: Batch system accumulates calls by texture0 changes, additional textures are enabled for all the draw calls + glActiveTexture(GL_TEXTURE0); + + for (int i = 0, vertexOffset = 0; i < batch->drawCounter; i++) + { + // Bind current draw call texture, activated as GL_TEXTURE0 and Bound to sampler2D texture0 by default + glBindTexture(GL_TEXTURE_2D, batch->draws[i].textureId); + + if ((batch->draws[i].mode == RL_LINES) || (batch->draws[i].mode == RL_TRIANGLES)) glDrawArrays(batch->draws[i].mode, vertexOffset, batch->draws[i].vertexCount); + else + { + #if defined(GRAPHICS_API_OPENGL_33) + // We need to define the number of indices to be processed: elementCount*6 + // NOTE: The final parameter tells the GPU the offset in bytes from the + // start of the index buffer to the location of the first index to process + glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(vertexOffset/4*6*sizeof(GLuint))); + #endif + #if defined(GRAPHICS_API_OPENGL_ES2) + glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(vertexOffset/4*6*sizeof(GLushort))); + #endif + } + + vertexOffset += (batch->draws[i].vertexCount + batch->draws[i].vertexAlignment); + } + + if (!RLGL.ExtSupported.vao) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + } + + if (RLGL.ExtSupported.vao) glBindVertexArray(0); // Unbind VAO + + glUseProgram(0); // Unbind shader program + } + + // Restore viewport to default measures + if (eyeCount == 2) rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); + //------------------------------------------------------------------------------------------------------------ + + // Reset batch buffers + //------------------------------------------------------------------------------------------------------------ + // Reset vertex counter for next frame + RLGL.State.vertexCounter = 0; + + // Reset depth for next draw + batch->currentDepth = -1.0f; + + // Restore projection/modelview matrices + RLGL.State.projection = matProjection; + RLGL.State.modelview = matModelView; + + // Reset RLGL.currentBatch->draws array + for (int i = 0; i < RL_DEFAULT_BATCH_DRAWCALLS; i++) + { + batch->draws[i].mode = RL_QUADS; + batch->draws[i].vertexCount = 0; + batch->draws[i].textureId = RLGL.State.defaultTextureId; + } + + // Reset active texture units for next batch + for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) RLGL.State.activeTextureId[i] = 0; + + // Reset draws counter to one draw for the batch + batch->drawCounter = 1; + //------------------------------------------------------------------------------------------------------------ + + // Change to next buffer in the list (in case of multi-buffering) + batch->currentBuffer++; + if (batch->currentBuffer >= batch->bufferCount) batch->currentBuffer = 0; +#endif +} + +// Set the active render batch for rlgl +void rlSetRenderBatchActive(rlRenderBatch *batch) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlDrawRenderBatch(RLGL.currentBatch); + + if (batch != NULL) RLGL.currentBatch = batch; + else RLGL.currentBatch = &RLGL.defaultBatch; +#endif +} + +// Update and draw internal render batch +void rlDrawRenderBatchActive(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside +#endif +} + +// Check internal buffer overflow for a given number of vertex +// and force a rlRenderBatch draw call if required +bool rlCheckRenderBatchLimit(int vCount) +{ + bool overflow = false; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.State.vertexCounter + vCount) >= + (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4)) + { + overflow = true; + + // Store current primitive drawing mode and texture id + int currentMode = RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode; + int currentTexture = RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId; + + rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside + + // Restore state of last batch so we can continue adding vertices + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = currentMode; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = currentTexture; + } +#endif + + return overflow; +} + +// Textures data management +//----------------------------------------------------------------------------------------- +// Convert image data to OpenGL texture (returns OpenGL valid Id) +unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount) +{ + unsigned int id = 0; + + glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding + + // Check texture format support by OpenGL 1.1 (compressed textures not supported) +#if defined(GRAPHICS_API_OPENGL_11) + if (format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) + { + TRACELOG(RL_LOG_WARNING, "GL: OpenGL 1.1 does not support GPU compressed texture formats"); + return id; + } +#else + if ((!RLGL.ExtSupported.texCompDXT) && ((format == RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) || (format == RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA) || + (format == RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA) || (format == RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA))) + { + TRACELOG(RL_LOG_WARNING, "GL: DXT compressed texture format not supported"); + return id; + } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((!RLGL.ExtSupported.texCompETC1) && (format == RL_PIXELFORMAT_COMPRESSED_ETC1_RGB)) + { + TRACELOG(RL_LOG_WARNING, "GL: ETC1 compressed texture format not supported"); + return id; + } + + if ((!RLGL.ExtSupported.texCompETC2) && ((format == RL_PIXELFORMAT_COMPRESSED_ETC2_RGB) || (format == RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA))) + { + TRACELOG(RL_LOG_WARNING, "GL: ETC2 compressed texture format not supported"); + return id; + } + + if ((!RLGL.ExtSupported.texCompPVRT) && ((format == RL_PIXELFORMAT_COMPRESSED_PVRT_RGB) || (format == RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA))) + { + TRACELOG(RL_LOG_WARNING, "GL: PVRT compressed texture format not supported"); + return id; + } + + if ((!RLGL.ExtSupported.texCompASTC) && ((format == RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA) || (format == RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA))) + { + TRACELOG(RL_LOG_WARNING, "GL: ASTC compressed texture format not supported"); + return id; + } +#endif +#endif // GRAPHICS_API_OPENGL_11 + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glGenTextures(1, &id); // Generate texture id + + glBindTexture(GL_TEXTURE_2D, id); + + int mipWidth = width; + int mipHeight = height; + 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 + for (int i = 0; i < mipmapCount; i++) + { + unsigned int mipSize = rlGetPixelDataSize(mipWidth, mipHeight, format); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); + + if (glInternalFormat != 0) + { + 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) + else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, dataPtr); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) + if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) + { + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + else if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) + { +#if defined(GRAPHICS_API_OPENGL_21) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; +#elif defined(GRAPHICS_API_OPENGL_33) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; +#endif + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } +#endif + } + + mipWidth /= 2; + mipHeight /= 2; + mipOffset += mipSize; // Increment offset position to next mipmap + if (data != NULL) dataPtr += mipSize; // Increment data pointer to next mipmap + + // Security check for NPOT textures + if (mipWidth < 1) mipWidth = 1; + if (mipHeight < 1) mipHeight = 1; + } + + // Texture parameters configuration + // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used +#if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used + if (RLGL.ExtSupported.texNPOT) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis + } + else + { + // NOTE: If using negative texture coordinates (LoadOBJ()), it does not work! + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis + } +#else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis +#endif + + // Magnification and minification filters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Alternative: GL_LINEAR + +#if defined(GRAPHICS_API_OPENGL_33) + if (mipmapCount > 1) + { + // Activate Trilinear filtering if mipmaps are available + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } +#endif + + // At this point we have the texture loaded in GPU and texture parameters configured + + // NOTE: If mipmaps were not in data, they are not generated automatically + + // Unbind current texture + glBindTexture(GL_TEXTURE_2D, 0); + + if (id > 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Texture loaded successfully (%ix%i | %s | %i mipmaps)", id, width, height, rlGetPixelFormatName(format), mipmapCount); + else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load texture"); + + return id; +} + +// Load depth texture/renderbuffer (to be attached to fbo) +// WARNING: OpenGL ES 2.0 requires GL_OES_depth_texture and WebGL requires WEBGL_depth_texture extensions +unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // In case depth textures not supported, we force renderbuffer usage + if (!RLGL.ExtSupported.texDepth) useRenderBuffer = true; + + // NOTE: We let the implementation to choose the best bit-depth + // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F + unsigned int glInternalFormat = GL_DEPTH_COMPONENT; + +#if (defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_ES3)) + // 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 + if (!RLGL.ExtSupported.texDepthWebGL || useRenderBuffer) + { + if (RLGL.ExtSupported.maxDepthBits == 32) glInternalFormat = GL_DEPTH_COMPONENT32_OES; + else if (RLGL.ExtSupported.maxDepthBits == 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES; + else glInternalFormat = GL_DEPTH_COMPONENT16; + } +#endif + + if (!useRenderBuffer && RLGL.ExtSupported.texDepth) + { + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + glTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + + TRACELOG(RL_LOG_INFO, "TEXTURE: Depth texture loaded successfully"); + } + else + { + // Create the renderbuffer that will serve as the depth attachment for the framebuffer + // NOTE: A renderbuffer is simpler than a texture and could offer better performance on embedded devices + glGenRenderbuffers(1, &id); + glBindRenderbuffer(GL_RENDERBUFFER, id); + glRenderbufferStorage(GL_RENDERBUFFER, glInternalFormat, width, height); + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Depth renderbuffer loaded successfully (%i bits)", id, (RLGL.ExtSupported.maxDepthBits >= 24)? RLGL.ExtSupported.maxDepthBits : 16); + } +#endif + + return id; +} + +// Load texture cubemap +// NOTE: Cubemap data is expected to be 6 images in a single data array (one after the other), +// expected the following convention: +X, -X, +Y, -Y, +Z, -Z +unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mipmapCount) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int mipSize = size; + + // NOTE: Added pointer math separately from function to avoid UBSAN complaining + unsigned char *dataPtr = NULL; + if (data != NULL) dataPtr = (unsigned char *)data; + + unsigned int dataSize = rlGetPixelDataSize(size, size, format); + + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_CUBE_MAP, id); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + if (glInternalFormat != 0) + { + // Load cubemap faces/mipmaps + for (int i = 0; i < 6*mipmapCount; i++) + { + int mipmapLevel = i/6; + int face = i%6; + + if (data == NULL) + { + if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) + { + if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || + (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32) || + (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) || + (format == RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); + else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, glFormat, glType, NULL); + } + else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format"); + } + else + { + if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, glFormat, glType, (unsigned char *)dataPtr + face*dataSize); + else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, dataSize, (unsigned char *)dataPtr + face*dataSize); + } + +#if defined(GRAPHICS_API_OPENGL_33) + if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) + { + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + else if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) + { +#if defined(GRAPHICS_API_OPENGL_21) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; +#elif defined(GRAPHICS_API_OPENGL_33) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; +#endif + glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } +#endif + if (face == 5) + { + mipSize /= 2; + if (data != NULL) dataPtr += dataSize*6; // Increment data pointer to next mipmap + + // Security check for NPOT textures + if (mipSize < 1) mipSize = 1; + + dataSize = rlGetPixelDataSize(mipSize, mipSize, format); + } + } + } + + // Set cubemap texture sampling parameters + if (mipmapCount > 1) glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + else glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#if defined(GRAPHICS_API_OPENGL_33) + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Flag not supported on OpenGL ES 2.0 +#endif + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif + + if (id > 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Cubemap texture loaded successfully (%ix%i)", id, size, size); + else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load cubemap texture"); + + return id; +} + +// Update already loaded texture in GPU with new data +// NOTE: We don't know safely if internal texture format is the expected one... +void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data) +{ + glBindTexture(GL_TEXTURE_2D, id); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + if ((glInternalFormat != 0) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, data); + } + else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to update for current texture format (%i)", id, format); +} + +// Get OpenGL internal formats and data type from raylib PixelFormat +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType) +{ + *glInternalFormat = 0; + *glFormat = 0; + *glType = 0; + + switch (format) + { + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; 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; + #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_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 + #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 + #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_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; 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_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 + #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_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: if (RLGL.ExtSupported.texCompETC1) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + #endif + default: TRACELOG(RL_LOG_WARNING, "TEXTURE: Current format not supported (%i)", format); break; + } +} + +// Unload texture from GPU memory +void rlUnloadTexture(unsigned int id) +{ + glDeleteTextures(1, &id); +} + +// Generate mipmap data for selected texture +// NOTE: Only supports GPU mipmap generation +void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindTexture(GL_TEXTURE_2D, id); + + // Check if texture is power-of-two (POT) + bool texIsPOT = false; + + if (((width > 0) && ((width & (width - 1)) == 0)) && + ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; + + if ((texIsPOT) || (RLGL.ExtSupported.texNPOT)) + { + //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorithm: GL_FASTEST, GL_NICEST, GL_DONT_CARE + glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically + + #define MIN(a,b) (((a)<(b))? (a):(b)) + #define MAX(a,b) (((a)>(b))? (a):(b)) + + *mipmaps = 1 + (int)floor(log(MAX(width, height))/log(2)); + TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Mipmaps generated automatically, total: %i", id, *mipmaps); + } + else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps", id); + + glBindTexture(GL_TEXTURE_2D, 0); +#else + TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] GPU mipmap generation not supported", id); +#endif +} + +// Read texture pixel data +void *rlReadTexturePixels(unsigned int id, int width, int height, int format) +{ + void *pixels = NULL; + +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + glBindTexture(GL_TEXTURE_2D, id); + + // NOTE: Using texture id, we can retrieve some texture info (but not on OpenGL ES 2.0) + // Possible texture info: GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE + //int width, height, format; + //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); + + // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding + // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting + // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) + // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + unsigned int size = rlGetPixelDataSize(width, height, format); + + if ((glInternalFormat != 0) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) + { + pixels = RL_MALLOC(size); + glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); + } + else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Data retrieval not suported for pixel format (%i)", id, format); + + glBindTexture(GL_TEXTURE_2D, 0); +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + // glGetTexImage() is not available on OpenGL ES 2.0 + // Texture width and height are required on OpenGL ES 2.0, there is no way to get it from texture id + // Two possible Options: + // 1 - Bind texture to color fbo attachment and glReadPixels() + // 2 - Create an fbo, activate it, render quad with texture, glReadPixels() + // We are using Option 1, just need to care for texture format on retrieval + // NOTE: This behaviour could be conditioned by graphic driver... + unsigned int fboId = rlLoadFramebuffer(); + + glBindFramebuffer(GL_FRAMEBUFFER, fboId); + glBindTexture(GL_TEXTURE_2D, 0); + + // Attach our texture to FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0); + + // We read data as RGBA because FBO texture is configured as RGBA, despite binding another texture format + pixels = (unsigned char *)RL_MALLOC(rlGetPixelDataSize(width, height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Clean up temporal fbo + rlUnloadFramebuffer(fboId); +#endif + + return pixels; +} + +// Read screen pixel data (color buffer) +unsigned char *rlReadScreenPixels(int width, int height) +{ + unsigned char *screenData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char)); + + // NOTE 1: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer + // NOTE 2: We are getting alpha channel! Be careful, it can be transparent if not cleared properly! + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData); + + // Flip image vertically! + unsigned char *imgData = (unsigned char *)RL_MALLOC(width*height*4*sizeof(unsigned char)); + + for (int y = height - 1; y >= 0; y--) + { + for (int x = 0; x < (width*4); x++) + { + imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; // Flip line + + // Set alpha component value to 255 (no trasparent image retrieval) + // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it! + if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255; + } + } + + RL_FREE(screenData); + + return imgData; // NOTE: image data should be freed +} + +// Framebuffer management (fbo) +//----------------------------------------------------------------------------------------- +// Load a framebuffer to be used for rendering +// NOTE: No textures attached +unsigned int rlLoadFramebuffer(void) +{ + unsigned int fboId = 0; + +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glGenFramebuffers(1, &fboId); // Create the framebuffer object + glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer +#endif + + return fboId; +} + +// Attach color buffer texture to an fbo (unloads previous attachment) +// NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture +void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, fboId); + + switch (attachType) + { + case RL_ATTACHMENT_COLOR_CHANNEL0: + case RL_ATTACHMENT_COLOR_CHANNEL1: + case RL_ATTACHMENT_COLOR_CHANNEL2: + case RL_ATTACHMENT_COLOR_CHANNEL3: + case RL_ATTACHMENT_COLOR_CHANNEL4: + case RL_ATTACHMENT_COLOR_CHANNEL5: + case RL_ATTACHMENT_COLOR_CHANNEL6: + case RL_ATTACHMENT_COLOR_CHANNEL7: + { + if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, texId, mipLevel); + else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_RENDERBUFFER, texId); + else if (texType >= RL_ATTACHMENT_CUBEMAP_POSITIVE_X) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_CUBE_MAP_POSITIVE_X + texType, texId, mipLevel); + + } break; + case RL_ATTACHMENT_DEPTH: + { + if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); + else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, texId); + + } break; + case RL_ATTACHMENT_STENCIL: + { + if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); + else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, texId); + + } break; + default: break; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif +} + +// Verify render texture is complete +bool rlFramebufferComplete(unsigned int id) +{ + bool result = false; + +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, id); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + switch (status) + { + case GL_FRAMEBUFFER_UNSUPPORTED: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer is unsupported", id); break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete attachment", id); break; +#if defined(GRAPHICS_API_OPENGL_ES2) + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete dimensions", id); break; +#endif + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer has a missing attachment", id); break; + default: break; + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + result = (status == GL_FRAMEBUFFER_COMPLETE); +#endif + + return result; +} + +// Unload framebuffer from GPU memory +// NOTE: All attached textures/cubemaps/renderbuffers are also deleted +void rlUnloadFramebuffer(unsigned int id) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + // Query depth attachment to automatically delete texture/renderbuffer + int depthType = 0, depthId = 0; + glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type + 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); + + unsigned int depthIdU = (unsigned int)depthId; + if (depthType == GL_RENDERBUFFER) glDeleteRenderbuffers(1, &depthIdU); + else if (depthType == GL_TEXTURE) glDeleteTextures(1, &depthIdU); + + // NOTE: If a texture object is deleted while its image is attached to the *currently bound* framebuffer, + // the texture image is automatically detached from the currently bound framebuffer + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &id); + + TRACELOG(RL_LOG_INFO, "FBO: [ID %i] Unloaded framebuffer from VRAM (GPU)", id); +#endif +} + +// Vertex data management +//----------------------------------------------------------------------------------------- +// Load a new attributes buffer +unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glGenBuffers(1, &id); + glBindBuffer(GL_ARRAY_BUFFER, id); + glBufferData(GL_ARRAY_BUFFER, size, buffer, dynamic? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); +#endif + + return id; +} + +// Load a new attributes element buffer +unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glGenBuffers(1, &id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, buffer, dynamic? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); +#endif + + return id; +} + +// Enable vertex buffer (VBO) +void rlEnableVertexBuffer(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ARRAY_BUFFER, id); +#endif +} + +// Disable vertex buffer (VBO) +void rlDisableVertexBuffer(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ARRAY_BUFFER, 0); +#endif +} + +// Enable vertex buffer element (VBO element) +void rlEnableVertexBufferElement(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); +#endif +} + +// Disable vertex buffer element (VBO element) +void rlDisableVertexBufferElement(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#endif +} + +// Update vertex buffer with new data +// NOTE: dataSize and offset must be provided in bytes +void rlUpdateVertexBuffer(unsigned int id, const void *data, int dataSize, int offset) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ARRAY_BUFFER, id); + glBufferSubData(GL_ARRAY_BUFFER, offset, dataSize, data); +#endif +} + +// Update vertex buffer elements with new data +// NOTE: dataSize and offset must be provided in bytes +void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, dataSize, data); +#endif +} + +// Enable vertex array object (VAO) +bool rlEnableVertexArray(unsigned int vaoId) +{ + bool result = false; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.vao) + { + glBindVertexArray(vaoId); + result = true; + } +#endif + return result; +} + +// Disable vertex array object (VAO) +void rlDisableVertexArray(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.vao) glBindVertexArray(0); +#endif +} + +// Enable vertex attribute index +void rlEnableVertexAttribute(unsigned int index) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glEnableVertexAttribArray(index); +#endif +} + +// Disable vertex attribute index +void rlDisableVertexAttribute(unsigned int index) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDisableVertexAttribArray(index); +#endif +} + +// Draw vertex array +void rlDrawVertexArray(int offset, int count) +{ + glDrawArrays(GL_TRIANGLES, offset, count); +} + +// Draw vertex array elements +void rlDrawVertexArrayElements(int offset, int count, const void *buffer) +{ + // 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 +void rlDrawVertexArrayInstanced(int offset, int count, int instances) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDrawArraysInstanced(GL_TRIANGLES, 0, count, instances); +#endif +} + +// Draw vertex array elements instanced +void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // 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 +} + +#if defined(GRAPHICS_API_OPENGL_11) +// Enable vertex state pointer +void rlEnableStatePointer(int vertexAttribType, void *buffer) +{ + if (buffer != NULL) glEnableClientState(vertexAttribType); + switch (vertexAttribType) + { + case GL_VERTEX_ARRAY: glVertexPointer(3, GL_FLOAT, 0, buffer); break; + case GL_TEXTURE_COORD_ARRAY: glTexCoordPointer(2, GL_FLOAT, 0, buffer); break; + case GL_NORMAL_ARRAY: if (buffer != NULL) glNormalPointer(GL_FLOAT, 0, buffer); break; + case GL_COLOR_ARRAY: if (buffer != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, buffer); break; + //case GL_INDEX_ARRAY: if (buffer != NULL) glIndexPointer(GL_SHORT, 0, buffer); break; // Indexed colors + default: break; + } +} + +// Disable vertex state pointer +void rlDisableStatePointer(int vertexAttribType) +{ + glDisableClientState(vertexAttribType); +} +#endif + +// Load vertex array object (VAO) +unsigned int rlLoadVertexArray(void) +{ + unsigned int vaoId = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.vao) + { + glGenVertexArrays(1, &vaoId); + } +#endif + return vaoId; +} + +// Set vertex attribute +void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, int offset) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: Data type could be: GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT + // Additional types (depends on OpenGL version or extensions): + // - GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, GL_FIXED, + // - GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_10F_11F_11F_REV + + size_t offsetNative = offset; + glVertexAttribPointer(index, compSize, type, normalized, stride, (void *)offsetNative); +#endif +} + +// Set vertex attribute divisor +void rlSetVertexAttributeDivisor(unsigned int index, int divisor) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glVertexAttribDivisor(index, divisor); +#endif +} + +// Unload vertex array object (VAO) +void rlUnloadVertexArray(unsigned int vaoId) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.vao) + { + glBindVertexArray(0); + glDeleteVertexArrays(1, &vaoId); + TRACELOG(RL_LOG_INFO, "VAO: [ID %i] Unloaded vertex array data from VRAM (GPU)", vaoId); + } +#endif +} + +// Unload vertex buffer (VBO) +void rlUnloadVertexBuffer(unsigned int vboId) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDeleteBuffers(1, &vboId); + //TRACELOG(RL_LOG_INFO, "VBO: Unloaded vertex data from VRAM (GPU)"); +#endif +} + +// Shaders management +//----------------------------------------------------------------------------------------------- +// Load shader from code strings +// NOTE: If shader string is NULL, using default vertex/fragment shaders +unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int vertexShaderId = 0; + unsigned int fragmentShaderId = 0; + + // Compile vertex shader (if provided) + // NOTE: If not vertex shader is provided, use default one + if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER); + else vertexShaderId = RLGL.State.defaultVShaderId; + + // Compile fragment shader (if provided) + // NOTE: If not vertex shader is provided, use default one + if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER); + else fragmentShaderId = RLGL.State.defaultFShaderId; + + // In case vertex and fragment shader are the default ones, no need to recompile, we can just assign the default shader program id + if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId; + else if ((vertexShaderId > 0) && (fragmentShaderId > 0)) + { + // One of or both shader are new, we need to compile a new shader program + id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId); + + // We can detach and delete vertex/fragment shaders (if not default ones) + // NOTE: We detach shader before deletion to make sure memory is freed + if (vertexShaderId != RLGL.State.defaultVShaderId) + { + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, vertexShaderId); + glDeleteShader(vertexShaderId); + } + if (fragmentShaderId != RLGL.State.defaultFShaderId) + { + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, fragmentShaderId); + glDeleteShader(fragmentShaderId); + } + + // In case shader program loading failed, we assign default shader + if (id == 0) + { + // In case shader loading fails, we return the default shader + TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader"); + id = RLGL.State.defaultShaderId; + } + /* + else + { + // Get available shader uniforms + // NOTE: This information is useful for debug... + int uniformCount = -1; + glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); + + for (int i = 0; i < uniformCount; i++) + { + int namelen = -1; + int num = -1; + char name[256] = { 0 }; // Assume no variable names longer than 256 + GLenum type = GL_ZERO; + + // Get the name of the uniforms + glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name); + + name[namelen] = 0; + TRACELOGD("SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); + } + } + */ + } +#endif + + return id; +} + +// Compile custom shader and return shader id +unsigned int rlCompileShader(const char *shaderCode, int type) +{ + unsigned int shader = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + shader = glCreateShader(type); + glShaderSource(shader, 1, &shaderCode, NULL); + + GLint success = 0; + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (success == GL_FALSE) + { + switch (type) + { + case GL_VERTEX_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile vertex shader code", shader); break; + case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile fragment shader code", shader); break; + //case GL_GEOMETRY_SHADER: + #if defined(GRAPHICS_API_OPENGL_43) + case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile compute shader code", shader); break; + #elif defined(GRAPHICS_API_OPENGL_33) + case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break; + #endif + default: break; + } + + int maxLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + + if (maxLength > 0) + { + int length = 0; + char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); + glGetShaderInfoLog(shader, maxLength, &length, log); + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shader, log); + RL_FREE(log); + } + + shader = 0; + } + else + { + switch (type) + { + case GL_VERTEX_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Vertex shader compiled successfully", shader); break; + case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Fragment shader compiled successfully", shader); break; + //case GL_GEOMETRY_SHADER: + #if defined(GRAPHICS_API_OPENGL_43) + case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader compiled successfully", shader); break; + #elif defined(GRAPHICS_API_OPENGL_33) + case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break; + #endif + default: break; + } + } +#endif + + return shader; +} + +// Load custom shader strings and return program id +unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) +{ + unsigned int program = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + GLint success = 0; + program = glCreateProgram(); + + glAttachShader(program, vShaderId); + glAttachShader(program, fShaderId); + + // NOTE: Default attribute shader locations must be Bound before linking + glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION); + glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); + glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL); + glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR); + glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT); + glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2); + +#ifdef RL_SUPPORT_MESH_GPU_SKINNING + glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS); + glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS); +#endif + + // NOTE: If some attrib name is no found on the shader, it locations becomes -1 + + glLinkProgram(program); + + // NOTE: All uniform variables are intitialised to 0 when a program links + + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link shader program", program); + + int maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + if (maxLength > 0) + { + int length = 0; + char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); + glGetProgramInfoLog(program, maxLength, &length, log); + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log); + RL_FREE(log); + } + + glDeleteProgram(program); + + program = 0; + } + else + { + // Get the size of compiled shader program (not available on OpenGL ES 2.0) + // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero + //GLint binarySize = 0; + //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Program shader loaded successfully", program); + } +#endif + return program; +} + +// Unload shader program +void rlUnloadShaderProgram(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDeleteProgram(id); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Unloaded shader program data from VRAM (GPU)", id); +#endif +} + +// Get shader location uniform +int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) +{ + int location = -1; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + location = glGetUniformLocation(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); +#endif + return location; +} + +// Get shader location attribute +int rlGetLocationAttrib(unsigned int shaderId, const char *attribName) +{ + int location = -1; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + location = glGetAttribLocation(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); +#endif + return location; +} + +// Set shader value uniform +void rlSetUniform(int locIndex, const void *value, int uniformType, int count) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + switch (uniformType) + { + case RL_SHADER_UNIFORM_FLOAT: glUniform1fv(locIndex, count, (float *)value); break; + case RL_SHADER_UNIFORM_VEC2: glUniform2fv(locIndex, count, (float *)value); break; + case RL_SHADER_UNIFORM_VEC3: glUniform3fv(locIndex, count, (float *)value); break; + case RL_SHADER_UNIFORM_VEC4: glUniform4fv(locIndex, count, (float *)value); break; + case RL_SHADER_UNIFORM_INT: glUniform1iv(locIndex, count, (int *)value); break; + case RL_SHADER_UNIFORM_IVEC2: glUniform2iv(locIndex, count, (int *)value); break; + case RL_SHADER_UNIFORM_IVEC3: glUniform3iv(locIndex, count, (int *)value); break; + case RL_SHADER_UNIFORM_IVEC4: glUniform4iv(locIndex, count, (int *)value); break; + #if !defined(GRAPHICS_API_OPENGL_ES2) + case RL_SHADER_UNIFORM_UINT: glUniform1uiv(locIndex, count, (unsigned int *)value); break; + case RL_SHADER_UNIFORM_UIVEC2: glUniform2uiv(locIndex, count, (unsigned int *)value); break; + case RL_SHADER_UNIFORM_UIVEC3: glUniform3uiv(locIndex, count, (unsigned int *)value); break; + case RL_SHADER_UNIFORM_UIVEC4: glUniform4uiv(locIndex, count, (unsigned int *)value); break; + #endif + case RL_SHADER_UNIFORM_SAMPLER2D: glUniform1iv(locIndex, count, (int *)value); break; + default: TRACELOG(RL_LOG_WARNING, "SHADER: Failed to set uniform value, data type not recognized"); + + // TODO: Support glUniform1uiv(), glUniform2uiv(), glUniform3uiv(), glUniform4uiv() + } +#endif +} + +// Set shader value attribute +void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + switch (attribType) + { + case RL_SHADER_ATTRIB_FLOAT: if (count == 1) glVertexAttrib1fv(locIndex, (float *)value); break; + case RL_SHADER_ATTRIB_VEC2: if (count == 2) glVertexAttrib2fv(locIndex, (float *)value); break; + case RL_SHADER_ATTRIB_VEC3: if (count == 3) glVertexAttrib3fv(locIndex, (float *)value); break; + case RL_SHADER_ATTRIB_VEC4: if (count == 4) glVertexAttrib4fv(locIndex, (float *)value); break; + default: TRACELOG(RL_LOG_WARNING, "SHADER: Failed to set attrib default value, data type not recognized"); + } +#endif +} + +// Set shader value uniform matrix +void rlSetUniformMatrix(int locIndex, Matrix mat) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + float matfloat[16] = { + mat.m0, mat.m1, mat.m2, mat.m3, + mat.m4, mat.m5, mat.m6, mat.m7, + mat.m8, mat.m9, mat.m10, mat.m11, + mat.m12, mat.m13, mat.m14, mat.m15 + }; + glUniformMatrix4fv(locIndex, 1, false, matfloat); +#endif +} + +// Set shader value uniform matrix +void rlSetUniformMatrices(int locIndex, const Matrix *matrices, int count) +{ +#if defined(GRAPHICS_API_OPENGL_33) + glUniformMatrix4fv(locIndex, count, true, (const float *)matrices); +#elif defined(GRAPHICS_API_OPENGL_ES2) + // WARNING: WebGL does not support Matrix transpose ("true" parameter) + // REF: https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/uniformMatrix + glUniformMatrix4fv(locIndex, count, false, (const float *)matrices); +#endif +} + +// Set shader value uniform sampler +void rlSetUniformSampler(int locIndex, unsigned int textureId) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Check if texture is already active + for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) + { + if (RLGL.State.activeTextureId[i] == textureId) + { + glUniform1i(locIndex, 1 + i); + return; + } + } + + // Register a new active texture for the internal batch system + // NOTE: Default texture is always activated as GL_TEXTURE0 + for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) + { + if (RLGL.State.activeTextureId[i] == 0) + { + glUniform1i(locIndex, 1 + i); // Activate new texture unit + RLGL.State.activeTextureId[i] = textureId; // Save texture id for binding on drawing + break; + } + } +#endif +} + +// Set shader currently active (id and locations) +void rlSetShader(unsigned int id, int *locs) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.State.currentShaderId != id) + { + rlDrawRenderBatch(RLGL.currentBatch); + RLGL.State.currentShaderId = id; + RLGL.State.currentShaderLocs = locs; + } +#endif +} + +// Load compute shader program +unsigned int rlLoadComputeShaderProgram(unsigned int shaderId) +{ + unsigned int program = 0; + +#if defined(GRAPHICS_API_OPENGL_43) + GLint success = 0; + program = glCreateProgram(); + glAttachShader(program, shaderId); + glLinkProgram(program); + + // NOTE: All uniform variables are intitialised to 0 when a program links + + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link compute shader program", program); + + int maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + if (maxLength > 0) + { + int length = 0; + char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); + glGetProgramInfoLog(program, maxLength, &length, log); + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log); + RL_FREE(log); + } + + glDeleteProgram(program); + + program = 0; + } + else + { + // Get the size of compiled shader program (not available on OpenGL ES 2.0) + // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero + //GLint binarySize = 0; + //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", program); + } +#else + TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43"); +#endif + + return program; +} + +// Dispatch compute shader (equivalent to *draw* for graphics pilepine) +void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glDispatchCompute(groupX, groupY, groupZ); +#endif +} + +// Load shader storage buffer object (SSBO) +unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHint) +{ + unsigned int ssbo = 0; + +#if defined(GRAPHICS_API_OPENGL_43) + glGenBuffers(1, &ssbo); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); + glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY); + 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); +#else + TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43"); +#endif + + return ssbo; +} + +// Unload shader storage buffer object (SSBO) +void rlUnloadShaderBuffer(unsigned int ssboId) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glDeleteBuffers(1, &ssboId); +#else + TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43"); +#endif + +} + +// Update SSBO buffer data +void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSize, unsigned int offset) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); + glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, dataSize, data); +#endif +} + +// Get SSBO buffer size +unsigned int rlGetShaderBufferSize(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_43) + GLint64 size = 0; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); + glGetBufferParameteri64v(GL_SHADER_STORAGE_BUFFER, GL_BUFFER_SIZE, &size); + return (size > 0)? (unsigned int)size : 0; +#else + return 0; +#endif +} + +// Read SSBO buffer data (GPU->CPU) +void rlReadShaderBuffer(unsigned int id, void *dest, unsigned int count, unsigned int offset) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); + glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, count, dest); +#endif +} + +// Bind SSBO buffer +void rlBindShaderBuffer(unsigned int id, unsigned int index) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, index, id); +#endif +} + +// Copy SSBO buffer data +void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned int destOffset, unsigned int srcOffset, unsigned int count) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glBindBuffer(GL_COPY_READ_BUFFER, srcId); + glBindBuffer(GL_COPY_WRITE_BUFFER, destId); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, srcOffset, destOffset, count); +#endif +} + +// Bind image texture +void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool readonly) +{ +#if defined(GRAPHICS_API_OPENGL_43) + unsigned int glInternalFormat = 0, glFormat = 0, glType = 0; + + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + glBindImageTexture(index, id, 0, 0, 0, readonly? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat); +#else + TRACELOG(RL_LOG_WARNING, "TEXTURE: Image texture binding not enabled. Define GRAPHICS_API_OPENGL_43"); +#endif +} + +// Matrix state management +//----------------------------------------------------------------------------------------- +// Get internal modelview matrix +Matrix rlGetMatrixModelview(void) +{ + Matrix matrix = rlMatrixIdentity(); +#if defined(GRAPHICS_API_OPENGL_11) + float mat[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, mat); + matrix.m0 = mat[0]; + matrix.m1 = mat[1]; + matrix.m2 = mat[2]; + matrix.m3 = mat[3]; + matrix.m4 = mat[4]; + matrix.m5 = mat[5]; + matrix.m6 = mat[6]; + matrix.m7 = mat[7]; + matrix.m8 = mat[8]; + matrix.m9 = mat[9]; + matrix.m10 = mat[10]; + matrix.m11 = mat[11]; + matrix.m12 = mat[12]; + matrix.m13 = mat[13]; + matrix.m14 = mat[14]; + matrix.m15 = mat[15]; +#else + matrix = RLGL.State.modelview; +#endif + return matrix; +} + +// Get internal projection matrix +Matrix rlGetMatrixProjection(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + float mat[16]; + glGetFloatv(GL_PROJECTION_MATRIX,mat); + Matrix m; + m.m0 = mat[0]; + m.m1 = mat[1]; + m.m2 = mat[2]; + m.m3 = mat[3]; + m.m4 = mat[4]; + m.m5 = mat[5]; + m.m6 = mat[6]; + m.m7 = mat[7]; + m.m8 = mat[8]; + m.m9 = mat[9]; + m.m10 = mat[10]; + m.m11 = mat[11]; + m.m12 = mat[12]; + m.m13 = mat[13]; + m.m14 = mat[14]; + m.m15 = mat[15]; + return m; +#else + return RLGL.State.projection; +#endif +} + +// Get internal accumulated transform matrix +Matrix rlGetMatrixTransform(void) +{ + Matrix mat = rlMatrixIdentity(); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // TODO: Consider possible transform matrices in the RLGL.State.stack + // Is this the right order? or should we start with the first stored matrix instead of the last one? + //Matrix matStackTransform = rlMatrixIdentity(); + //for (int i = RLGL.State.stackCounter; i > 0; i--) matStackTransform = rlMatrixMultiply(RLGL.State.stack[i], matStackTransform); + mat = RLGL.State.transform; +#endif + return mat; +} + +// Get internal projection matrix for stereo render (selected eye) +Matrix rlGetMatrixProjectionStereo(int eye) +{ + Matrix mat = rlMatrixIdentity(); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + mat = RLGL.State.projectionStereo[eye]; +#endif + return mat; +} + +// Get internal view offset matrix for stereo render (selected eye) +Matrix rlGetMatrixViewOffsetStereo(int eye) +{ + Matrix mat = rlMatrixIdentity(); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + mat = RLGL.State.viewOffsetStereo[eye]; +#endif + return mat; +} + +// Set a custom modelview matrix (replaces internal modelview matrix) +void rlSetMatrixModelview(Matrix view) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.modelview = view; +#endif +} + +// Set a custom projection matrix (replaces internal projection matrix) +void rlSetMatrixProjection(Matrix projection) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.projection = projection; +#endif +} + +// Set eyes projection matrices for stereo rendering +void rlSetMatrixProjectionStereo(Matrix right, Matrix left) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.projectionStereo[0] = right; + RLGL.State.projectionStereo[1] = left; +#endif +} + +// Set eyes view offsets matrices for stereo rendering +void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.viewOffsetStereo[0] = right; + RLGL.State.viewOffsetStereo[1] = left; +#endif +} + +// Load and draw a quad in NDC +void rlLoadDrawQuad(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int quadVAO = 0; + unsigned int quadVBO = 0; + + float vertices[] = { + // Positions Texcoords + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + }; + + // Gen VAO to contain VBO + glGenVertexArrays(1, &quadVAO); + glBindVertexArray(quadVAO); + + // Gen and fill vertex buffer (VBO) + glGenBuffers(1, &quadVBO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW); + + // Bind vertex attributes (position, texcoords) + glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION); + glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0); // Positions + glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)(3*sizeof(float))); // Texcoords + + // Draw quad + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + + // Delete buffers (VBO and VAO) + glDeleteBuffers(1, &quadVBO); + glDeleteVertexArrays(1, &quadVAO); +#endif +} + +// Load and draw a cube in NDC +void rlLoadDrawCube(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int cubeVAO = 0; + unsigned int cubeVBO = 0; + + float vertices[] = { + // Positions Normals Texcoords + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f + }; + + // Gen VAO to contain VBO + glGenVertexArrays(1, &cubeVAO); + glBindVertexArray(cubeVAO); + + // Gen and fill vertex buffer (VBO) + glGenBuffers(1, &cubeVBO); + glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + // Bind vertex attributes (position, normals, texcoords) + glBindVertexArray(cubeVAO); + glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION); + glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)0); // Positions + glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL); + glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(3*sizeof(float))); // Normals + glEnableVertexAttribArray(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + glVertexAttribPointer(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(6*sizeof(float))); // Texcoords + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + // Draw cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); + + // Delete VBO and VAO + glDeleteBuffers(1, &cubeVBO); + glDeleteVertexArrays(1, &cubeVAO); +#endif +} + +// Get name string for pixel format +const char *rlGetPixelFormatName(unsigned int format) +{ + switch (format) + { + case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: return "GRAYSCALE"; break; // 8 bit per pixel (no alpha) + case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: return "GRAY_ALPHA"; break; // 8*2 bpp (2 channels) + case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: return "R5G6B5"; break; // 16 bpp + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: return "R8G8B8"; break; // 24 bpp + case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: return "R5G5B5A1"; break; // 16 bpp (1 bit alpha) + case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: return "R4G4B4A4"; break; // 16 bpp (4 bit alpha) + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: return "R8G8B8A8"; break; // 32 bpp + 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_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_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_DXT5_RGBA: return "DXT5_RGBA"; break; // 8 bpp + case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: return "ETC1_RGB"; break; // 4 bpp + case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB: return "ETC2_RGB"; break; // 4 bpp + case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: return "ETC2_RGBA"; break; // 8 bpp + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB: return "PVRT_RGB"; break; // 4 bpp + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: return "PVRT_RGBA"; break; // 4 bpp + case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: return "ASTC_4x4_RGBA"; break; // 8 bpp + case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: return "ASTC_8x8_RGBA"; break; // 2 bpp + default: return "UNKNOWN"; break; + } +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Load default shader (just vertex positioning and texture coloring) +// NOTE: This shader program is used for internal buffers +// NOTE: Loaded: RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs +static void rlLoadShaderDefault(void) +{ + RLGL.State.defaultShaderLocs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); + + // NOTE: All locations must be reseted to -1 (no location) + for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) RLGL.State.defaultShaderLocs[i] = -1; + + // Vertex shader directly defined, no external file required + const char *defaultVShaderCode = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" + "attribute vec3 vertexPosition; \n" + "attribute vec2 vertexTexCoord; \n" + "attribute vec4 vertexColor; \n" + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec3 vertexPosition; \n" + "in vec2 vertexTexCoord; \n" + "in vec4 vertexColor; \n" + "out vec2 fragTexCoord; \n" + "out vec4 fragColor; \n" +#endif + +#if defined(GRAPHICS_API_OPENGL_ES3) + "#version 300 es \n" + "precision mediump float; \n" // Precision required for OpenGL ES3 (WebGL 2) (on some browsers) + "in vec3 vertexPosition; \n" + "in vec2 vertexTexCoord; \n" + "in vec4 vertexColor; \n" + "out vec2 fragTexCoord; \n" + "out vec4 fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_ES2) + "#version 100 \n" + "precision mediump float; \n" // Precision required for OpenGL ES2 (WebGL) (on some browsers) + "attribute vec3 vertexPosition; \n" + "attribute vec2 vertexTexCoord; \n" + "attribute vec4 vertexColor; \n" + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" +#endif + + "uniform mat4 mvp; \n" + "void main() \n" + "{ \n" + " fragTexCoord = vertexTexCoord; \n" + " fragColor = vertexColor; \n" + " gl_Position = mvp*vec4(vertexPosition, 1.0); \n" + "} \n"; + + // Fragment shader directly defined, no external file required + const char *defaultFShaderCode = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" + " gl_FragColor = texelColor*colDiffuse*fragColor; \n" + "} \n"; +#elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec2 fragTexCoord; \n" + "in vec4 fragColor; \n" + "out vec4 finalColor; \n" + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" + " vec4 texelColor = texture(texture0, fragTexCoord); \n" + " finalColor = texelColor*colDiffuse*fragColor; \n" + "} \n"; +#endif + +#if defined(GRAPHICS_API_OPENGL_ES3) + "#version 300 es \n" + "precision mediump float; \n" // Precision required for OpenGL ES3 (WebGL 2) + "in vec2 fragTexCoord; \n" + "in vec4 fragColor; \n" + "out vec4 finalColor; \n" + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" + " vec4 texelColor = texture(texture0, fragTexCoord); \n" + " finalColor = texelColor*colDiffuse*fragColor; \n" + "} \n"; +#elif defined(GRAPHICS_API_OPENGL_ES2) + "#version 100 \n" + "precision mediump float; \n" // Precision required for OpenGL ES2 (WebGL) + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" + " gl_FragColor = texelColor*colDiffuse*fragColor; \n" + "} \n"; +#endif + + // NOTE: Compiled vertex/fragment shaders are not deleted, + // they are kept for re-use as default shaders in case some shader loading fails + RLGL.State.defaultVShaderId = rlCompileShader(defaultVShaderCode, GL_VERTEX_SHADER); // Compile default vertex shader + RLGL.State.defaultFShaderId = rlCompileShader(defaultFShaderCode, GL_FRAGMENT_SHADER); // Compile default fragment shader + + RLGL.State.defaultShaderId = rlLoadShaderProgram(RLGL.State.defaultVShaderId, RLGL.State.defaultFShaderId); + + if (RLGL.State.defaultShaderId > 0) + { + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Default shader loaded successfully", RLGL.State.defaultShaderId); + + // Set default shader locations: attributes locations + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_POSITION] = glGetAttribLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION); + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_COLOR] = glGetAttribLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR); + + // Set default shader locations: uniform locations + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MATRIX_MVP] = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP); + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_COLOR_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR); + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MAP_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0); + } + else TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to load default shader", RLGL.State.defaultShaderId); +} + +// Unload default shader +// NOTE: Unloads: RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs +static void rlUnloadShaderDefault(void) +{ + glUseProgram(0); + + glDetachShader(RLGL.State.defaultShaderId, RLGL.State.defaultVShaderId); + glDetachShader(RLGL.State.defaultShaderId, RLGL.State.defaultFShaderId); + glDeleteShader(RLGL.State.defaultVShaderId); + glDeleteShader(RLGL.State.defaultFShaderId); + + glDeleteProgram(RLGL.State.defaultShaderId); + + RL_FREE(RLGL.State.defaultShaderLocs); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Default shader unloaded successfully", RLGL.State.defaultShaderId); +} + +#if defined(RLGL_SHOW_GL_DETAILS_INFO) +// Get compressed format official GL identifier name +static const char *rlGetCompressedFormatName(int format) +{ + switch (format) + { + // GL_EXT_texture_compression_s3tc + case 0x83F0: return "GL_COMPRESSED_RGB_S3TC_DXT1_EXT"; break; + case 0x83F1: return "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT"; break; + case 0x83F2: return "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT"; break; + case 0x83F3: return "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT"; break; + // GL_3DFX_texture_compression_FXT1 + case 0x86B0: return "GL_COMPRESSED_RGB_FXT1_3DFX"; break; + case 0x86B1: return "GL_COMPRESSED_RGBA_FXT1_3DFX"; break; + // GL_IMG_texture_compression_pvrtc + case 0x8C00: return "GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG"; break; + case 0x8C01: return "GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG"; break; + case 0x8C02: return "GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG"; break; + case 0x8C03: return "GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG"; break; + // GL_OES_compressed_ETC1_RGB8_texture + case 0x8D64: return "GL_ETC1_RGB8_OES"; break; + // GL_ARB_texture_compression_rgtc + case 0x8DBB: return "GL_COMPRESSED_RED_RGTC1"; break; + case 0x8DBC: return "GL_COMPRESSED_SIGNED_RED_RGTC1"; break; + case 0x8DBD: return "GL_COMPRESSED_RG_RGTC2"; break; + case 0x8DBE: return "GL_COMPRESSED_SIGNED_RG_RGTC2"; break; + // GL_ARB_texture_compression_bptc + case 0x8E8C: return "GL_COMPRESSED_RGBA_BPTC_UNORM_ARB"; break; + case 0x8E8D: return "GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB"; break; + case 0x8E8E: return "GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB"; break; + case 0x8E8F: return "GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB"; break; + // GL_ARB_ES3_compatibility + case 0x9274: return "GL_COMPRESSED_RGB8_ETC2"; break; + case 0x9275: return "GL_COMPRESSED_SRGB8_ETC2"; break; + case 0x9276: return "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2"; break; + case 0x9277: return "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2"; break; + case 0x9278: return "GL_COMPRESSED_RGBA8_ETC2_EAC"; break; + case 0x9279: return "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC"; break; + case 0x9270: return "GL_COMPRESSED_R11_EAC"; break; + case 0x9271: return "GL_COMPRESSED_SIGNED_R11_EAC"; break; + case 0x9272: return "GL_COMPRESSED_RG11_EAC"; break; + case 0x9273: return "GL_COMPRESSED_SIGNED_RG11_EAC"; break; + // GL_KHR_texture_compression_astc_hdr + case 0x93B0: return "GL_COMPRESSED_RGBA_ASTC_4x4_KHR"; break; + case 0x93B1: return "GL_COMPRESSED_RGBA_ASTC_5x4_KHR"; break; + case 0x93B2: return "GL_COMPRESSED_RGBA_ASTC_5x5_KHR"; break; + case 0x93B3: return "GL_COMPRESSED_RGBA_ASTC_6x5_KHR"; break; + case 0x93B4: return "GL_COMPRESSED_RGBA_ASTC_6x6_KHR"; break; + case 0x93B5: return "GL_COMPRESSED_RGBA_ASTC_8x5_KHR"; break; + case 0x93B6: return "GL_COMPRESSED_RGBA_ASTC_8x6_KHR"; break; + case 0x93B7: return "GL_COMPRESSED_RGBA_ASTC_8x8_KHR"; break; + case 0x93B8: return "GL_COMPRESSED_RGBA_ASTC_10x5_KHR"; break; + case 0x93B9: return "GL_COMPRESSED_RGBA_ASTC_10x6_KHR"; break; + case 0x93BA: return "GL_COMPRESSED_RGBA_ASTC_10x8_KHR"; break; + case 0x93BB: return "GL_COMPRESSED_RGBA_ASTC_10x10_KHR"; break; + case 0x93BC: return "GL_COMPRESSED_RGBA_ASTC_12x10_KHR"; break; + case 0x93BD: return "GL_COMPRESSED_RGBA_ASTC_12x12_KHR"; break; + case 0x93D0: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR"; break; + case 0x93D1: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR"; break; + case 0x93D2: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR"; break; + case 0x93D3: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR"; break; + case 0x93D4: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR"; break; + case 0x93D5: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR"; break; + case 0x93D6: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR"; break; + case 0x93D7: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR"; break; + case 0x93D8: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR"; break; + case 0x93D9: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR"; break; + case 0x93DA: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR"; break; + case 0x93DB: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR"; break; + case 0x93DC: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR"; break; + case 0x93DD: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR"; break; + default: return "GL_COMPRESSED_UNKNOWN"; break; + } +} +#endif // RLGL_SHOW_GL_DETAILS_INFO + +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +// Get pixel data size in bytes (image or texture) +// NOTE: Size depends on pixel format +static int rlGetPixelDataSize(int width, int height, int format) +{ + int dataSize = 0; // Size in bytes + int bpp = 0; // Bits per pixel + + switch (format) + { + case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; 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_RGBA: + case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: + case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB: + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB: + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; + case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: + case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: + case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: + case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break; + case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; + default: break; + } + + double bytesPerPixel = (double)bpp/8.0; + dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes + + // Most compressed formats works on 4x4 blocks, + // if texture is smaller, minimum dataSize is 8 or 16 + if ((width < 4) && (height < 4)) + { + if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8; + else if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16; + } + + return dataSize; +} + +// Auxiliar math functions + +// Get float array of matrix data +static rl_float16 rlMatrixToFloatV(Matrix mat) +{ + rl_float16 result = { 0 }; + + result.v[0] = mat.m0; + result.v[1] = mat.m1; + result.v[2] = mat.m2; + result.v[3] = mat.m3; + result.v[4] = mat.m4; + result.v[5] = mat.m5; + result.v[6] = mat.m6; + result.v[7] = mat.m7; + result.v[8] = mat.m8; + result.v[9] = mat.m9; + result.v[10] = mat.m10; + result.v[11] = mat.m11; + result.v[12] = mat.m12; + result.v[13] = mat.m13; + result.v[14] = mat.m14; + result.v[15] = mat.m15; + + return result; +} + +// Get identity matrix +static Matrix rlMatrixIdentity(void) +{ + Matrix result = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + return result; +} + +// Get two matrix multiplication +// NOTE: When multiplying matrices... the order matters! +static Matrix rlMatrixMultiply(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; + result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; + result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; + result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; + result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; + result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; + result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; + result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; + result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; + result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; + result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; + result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; + result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; + result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; + result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; + result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; + + return result; +} + +// Transposes provided matrix +static Matrix rlMatrixTranspose(Matrix mat) +{ + Matrix result = { 0 }; + + result.m0 = mat.m0; + result.m1 = mat.m4; + result.m2 = mat.m8; + result.m3 = mat.m12; + result.m4 = mat.m1; + result.m5 = mat.m5; + result.m6 = mat.m9; + result.m7 = mat.m13; + result.m8 = mat.m2; + result.m9 = mat.m6; + result.m10 = mat.m10; + result.m11 = mat.m14; + result.m12 = mat.m3; + result.m13 = mat.m7; + result.m14 = mat.m11; + result.m15 = mat.m15; + + return result; +} + +// Invert provided matrix +static Matrix rlMatrixInvert(Matrix mat) +{ + Matrix result = { 0 }; + + // Cache the matrix values (speed optimization) + float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; + float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; + float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; + float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; + + float b00 = a00*a11 - a01*a10; + float b01 = a00*a12 - a02*a10; + float b02 = a00*a13 - a03*a10; + float b03 = a01*a12 - a02*a11; + float b04 = a01*a13 - a03*a11; + float b05 = a02*a13 - a03*a12; + float b06 = a20*a31 - a21*a30; + float b07 = a20*a32 - a22*a30; + float b08 = a20*a33 - a23*a30; + float b09 = a21*a32 - a22*a31; + float b10 = a21*a33 - a23*a31; + float b11 = a22*a33 - a23*a32; + + // Calculate the invert determinant (inlined to avoid double-caching) + float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); + + result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; + result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; + result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; + result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; + result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; + result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; + result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; + result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; + result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; + result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; + result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; + result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; + result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; + result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; + result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; + result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; + + return result; +} + +#endif // RLGL_IMPLEMENTATION diff --git a/Game of Life/lib/raylib.lib b/Game of Life/lib/raylib.lib new file mode 100644 index 0000000000000000000000000000000000000000..736bdec69c4b0acb0d6a1b47f0be7b50685ce6fa GIT binary patch literal 5009534 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 &gdPoz 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

UJoj1S8_@_E9u^ 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


$)OLCj@RInYU5t}}8WsmpA5!uiLu#Zg+yZr4A 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 z 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
rc(?mvIwQ`DwHbF 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%0D3^ykEg zKNosTQN*mam{lAN=;GCY{Q?3Bo)sTUvM9z%66A``0FJ|+BW%bpIb230)E{aPE-@=2 zck?*hI^lAqcHab3-ix{PySNi(ojC{K>!<*U-bk7e5yf`N5-Uv|zl~hwae9sm><1w9 z80d-Or$OK z$_-UsH z1WWXtVs-0j`RfWVA&L^WzeOT?on7fAL{Z{SNmS;UO7ypnLw|c=Vj|(qqxM^0IE6C9 z7>J_ercl?5;`)*f_WM&_LKG#wD~V(-_OVT2<_Mm^x+U`Nh~!mXQWPbBU?r>0FEi&K z%rR|ybIs!2d1fJ6*9*|{o`;tAEVR5!(eh5BSCpn#R>WGNKrn7bJB?i8*6; z7JB2d&7v(iX3i$em*0R9fj(Siz2Q}MMWohPYiM4Pd)ZP>BQ`-;QBl_V&z&dDT

NiuSO-xFzH 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ô-uuBxG`V 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

sC=yi)b#vaZJU}3bvPOaIdi$ZZR2? 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 XrY)Au6ksV-Ve*BE30TJ( z`PJ$SkjfzA^BQpL&QolMGqcK{<4gAmbSU%%eLKVXiXQfdOTyW9zE*8CYxU|lWFCeE z3^o$rL;@EW(V0(9sWYGQ&I8u4ynz?)z&w1KKhOQtzECeZWHX<1&Ea|*p4L$sWT9SC zAmOF*M1C3$-!rg-B`KT@tl^%3LE9)y>M9&xD^BTQHU@_e`qo7rz6Iwg&qEeKNncn9 zwHLI#Gy%{5yAD+WJg=jlJ%_eKH`iCe7K5r-E7TjsJe-NcDe5}73^!4P@9W{k4Kk|= zO_nMpIO;IK-6FkkA>_ZplYr&8&QP=PiYClS7*!rmleq7Z6wg2von`?uuA!tZjle}x z_%;Nr;+Si?td)mJ;1sUT(`749@pMRWm>kOUP9{Z&sbZyA=dYFI^G9aJGHRg0v&UF1 zuDd99Ji($gTT?k|Q&UH^ni@wbUmrBKXqLmb(%AcY9(sCMHOkdBHTd?cHpA8iW)^>< zUe|;>V6c~F;rp1{h9g3UAPP1xd~F(rii?sN*GG;5U!56WrPg54hj9H3_Ry-lfR{t^ zR5iUYIA)cEJe&t=hn95nLxK5nxSnFuaBTpOMU3JSh^s$-#R*cR9yinNLK|m9t>28` zlGrRZnob3Yp?#C