diff options
author | Luca Sas <sas.luca.alex@gmail.com> | 2020-03-06 17:48:44 +0000 |
---|---|---|
committer | Luca Sas <sas.luca.alex@gmail.com> | 2020-03-06 17:48:44 +0000 |
commit | 581538a8b371c0a9003dc0f1bf081222b8c4fdd9 (patch) | |
tree | f5759a699424211d4a66e24365a596072818ab33 /libs/raylib/src/rlgl.h | |
download | gamejam-slgj-2024-581538a8b371c0a9003dc0f1bf081222b8c4fdd9.tar.gz gamejam-slgj-2024-581538a8b371c0a9003dc0f1bf081222b8c4fdd9.tar.bz2 gamejam-slgj-2024-581538a8b371c0a9003dc0f1bf081222b8c4fdd9.zip |
Setup the project
Diffstat (limited to 'libs/raylib/src/rlgl.h')
-rw-r--r-- | libs/raylib/src/rlgl.h | 4661 |
1 files changed, 4661 insertions, 0 deletions
diff --git a/libs/raylib/src/rlgl.h b/libs/raylib/src/rlgl.h new file mode 100644 index 0000000..349ea50 --- /dev/null +++ b/libs/raylib/src/rlgl.h @@ -0,0 +1,4661 @@ +/********************************************************************************************** +* +* rlgl - raylib OpenGL abstraction layer +* +* rlgl is a wrapper for multiple OpenGL versions (1.1, 2.1, 3.3 Core, ES 2.0) to +* pseudo-OpenGL 1.1 style functions (rlVertex, rlTranslate, rlRotate...). +* +* When chosing an OpenGL version greater than OpenGL 1.1, rlgl stores vertex data on internal +* VBO buffers (and VAOs if available). It requires calling 3 functions: +* rlglInit() - Initialize internal buffers and auxiliar resources +* rlglDraw() - Process internal buffers and send required draw calls +* rlglClose() - De-initialize internal buffers data and other auxiliar resources +* +* CONFIGURATION: +* +* #define GRAPHICS_API_OPENGL_11 +* #define GRAPHICS_API_OPENGL_21 +* #define GRAPHICS_API_OPENGL_33 +* #define GRAPHICS_API_OPENGL_ES2 +* 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() tocheck 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_STANDALONE +* Use rlgl as standalone library (no raylib dependency) +* +* #define SUPPORT_VR_SIMULATOR +* Support VR simulation functionality (stereo rendering) +* +* DEPENDENCIES: +* raymath - 3D math functionality (Vector3, Matrix, Quaternion) +* GLAD - OpenGL extensions loading (OpenGL 3.3 Core only) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2020 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 + +#if defined(RLGL_STANDALONE) + #define RAYMATH_STANDALONE + #define RAYMATH_HEADER_ONLY + + #define RLAPI // We are building or using rlgl as a static library (or Linux shared library) + + #if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #define RLAPI __declspec(dllexport) // We are building raylib as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RLAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) + #endif + #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 +#else + #include "raylib.h" // Required for: Model, Shader, Texture2D, TraceLog() +#endif + +#include "raymath.h" // Required for: Vector3, Matrix + +// 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_ES2) + #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_ES2) + #undef GRAPHICS_API_OPENGL_ES2 + #endif +#endif + +#if defined(GRAPHICS_API_OPENGL_21) + #define GRAPHICS_API_OPENGL_33 +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#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 MAX_BATCH_ELEMENTS 8192 +#elif 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 MAX_BATCH_ELEMENTS 2048 +#endif + +#define MAX_BATCH_BUFFERING 1 // Max number of buffers for batching (multi-buffering) +#define MAX_MATRIX_STACK_SIZE 32 // Max size of Matrix stack +#define MAX_DRAWCALL_REGISTERED 256 // Max draws by state changes (mode, texture) + +// Shader and material limits +#define MAX_SHADER_LOCATIONS 32 // Maximum number of predefined locations stored in shader struct +#define MAX_MATERIAL_MAPS 12 // Maximum number of texture maps stored in shader struct + +// 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_ANISOTROPIC_FILTER 0x3000 // Anisotropic filter (custom identifier) + +#define RL_FILTER_NEAREST 0x2600 // GL_NEAREST +#define RL_FILTER_LINEAR 0x2601 // GL_LINEAR +#define RL_FILTER_MIP_NEAREST 0x2700 // GL_NEAREST_MIPMAP_NEAREST +#define RL_FILTER_NEAREST_MIP_LINEAR 0x2702 // GL_NEAREST_MIPMAP_LINEAR +#define RL_FILTER_LINEAR_MIP_NEAREST 0x2701 // GL_LINEAR_MIPMAP_NEAREST +#define RL_FILTER_MIP_LINEAR 0x2703 // GL_LINEAR_MIPMAP_LINEAR + +#define RL_WRAP_REPEAT 0x2901 // GL_REPEAT +#define RL_WRAP_CLAMP 0x812F // GL_CLAMP_TO_EDGE +#define RL_WRAP_MIRROR_REPEAT 0x8370 // GL_MIRRORED_REPEAT +#define RL_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 + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion; + +typedef unsigned char byte; + +#if defined(RLGL_STANDALONE) + #ifndef __cplusplus + // Boolean type + typedef enum { false, true } bool; + #endif + + // Color type, RGBA (32bit) + typedef struct Color { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + } Color; + + // Texture2D type + // NOTE: Data stored in GPU memory + typedef struct Texture2D { + 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) + } Texture2D; + + // Texture type, same as Texture2D + typedef Texture2D Texture; + + // TextureCubemap type, actually, same as Texture2D + typedef Texture2D TextureCubemap; + + // RenderTexture2D type, for texture rendering + typedef struct RenderTexture2D { + unsigned int id; // OpenGL framebuffer (fbo) id + Texture2D texture; // Color buffer attachment texture + Texture2D depth; // Depth buffer attachment texture + bool depthTexture; // Track if depth attachment is a texture or renderbuffer + } RenderTexture2D; + + // RenderTexture type, same as RenderTexture2D + typedef RenderTexture2D RenderTexture; + + // Vertex data definning a mesh + typedef struct Mesh { + int vertexCount; // number of vertices stored in arrays + int triangleCount; // number of triangles stored (indexed or not) + 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 second texture coordinates (useful for lightmaps) (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) + int *boneIds; // Vertex bone ids, up to 4 bones influence by vertex (skinning) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) + + // OpenGL identifiers + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int *vboId; // OpenGL Vertex Buffer Objects id (7 types of vertex data) + } Mesh; + + // Shader and material limits + #define MAX_SHADER_LOCATIONS 32 + #define MAX_MATERIAL_MAPS 12 + + // Shader type (generic) + typedef struct Shader { + unsigned int id; // Shader program id + int *locs; // Shader locations array (MAX_SHADER_LOCATIONS) + } Shader; + + // Material texture map + typedef struct MaterialMap { + Texture2D texture; // Material map texture + Color color; // Material map color + float value; // Material map value + } MaterialMap; + + // Material type (generic) + typedef struct Material { + Shader shader; // Material shader + MaterialMap *maps; // Material maps (MAX_MATERIAL_MAPS) + float *params; // Material generic parameters (if required) + } Material; + + // Camera type, defines a camera position/orientation in 3d space + typedef struct Camera { + 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 apperture in Y (degrees) + } Camera; + + // Head-Mounted-Display device parameters + typedef struct VrDeviceInfo { + int hResolution; // HMD horizontal resolution in pixels + int vResolution; // HMD vertical resolution in pixels + float hScreenSize; // HMD horizontal size in meters + float vScreenSize; // HMD vertical size in meters + float vScreenCenter; // HMD screen center in meters + float eyeToScreenDistance; // HMD distance between eye and display in meters + float lensSeparationDistance; // HMD lens separation distance in meters + float interpupillaryDistance; // HMD IPD (distance between pupils) in meters + float lensDistortionValues[4]; // HMD lens distortion constant parameters + float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters + } VrDeviceInfo; + + // VR Stereo rendering configuration for simulator + typedef struct VrStereoConfig { + Shader distortionShader; // VR stereo rendering distortion shader + Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices + Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices + int eyeViewportRight[4]; // VR stereo rendering right eye viewport [x, y, w, h] + int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] + } VrStereoConfig; + + + // TraceLog message types + typedef enum { + LOG_ALL, + LOG_TRACE, + LOG_DEBUG, + LOG_INFO, + LOG_WARNING, + LOG_ERROR, + LOG_FATAL, + LOG_NONE + } TraceLogType; + + // Texture formats (support depends on OpenGL version) + typedef enum { + UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + UNCOMPRESSED_GRAY_ALPHA, + UNCOMPRESSED_R5G6B5, // 16 bpp + UNCOMPRESSED_R8G8B8, // 24 bpp + UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + UNCOMPRESSED_R8G8B8A8, // 32 bpp + UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) + UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) + COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) + COMPRESSED_DXT3_RGBA, // 8 bpp + COMPRESSED_DXT5_RGBA, // 8 bpp + COMPRESSED_ETC1_RGB, // 4 bpp + COMPRESSED_ETC2_RGB, // 4 bpp + COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + COMPRESSED_PVRT_RGB, // 4 bpp + COMPRESSED_PVRT_RGBA, // 4 bpp + COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + 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 { + FILTER_POINT = 0, // No filter, just pixel aproximation + FILTER_BILINEAR, // Linear filtering + FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) + FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x + FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x + FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x + } TextureFilterMode; + + // Color blending modes (pre-defined) + typedef enum { + BLEND_ALPHA = 0, + BLEND_ADDITIVE, + BLEND_MULTIPLIED + } BlendMode; + + // Shader location point type + typedef enum { + LOC_VERTEX_POSITION = 0, + LOC_VERTEX_TEXCOORD01, + LOC_VERTEX_TEXCOORD02, + LOC_VERTEX_NORMAL, + LOC_VERTEX_TANGENT, + LOC_VERTEX_COLOR, + LOC_MATRIX_MVP, + LOC_MATRIX_MODEL, + LOC_MATRIX_VIEW, + LOC_MATRIX_PROJECTION, + LOC_VECTOR_VIEW, + LOC_COLOR_DIFFUSE, + LOC_COLOR_SPECULAR, + LOC_COLOR_AMBIENT, + LOC_MAP_ALBEDO, // LOC_MAP_DIFFUSE + LOC_MAP_METALNESS, // LOC_MAP_SPECULAR + LOC_MAP_NORMAL, + LOC_MAP_ROUGHNESS, + LOC_MAP_OCCLUSION, + LOC_MAP_EMISSION, + LOC_MAP_HEIGHT, + LOC_MAP_CUBEMAP, + LOC_MAP_IRRADIANCE, + LOC_MAP_PREFILTER, + LOC_MAP_BRDF + } ShaderLocationIndex; + + // Shader uniform data types + typedef enum { + UNIFORM_FLOAT = 0, + UNIFORM_VEC2, + UNIFORM_VEC3, + UNIFORM_VEC4, + UNIFORM_INT, + UNIFORM_IVEC2, + UNIFORM_IVEC3, + UNIFORM_IVEC4, + UNIFORM_SAMPLER2D + } ShaderUniformDataType; + + #define LOC_MAP_DIFFUSE LOC_MAP_ALBEDO + #define LOC_MAP_SPECULAR LOC_MAP_METALNESS + + // Material map type + typedef enum { + MAP_ALBEDO = 0, // MAP_DIFFUSE + MAP_METALNESS = 1, // MAP_SPECULAR + MAP_NORMAL = 2, + MAP_ROUGHNESS = 3, + MAP_OCCLUSION, + MAP_EMISSION, + MAP_HEIGHT, + MAP_CUBEMAP, // NOTE: Uses GL_TEXTURE_CUBE_MAP + MAP_IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP + MAP_PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP + MAP_BRDF + } MaterialMapType; + + #define MAP_DIFFUSE MAP_ALBEDO + #define MAP_SPECULAR MAP_METALNESS +#endif + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +//------------------------------------------------------------------------------------ +// Functions Declaration - Matrix operations +//------------------------------------------------------------------------------------ +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 lattest 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 angleDeg, 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(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 + +//------------------------------------------------------------------------------------ +// 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(byte r, byte g, byte b, byte 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 equivalent functions (common to 1.1, 3.3+, ES2) +// NOTE: This functions are used to completely abstract raylib code from OpenGL layer +//------------------------------------------------------------------------------------ +RLAPI void rlEnableTexture(unsigned int id); // Enable texture usage +RLAPI void rlDisableTexture(void); // Disable texture usage +RLAPI void rlTextureParameters(unsigned int id, int param, int value); // Set texture parameters (filter, wrap) +RLAPI void rlEnableRenderTexture(unsigned int id); // Enable render texture (fbo) +RLAPI void rlDisableRenderTexture(void); // Disable render texture (fbo), return to default framebuffer +RLAPI void rlEnableDepthTest(void); // Enable depth test +RLAPI void rlDisableDepthTest(void); // Disable depth test +RLAPI void rlEnableBackfaceCulling(void); // Enable backface culling +RLAPI void rlDisableBackfaceCulling(void); // Disable backface culling +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 rlDisableWireMode(void); // Disable wire mode +RLAPI void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU +RLAPI void rlDeleteRenderTextures(RenderTexture2D target); // Delete render textures (fbo) from GPU +RLAPI void rlDeleteShader(unsigned int id); // Delete OpenGL shader program from GPU +RLAPI void rlDeleteVertexArrays(unsigned int id); // Unload vertex data (VAO) from GPU memory +RLAPI void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from GPU memory +RLAPI void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color +RLAPI void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) +RLAPI void rlUpdateBuffer(int bufferId, void *data, int dataSize); // Update GPU buffer with new data +RLAPI unsigned int rlLoadAttribBuffer(unsigned int vaoId, int shaderLoc, void *buffer, int size, bool dynamic); // Load a new attributes buffer + +//------------------------------------------------------------------------------------ +// Functions Declaration - rlgl functionality +//------------------------------------------------------------------------------------ +RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) +RLAPI void rlglClose(void); // De-inititialize rlgl (buffers, shaders, textures) +RLAPI void rlglDraw(void); // Update and draw default internal buffers + +RLAPI int rlGetVersion(void); // Returns current OpenGL version +RLAPI bool rlCheckBufferLimit(int vCount); // Check internal buffer overflow for a given number of vertex +RLAPI void rlSetDebugMarker(const char *text); // Set debug marker for analysis +RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions +RLAPI Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates + +// Textures data management +RLAPI unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU +RLAPI unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo) +RLAPI unsigned int rlLoadTextureCubemap(void *data, int size, int format); // Load texture cubemap +RLAPI void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data +RLAPI void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats +RLAPI void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory + +RLAPI void rlGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture +RLAPI void *rlReadTexturePixels(Texture2D texture); // Read texture pixel data +RLAPI unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) + +// Render texture management (fbo) +RLAPI RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depthBits, bool useDepthTexture); // Load a render texture (with color and depth attachments) +RLAPI void rlRenderTextureAttach(RenderTexture target, unsigned int id, int attachType); // Attach texture/renderbuffer to an fbo +RLAPI bool rlRenderTextureComplete(RenderTexture target); // Verify render texture is complete + +// Vertex data management +RLAPI void rlLoadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids +RLAPI void rlUpdateMesh(Mesh mesh, int buffer, int num); // Update vertex or index data on GPU (upload new data to one buffer) +RLAPI void rlUpdateMeshAt(Mesh mesh, int buffer, int num, int index); // Update vertex or index data on GPU, at index +RLAPI void rlDrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform +RLAPI void rlUnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU + +// NOTE: There is a set of shader related functions that are available to end user, +// to avoid creating function wrappers through core module, they have been directly declared in raylib.h + +#if defined(RLGL_STANDALONE) +//------------------------------------------------------------------------------------ +// Shaders System Functions (Module: rlgl) +// NOTE: This functions are useless when using OpenGL 1.1 +//------------------------------------------------------------------------------------ +// Shader loading/unloading functions +RLAPI char *LoadText(const char *fileName); // Load chars array from text file +RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations +RLAPI Shader LoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations +RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) + +RLAPI Shader GetShaderDefault(void); // Get default shader +RLAPI Texture2D GetTextureDefault(void); // Get default texture + +// Shader configuration functions +RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location +RLAPI void SetShaderValue(Shader shader, int uniformLoc, const void *value, int uniformType); // Set shader uniform value +RLAPI void SetShaderValueV(Shader shader, int uniformLoc, const void *value, int uniformType, int count); // Set shader uniform value vector +RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) +RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) +RLAPI Matrix GetMatrixModelview(void); // Get internal modelview matrix + +// Texture maps generation (PBR) +// NOTE: Required shaders should be provided +RLAPI Texture2D GenTextureCubemap(Shader shader, Texture2D map, int size); // Generate cubemap texture from HDR texture +RLAPI Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data +RLAPI Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data +RLAPI Texture2D GenTextureBRDF(Shader shader, int size); // Generate BRDF texture using cubemap data + +// Shading begin/end functions +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) +RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending) + +// VR control functions +RLAPI void InitVrSimulator(void); // Init VR simulator for selected device parameters +RLAPI void CloseVrSimulator(void); // Close VR simulator for current device +RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +RLAPI void SetVrConfiguration(VrDeviceInfo info, Shader distortion); // Set stereo rendering configuration parameters +RLAPI bool IsVrSimulatorReady(void); // Detect if VR simulator is ready +RLAPI void ToggleVrMode(void); // Enable/Disable VR experience +RLAPI void BeginVrDrawing(void); // Begin VR simulator stereo rendering +RLAPI void EndVrDrawing(void); // End VR simulator stereo rendering + +RLAPI void TraceLog(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) +RLAPI int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture) +#endif + +#if defined(__cplusplus) +} +#endif + +#endif // RLGL_H + +/*********************************************************************************** +* +* RLGL IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RLGL_IMPLEMENTATION) + +#if !defined(RLGL_STANDALONE) + // Check if config flags have been externally provided on compilation line + #if !defined(EXTERNAL_CONFIG_FLAGS) + #include "config.h" // Defines module configuration flags + #endif +#endif + +#include <stdio.h> // Required for: fopen(), fclose(), fread()... [Used only on LoadText()] +#include <stdlib.h> // Required for: malloc(), free(), rand() +#include <string.h> // Required for: strcmp(), strlen(), strtok() [Used only in extensions loading] +#include <math.h> // Required for: atan2() + +#if !defined(RLGL_STANDALONE) + #include "raymath.h" // Required for: Vector3 and Matrix functions +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + #if defined(__APPLE__) + #include <OpenGL/gl.h> // OpenGL 1.1 library for OSX + #include <OpenGL/glext.h> + #else + // APIENTRY for OpenGL function pointer declarations is required + #ifndef 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 <GL/gl.h> // OpenGL 1.1 library + #endif +#endif + +#if defined(GRAPHICS_API_OPENGL_21) + #define GRAPHICS_API_OPENGL_33 // OpenGL 2.1 uses mostly OpenGL 3.3 Core functionality +#endif + +#if defined(GRAPHICS_API_OPENGL_33) + #if defined(__APPLE__) + #include <OpenGL/gl3.h> // OpenGL 3 library for OSX + #include <OpenGL/gl3ext.h> // OpenGL 3 extensions library for OSX + #else + #define GLAD_IMPLEMENTATION + #if defined(RLGL_STANDALONE) + #include "glad.h" // GLAD extensions loading library, includes OpenGL headers + #else + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers + #endif + #endif +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + #include <EGL/egl.h> // EGL library + #include <GLES2/gl2.h> // OpenGL ES 2.0 library + #include <GLES2/gl2ext.h> // OpenGL ES 2.0 extensions library +#endif + +#if defined(RLGL_STANDALONE) + #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() [Used only on TraceLog()] +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#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 + +#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 + #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER + #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER +#endif + +// Default vertex attribute names on shader to set location points +#define DEFAULT_ATTRIB_POSITION_NAME "vertexPosition" // shader-location = 0 +#define DEFAULT_ATTRIB_TEXCOORD_NAME "vertexTexCoord" // shader-location = 1 +#define DEFAULT_ATTRIB_NORMAL_NAME "vertexNormal" // shader-location = 2 +#define DEFAULT_ATTRIB_COLOR_NAME "vertexColor" // shader-location = 3 +#define DEFAULT_ATTRIB_TANGENT_NAME "vertexTangent" // shader-location = 4 +#define DEFAULT_ATTRIB_TEXCOORD2_NAME "vertexTexCoord2" // shader-location = 5 + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// Dynamic vertex buffers (position + texcoords + colors + indices arrays) +typedef struct DynamicBuffer { + int vCounter; // vertex position counter to process (and draw) from full buffer + int tcCounter; // vertex texcoord counter to process (and draw) from full buffer + int cCounter; // vertex color counter to process (and draw) from full buffer + 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) + 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) +#elif defined(GRAPHICS_API_OPENGL_ES2) + unsigned short *indices; // vertex indices (in case vertex data comes indexed) (6 indices per quad) + // NOTE: 6*2 byte = 12 byte, not alignment problem! +#endif + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int vboId[4]; // OpenGL Vertex Buffer Objects id (4 types of vertex data) +} DynamicBuffer; + +// Draw call type +typedef struct DrawCall { + 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 + //unsigned int shaderId; // Shader id to be used on the draw + unsigned int textureId; // Texture id to be used on the draw + // TODO: Support additional texture units? + + //Matrix projection; // Projection matrix for this draw + //Matrix modelview; // Modelview matrix for this draw +} DrawCall; + +#if defined(SUPPORT_VR_SIMULATOR) +// VR Stereo rendering configuration for simulator +typedef struct VrStereoConfig { + Shader distortionShader; // VR stereo rendering distortion shader + Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices + Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices + int eyeViewportRight[4]; // VR stereo rendering right eye viewport [x, y, w, h] + int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] +} VrStereoConfig; +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +static Matrix stack[MAX_MATRIX_STACK_SIZE] = { 0 }; // Matrix stack for push/pop +static int stackCounter = 0; // Matrix stack counter +static Matrix modelview = { 0 }; // Default modelview matrix +static Matrix projection = { 0 }; // Default projection matrix +static Matrix *currentMatrix = NULL; // Current matrix pointer +static int currentMatrixMode = -1; // Current matrix mode +static float currentDepth = -1.0f; // Current depth value + +// Default dynamic buffer for elements data +// NOTE: A multi-buffering system is supported +static DynamicBuffer vertexData[MAX_BATCH_BUFFERING] = { 0 }; +static int currentBuffer = 0; // Current buffer tracking + +static Matrix transformMatrix = { 0 }; // Transform matrix to be used with rlTranslate, rlRotate, rlScale +static bool useTransformMatrix = false; // Use transform matrix against vertex (if required) + +static DrawCall *draws = NULL; // Draw calls array +static int drawsCounter = 0; // Draw calls counter + +static unsigned int defaultTextureId = 0; // Default texture used on shapes/poly drawing (required by shader) +static unsigned int defaultVShaderId = 0; // Default vertex shader id (used by default shader program) +static unsigned int defaultFShaderId = 0; // Default fragment shader Id (used by default shader program) +static Shader defaultShader = { 0 }; // Basic shader, support vertex color and diffuse texture +static Shader currentShader = { 0 }; // Shader to be used on rendering (by default, defaultShader) + +// Extensions supported flags +static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support VAO extension) +static bool texCompDXTSupported = false; // DDS texture compression support +static bool texCompETC1Supported = false; // ETC1 texture compression support +static bool texCompETC2Supported = false; // ETC2/EAC texture compression support +static bool texCompPVRTSupported = false; // PVR texture compression support +static bool texCompASTCSupported = false; // ASTC texture compression support +static bool texNPOTSupported = false; // NPOT textures full support +static bool texFloatSupported = false; // float textures support (32 bit per channel) +static bool texDepthSupported = false; // Depth textures supported +static bool texMirrorClampSupported = false;// Clamp mirror wrap mode supported +static bool texAnisoFilterSupported = false;// Anisotropic texture filtering support +static bool debugMarkerSupported = false; // Debug marker support +static int maxDepthBits = 16; // Maximum bits for depth component +static float maxAnisotropicLevel = 0.0f; // Maximum anisotropy level supported (minimum is 2.0f) + +#if defined(GRAPHICS_API_OPENGL_ES2) +// NOTE: VAO functionality is exposed through extensions (OES) +static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays; // Entry point pointer to function glGenVertexArrays() +static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray; // Entry point pointer to function glBindVertexArray() +static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; // Entry point pointer to function glDeleteVertexArrays() +#endif + +#if defined(SUPPORT_VR_SIMULATOR) +// VR global variables +static VrStereoConfig vrConfig = { 0 }; // VR stereo configuration for simulator +static RenderTexture2D stereoFbo = { 0 }; // VR stereo rendering framebuffer +static bool vrSimulatorReady = false; // VR simulator ready flag +static bool vrStereoRender = false; // VR stereo rendering enabled/disabled flag + // NOTE: This flag is useful to render data over stereo image (i.e. FPS) +#endif // SUPPORT_VR_SIMULATOR + +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +static int blendMode = 0; // Track current blending mode + +// Default framebuffer size +static int framebufferWidth = 0; // Default framebuffer width +static int framebufferHeight = 0; // Default framebuffer height + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +static unsigned int CompileShader(const char *shaderStr, int type); // Compile custom shader and return shader id +static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program + +static Shader LoadShaderDefault(void); // Load default shader (just vertex positioning and texture coloring) +static void SetShaderDefaultLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) +static void UnloadShaderDefault(void); // Unload default shader + +static void LoadBuffersDefault(void); // Load default internal buffers +static void UpdateBuffersDefault(void); // Update default internal buffers (VAOs/VBOs) with vertex data +static void DrawBuffersDefault(void); // Draw default internal buffers vertex data +static void UnloadBuffersDefault(void); // Unload default internal buffers vertex data from CPU and GPU + +static void GenDrawCube(void); // Generate and draw cube +static void GenDrawQuad(void); // Generate and draw quad + +#if defined(SUPPORT_VR_SIMULATOR) +static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); // Set internal projection and modelview matrix depending on eye +#endif + +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +#if defined(GRAPHICS_API_OPENGL_11) +static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); +static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight); +#endif + +//---------------------------------------------------------------------------------- +// 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 angleDeg, float x, float y, float z) { glRotatef(angleDeg, x, y, z); } +void rlScalef(float x, float y, float z) { glScalef(x, y, z); } +void rlMultMatrixf(float *matf) { glMultMatrixf(matf); } + +#elif 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) currentMatrix = &projection; + else if (mode == RL_MODELVIEW) currentMatrix = &modelview; + //else if (mode == RL_TEXTURE) // Not supported + + currentMatrixMode = mode; +} + +// Push the current matrix into stack +void rlPushMatrix(void) +{ + if (stackCounter >= MAX_MATRIX_STACK_SIZE) TraceLog(LOG_ERROR, "Matrix stack overflow"); + + if (currentMatrixMode == RL_MODELVIEW) + { + useTransformMatrix = true; + currentMatrix = &transformMatrix; + } + + stack[stackCounter] = *currentMatrix; + stackCounter++; +} + +// Pop lattest inserted matrix from stack +void rlPopMatrix(void) +{ + if (stackCounter > 0) + { + Matrix mat = stack[stackCounter - 1]; + *currentMatrix = mat; + stackCounter--; + } + + if ((stackCounter == 0) && (currentMatrixMode == RL_MODELVIEW)) + { + currentMatrix = &modelview; + useTransformMatrix = false; + } +} + +// Reset current matrix to identity matrix +void rlLoadIdentity(void) +{ + *currentMatrix = MatrixIdentity(); +} + +// Multiply the current matrix by a translation matrix +void rlTranslatef(float x, float y, float z) +{ + Matrix matTranslation = MatrixTranslate(x, y, z); + + // NOTE: We transpose matrix with multiplication order + *currentMatrix = MatrixMultiply(matTranslation, *currentMatrix); +} + +// Multiply the current matrix by a rotation matrix +void rlRotatef(float angleDeg, float x, float y, float z) +{ + Matrix matRotation = MatrixIdentity(); + + Vector3 axis = (Vector3){ x, y, z }; + matRotation = MatrixRotate(Vector3Normalize(axis), angleDeg*DEG2RAD); + + // NOTE: We transpose matrix with multiplication order + *currentMatrix = MatrixMultiply(matRotation, *currentMatrix); +} + +// Multiply the current matrix by a scaling matrix +void rlScalef(float x, float y, float z) +{ + Matrix matScale = MatrixScale(x, y, z); + + // NOTE: We transpose matrix with multiplication order + *currentMatrix = MatrixMultiply(matScale, *currentMatrix); +} + +// Multiply the current matrix by another matrix +void rlMultMatrixf(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] }; + + *currentMatrix = MatrixMultiply(*currentMatrix, mat); +} + +// 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 matPerps = MatrixFrustum(left, right, bottom, top, znear, zfar); + + *currentMatrix = MatrixMultiply(*currentMatrix, matPerps); +} + +// 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) +{ + Matrix matOrtho = MatrixOrtho(left, right, bottom, top, znear, zfar); + + *currentMatrix = MatrixMultiply(*currentMatrix, matOrtho); +} + +#endif + +// Set the viewport area (transformation from normalized device coordinates to window coordinates) +// NOTE: Updates global variables: framebufferWidth, framebufferHeight +void rlViewport(int x, int y, int width, int height) +{ + glViewport(x, y, width, height); +} + +//---------------------------------------------------------------------------------- +// 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() { 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(byte r, byte g, byte b, byte 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); } + +#elif 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 (draws[drawsCounter - 1].mode != mode) + { + if (draws[drawsCounter - 1].vertexCount > 0) + { + // Make sure current 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 (draws[drawsCounter - 1].mode == RL_LINES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? draws[drawsCounter - 1].vertexCount : draws[drawsCounter - 1].vertexCount%4); + else if (draws[drawsCounter - 1].mode == RL_TRIANGLES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? 1 : (4 - (draws[drawsCounter - 1].vertexCount%4))); + + else draws[drawsCounter - 1].vertexAlignment = 0; + + if (rlCheckBufferLimit(draws[drawsCounter - 1].vertexAlignment)) rlglDraw(); + else + { + vertexData[currentBuffer].vCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].cCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].tcCounter += draws[drawsCounter - 1].vertexAlignment; + + drawsCounter++; + } + } + + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); + + draws[drawsCounter - 1].mode = mode; + draws[drawsCounter - 1].vertexCount = 0; + draws[drawsCounter - 1].textureId = defaultTextureId; + } +} + +// Finish vertex providing +void rlEnd(void) +{ + // Make sure vertexCount is the same for vertices, texcoords, colors and normals + // NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls + + // Make sure colors count match vertex count + if (vertexData[currentBuffer].vCounter != vertexData[currentBuffer].cCounter) + { + int addColors = vertexData[currentBuffer].vCounter - vertexData[currentBuffer].cCounter; + + for (int i = 0; i < addColors; i++) + { + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 4]; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 1] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 3]; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 2] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 2]; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 3] = vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter - 1]; + vertexData[currentBuffer].cCounter++; + } + } + + // Make sure texcoords count match vertex count + if (vertexData[currentBuffer].vCounter != vertexData[currentBuffer].tcCounter) + { + int addTexCoords = vertexData[currentBuffer].vCounter - vertexData[currentBuffer].tcCounter; + + for (int i = 0; i < addTexCoords; i++) + { + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter] = 0.0f; + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter + 1] = 0.0f; + vertexData[currentBuffer].tcCounter++; + } + } + + // TODO: Make sure normals count match vertex count... if normals support is added in a future... :P + + // 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) + currentDepth += (1.0f/20000.0f); + + // Verify internal buffers limits + // NOTE: This check is combined with usage of rlCheckBufferLimit() + if ((vertexData[currentBuffer].vCounter) >= (MAX_BATCH_ELEMENTS*4 - 4)) + { + // WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(), + // we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call! + // If we have multiple matrix pushed, it will require "stackCounter" pops before launching the draw + for (int i = stackCounter; i >= 0; i--) rlPopMatrix(); + rlglDraw(); + } +} + +// Define one vertex (position) +// NOTE: Vertex position data is the basic information required for drawing +void rlVertex3f(float x, float y, float z) +{ + Vector3 vec = { x, y, z }; + + // Transform provided vector if required + if (useTransformMatrix) vec = Vector3Transform(vec, transformMatrix); + + // Verify that MAX_BATCH_ELEMENTS limit not reached + if (vertexData[currentBuffer].vCounter < (MAX_BATCH_ELEMENTS*4)) + { + vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter] = vec.x; + vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter + 1] = vec.y; + vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter + 2] = vec.z; + vertexData[currentBuffer].vCounter++; + + draws[drawsCounter - 1].vertexCount++; + } + else TraceLog(LOG_ERROR, "MAX_BATCH_ELEMENTS overflow"); +} + +// Define one vertex (position) +void rlVertex2f(float x, float y) +{ + rlVertex3f(x, y, currentDepth); +} + +// Define one vertex (position) +void rlVertex2i(int x, int y) +{ + rlVertex3f((float)x, (float)y, currentDepth); +} + +// Define one vertex (texture coordinate) +// NOTE: Texture coordinates are limited to QUADS only +void rlTexCoord2f(float x, float y) +{ + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter] = x; + vertexData[currentBuffer].texcoords[2*vertexData[currentBuffer].tcCounter + 1] = y; + vertexData[currentBuffer].tcCounter++; +} + +// Define one vertex (normal) +// NOTE: Normals limited to TRIANGLES only? +void rlNormal3f(float x, float y, float z) +{ + // TODO: Normals usage... +} + +// Define one vertex (color) +void rlColor4ub(byte x, byte y, byte z, byte w) +{ + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter] = x; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 1] = y; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 2] = z; + vertexData[currentBuffer].colors[4*vertexData[currentBuffer].cCounter + 3] = w; + vertexData[currentBuffer].cCounter++; +} + +// Define one vertex (color) +void rlColor4f(float r, float g, float b, float a) +{ + rlColor4ub((byte)(r*255), (byte)(g*255), (byte)(b*255), (byte)(a*255)); +} + +// Define one vertex (color) +void rlColor3f(float x, float y, float z) +{ + rlColor4ub((byte)(x*255), (byte)(y*255), (byte)(z*255), 255); +} + +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition - OpenGL equivalent functions (common to 1.1, 3.3+, ES2) +//---------------------------------------------------------------------------------- + +// Enable texture usage +void rlEnableTexture(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, id); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (draws[drawsCounter - 1].textureId != id) + { + if (draws[drawsCounter - 1].vertexCount > 0) + { + // Make sure current 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 (draws[drawsCounter - 1].mode == RL_LINES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? draws[drawsCounter - 1].vertexCount : draws[drawsCounter - 1].vertexCount%4); + else if (draws[drawsCounter - 1].mode == RL_TRIANGLES) draws[drawsCounter - 1].vertexAlignment = ((draws[drawsCounter - 1].vertexCount < 4)? 1 : (4 - (draws[drawsCounter - 1].vertexCount%4))); + + else draws[drawsCounter - 1].vertexAlignment = 0; + + if (rlCheckBufferLimit(draws[drawsCounter - 1].vertexAlignment)) rlglDraw(); + else + { + vertexData[currentBuffer].vCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].cCounter += draws[drawsCounter - 1].vertexAlignment; + vertexData[currentBuffer].tcCounter += draws[drawsCounter - 1].vertexAlignment; + + drawsCounter++; + } + } + + if (drawsCounter >= MAX_DRAWCALL_REGISTERED) rlglDraw(); + + draws[drawsCounter - 1].textureId = id; + draws[drawsCounter - 1].vertexCount = 0; + } +#endif +} + +// Disable texture usage +void rlDisableTexture(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +#else + // NOTE: If quads batch limit is reached, + // we force a draw call and next batch starts + if (vertexData[currentBuffer].vCounter >= (MAX_BATCH_ELEMENTS*4)) rlglDraw(); +#endif +} + +// Set texture parameters (wrap mode/filter mode) +void rlTextureParameters(unsigned int id, int param, int value) +{ + glBindTexture(GL_TEXTURE_2D, id); + + switch (param) + { + case RL_TEXTURE_WRAP_S: + case RL_TEXTURE_WRAP_T: + { + if (value == RL_WRAP_MIRROR_CLAMP) + { +#if !defined(GRAPHICS_API_OPENGL_11) + if (texMirrorClampSupported) glTexParameteri(GL_TEXTURE_2D, param, value); + else TraceLog(LOG_WARNING, "Clamp mirror wrap mode not supported"); +#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_ANISOTROPIC_FILTER: + { +#if !defined(GRAPHICS_API_OPENGL_11) + if (value <= maxAnisotropicLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + else if (maxAnisotropicLevel > 0.0f) + { + TraceLog(LOG_WARNING, "[TEX ID %i] Maximum anisotropic filter level supported is %iX", id, maxAnisotropicLevel); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + } + else TraceLog(LOG_WARNING, "Anisotropic filtering not supported"); +#endif + } break; + default: break; + } + + glBindTexture(GL_TEXTURE_2D, 0); +} + +// Enable rendering to texture (fbo) +void rlEnableRenderTexture(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindFramebuffer(GL_FRAMEBUFFER, id); + + //glDisable(GL_CULL_FACE); // Allow double side drawing for texture flipping + //glCullFace(GL_FRONT); +#endif +} + +// Disable rendering to texture +void rlDisableRenderTexture(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + //glEnable(GL_CULL_FACE); + //glCullFace(GL_BACK); +#endif +} + +// Enable depth test +void rlEnableDepthTest(void) { glEnable(GL_DEPTH_TEST); } + +// Disable depth test +void rlDisableDepthTest(void) { glDisable(GL_DEPTH_TEST); } + +// Enable backface culling +void rlEnableBackfaceCulling(void) { glEnable(GL_CULL_FACE); } + +// Disable backface culling +void rlDisableBackfaceCulling(void) { glDisable(GL_CULL_FACE); } + +// Enable scissor test +RLAPI void rlEnableScissorTest(void) { glEnable(GL_SCISSOR_TEST); } + +// Disable scissor test +RLAPI void rlDisableScissorTest(void) { glDisable(GL_SCISSOR_TEST); } + +// Scissor test +RLAPI 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 +} + +// 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 +} + +// Unload texture from GPU memory +void rlDeleteTextures(unsigned int id) +{ + if (id > 0) glDeleteTextures(1, &id); +} + +// Unload render texture from GPU memory +void rlDeleteRenderTextures(RenderTexture2D target) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id); + if (target.depth.id > 0) + { + if (target.depthTexture) glDeleteTextures(1, &target.depth.id); + else glDeleteRenderbuffers(1, &target.depth.id); + } + + if (target.id > 0) glDeleteFramebuffers(1, &target.id); + + TraceLog(LOG_INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id); +#endif +} + +// Unload shader from GPU memory +void rlDeleteShader(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (id != 0) glDeleteProgram(id); +#endif +} + +// Unload vertex data (VAO) from GPU memory +void rlDeleteVertexArrays(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vaoSupported) + { + if (id != 0) glDeleteVertexArrays(1, &id); + TraceLog(LOG_INFO, "[VAO ID %i] Unloaded model data from VRAM (GPU)", id); + } +#endif +} + +// Unload vertex data (VBO) from GPU memory +void rlDeleteBuffers(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (id != 0) + { + glDeleteBuffers(1, &id); + if (!vaoSupported) TraceLog(LOG_INFO, "[VBO ID %i] Unloaded model vertex data from VRAM (GPU)", id); + } +#endif +} + +// Clear color buffer with color +void rlClearColor(byte r, byte g, byte b, byte 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... +} + +// Update GPU buffer with new data +void rlUpdateBuffer(int bufferId, void *data, int dataSize) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ARRAY_BUFFER, bufferId); + glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, data); +#endif +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - rlgl Functions +//---------------------------------------------------------------------------------- + +// Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states +void rlglInit(int width, int height) +{ + // Check OpenGL information and capabilities + //------------------------------------------------------------------------------ + + // Print current OpenGL and GLSL version + TraceLog(LOG_INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); + TraceLog(LOG_INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); + TraceLog(LOG_INFO, "GPU: Version: %s", glGetString(GL_VERSION)); + TraceLog(LOG_INFO, "GPU: GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + + // NOTE: We can get a bunch of extra information about GPU capabilities (glGet*) + //int maxTexSize; + //glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); + //TraceLog(LOG_INFO, "GL_MAX_TEXTURE_SIZE: %i", maxTexSize); + + //GL_MAX_TEXTURE_IMAGE_UNITS + //GL_MAX_VIEWPORT_DIMS + + //int numAuxBuffers; + //glGetIntegerv(GL_AUX_BUFFERS, &numAuxBuffers); + //TraceLog(LOG_INFO, "GL_AUX_BUFFERS: %i", numAuxBuffers); + + //GLint numComp = 0; + //GLint format[32] = { 0 }; + //glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); + //glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, format); + //for (int i = 0; i < numComp; i++) TraceLog(LOG_INFO, "Supported compressed format: 0x%x", format[i]); + + // NOTE: We don't need that much data on screen... right now... + + // TODO: Automatize extensions loading using rlLoadExtensions() and GLAD + // Actually, when rlglInit() is called in InitWindow() in core.c, + // OpenGL required extensions have already been loaded (PLATFORM_DESKTOP) + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Get supported extensions list + GLint numExt = 0; + +#if defined(GRAPHICS_API_OPENGL_33) + // NOTE: On OpenGL 3.3 VAO and NPOT are supported by default + vaoSupported = true; + + // Multiple texture extensions supported by default + texNPOTSupported = true; + texFloatSupported = true; + texDepthSupported = true; + + // We get a list of available extensions and we check for some of them (compressed textures) + // NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that) + glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); + + // Allocate numExt strings pointers + const char **extList = RL_MALLOC(sizeof(const char *)*numExt); + + // Get extensions strings + for (int i = 0; i < numExt; i++) extList[i] = (const char *)glGetStringi(GL_EXTENSIONS, i); + +#elif defined(GRAPHICS_API_OPENGL_ES2) + // Allocate 512 strings pointers (2 KB) + const char **extList = RL_MALLOC(sizeof(const char *)*512); + + 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 len = strlen(extensions) + 1; + char *extensionsDup = (char *)RL_CALLOC(len, sizeof(char)); + strcpy(extensionsDup, extensions); + + extList[numExt] = extensionsDup; + + for (int i = 0; i < len; i++) + { + if (extensionsDup[i] == ' ') + { + extensionsDup[i] = '\0'; + + numExt++; + extList[numExt] = &extensionsDup[i + 1]; + } + } + + // NOTE: Duplicated string (extensionsDup) must be deallocated +#endif + + TraceLog(LOG_INFO, "Number of supported extensions: %i", numExt); + + // Show supported extensions + //for (int i = 0; i < numExt; i++) TraceLog(LOG_INFO, "Supported extension: %s", extList[i]); + + // Check required extensions + for (int i = 0; i < numExt; i++) + { +#if defined(GRAPHICS_API_OPENGL_ES2) + // 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)eglGetProcAddress("glGenVertexArraysOES"); + glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); + glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); + //glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); // NOTE: Fails in WebGL, omitted + + if ((glGenVertexArrays != NULL) && (glBindVertexArray != NULL) && (glDeleteVertexArrays != NULL)) vaoSupported = 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) texNPOTSupported = true; + + // Check texture float support + if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) texFloatSupported = true; + + // Check depth texture support + if ((strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBGL_depth_texture") == 0)) texDepthSupported = true; + + if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) maxDepthBits = 24; + if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) maxDepthBits = 32; +#endif + // DDS texture compression support + 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)) texCompDXTSupported = true; + + // ETC1 texture compression support + if ((strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_etc1") == 0)) texCompETC1Supported = true; + + // ETC2/EAC texture compression support + if (strcmp(extList[i], (const char *)"GL_ARB_ES3_compatibility") == 0) texCompETC2Supported = true; + + // PVR texture compression support + if (strcmp(extList[i], (const char *)"GL_IMG_texture_compression_pvrtc") == 0) texCompPVRTSupported = true; + + // ASTC texture compression support + if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) texCompASTCSupported = true; + + // Anisotropic texture filter support + if (strcmp(extList[i], (const char *)"GL_EXT_texture_filter_anisotropic") == 0) + { + texAnisoFilterSupported = true; + glGetFloatv(0x84FF, &maxAnisotropicLevel); // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + } + + // Clamp mirror wrap mode supported + if (strcmp(extList[i], (const char *)"GL_EXT_texture_mirror_clamp") == 0) texMirrorClampSupported = true; + + // Debug marker support + if (strcmp(extList[i], (const char *)"GL_EXT_debug_marker") == 0) debugMarkerSupported = true; + } + + // Free extensions pointers + RL_FREE(extList); + +#if defined(GRAPHICS_API_OPENGL_ES2) + RL_FREE(extensionsDup); // Duplicated string must be deallocated + + if (vaoSupported) TraceLog(LOG_INFO, "[EXTENSION] VAO extension detected, VAO functions initialized successfully"); + else TraceLog(LOG_WARNING, "[EXTENSION] VAO extension not found, VAO usage not supported"); + + if (texNPOTSupported) TraceLog(LOG_INFO, "[EXTENSION] NPOT textures extension detected, full NPOT textures supported"); + else TraceLog(LOG_WARNING, "[EXTENSION] NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); +#endif + + if (texCompDXTSupported) TraceLog(LOG_INFO, "[EXTENSION] DXT compressed textures supported"); + if (texCompETC1Supported) TraceLog(LOG_INFO, "[EXTENSION] ETC1 compressed textures supported"); + if (texCompETC2Supported) TraceLog(LOG_INFO, "[EXTENSION] ETC2/EAC compressed textures supported"); + if (texCompPVRTSupported) TraceLog(LOG_INFO, "[EXTENSION] PVRT compressed textures supported"); + if (texCompASTCSupported) TraceLog(LOG_INFO, "[EXTENSION] ASTC compressed textures supported"); + + if (texAnisoFilterSupported) TraceLog(LOG_INFO, "[EXTENSION] Anisotropic textures filtering supported (max: %.0fX)", maxAnisotropicLevel); + if (texMirrorClampSupported) TraceLog(LOG_INFO, "[EXTENSION] Mirror clamp wrap texture mode supported"); + + if (debugMarkerSupported) TraceLog(LOG_INFO, "[EXTENSION] Debug Marker supported"); + + // Initialize buffers, default shaders and default textures + //---------------------------------------------------------- + // Init default white texture + unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) + defaultTextureId = rlLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1); + + if (defaultTextureId != 0) TraceLog(LOG_INFO, "[TEX ID %i] Base white texture loaded successfully", defaultTextureId); + else TraceLog(LOG_WARNING, "Base white texture could not be loaded"); + + // Init default Shader (customized for GL 3.3 and ES2) + defaultShader = LoadShaderDefault(); + currentShader = defaultShader; + + // Init default vertex arrays buffers + LoadBuffersDefault(); + + // Init transformations matrix accumulator + transformMatrix = MatrixIdentity(); + + // Init draw calls tracking system + draws = (DrawCall *)RL_MALLOC(sizeof(DrawCall)*MAX_DRAWCALL_REGISTERED); + + for (int i = 0; i < MAX_DRAWCALL_REGISTERED; i++) + { + draws[i].mode = RL_QUADS; + draws[i].vertexCount = 0; + draws[i].vertexAlignment = 0; + //draws[i].vaoId = 0; + //draws[i].shaderId = 0; + draws[i].textureId = defaultTextureId; + //draws[i].projection = MatrixIdentity(); + //draws[i].modelview = MatrixIdentity(); + } + + drawsCounter = 1; + + // Init internal matrix stack (emulating OpenGL 1.1) + for (int i = 0; i < MAX_MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); + + // Init internal projection and modelview matrices + projection = MatrixIdentity(); + modelview = MatrixIdentity(); + currentMatrix = &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 + +#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 + + // 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) + + // Store screen size into global variables + framebufferWidth = width; + framebufferHeight = height; + + TraceLog(LOG_INFO, "OpenGL default states initialized successfully"); +} + +// Vertex Buffer Object deinitialization (memory free) +void rlglClose(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + UnloadShaderDefault(); // Unload default shader + UnloadBuffersDefault(); // Unload default buffers + glDeleteTextures(1, &defaultTextureId); // Unload default texture + + TraceLog(LOG_INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", defaultTextureId); + + RL_FREE(draws); +#endif +} + +// Update and draw internal buffers +void rlglDraw(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Only process data if we have data to process + if (vertexData[currentBuffer].vCounter > 0) + { + UpdateBuffersDefault(); + DrawBuffersDefault(); // NOTE: Stereo rendering is checked inside + } +#endif +} + +// Returns current OpenGL version +int rlGetVersion(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + return OPENGL_11; +#elif defined(GRAPHICS_API_OPENGL_21) + #if defined(__APPLE__) + return OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX + #else + return OPENGL_21; + #endif +#elif defined(GRAPHICS_API_OPENGL_33) + return OPENGL_33; +#elif defined(GRAPHICS_API_OPENGL_ES2) + return OPENGL_ES_20; +#endif +} + +// Check internal buffer overflow for a given number of vertex +bool rlCheckBufferLimit(int vCount) +{ + bool overflow = false; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((vertexData[currentBuffer].vCounter + vCount) >= (MAX_BATCH_ELEMENTS*4)) overflow = true; +#endif + return overflow; +} + +// Set debug marker +void rlSetDebugMarker(const char *text) +{ +#if defined(GRAPHICS_API_OPENGL_33) + if (debugMarkerSupported) glInsertEventMarkerEXT(0, text); +#endif +} + +// Load OpenGL extensions +// NOTE: External loader function could be passed as a pointer +void rlLoadExtensions(void *loader) +{ +#if defined(GRAPHICS_API_OPENGL_33) + // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions (and lower versions) + #if !defined(__APPLE__) + if (!gladLoadGLLoader((GLADloadproc)loader)) TraceLog(LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); + else TraceLog(LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); + + #if defined(GRAPHICS_API_OPENGL_21) + if (GLAD_GL_VERSION_2_1) TraceLog(LOG_INFO, "OpenGL 2.1 profile supported"); + #elif defined(GRAPHICS_API_OPENGL_33) + if (GLAD_GL_VERSION_3_3) TraceLog(LOG_INFO, "OpenGL 3.3 Core profile supported"); + else TraceLog(LOG_ERROR, "OpenGL 3.3 Core profile not supported"); + #endif + #endif + + // With GLAD, we can check if an extension is supported using the GLAD_GL_xxx booleans + //if (GLAD_GL_ARB_vertex_array_object) // Use GL_ARB_vertex_array_object +#endif +} + +// Get world coordinates from screen coordinates +Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view) +{ + Vector3 result = { 0.0f, 0.0f, 0.0f }; + + // Calculate unproject matrix (multiply view patrix by projection matrix) and invert it + Matrix matViewProj = MatrixMultiply(view, proj); + matViewProj = MatrixInvert(matViewProj); + + // Create quaternion from source point + Quaternion quat = { source.x, source.y, source.z, 1.0f }; + + // Multiply quat point by unproject matrix + quat = QuaternionTransform(quat, matViewProj); + + // Normalized world points in vectors + result.x = quat.x/quat.w; + result.y = quat.y/quat.w; + result.z = quat.z/quat.w; + + return result; +} + +// Convert image data to OpenGL texture (returns OpenGL valid Id) +unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount) +{ + glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding + + unsigned int id = 0; + + // Check texture format support by OpenGL 1.1 (compressed textures not supported) +#if defined(GRAPHICS_API_OPENGL_11) + if (format >= COMPRESSED_DXT1_RGB) + { + TraceLog(LOG_WARNING, "OpenGL 1.1 does not support GPU compressed texture formats"); + return id; + } +#else + if ((!texCompDXTSupported) && ((format == COMPRESSED_DXT1_RGB) || (format == COMPRESSED_DXT1_RGBA) || + (format == COMPRESSED_DXT3_RGBA) || (format == COMPRESSED_DXT5_RGBA))) + { + TraceLog(LOG_WARNING, "DXT compressed texture format not supported"); + return id; + } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((!texCompETC1Supported) && (format == COMPRESSED_ETC1_RGB)) + { + TraceLog(LOG_WARNING, "ETC1 compressed texture format not supported"); + return id; + } + + if ((!texCompETC2Supported) && ((format == COMPRESSED_ETC2_RGB) || (format == COMPRESSED_ETC2_EAC_RGBA))) + { + TraceLog(LOG_WARNING, "ETC2 compressed texture format not supported"); + return id; + } + + if ((!texCompPVRTSupported) && ((format == COMPRESSED_PVRT_RGB) || (format == COMPRESSED_PVRT_RGBA))) + { + TraceLog(LOG_WARNING, "PVRT compressed texture format not supported"); + return id; + } + + if ((!texCompASTCSupported) && ((format == COMPRESSED_ASTC_4x4_RGBA) || (format == COMPRESSED_ASTC_8x8_RGBA))) + { + TraceLog(LOG_WARNING, "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 + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + //glActiveTexture(GL_TEXTURE0); // If not defined, using GL_TEXTURE0 by default (shader texture) +#endif + + glBindTexture(GL_TEXTURE_2D, id); + + int mipWidth = width; + int mipHeight = height; + int mipOffset = 0; // Mipmap data offset + + TraceLog(LOG_DEBUG, "Load texture from data memory address: 0x%x", data); + + // Load the different mipmap levels + for (int i = 0; i < mipmapCount; i++) + { + unsigned int mipSize = GetPixelDataSize(mipWidth, mipHeight, format); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + TraceLog(LOG_DEBUG, "Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); + + if (glInternalFormat != -1) + { + if (format < COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, (unsigned char *)data + mipOffset); + #if !defined(GRAPHICS_API_OPENGL_11) + else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, (unsigned char *)data + mipOffset); + #endif + + #if defined(GRAPHICS_API_OPENGL_33) + if (format == UNCOMPRESSED_GRAYSCALE) + { + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + else if (format == 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; + + // 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 (texNPOTSupported) + { + 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(LOG_INFO, "[TEX ID %i] Texture created successfully (%ix%i - %i mipmaps)", id, width, height, mipmapCount); + else TraceLog(LOG_WARNING, "Texture could not be created"); + + return id; +} + +// Load depth texture/renderbuffer (to be attached to fbo) +// WARNING: OpenGL ES 2.0 requires GL_OES_depth_texture/WEBGL_depth_texture extensions +unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderBuffer) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int glInternalFormat = GL_DEPTH_COMPONENT16; + + if ((bits != 16) && (bits != 24) && (bits != 32)) bits = 16; + + if (bits == 24) + { +#if defined(GRAPHICS_API_OPENGL_33) + glInternalFormat = GL_DEPTH_COMPONENT24; +#elif defined(GRAPHICS_API_OPENGL_ES2) + if (maxDepthBits >= 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES; +#endif + } + + if (bits == 32) + { +#if defined(GRAPHICS_API_OPENGL_33) + glInternalFormat = GL_DEPTH_COMPONENT32; +#elif defined(GRAPHICS_API_OPENGL_ES2) + if (maxDepthBits == 32) glInternalFormat = GL_DEPTH_COMPONENT32_OES; +#endif + } + + if (!useRenderBuffer && texDepthSupported) + { + 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); + } + 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); + } +#endif + + return id; +} + +// Load texture cubemap +// NOTE: Cubemap data is expected to be 6 images in a single column, +// expected the following convention: +X, -X, +Y, -Y, +Z, -Z +unsigned int rlLoadTextureCubemap(void *data, int size, int format) +{ + unsigned int cubemapId = 0; + unsigned int dataSize = GetPixelDataSize(size, size, format); + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glGenTextures(1, &cubemapId); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapId); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + if (glInternalFormat != -1) + { + // Load cubemap faces + for (unsigned int i = 0; i < 6; i++) + { + if (format < COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize); +#if !defined(GRAPHICS_API_OPENGL_11) + else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, dataSize, (unsigned char *)data + i*dataSize); +#endif +#if defined(GRAPHICS_API_OPENGL_33) + if (format == 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 == 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 + } + } + + // Set cubemap texture sampling parameters + 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 + + return cubemapId; +} + +// 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 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 != -1) && (format < COMPRESSED_DXT1_RGB)) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, glFormat, glType, (unsigned char *)data); + } + else TraceLog(LOG_WARNING, "Texture format updating not supported"); +} + +// Get OpenGL internal formats and data type from raylib PixelFormat +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType) +{ + *glInternalFormat = -1; + *glFormat = -1; + *glType = -1; + + 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 UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + #if !defined(GRAPHICS_API_OPENGL_11) + case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + #endif + #elif defined(GRAPHICS_API_OPENGL_33) + case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; + case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; + case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + #endif + #if !defined(GRAPHICS_API_OPENGL_11) + case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; + case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + #endif + default: TraceLog(LOG_WARNING, "Texture format not supported"); break; + } +} + +// Unload texture from GPU memory +void rlUnloadTexture(unsigned int id) +{ + if (id > 0) glDeleteTextures(1, &id); +} + +// Load a texture to be used for rendering (fbo with default color and depth attachments) +// NOTE: If colorFormat or depthBits are no supported, no attachment is done +RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depthBits, bool useDepthTexture) +{ + RenderTexture2D target = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (useDepthTexture && texDepthSupported) target.depthTexture = true; + + // Create the framebuffer object + glGenFramebuffers(1, &target.id); + glBindFramebuffer(GL_FRAMEBUFFER, target.id); + + // Create fbo color texture attachment + //----------------------------------------------------------------------------------------------------- + if ((format != -1) && (format < COMPRESSED_DXT1_RGB)) + { + // WARNING: Some texture formats are not supported for fbo color attachment + target.texture.id = rlLoadTexture(NULL, width, height, format, 1); + target.texture.width = width; + target.texture.height = height; + target.texture.format = format; + target.texture.mipmaps = 1; + } + //----------------------------------------------------------------------------------------------------- + + // Create fbo depth renderbuffer/texture + //----------------------------------------------------------------------------------------------------- + if (depthBits > 0) + { + target.depth.id = rlLoadTextureDepth(width, height, depthBits, !useDepthTexture); + target.depth.width = width; + target.depth.height = height; + target.depth.format = 19; //DEPTH_COMPONENT_24BIT? + target.depth.mipmaps = 1; + } + //----------------------------------------------------------------------------------------------------- + + // Attach color texture and depth renderbuffer to FBO + //----------------------------------------------------------------------------------------------------- + rlRenderTextureAttach(target, target.texture.id, 0); // COLOR attachment + rlRenderTextureAttach(target, target.depth.id, 1); // DEPTH attachment + //----------------------------------------------------------------------------------------------------- + + // Check if fbo is complete with attachments (valid) + //----------------------------------------------------------------------------------------------------- + if (rlRenderTextureComplete(target)) TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); + //----------------------------------------------------------------------------------------------------- + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + + return target; +} + +// Attach color buffer texture to an fbo (unloads previous attachment) +// NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture +void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachType) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindFramebuffer(GL_FRAMEBUFFER, target.id); + + if (attachType == 0) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0); + else if (attachType == 1) + { + if (target.depthTexture) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, id, 0); + else glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, id); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif +} + +// Verify render texture is complete +bool rlRenderTextureComplete(RenderTexture target) +{ + bool result = false; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindFramebuffer(GL_FRAMEBUFFER, target.id); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + switch (status) + { + case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(LOG_WARNING, "Framebuffer is unsupported"); break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TraceLog(LOG_WARNING, "Framebuffer has incomplete attachment"); break; +#if defined(GRAPHICS_API_OPENGL_ES2) + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TraceLog(LOG_WARNING, "Framebuffer has incomplete dimensions"); break; +#endif + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TraceLog(LOG_WARNING, "Framebuffer has a missing attachment"); break; + default: break; + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + result = (status == GL_FRAMEBUFFER_COMPLETE); +#endif + + return result; +} + +// Generate mipmap data for selected texture +void rlGenerateMipmaps(Texture2D *texture) +{ + glBindTexture(GL_TEXTURE_2D, texture->id); + + // Check if texture is power-of-two (POT) + bool texIsPOT = false; + + if (((texture->width > 0) && ((texture->width & (texture->width - 1)) == 0)) && + ((texture->height > 0) && ((texture->height & (texture->height - 1)) == 0))) texIsPOT = true; + +#if defined(GRAPHICS_API_OPENGL_11) + if (texIsPOT) + { + // WARNING: Manual mipmap generation only works for RGBA 32bit textures! + if (texture->format == UNCOMPRESSED_R8G8B8A8) + { + // Retrieve texture data from VRAM + void *data = rlReadTexturePixels(*texture); + + // NOTE: data size is reallocated to fit mipmaps data + // NOTE: CPU mipmap generation only supports RGBA 32bit data + int mipmapCount = GenerateMipmaps(data, texture->width, texture->height); + + int size = texture->width*texture->height*4; + int offset = size; + + int mipWidth = texture->width/2; + int mipHeight = texture->height/2; + + // Load the mipmaps + for (int level = 1; level < mipmapCount; level++) + { + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data + offset); + + size = mipWidth*mipHeight*4; + offset += size; + + mipWidth /= 2; + mipHeight /= 2; + } + + texture->mipmaps = mipmapCount + 1; + RL_FREE(data); // Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data + + TraceLog(LOG_WARNING, "[TEX ID %i] Mipmaps [%i] generated manually on CPU side", texture->id, texture->mipmaps); + } + else TraceLog(LOG_WARNING, "[TEX ID %i] Mipmaps could not be generated for texture format", texture->id); + } +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((texIsPOT) || (texNPOTSupported)) + { + //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorythm: GL_FASTEST, GL_NICEST, GL_DONT_CARE + glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically + TraceLog(LOG_INFO, "[TEX ID %i] Mipmaps generated automatically", texture->id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps + + #define MIN(a,b) (((a)<(b))?(a):(b)) + #define MAX(a,b) (((a)>(b))?(a):(b)) + + texture->mipmaps = 1 + (int)floor(log(MAX(texture->width, texture->height))/log(2)); + } +#endif + else TraceLog(LOG_WARNING, "[TEX ID %i] Mipmaps can not be generated", texture->id); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +// Upload vertex data into a VAO (if supported) and VBO +void rlLoadMesh(Mesh *mesh, bool dynamic) +{ + if (mesh->vaoId > 0) + { + // Check if mesh has already been loaded in GPU + TraceLog(LOG_WARNING, "Trying to re-load an already loaded mesh"); + return; + } + + mesh->vaoId = 0; // Vertex Array Object + mesh->vboId[0] = 0; // Vertex positions VBO + mesh->vboId[1] = 0; // Vertex texcoords VBO + mesh->vboId[2] = 0; // Vertex normals VBO + mesh->vboId[3] = 0; // Vertex colors VBO + mesh->vboId[4] = 0; // Vertex tangents VBO + mesh->vboId[5] = 0; // Vertex texcoords2 VBO + mesh->vboId[6] = 0; // Vertex indices VBO + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; + + if (vaoSupported) + { + // Initialize Quads VAO (Buffer A) + glGenVertexArrays(1, &mesh->vaoId); + glBindVertexArray(mesh->vaoId); + } + + // NOTE: Attributes must be uploaded considering default locations points + + // Enable vertex attributes: position (shader-location = 0) + glGenBuffers(1, &mesh->vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->vertices, drawHint); + glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(0); + + // Enable vertex attributes: texcoords (shader-location = 1) + glGenBuffers(1, &mesh->vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords, drawHint); + glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(1); + + // Enable vertex attributes: normals (shader-location = 2) + if (mesh->normals != NULL) + { + glGenBuffers(1, &mesh->vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->normals, drawHint); + glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(2); + } + else + { + // Default color vertex attribute set to WHITE + glVertexAttrib3f(2, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(2); + } + + // Default color vertex attribute (shader-location = 3) + if (mesh->colors != NULL) + { + glGenBuffers(1, &mesh->vboId[3]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[3]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh->vertexCount, mesh->colors, drawHint); + glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(3); + } + else + { + // Default color vertex attribute set to WHITE + glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(3); + } + + // Default tangent vertex attribute (shader-location = 4) + if (mesh->tangents != NULL) + { + glGenBuffers(1, &mesh->vboId[4]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[4]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*mesh->vertexCount, mesh->tangents, drawHint); + glVertexAttribPointer(4, 4, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(4); + } + else + { + // Default tangents vertex attribute + glVertexAttrib4f(4, 0.0f, 0.0f, 0.0f, 0.0f); + glDisableVertexAttribArray(4); + } + + // Default texcoord2 vertex attribute (shader-location = 5) + if (mesh->texcoords2 != NULL) + { + glGenBuffers(1, &mesh->vboId[5]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[5]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords2, drawHint); + glVertexAttribPointer(5, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(5); + } + else + { + // Default texcoord2 vertex attribute + glVertexAttrib2f(5, 0.0f, 0.0f); + glDisableVertexAttribArray(5); + } + + if (mesh->indices != NULL) + { + glGenBuffers(1, &mesh->vboId[6]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->vboId[6]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*mesh->triangleCount*3, mesh->indices, drawHint); + } + + if (vaoSupported) + { + if (mesh->vaoId > 0) TraceLog(LOG_INFO, "[VAO ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId); + else TraceLog(LOG_WARNING, "Mesh could not be uploaded to VRAM (GPU)"); + } + else + { + TraceLog(LOG_INFO, "[VBOs] Mesh uploaded successfully to VRAM (GPU)"); + } +#endif +} + +// Load a new attributes buffer +unsigned int rlLoadAttribBuffer(unsigned int vaoId, int shaderLoc, void *buffer, int size, bool dynamic) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; + + if (vaoSupported) glBindVertexArray(vaoId); + + glGenBuffers(1, &id); + glBindBuffer(GL_ARRAY_BUFFER, id); + glBufferData(GL_ARRAY_BUFFER, size, buffer, drawHint); + glVertexAttribPointer(shaderLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(shaderLoc); + + if (vaoSupported) glBindVertexArray(0); +#endif + + return id; +} + +// Update vertex or index data on GPU (upload new data to one buffer) +void rlUpdateMesh(Mesh mesh, int buffer, int num) +{ + rlUpdateMeshAt(mesh, buffer, num, 0); +} + +// Update vertex or index data on GPU, at index +// WARNING: error checking is in place that will cause the data to not be +// updated if offset + size exceeds what the buffer can hold +void rlUpdateMeshAt(Mesh mesh, int buffer, int num, int index) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Activate mesh VAO + if (vaoSupported) glBindVertexArray(mesh.vaoId); + + switch (buffer) + { + case 0: // Update vertices (vertex position) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + if (index == 0 && num >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*num, mesh.vertices, GL_DYNAMIC_DRAW); + else if (index + num >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*3*index, sizeof(float)*3*num, mesh.vertices); + + } break; + case 1: // Update texcoords (vertex texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + if (index == 0 && num >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*num, mesh.texcoords, GL_DYNAMIC_DRAW); + else if (index + num >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*2*index, sizeof(float)*2*num, mesh.texcoords); + + } break; + case 2: // Update normals (vertex normals) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + if (index == 0 && num >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*num, mesh.normals, GL_DYNAMIC_DRAW); + else if (index + num >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*3*index, sizeof(float)*3*num, mesh.normals); + + } break; + case 3: // Update colors (vertex colors) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + if (index == 0 && num >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*num, mesh.colors, GL_DYNAMIC_DRAW); + else if (index + num >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*index, sizeof(unsigned char)*4*num, mesh.colors); + + } break; + case 4: // Update tangents (vertex tangents) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); + if (index == 0 && num >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*num, mesh.tangents, GL_DYNAMIC_DRAW); + else if (index + num >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*4*index, sizeof(float)*4*num, mesh.tangents); + } break; + case 5: // Update texcoords2 (vertex second texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); + if (index == 0 && num >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*num, mesh.texcoords2, GL_DYNAMIC_DRAW); + else if (index + num >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*2*index, sizeof(float)*2*num, mesh.texcoords2); + } break; + case 6: // Update indices (triangle index buffer) + { + // the * 3 is because each triangle has 3 indices + unsigned short *indices = mesh.indices; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vboId[6]); + if (index == 0 && num >= mesh.triangleCount) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(*indices)*num*3, indices, GL_DYNAMIC_DRAW); + else if (index + num >= mesh.triangleCount) + break; + else + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, sizeof(*indices)*index*3, sizeof(*indices)*num*3, indices); + } break; + default: break; + } + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); + + // Another option would be using buffer mapping... + //mesh.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // Now we can modify vertices + //glUnmapBuffer(GL_ARRAY_BUFFER); +#endif +} + +// Draw a 3d mesh with material and transform +void rlDrawMesh(Mesh mesh, Material material, Matrix transform) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, material.maps[MAP_DIFFUSE].texture.id); + + // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model + glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array + glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array + if (mesh.normals != NULL) glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array + if (mesh.colors != NULL) glEnableClientState(GL_COLOR_ARRAY); // Enable colors array + + glVertexPointer(3, GL_FLOAT, 0, mesh.vertices); // Pointer to vertex coords array + glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords); // Pointer to texture coords array + if (mesh.normals != NULL) glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array + if (mesh.colors != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array + + rlPushMatrix(); + rlMultMatrixf(MatrixToFloat(transform)); + rlColor4ub(material.maps[MAP_DIFFUSE].color.r, material.maps[MAP_DIFFUSE].color.g, material.maps[MAP_DIFFUSE].color.b, material.maps[MAP_DIFFUSE].color.a); + + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, mesh.indices); + else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + rlPopMatrix(); + + glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array + glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array + if (mesh.normals != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array + if (mesh.colors != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable colors array + + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Bind shader program + glUseProgram(material.shader.id); + + // Matrices and other values required by shader + //----------------------------------------------------- + // Calculate and send to shader model matrix (used by PBR shader) + if (material.shader.locs[LOC_MATRIX_MODEL] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_MODEL], transform); + + // Upload to shader material.colDiffuse + if (material.shader.locs[LOC_COLOR_DIFFUSE] != -1) + glUniform4f(material.shader.locs[LOC_COLOR_DIFFUSE], (float)material.maps[MAP_DIFFUSE].color.r/255.0f, + (float)material.maps[MAP_DIFFUSE].color.g/255.0f, + (float)material.maps[MAP_DIFFUSE].color.b/255.0f, + (float)material.maps[MAP_DIFFUSE].color.a/255.0f); + + // Upload to shader material.colSpecular (if available) + if (material.shader.locs[LOC_COLOR_SPECULAR] != -1) + glUniform4f(material.shader.locs[LOC_COLOR_SPECULAR], (float)material.maps[MAP_SPECULAR].color.r/255.0f, + (float)material.maps[MAP_SPECULAR].color.g/255.0f, + (float)material.maps[MAP_SPECULAR].color.b/255.0f, + (float)material.maps[MAP_SPECULAR].color.a/255.0f); + + if (material.shader.locs[LOC_MATRIX_VIEW] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_VIEW], modelview); + if (material.shader.locs[LOC_MATRIX_PROJECTION] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_PROJECTION], projection); + + // At this point the modelview matrix just contains the view matrix (camera) + // That's because BeginMode3D() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() + Matrix matView = modelview; // View matrix (camera) + Matrix matProjection = projection; // Projection matrix (perspective) + + // TODO: Consider possible transform matrices in the stack + // Is this the right order? or should we start with the first stored matrix instead of the last one? + //Matrix matStackTransform = MatrixIdentity(); + //for (int i = stackCounter; i > 0; i--) matStackTransform = MatrixMultiply(stack[i], matStackTransform); + + // Transform to camera-space coordinates + Matrix matModelView = MatrixMultiply(transform, MatrixMultiply(transformMatrix, matView)); + //----------------------------------------------------- + + // Bind active texture maps (if available) + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + { + if (material.maps[i].texture.id > 0) + { + glActiveTexture(GL_TEXTURE0 + i); + if ((i == MAP_IRRADIANCE) || (i == MAP_PREFILTER) || (i == MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, material.maps[i].texture.id); + else glBindTexture(GL_TEXTURE_2D, material.maps[i].texture.id); + + glUniform1i(material.shader.locs[LOC_MAP_DIFFUSE + i], i); + } + } + + // Bind vertex array objects (or VBOs) + if (vaoSupported) glBindVertexArray(mesh.vaoId); + else + { + // Bind mesh VBO data: vertex position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_POSITION]); + + // Bind mesh VBO data: vertex texcoords (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TEXCOORD01]); + + // Bind mesh VBO data: vertex normals (shader-location = 2, if available) + if (material.shader.locs[LOC_VERTEX_NORMAL] != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_NORMAL]); + } + + // Bind mesh VBO data: vertex colors (shader-location = 3, if available) + if (material.shader.locs[LOC_VERTEX_COLOR] != -1) + { + if (mesh.vboId[3] != 0) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_COLOR]); + } + else + { + // Set default value for unused attribute + // NOTE: Required when using default shader and no VAO support + glVertexAttrib4f(material.shader.locs[LOC_VERTEX_COLOR], 1.0f, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(material.shader.locs[LOC_VERTEX_COLOR]); + } + } + + // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) + if (material.shader.locs[LOC_VERTEX_TANGENT] != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TANGENT], 4, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TANGENT]); + } + + // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) + if (material.shader.locs[LOC_VERTEX_TEXCOORD02] != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TEXCOORD02], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TEXCOORD02]); + } + + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vboId[6]); + } + + int eyesCount = 1; +#if defined(SUPPORT_VR_SIMULATOR) + if (vrStereoRender) eyesCount = 2; +#endif + + for (int eye = 0; eye < eyesCount; eye++) + { + if (eyesCount == 1) modelview = matModelView; + #if defined(SUPPORT_VR_SIMULATOR) + else SetStereoView(eye, matProjection, matModelView); + #endif + + // Calculate model-view-projection matrix (MVP) + Matrix matMVP = MatrixMultiply(modelview, projection); // Transform to screen-space coordinates + + // Send combined model-view-projection matrix to shader + glUniformMatrix4fv(material.shader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); + + // Draw call! + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0); // Indexed vertices draw + else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + } + + // Unbind all binded texture maps + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + { + glActiveTexture(GL_TEXTURE0 + i); // Set shader active texture + if ((i == MAP_IRRADIANCE) || (i == MAP_PREFILTER) || (i == MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + else glBindTexture(GL_TEXTURE_2D, 0); // Unbind current active texture + } + + // Unind vertex array objects (or VBOs) + if (vaoSupported) glBindVertexArray(0); + else + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + // Unbind shader program + glUseProgram(0); + + // Restore projection/modelview matrices + // NOTE: In stereo rendering matrices are being modified to fit every eye + projection = matProjection; + modelview = matView; +#endif +} + +// Unload mesh data from CPU and GPU +void rlUnloadMesh(Mesh mesh) +{ + RL_FREE(mesh.vertices); + RL_FREE(mesh.texcoords); + RL_FREE(mesh.normals); + RL_FREE(mesh.colors); + RL_FREE(mesh.tangents); + RL_FREE(mesh.texcoords2); + RL_FREE(mesh.indices); + + RL_FREE(mesh.animVertices); + RL_FREE(mesh.animNormals); + RL_FREE(mesh.boneWeights); + RL_FREE(mesh.boneIds); + + rlDeleteBuffers(mesh.vboId[0]); // vertex + rlDeleteBuffers(mesh.vboId[1]); // texcoords + rlDeleteBuffers(mesh.vboId[2]); // normals + rlDeleteBuffers(mesh.vboId[3]); // colors + rlDeleteBuffers(mesh.vboId[4]); // tangents + rlDeleteBuffers(mesh.vboId[5]); // texcoords2 + rlDeleteBuffers(mesh.vboId[6]); // indices + + rlDeleteVertexArrays(mesh.vaoId); +} + +// 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*sizeof(unsigned char)*4); + + 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 +} + +// Read texture pixel data +void *rlReadTexturePixels(Texture2D texture) +{ + void *pixels = NULL; + +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + glBindTexture(GL_TEXTURE_2D, texture.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(texture.format, &glInternalFormat, &glFormat, &glType); + unsigned int size = GetPixelDataSize(texture.width, texture.height, texture.format); + + if ((glInternalFormat != -1) && (texture.format < COMPRESSED_DXT1_RGB)) + { + pixels = (unsigned char *)RL_MALLOC(size); + glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); + } + else TraceLog(LOG_WARNING, "Texture data retrieval not suported for pixel format"); + + glBindTexture(GL_TEXTURE_2D, 0); +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + // glGetTexImage() is not available on OpenGL ES 2.0 + // Texture2D 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... + RenderTexture2D fbo = rlLoadRenderTexture(texture.width, texture.height, UNCOMPRESSED_R8G8B8A8, 16, false); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo.id); + glBindTexture(GL_TEXTURE_2D, 0); + + // Attach our texture to FBO + // NOTE: Previoust attached texture is automatically detached + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.id, 0); + + // We read data as RGBA because FBO texture is configured as RGBA, despite binding another texture format + pixels = (unsigned char *)RL_MALLOC(GetPixelDataSize(texture.width, texture.height, UNCOMPRESSED_R8G8B8A8)); + glReadPixels(0, 0, texture.width, texture.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Re-attach internal FBO color texture before deleting it + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.texture.id, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Clean up temporal fbo + rlDeleteRenderTextures(fbo); +#endif + + return pixels; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Shaders Functions +// NOTE: Those functions are exposed directly to the user in raylib.h +//---------------------------------------------------------------------------------- + +// Get default internal texture (white texture) +Texture2D GetTextureDefault(void) +{ + Texture2D texture = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + texture.id = defaultTextureId; + texture.width = 1; + texture.height = 1; + texture.mipmaps = 1; + texture.format = UNCOMPRESSED_R8G8B8A8; +#endif + return texture; +} + +// Get default shader +Shader GetShaderDefault(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + return defaultShader; +#else + Shader shader = { 0 }; + return shader; +#endif +} + +// Load text data from file +// NOTE: text chars array should be freed manually +char *LoadText(const char *fileName) +{ + FILE *textFile = NULL; + char *text = NULL; + + if (fileName != NULL) + { + textFile = fopen(fileName,"rt"); + + if (textFile != NULL) + { + fseek(textFile, 0, SEEK_END); + int size = ftell(textFile); + fseek(textFile, 0, SEEK_SET); + + if (size > 0) + { + text = (char *)RL_MALLOC(sizeof(char)*(size + 1)); + int count = fread(text, sizeof(char), size, textFile); + text[count] = '\0'; + } + + fclose(textFile); + } + else TraceLog(LOG_WARNING, "[%s] Text file could not be opened", fileName); + } + + return text; +} + +// Load shader from files and bind default locations +// NOTE: If shader string is NULL, using default vertex/fragment shaders +Shader LoadShader(const char *vsFileName, const char *fsFileName) +{ + Shader shader = { 0 }; + + // NOTE: Shader.locs is allocated by LoadShaderCode() + + char *vShaderStr = NULL; + char *fShaderStr = NULL; + + if (vsFileName != NULL) vShaderStr = LoadText(vsFileName); + if (fsFileName != NULL) fShaderStr = LoadText(fsFileName); + + shader = LoadShaderCode(vShaderStr, fShaderStr); + + if (vShaderStr != NULL) RL_FREE(vShaderStr); + if (fShaderStr != NULL) RL_FREE(fShaderStr); + + return shader; +} + +// Load shader from code strings +// NOTE: If shader string is NULL, using default vertex/fragment shaders +Shader LoadShaderCode(const char *vsCode, const char *fsCode) +{ + Shader shader = { 0 }; + shader.locs = (int *)RL_CALLOC(MAX_SHADER_LOCATIONS, sizeof(int)); + + // NOTE: All locations must be reseted to -1 (no location) + for (int i = 0; i < MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int vertexShaderId = defaultVShaderId; + unsigned int fragmentShaderId = defaultFShaderId; + + if (vsCode != NULL) vertexShaderId = CompileShader(vsCode, GL_VERTEX_SHADER); + if (fsCode != NULL) fragmentShaderId = CompileShader(fsCode, GL_FRAGMENT_SHADER); + + if ((vertexShaderId == defaultVShaderId) && (fragmentShaderId == defaultFShaderId)) shader = defaultShader; + else + { + shader.id = LoadShaderProgram(vertexShaderId, fragmentShaderId); + + if (vertexShaderId != defaultVShaderId) glDeleteShader(vertexShaderId); + if (fragmentShaderId != defaultFShaderId) glDeleteShader(fragmentShaderId); + + if (shader.id == 0) + { + TraceLog(LOG_WARNING, "Custom shader could not be loaded"); + shader = defaultShader; + } + + // After shader loading, we TRY to set default location names + if (shader.id > 0) SetShaderDefaultLocations(&shader); + } + + // Get available shader uniforms + // NOTE: This information is useful for debug... + int uniformCount = -1; + + glGetProgramiv(shader.id, GL_ACTIVE_UNIFORMS, &uniformCount); + + for (int i = 0; i < uniformCount; i++) + { + int namelen = -1; + int num = -1; + char name[256]; // Assume no variable names longer than 256 + GLenum type = GL_ZERO; + + // Get the name of the uniforms + glGetActiveUniform(shader.id, i,sizeof(name) - 1, &namelen, &num, &type, name); + + name[namelen] = 0; + + // Get the location of the named uniform + unsigned int location = glGetUniformLocation(shader.id, name); + + TraceLog(LOG_DEBUG, "[SHDR ID %i] Active uniform [%s] set at location: %i", shader.id, name, location); + } +#endif + + return shader; +} + +// Unload shader from GPU memory (VRAM) +void UnloadShader(Shader shader) +{ + if (shader.id > 0) + { + rlDeleteShader(shader.id); + TraceLog(LOG_INFO, "[SHDR ID %i] Unloaded shader program data", shader.id); + } + + RL_FREE(shader.locs); +} + +// Begin custom shader mode +void BeginShaderMode(Shader shader) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (currentShader.id != shader.id) + { + rlglDraw(); + currentShader = shader; + } +#endif +} + +// End custom shader mode (returns to default shader) +void EndShaderMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + BeginShaderMode(defaultShader); +#endif +} + +// Get shader uniform location +int GetShaderLocation(Shader shader, const char *uniformName) +{ + int location = -1; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + location = glGetUniformLocation(shader.id, uniformName); + + if (location == -1) TraceLog(LOG_WARNING, "[SHDR ID %i][%s] Shader uniform could not be found", shader.id, uniformName); + else TraceLog(LOG_INFO, "[SHDR ID %i][%s] Shader uniform set at location: %i", shader.id, uniformName, location); +#endif + return location; +} + +// Set shader uniform value +void SetShaderValue(Shader shader, int uniformLoc, const void *value, int uniformType) +{ + SetShaderValueV(shader, uniformLoc, value, uniformType, 1); +} + +// Set shader uniform value vector +void SetShaderValueV(Shader shader, int uniformLoc, const void *value, int uniformType, int count) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(shader.id); + + switch (uniformType) + { + case UNIFORM_FLOAT: glUniform1fv(uniformLoc, count, (float *)value); break; + case UNIFORM_VEC2: glUniform2fv(uniformLoc, count, (float *)value); break; + case UNIFORM_VEC3: glUniform3fv(uniformLoc, count, (float *)value); break; + case UNIFORM_VEC4: glUniform4fv(uniformLoc, count, (float *)value); break; + case UNIFORM_INT: glUniform1iv(uniformLoc, count, (int *)value); break; + case UNIFORM_IVEC2: glUniform2iv(uniformLoc, count, (int *)value); break; + case UNIFORM_IVEC3: glUniform3iv(uniformLoc, count, (int *)value); break; + case UNIFORM_IVEC4: glUniform4iv(uniformLoc, count, (int *)value); break; + case UNIFORM_SAMPLER2D: glUniform1iv(uniformLoc, count, (int *)value); break; + default: TraceLog(LOG_WARNING, "Shader uniform could not be set data type not recognized"); + } + + //glUseProgram(0); // Avoid reseting current shader program, in case other uniforms are set +#endif +} + + +// Set shader uniform value (matrix 4x4) +void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(shader.id); + + glUniformMatrix4fv(uniformLoc, 1, false, MatrixToFloat(mat)); + + //glUseProgram(0); +#endif +} + +// Set shader uniform value for texture +void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D texture) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(shader.id); + + glUniform1i(uniformLoc, texture.id); + + //glUseProgram(0); +#endif +} + +// Set a custom projection matrix (replaces internal projection matrix) +void SetMatrixProjection(Matrix proj) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + projection = proj; +#endif +} + +// Return internal projection matrix +Matrix GetMatrixProjection(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 projection; +#endif +# +} + +// Set a custom modelview matrix (replaces internal modelview matrix) +void SetMatrixModelview(Matrix view) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + modelview = view; +#endif +} + +// Return internal modelview matrix +Matrix GetMatrixModelview(void) +{ + Matrix matrix = MatrixIdentity(); +#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 = modelview; +#endif + return matrix; +} + +// Generate cubemap texture from HDR texture +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTextureCubemap(Shader shader, Texture2D map, int size) +{ + Texture2D cubemap = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader + // Other locations should be setup externally in shader before calling the function + + // Set up depth face culling and cubemap seamless + glDisable(GL_CULL_FACE); +#if defined(GRAPHICS_API_OPENGL_33) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Flag not supported on OpenGL ES 2.0 +#endif + + // Setup framebuffer + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); +#if defined(GRAPHICS_API_OPENGL_33) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size); +#endif + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + // Set up cubemap to render and attach to framebuffer + // NOTE: Faces are stored as 32 bit floating point values + glGenTextures(1, &cubemap.id); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + for (unsigned int i = 0; i < 6; i++) + { +#if defined(GRAPHICS_API_OPENGL_33) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, size, size, 0, GL_RGB, GL_FLOAT, NULL); +#elif defined(GRAPHICS_API_OPENGL_ES2) + if (texFloatSupported) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); +#endif + } + + 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 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Create projection and different views for each face + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); + Matrix fboViews[6] = { + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }) + }; + + // Convert HDR equirectangular environment map to cubemap equivalent + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, map.id); + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_PROJECTION], fboProjection); + + // Note: don't forget to configure the viewport to the capture dimensions + glViewport(0, 0, size, size); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + for (int i = 0; i < 6; i++) + { + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap.id, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawCube(); + } + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, framebufferWidth, framebufferHeight); + //glEnable(GL_CULL_FACE); + + // NOTE: Texture2D is a GL_TEXTURE_CUBE_MAP, not a GL_TEXTURE_2D! + cubemap.width = size; + cubemap.height = size; + cubemap.mipmaps = 1; + cubemap.format = UNCOMPRESSED_R32G32B32; +#endif + return cubemap; +} + +// Generate irradiance texture using cubemap data +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size) +{ + Texture2D irradiance = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader + // Other locations should be setup externally in shader before calling the function + + // Setup framebuffer + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + // Create an irradiance cubemap, and re-scale capture FBO to irradiance scale + glGenTextures(1, &irradiance.id); + glBindTexture(GL_TEXTURE_CUBE_MAP, irradiance.id); + for (unsigned int i = 0; i < 6; i++) + { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + } + + 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); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Create projection (transposed) and different views for each face + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); + Matrix fboViews[6] = { + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }) + }; + + // Solve diffuse integral by convolution to create an irradiance cubemap + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_PROJECTION], fboProjection); + + // Note: don't forget to configure the viewport to the capture dimensions + glViewport(0, 0, size, size); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + for (int i = 0; i < 6; i++) + { + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradiance.id, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawCube(); + } + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, framebufferWidth, framebufferHeight); + + irradiance.width = size; + irradiance.height = size; + irradiance.mipmaps = 1; + //irradiance.format = UNCOMPRESSED_R16G16B16; +#endif + return irradiance; +} + +// Generate prefilter texture using cubemap data +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) +{ + Texture2D prefilter = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader + // Other locations should be setup externally in shader before calling the function + // TODO: Locations should be taken out of this function... too shader dependant... + int roughnessLoc = GetShaderLocation(shader, "roughness"); + + // Setup framebuffer + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + // Create a prefiltered HDR environment map + glGenTextures(1, &prefilter.id); + glBindTexture(GL_TEXTURE_CUBE_MAP, prefilter.id); + for (unsigned int i = 0; i < 6; i++) + { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + } + + 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); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Generate mipmaps for the prefiltered HDR texture + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + + // Create projection (transposed) and different views for each face + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); + Matrix fboViews[6] = { + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }) + }; + + // Prefilter HDR and store data into mipmap levels + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_PROJECTION], fboProjection); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + #define MAX_MIPMAP_LEVELS 5 // Max number of prefilter texture mipmaps + + for (int mip = 0; mip < MAX_MIPMAP_LEVELS; mip++) + { + // Resize framebuffer according to mip-level size. + unsigned int mipWidth = size*(int)powf(0.5f, (float)mip); + unsigned int mipHeight = size*(int)powf(0.5f, (float)mip); + + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight); + glViewport(0, 0, mipWidth, mipHeight); + + float roughness = (float)mip/(float)(MAX_MIPMAP_LEVELS - 1); + glUniform1f(roughnessLoc, roughness); + + for (int i = 0; i < 6; i++) + { + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilter.id, mip); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawCube(); + } + } + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, framebufferWidth, framebufferHeight); + + prefilter.width = size; + prefilter.height = size; + //prefilter.mipmaps = 1 + (int)floor(log(size)/log(2)); + //prefilter.format = UNCOMPRESSED_R16G16B16; +#endif + return prefilter; +} + +// Generate BRDF texture using cubemap data +// NOTE: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +// TODO: Review implementation: https://github.com/HectorMF/BRDFGenerator +Texture2D GenTextureBRDF(Shader shader, int size) +{ + Texture2D brdf = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Generate BRDF convolution texture + glGenTextures(1, &brdf.id); + glBindTexture(GL_TEXTURE_2D, brdf.id); +#if defined(GRAPHICS_API_OPENGL_33) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, size, size, 0, GL_RGB, GL_FLOAT, NULL); +#elif defined(GRAPHICS_API_OPENGL_ES2) + if (texFloatSupported) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); +#endif + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Render BRDF LUT into a quad using FBO + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); +#if defined(GRAPHICS_API_OPENGL_33) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size); +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdf.id, 0); + + glViewport(0, 0, size, size); + glUseProgram(shader.id); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawQuad(); + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Unload framebuffer but keep color texture + glDeleteRenderbuffers(1, &rbo); + glDeleteFramebuffers(1, &fbo); + + // Reset viewport dimensions to default + glViewport(0, 0, framebufferWidth, framebufferHeight); + + brdf.width = size; + brdf.height = size; + brdf.mipmaps = 1; + brdf.format = UNCOMPRESSED_R32G32B32; +#endif + return brdf; +} + +// Begin blending mode (alpha, additive, multiplied) +// NOTE: Only 3 blending modes supported, default blend mode is alpha +void BeginBlendMode(int mode) +{ + if ((blendMode != mode) && (mode < 3)) + { + rlglDraw(); + + switch (mode) + { + case BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; + case BLEND_ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; // Alternative: glBlendFunc(GL_ONE, GL_ONE); + case BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break; + default: break; + } + + blendMode = mode; + } +} + +// End blending mode (reset to default: alpha blending) +void EndBlendMode(void) +{ + BeginBlendMode(BLEND_ALPHA); +} + +#if defined(SUPPORT_VR_SIMULATOR) +// Init VR simulator for selected device parameters +// NOTE: It modifies the global variable: stereoFbo +void InitVrSimulator(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Initialize framebuffer and textures for stereo rendering + // NOTE: Screen size should match HMD aspect ratio + stereoFbo = rlLoadRenderTexture(framebufferWidth, framebufferHeight, UNCOMPRESSED_R8G8B8A8, 24, false); + + vrSimulatorReady = true; +#else + TraceLog(LOG_WARNING, "VR Simulator not supported on OpenGL 1.1"); +#endif +} + +// Update VR tracking (position and orientation) and camera +// NOTE: Camera (position, target, up) gets update with head tracking information +void UpdateVrTracking(Camera *camera) +{ + // TODO: Simulate 1st person camera system +} + +// Close VR simulator for current device +void CloseVrSimulator(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vrSimulatorReady) rlDeleteRenderTextures(stereoFbo); // Unload stereo framebuffer and texture +#endif +} + +// Set stereo rendering configuration parameters +void SetVrConfiguration(VrDeviceInfo hmd, Shader distortion) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Reset vrConfig for a new values assignment + memset(&vrConfig, 0, sizeof(vrConfig)); + + // Assign distortion shader + vrConfig.distortionShader = distortion; + + // Compute aspect ratio + float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; + + // Compute lens parameters + float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; + float leftLensCenter[2] = { 0.25f + lensShift, 0.5f }; + float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; + float leftScreenCenter[2] = { 0.25f, 0.5f }; + float rightScreenCenter[2] = { 0.75f, 0.5f }; + + // Compute distortion scale parameters + // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] + float lensRadius = (float)fabs(-1.0f - 4.0f*lensShift); + float lensRadiusSq = lensRadius*lensRadius; + float distortionScale = hmd.lensDistortionValues[0] + + hmd.lensDistortionValues[1]*lensRadiusSq + + hmd.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq + + hmd.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; + + TraceLog(LOG_DEBUG, "VR: Distortion Scale: %f", distortionScale); + + float normScreenWidth = 0.5f; + float normScreenHeight = 1.0f; + float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; + float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; + + TraceLog(LOG_DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); + + // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance) + // ...but with lens distortion it is increased (see Oculus SDK Documentation) + //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance); // Really need distortionScale? + float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance); + + // Compute camera projection matrices + float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] + Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); + vrConfig.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); + vrConfig.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); + + // Compute camera transformation matrices + // NOTE: Camera movement might seem more natural if we model the head. + // Our axis of rotation is the base of our head, so we might want to add + // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. + vrConfig.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + + // Compute eyes Viewports + vrConfig.eyeViewportRight[2] = hmd.hResolution/2; + vrConfig.eyeViewportRight[3] = hmd.vResolution; + + vrConfig.eyeViewportLeft[0] = hmd.hResolution/2; + vrConfig.eyeViewportLeft[1] = 0; + vrConfig.eyeViewportLeft[2] = hmd.hResolution/2; + vrConfig.eyeViewportLeft[3] = hmd.vResolution; + + if (vrConfig.distortionShader.id > 0) + { + // Update distortion shader with lens and distortion-scale parameters + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftLensCenter"), leftLensCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightLensCenter"), rightLensCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftScreenCenter"), leftScreenCenter, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightScreenCenter"), rightScreenCenter, UNIFORM_VEC2); + + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scale"), scale, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scaleIn"), scaleIn, UNIFORM_VEC2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "hmdWarpParam"), hmd.lensDistortionValues, UNIFORM_VEC4); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, UNIFORM_VEC4); + } +#endif +} + +// Detect if VR simulator is running +bool IsVrSimulatorReady(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + return vrSimulatorReady; +#else + return false; +#endif +} + +// Enable/Disable VR experience (device or simulator) +void ToggleVrMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + vrSimulatorReady = !vrSimulatorReady; + + if (!vrSimulatorReady) + { + vrStereoRender = false; + + // Reset viewport and default projection-modelview matrices + rlViewport(0, 0, framebufferWidth, framebufferHeight); + projection = MatrixOrtho(0.0, framebufferWidth, framebufferHeight, 0.0, 0.0, 1.0); + modelview = MatrixIdentity(); + } + else vrStereoRender = true; +#endif +} + +// Begin VR drawing configuration +void BeginVrDrawing(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vrSimulatorReady) + { + + rlEnableRenderTexture(stereoFbo.id); // Setup framebuffer for stereo rendering + //glEnable(GL_FRAMEBUFFER_SRGB); // Enable SRGB framebuffer (only if required) + + //glViewport(0, 0, buffer.width, buffer.height); // Useful if rendering to separate framebuffers (every eye) + rlClearScreenBuffers(); // Clear current framebuffer + + vrStereoRender = true; + } +#endif +} + +// End VR drawing process (and desktop mirror) +void EndVrDrawing(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vrSimulatorReady) + { + vrStereoRender = false; // Disable stereo render + + rlDisableRenderTexture(); // Unbind current framebuffer + + rlClearScreenBuffers(); // Clear current framebuffer + + // Set viewport to default framebuffer size (screen size) + rlViewport(0, 0, framebufferWidth, framebufferHeight); + + // Let rlgl reconfigure internal matrices + rlMatrixMode(RL_PROJECTION); // Enable internal projection matrix + rlLoadIdentity(); // Reset internal projection matrix + rlOrtho(0.0, framebufferWidth, framebufferHeight, 0.0, 0.0, 1.0); // Recalculate internal projection matrix + rlMatrixMode(RL_MODELVIEW); // Enable internal modelview matrix + rlLoadIdentity(); // Reset internal modelview matrix + + // Draw RenderTexture (stereoFbo) using distortion shader if available + if (vrConfig.distortionShader.id > 0) currentShader = vrConfig.distortionShader; + else currentShader = GetShaderDefault(); + + rlEnableTexture(stereoFbo.texture.id); + + rlPushMatrix(); + rlBegin(RL_QUADS); + rlColor4ub(255, 255, 255, 255); + rlNormal3f(0.0f, 0.0f, 1.0f); + + // Bottom-left corner for texture and quad + rlTexCoord2f(0.0f, 1.0f); + rlVertex2f(0.0f, 0.0f); + + // Bottom-right corner for texture and quad + rlTexCoord2f(0.0f, 0.0f); + rlVertex2f(0.0f, (float)stereoFbo.texture.height); + + // Top-right corner for texture and quad + rlTexCoord2f(1.0f, 0.0f); + rlVertex2f( (float)stereoFbo.texture.width, (float)stereoFbo.texture.height); + + // Top-left corner for texture and quad + rlTexCoord2f(1.0f, 1.0f); + rlVertex2f( (float)stereoFbo.texture.width, 0.0f); + rlEnd(); + rlPopMatrix(); + + rlDisableTexture(); + + // Update and draw render texture fbo with distortion to backbuffer + UpdateBuffersDefault(); + DrawBuffersDefault(); + + // Restore defaultShader + currentShader = defaultShader; + + // Reset viewport and default projection-modelview matrices + rlViewport(0, 0, framebufferWidth, framebufferHeight); + projection = MatrixOrtho(0.0, framebufferWidth, framebufferHeight, 0.0, 0.0, 1.0); + modelview = MatrixIdentity(); + + rlDisableDepthTest(); + } +#endif +} +#endif // SUPPORT_VR_SIMULATOR + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Compile custom shader and return shader id +static unsigned int CompileShader(const char *shaderStr, int type) +{ + unsigned int shader = glCreateShader(type); + glShaderSource(shader, 1, &shaderStr, NULL); + + GLint success = 0; + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (success != GL_TRUE) + { + TraceLog(LOG_WARNING, "[SHDR ID %i] Failed to compile shader...", shader); + int maxLength = 0; + int length; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + +#if defined(_MSC_VER) + char *log = RL_MALLOC(maxLength); +#else + char log[maxLength]; +#endif + glGetShaderInfoLog(shader, maxLength, &length, log); + + TraceLog(LOG_INFO, "%s", log); + +#if defined(_MSC_VER) + RL_FREE(log); +#endif + } + else TraceLog(LOG_INFO, "[SHDR ID %i] Shader compiled successfully", shader); + + return shader; +} + +// Load custom shader strings and return program id +static unsigned int LoadShaderProgram(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 binded before linking + glBindAttribLocation(program, 0, DEFAULT_ATTRIB_POSITION_NAME); + glBindAttribLocation(program, 1, DEFAULT_ATTRIB_TEXCOORD_NAME); + glBindAttribLocation(program, 2, DEFAULT_ATTRIB_NORMAL_NAME); + glBindAttribLocation(program, 3, DEFAULT_ATTRIB_COLOR_NAME); + glBindAttribLocation(program, 4, DEFAULT_ATTRIB_TANGENT_NAME); + glBindAttribLocation(program, 5, DEFAULT_ATTRIB_TEXCOORD2_NAME); + + // 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(LOG_WARNING, "[SHDR ID %i] Failed to link shader program...", program); + + int maxLength = 0; + int length; + + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + +#if defined(_MSC_VER) + char *log = RL_MALLOC(maxLength); +#else + char log[maxLength]; +#endif + glGetProgramInfoLog(program, maxLength, &length, log); + + TraceLog(LOG_INFO, "%s", log); + +#if defined(_MSC_VER) + RL_FREE(log); +#endif + glDeleteProgram(program); + + program = 0; + } + else TraceLog(LOG_INFO, "[SHDR ID %i] Shader program loaded successfully", program); +#endif + return program; +} + + +// Load default shader (just vertex positioning and texture coloring) +// NOTE: This shader program is used for internal buffers +static Shader LoadShaderDefault(void) +{ + Shader shader = { 0 }; + shader.locs = (int *)RL_CALLOC(MAX_SHADER_LOCATIONS, sizeof(int)); + + // NOTE: All locations must be reseted to -1 (no location) + for (int i = 0; i < MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; + + // Vertex shader directly defined, no external file required + const char *defaultVShaderStr = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" +#elif defined(GRAPHICS_API_OPENGL_ES2) + "#version 100 \n" +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + "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 + "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 *defaultFShaderStr = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" +#elif defined(GRAPHICS_API_OPENGL_ES2) + "#version 100 \n" + "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec2 fragTexCoord; \n" + "in vec4 fragColor; \n" + "out vec4 finalColor; \n" +#endif + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" +#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0 + " gl_FragColor = texelColor*colDiffuse*fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + " vec4 texelColor = texture(texture0, fragTexCoord); \n" + " finalColor = texelColor*colDiffuse*fragColor; \n" +#endif + "} \n"; + + // NOTE: Compiled vertex/fragment shaders are kept for re-use + defaultVShaderId = CompileShader(defaultVShaderStr, GL_VERTEX_SHADER); // Compile default vertex shader + defaultFShaderId = CompileShader(defaultFShaderStr, GL_FRAGMENT_SHADER); // Compile default fragment shader + + shader.id = LoadShaderProgram(defaultVShaderId, defaultFShaderId); + + if (shader.id > 0) + { + TraceLog(LOG_INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); + + // Set default shader locations: attributes locations + shader.locs[LOC_VERTEX_POSITION] = glGetAttribLocation(shader.id, "vertexPosition"); + shader.locs[LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(shader.id, "vertexTexCoord"); + shader.locs[LOC_VERTEX_COLOR] = glGetAttribLocation(shader.id, "vertexColor"); + + // Set default shader locations: uniform locations + shader.locs[LOC_MATRIX_MVP] = glGetUniformLocation(shader.id, "mvp"); + shader.locs[LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader.id, "colDiffuse"); + shader.locs[LOC_MAP_DIFFUSE] = glGetUniformLocation(shader.id, "texture0"); + + // NOTE: We could also use below function but in case DEFAULT_ATTRIB_* points are + // changed for external custom shaders, we just use direct bindings above + //SetShaderDefaultLocations(&shader); + } + else TraceLog(LOG_WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); + + return shader; +} + +// Get location handlers to for shader attributes and uniforms +// NOTE: If any location is not found, loc point becomes -1 +static void SetShaderDefaultLocations(Shader *shader) +{ + // NOTE: Default shader attrib locations have been fixed before linking: + // vertex position location = 0 + // vertex texcoord location = 1 + // vertex normal location = 2 + // vertex color location = 3 + // vertex tangent location = 4 + // vertex texcoord2 location = 5 + + // Get handles to GLSL input attibute locations + shader->locs[LOC_VERTEX_POSITION] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_POSITION_NAME); + shader->locs[LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD_NAME); + shader->locs[LOC_VERTEX_TEXCOORD02] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD2_NAME); + shader->locs[LOC_VERTEX_NORMAL] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_NORMAL_NAME); + shader->locs[LOC_VERTEX_TANGENT] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TANGENT_NAME); + shader->locs[LOC_VERTEX_COLOR] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_COLOR_NAME); + + // Get handles to GLSL uniform locations (vertex shader) + shader->locs[LOC_MATRIX_MVP] = glGetUniformLocation(shader->id, "mvp"); + shader->locs[LOC_MATRIX_PROJECTION] = glGetUniformLocation(shader->id, "projection"); + shader->locs[LOC_MATRIX_VIEW] = glGetUniformLocation(shader->id, "view"); + + // Get handles to GLSL uniform locations (fragment shader) + shader->locs[LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader->id, "colDiffuse"); + shader->locs[LOC_MAP_DIFFUSE] = glGetUniformLocation(shader->id, "texture0"); + shader->locs[LOC_MAP_SPECULAR] = glGetUniformLocation(shader->id, "texture1"); + shader->locs[LOC_MAP_NORMAL] = glGetUniformLocation(shader->id, "texture2"); +} + +// Unload default shader +static void UnloadShaderDefault(void) +{ + glUseProgram(0); + + glDetachShader(defaultShader.id, defaultVShaderId); + glDetachShader(defaultShader.id, defaultFShaderId); + glDeleteShader(defaultVShaderId); + glDeleteShader(defaultFShaderId); + + glDeleteProgram(defaultShader.id); +} + +// Load default internal buffers +static void LoadBuffersDefault(void) +{ + // Initialize CPU (RAM) arrays (vertex position, texcoord, color data and indexes) + //-------------------------------------------------------------------------------------------- + for (int i = 0; i < MAX_BATCH_BUFFERING; i++) + { + vertexData[i].vertices = (float *)RL_MALLOC(sizeof(float)*3*4*MAX_BATCH_ELEMENTS); // 3 float by vertex, 4 vertex by quad + vertexData[i].texcoords = (float *)RL_MALLOC(sizeof(float)*2*4*MAX_BATCH_ELEMENTS); // 2 float by texcoord, 4 texcoord by quad + vertexData[i].colors = (unsigned char *)RL_MALLOC(sizeof(unsigned char)*4*4*MAX_BATCH_ELEMENTS); // 4 float by color, 4 colors by quad +#if defined(GRAPHICS_API_OPENGL_33) + vertexData[i].indices = (unsigned int *)RL_MALLOC(sizeof(unsigned int)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) +#elif defined(GRAPHICS_API_OPENGL_ES2) + vertexData[i].indices = (unsigned short *)RL_MALLOC(sizeof(unsigned short)*6*MAX_BATCH_ELEMENTS); // 6 int by quad (indices) +#endif + + for (int j = 0; j < (3*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].vertices[j] = 0.0f; + for (int j = 0; j < (2*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].texcoords[j] = 0.0f; + for (int j = 0; j < (4*4*MAX_BATCH_ELEMENTS); j++) vertexData[i].colors[j] = 0; + + int k = 0; + + // Indices can be initialized right now + for (int j = 0; j < (6*MAX_BATCH_ELEMENTS); j += 6) + { + vertexData[i].indices[j] = 4*k; + vertexData[i].indices[j + 1] = 4*k + 1; + vertexData[i].indices[j + 2] = 4*k + 2; + vertexData[i].indices[j + 3] = 4*k; + vertexData[i].indices[j + 4] = 4*k + 2; + vertexData[i].indices[j + 5] = 4*k + 3; + + k++; + } + + vertexData[i].vCounter = 0; + vertexData[i].tcCounter = 0; + vertexData[i].cCounter = 0; + } + + TraceLog(LOG_INFO, "Internal buffers initialized successfully (CPU)"); + //-------------------------------------------------------------------------------------------- + + // Upload to GPU (VRAM) vertex data and initialize VAOs/VBOs + //-------------------------------------------------------------------------------------------- + for (int i = 0; i < MAX_BATCH_BUFFERING; i++) + { + if (vaoSupported) + { + // Initialize Quads VAO + glGenVertexArrays(1, &vertexData[i].vaoId); + glBindVertexArray(vertexData[i].vaoId); + } + + // Quads - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &vertexData[i].vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[i].vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_BATCH_ELEMENTS, vertexData[i].vertices, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + + // Vertex texcoord buffer (shader-location = 1) + glGenBuffers(1, &vertexData[i].vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[i].vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_BATCH_ELEMENTS, vertexData[i].texcoords, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &vertexData[i].vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, vertexData[i].vboId[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_BATCH_ELEMENTS, vertexData[i].colors, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + // Fill index buffer + glGenBuffers(1, &vertexData[i].vboId[3]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexData[i].vboId[3]); +#if defined(GRAPHICS_API_OPENGL_33) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_BATCH_ELEMENTS, vertexData[i].indices, GL_STATIC_DRAW); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_BATCH_ELEMENTS, vertexData[i].indices, GL_STATIC_DRAW); +#endif + } + + TraceLog(LOG_INFO, "Internal buffers uploaded successfully (GPU)"); + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); + //-------------------------------------------------------------------------------------------- +} + +// Update default internal buffers (VAOs/VBOs) with vertex array data +// NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) +// TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) +static void UpdateBuffersDefault(void) +{ + // Update vertex buffers data + if (vertexData[currentBuffer].vCounter > 0) + { + // Activate elements VAO + if (vaoSupported) glBindVertexArray(vertexData[currentBuffer].vaoId); + + // Vertex positions buffer + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].vertices); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].vertices, GL_DYNAMIC_DRAW); // Update all buffer + + // Texture coordinates buffer + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[1]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].texcoords); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_BATCH_ELEMENTS, vertexData[currentBuffer].texcoords, GL_DYNAMIC_DRAW); // Update all buffer + + // Colors buffer + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[2]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*vertexData[currentBuffer].vCounter, vertexData[currentBuffer].colors); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*MAX_BATCH_ELEMENTS, vertexData[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... + // vertexData[currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // if (vertexData[currentBuffer].vertices) + // { + // Update vertex data + // } + // glUnmapBuffer(GL_ARRAY_BUFFER); + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); + } +} + +// Draw default internal buffers vertex data +static void DrawBuffersDefault(void) +{ + Matrix matProjection = projection; + Matrix matModelView = modelview; + + int eyesCount = 1; +#if defined(SUPPORT_VR_SIMULATOR) + if (vrStereoRender) eyesCount = 2; +#endif + + for (int eye = 0; eye < eyesCount; eye++) + { +#if defined(SUPPORT_VR_SIMULATOR) + if (eyesCount == 2) SetStereoView(eye, matProjection, matModelView); +#endif + + // Draw buffers + if (vertexData[currentBuffer].vCounter > 0) + { + // Set current shader and upload current MVP matrix + glUseProgram(currentShader.id); + + // Create modelview-projection matrix + Matrix matMVP = MatrixMultiply(modelview, projection); + + glUniformMatrix4fv(currentShader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); + glUniform4f(currentShader.locs[LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1i(currentShader.locs[LOC_MAP_DIFFUSE], 0); // Provided value refers to the texture unit (active) + + // TODO: Support additional texture units on custom shader + //if (currentShader->locs[LOC_MAP_SPECULAR] > 0) glUniform1i(currentShader.locs[LOC_MAP_SPECULAR], 1); + //if (currentShader->locs[LOC_MAP_NORMAL] > 0) glUniform1i(currentShader.locs[LOC_MAP_NORMAL], 2); + + // NOTE: Right now additional map textures not considered for default buffers drawing + + int vertexOffset = 0; + + if (vaoSupported) glBindVertexArray(vertexData[currentBuffer].vaoId); + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[0]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + + // Bind vertex attrib: texcoord (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[1]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); + + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, vertexData[currentBuffer].vboId[2]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexData[currentBuffer].vboId[3]); + } + + glActiveTexture(GL_TEXTURE0); + + for (int i = 0; i < drawsCounter; i++) + { + glBindTexture(GL_TEXTURE_2D, draws[i].textureId); + + // TODO: Find some way to bind additional textures --> Use global texture IDs? Register them on draw[i]? + //if (currentShader->locs[LOC_MAP_SPECULAR] > 0) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, textureUnit1_id); } + //if (currentShader->locs[LOC_MAP_SPECULAR] > 0) { glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, textureUnit2_id); } + + if ((draws[i].mode == RL_LINES) || (draws[i].mode == RL_TRIANGLES)) glDrawArrays(draws[i].mode, vertexOffset, draws[i].vertexCount); + else + { +#if defined(GRAPHICS_API_OPENGL_33) + // We need to define the number of indices to be processed: quadsCount*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, draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(sizeof(GLuint)*vertexOffset/4*6)); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glDrawElements(GL_TRIANGLES, draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLushort)*vertexOffset/4*6)); +#endif + } + + vertexOffset += (draws[i].vertexCount + draws[i].vertexAlignment); + } + + if (!vaoSupported) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + } + + if (vaoSupported) glBindVertexArray(0); // Unbind VAO + + glUseProgram(0); // Unbind shader program + } + + // Reset vertex counters for next frame + vertexData[currentBuffer].vCounter = 0; + vertexData[currentBuffer].tcCounter = 0; + vertexData[currentBuffer].cCounter = 0; + + // Reset depth for next draw + currentDepth = -1.0f; + + // Restore projection/modelview matrices + projection = matProjection; + modelview = matModelView; + + // Reset draws array + for (int i = 0; i < MAX_DRAWCALL_REGISTERED; i++) + { + draws[i].mode = RL_QUADS; + draws[i].vertexCount = 0; + draws[i].textureId = defaultTextureId; + } + + drawsCounter = 1; + + // Change to next buffer in the list + currentBuffer++; + if (currentBuffer >= MAX_BATCH_BUFFERING) currentBuffer = 0; +} + +// Unload default internal buffers vertex data from CPU and GPU +static void UnloadBuffersDefault(void) +{ + // Unbind everything + if (vaoSupported) glBindVertexArray(0); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + for (int i = 0; i < MAX_BATCH_BUFFERING; i++) + { + // Delete VBOs from GPU (VRAM) + glDeleteBuffers(1, &vertexData[i].vboId[0]); + glDeleteBuffers(1, &vertexData[i].vboId[1]); + glDeleteBuffers(1, &vertexData[i].vboId[2]); + glDeleteBuffers(1, &vertexData[i].vboId[3]); + + // Delete VAOs from GPU (VRAM) + if (vaoSupported) glDeleteVertexArrays(1, &vertexData[i].vaoId); + + // Free vertex arrays memory from CPU (RAM) + RL_FREE(vertexData[i].vertices); + RL_FREE(vertexData[i].texcoords); + RL_FREE(vertexData[i].colors); + RL_FREE(vertexData[i].indices); + } +} + +// Renders a 1x1 XY quad in NDC +static void GenDrawQuad(void) +{ + unsigned int quadVAO = 0; + unsigned int quadVBO = 0; + + float vertices[] = { + // Positions // Texture Coords + -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, + }; + + // Set up plane VAO + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + + // Fill buffer + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW); + + // Link vertex attributes + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)(3*sizeof(float))); + + // Draw quad + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + + glDeleteBuffers(1, &quadVBO); + glDeleteVertexArrays(1, &quadVAO); +} + +// Renders a 1x1 3D cube in NDC +static void GenDrawCube(void) +{ + unsigned int cubeVAO = 0; + unsigned int cubeVBO = 0; + + float vertices[] = { + -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 + }; + + // Set up cube VAO + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &cubeVBO); + + // Fill buffer + glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + // Link vertex attributes + glBindVertexArray(cubeVAO); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(3*sizeof(float))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(6*sizeof(float))); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + // Draw cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); + + glDeleteBuffers(1, &cubeVBO); + glDeleteVertexArrays(1, &cubeVAO); +} + +#if defined(SUPPORT_VR_SIMULATOR) +// Set internal projection and modelview matrix depending on eyes tracking data +static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) +{ + Matrix eyeProjection = matProjection; + Matrix eyeModelView = matModelView; + + // Setup viewport and projection/modelview matrices using tracking data + rlViewport(eye*framebufferWidth/2, 0, framebufferWidth/2, framebufferHeight); + + // Apply view offset to modelview matrix + eyeModelView = MatrixMultiply(matModelView, vrConfig.eyesViewOffset[eye]); + + // Set current eye projection matrix + eyeProjection = vrConfig.eyesProjection[eye]; + + SetMatrixModelview(eyeModelView); + SetMatrixProjection(eyeProjection); +} +#endif // SUPPORT_VR_SIMULATOR + +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +#if defined(GRAPHICS_API_OPENGL_11) +// Mipmaps data is generated after image data +// NOTE: Only works with RGBA (4 bytes) data! +static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) +{ + int mipmapCount = 1; // Required mipmap levels count (including base level) + int width = baseWidth; + int height = baseHeight; + int size = baseWidth*baseHeight*4; // Size in bytes (will include mipmaps...), RGBA only + + // Count mipmap levels required + while ((width != 1) && (height != 1)) + { + if (width != 1) width /= 2; + if (height != 1) height /= 2; + + TraceLog(LOG_DEBUG, "Next mipmap size: %i x %i", width, height); + + mipmapCount++; + + size += (width*height*4); // Add mipmap size (in bytes) + } + + TraceLog(LOG_DEBUG, "Total mipmaps required: %i", mipmapCount); + TraceLog(LOG_DEBUG, "Total size of data required: %i", size); + + unsigned char *temp = RL_REALLOC(data, size); + + if (temp != NULL) data = temp; + else TraceLog(LOG_WARNING, "Mipmaps required memory could not be allocated"); + + width = baseWidth; + height = baseHeight; + size = (width*height*4); + + // Generate mipmaps + // NOTE: Every mipmap data is stored after data + Color *image = (Color *)RL_MALLOC(width*height*sizeof(Color)); + Color *mipmap = NULL; + int offset = 0; + int j = 0; + + for (int i = 0; i < size; i += 4) + { + image[j].r = data[i]; + image[j].g = data[i + 1]; + image[j].b = data[i + 2]; + image[j].a = data[i + 3]; + j++; + } + + TraceLog(LOG_DEBUG, "Mipmap base (%ix%i)", width, height); + + for (int mip = 1; mip < mipmapCount; mip++) + { + mipmap = GenNextMipmap(image, width, height); + + offset += (width*height*4); // Size of last mipmap + j = 0; + + width /= 2; + height /= 2; + size = (width*height*4); // Mipmap size to store after offset + + // Add mipmap to data + for (int i = 0; i < size; i += 4) + { + data[offset + i] = mipmap[j].r; + data[offset + i + 1] = mipmap[j].g; + data[offset + i + 2] = mipmap[j].b; + data[offset + i + 3] = mipmap[j].a; + j++; + } + + RL_FREE(image); + + image = mipmap; + mipmap = NULL; + } + + RL_FREE(mipmap); // free mipmap data + + return mipmapCount; +} + +// Manual mipmap generation (basic scaling algorithm) +static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight) +{ + int x2, y2; + Color prow, pcol; + + int width = srcWidth/2; + int height = srcHeight/2; + + Color *mipmap = (Color *)RL_MALLOC(width*height*sizeof(Color)); + + // Scaling algorithm works perfectly (box-filter) + for (int y = 0; y < height; y++) + { + y2 = 2*y; + + for (int x = 0; x < width; x++) + { + x2 = 2*x; + + prow.r = (srcData[y2*srcWidth + x2].r + srcData[y2*srcWidth + x2 + 1].r)/2; + prow.g = (srcData[y2*srcWidth + x2].g + srcData[y2*srcWidth + x2 + 1].g)/2; + prow.b = (srcData[y2*srcWidth + x2].b + srcData[y2*srcWidth + x2 + 1].b)/2; + prow.a = (srcData[y2*srcWidth + x2].a + srcData[y2*srcWidth + x2 + 1].a)/2; + + pcol.r = (srcData[(y2+1)*srcWidth + x2].r + srcData[(y2+1)*srcWidth + x2 + 1].r)/2; + pcol.g = (srcData[(y2+1)*srcWidth + x2].g + srcData[(y2+1)*srcWidth + x2 + 1].g)/2; + pcol.b = (srcData[(y2+1)*srcWidth + x2].b + srcData[(y2+1)*srcWidth + x2 + 1].b)/2; + pcol.a = (srcData[(y2+1)*srcWidth + x2].a + srcData[(y2+1)*srcWidth + x2 + 1].a)/2; + + mipmap[y*width + x].r = (prow.r + pcol.r)/2; + mipmap[y*width + x].g = (prow.g + pcol.g)/2; + mipmap[y*width + x].b = (prow.b + pcol.b)/2; + mipmap[y*width + x].a = (prow.a + pcol.a)/2; + } + } + + TraceLog(LOG_DEBUG, "Mipmap generated successfully (%ix%i)", width, height); + + return mipmap; +} +#endif + +#if defined(RLGL_STANDALONE) +// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) +void TraceLog(int msgType, const char *text, ...) +{ + va_list args; + va_start(args, text); + + switch (msgType) + { + case LOG_INFO: fprintf(stdout, "INFO: "); break; + case LOG_ERROR: fprintf(stdout, "ERROR: "); break; + case LOG_WARNING: fprintf(stdout, "WARNING: "); break; + case LOG_DEBUG: fprintf(stdout, "DEBUG: "); break; + default: break; + } + + vfprintf(stdout, text, args); + fprintf(stdout, "\n"); + + va_end(args); + + if (msgType == LOG_ERROR) exit(1); +} + +// Get pixel data size in bytes (image or texture) +// NOTE: Size depends on pixel format +int GetPixelDataSize(int width, int height, int format) +{ + int dataSize = 0; // Size in bytes + int bpp = 0; // Bits per pixel + + switch (format) + { + case UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case UNCOMPRESSED_GRAY_ALPHA: + case UNCOMPRESSED_R5G6B5: + case UNCOMPRESSED_R5G5B5A1: + case UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case UNCOMPRESSED_R8G8B8: bpp = 24; break; + case UNCOMPRESSED_R32: bpp = 32; break; + case UNCOMPRESSED_R32G32B32: bpp = 32*3; break; + case UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; + case COMPRESSED_DXT1_RGB: + case COMPRESSED_DXT1_RGBA: + case COMPRESSED_ETC1_RGB: + case COMPRESSED_ETC2_RGB: + case COMPRESSED_PVRT_RGB: + case COMPRESSED_PVRT_RGBA: bpp = 4; break; + case COMPRESSED_DXT3_RGBA: + case COMPRESSED_DXT5_RGBA: + case COMPRESSED_ETC2_EAC_RGBA: + case COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break; + case COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; + default: break; + } + + dataSize = width*height*bpp/8; // 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 >= COMPRESSED_DXT1_RGB) && (format < COMPRESSED_DXT3_RGBA)) dataSize = 8; + else if ((format >= COMPRESSED_DXT3_RGBA) && (format < COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16; + } + + return dataSize; +} +#endif // RLGL_STANDALONE + +#endif // RLGL_IMPLEMENTATION |