From 2ce28f75addd22a5b56dc68738e6a72af74f125d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 Mar 2021 20:45:04 +0100 Subject: WARNING: BREAKING: REDESIGNED: rlgl module - Many functions renamed to follow rl*() convention - Some internal functions exposed in the API - Some functionality moved to other modules - Reorganized all functions by categories - Make sure it keeps working with OpenGL 1.1 and 2.1 --- src/core.c | 60 +- src/raylib.h | 1 + src/rlgl.h | 3911 ++++++++++++++++++++++++++++---------------------------- src/textures.c | 6 + 4 files changed, 1998 insertions(+), 1980 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index 7419c415..7fbfd2ce 100644 --- a/src/core.c +++ b/src/core.c @@ -1047,7 +1047,7 @@ void ToggleFullscreen(void) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); int monitorCount = 0; - GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); int monitorIndex = GetCurrentMonitor(); // use GetCurrentMonitor so we correctly get the display the window is on @@ -1077,7 +1077,7 @@ void ToggleFullscreen(void) glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } - // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) // NOTE: V-Sync can be enabled by graphic driver configuration if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1); @@ -2034,7 +2034,37 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName) if (fShaderStr != NULL) RL_FREE(fShaderStr); // After shader loading, we TRY to set default location names - if (shader.id > 0) SetShaderDefaultLocations(&shader); + if (shader.id > 0) + { + // 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 + + // NOTE: If any location is not found, loc point becomes -1 + + // Get handles to GLSL input attibute locations + shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, DEFAULT_SHADER_ATTRIB_NAME_POSITION); + shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); + shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2); + shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, DEFAULT_SHADER_ATTRIB_NAME_NORMAL); + shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, DEFAULT_SHADER_ATTRIB_NAME_TANGENT); + shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, DEFAULT_SHADER_ATTRIB_NAME_COLOR); + + // Get handles to GLSL uniform locations (vertex shader) + shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, "mvp"); + shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, "projection"); + shader.locs[SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, "view"); + + // Get handles to GLSL uniform locations (fragment shader) + shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, "colDiffuse"); + shader.locs[SHADER_LOC_MAP_DIFFUSE] = rlGetLocationUniform(shader.id, "texture0"); + shader.locs[SHADER_LOC_MAP_SPECULAR] = rlGetLocationUniform(shader.id, "texture1"); + shader.locs[SHADER_LOC_MAP_NORMAL] = rlGetLocationUniform(shader.id, "texture2"); + } return shader; } @@ -2046,15 +2076,13 @@ void UnloadShader(Shader shader) { rlUnloadShaderProgram(shader.id); RL_FREE(shader.locs); - - TRACELOG(LOG_INFO, "SHADER: [ID %i] Unloaded shader program data from VRAM (GPU)", shader.id); } } // Begin custom shader mode void BeginShaderMode(Shader shader) { - rlSetShaderCurrent(shader); + rlSetShaderActive(shader); } // End custom shader mode (returns to default shader) @@ -2066,23 +2094,13 @@ void EndShaderMode(void) // Get shader uniform location int GetShaderLocation(Shader shader, const char *uniformName) { - int location = rlGetLocationUniform(shader.id, uniformName); - - if (location == -1) TRACELOG(LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shader.id, uniformName); - else TRACELOG(LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shader.id, uniformName, location); - - return location; + return rlGetLocationUniform(shader.id, uniformName); } // Get shader attribute location int GetShaderLocationAttrib(Shader shader, const char *attribName) { - int location = rlGetLocationAttrib(shader.id, attribName); - - if (location == -1) TRACELOG(LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shader.id, attribName); - else TRACELOG(LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shader.id, attribName, location); - - return location; + return rlGetLocationAttrib(shader.id, attribName); } // Set shader uniform value @@ -4373,9 +4391,9 @@ static void SetupViewport(int width, int height) CORE.Window.render.width = width; CORE.Window.render.height = height; - // Set viewport width and height - // NOTE: We consider render size (scaled) and offset in case black bars are required and - // render area does not match full display area (this situation is only applicable on fullscreen mode) + // Set viewport width and height + // NOTE: We consider render size (scaled) and offset in case black bars are required and + // render area does not match full display area (this situation is only applicable on fullscreen mode) #if defined(__APPLE__) float xScale = 1.0f, yScale = 1.0f; glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); diff --git a/src/raylib.h b/src/raylib.h index 8a4466d7..57d306ba 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1244,6 +1244,7 @@ RLAPI void UpdateTexture(Texture2D texture, const void *pixels); RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data RLAPI Image GetTextureData(Texture2D texture); // Get pixel data from GPU texture and return an Image RLAPI Image GetScreenData(void); // Get pixel data from screen buffer and return an Image (screenshot) +RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Define default texture used to draw shapes // Texture configuration functions RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture diff --git a/src/rlgl.h b/src/rlgl.h index 3f3e4fc6..82895d06 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rlgl v3.2 - raylib OpenGL abstraction layer +* rlgl v3.7 - 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...). @@ -92,7 +92,7 @@ #define RL_FREE(p) free(p) #endif #else - #include "raylib.h" // Required for: Model, Shader, Texture2D, TRACELOG() + #include "raylib.h" // Required for: Model, Mesh, Material, Shader, Texture2D #endif #include "raymath.h" // Required for: Vector3, Matrix @@ -233,7 +233,7 @@ typedef enum { RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z, RL_ATTACHMENT_TEXTURE2D = 100, RL_ATTACHMENT_RENDERBUFFER = 200, -} FramebufferTexType; +} FramebufferAttachTextureType; // Dynamic vertex buffers (position + texcoords + colors + indices arrays) typedef struct VertexBuffer { @@ -268,8 +268,8 @@ typedef struct DrawCall { //unsigned int shaderId; // Shader id to be used on the draw -> Using RLGL.currentShader.id unsigned int textureId; // Texture id to be used on the draw -> Use to create new draw call if changes - //Matrix projection; // Projection matrix for this draw -> Using RLGL.projection - //Matrix modelview; // Modelview matrix for this draw -> Using RLGL.modelview + //Matrix projection; // Projection matrix for this draw -> Using RLGL.projection by default + //Matrix modelview; // Modelview matrix for this draw -> Using RLGL.modelview by default } DrawCall; // RenderBatch type @@ -364,14 +364,6 @@ typedef struct RenderBatch { float params[4]; // 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; - // TraceLog message types typedef enum { LOG_ALL, @@ -537,8 +529,9 @@ RLAPI void rlColor3f(float x, float y, float z); // Define one vertex ( 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 +// Functions Declaration - OpenGL style functions (common to 1.1, 3.3+, ES2) +// NOTE: This functions are used to completely abstract raylib code from OpenGL layer, +// some of them are direct wrappers over OpenGL calls, some others are custom //------------------------------------------------------------------------------------ RLAPI void rlEnableTexture(unsigned int id); // Enable texture usage RLAPI void rlDisableTexture(void); // Disable texture usage @@ -567,19 +560,34 @@ RLAPI void rlDisableStereoRender(void); // Disable stereo renderin RLAPI void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a); // Clear color buffer with color RLAPI void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) +RLAPI void rlCheckErrors(void); // Check and log OpenGL error codes +RLAPI void rlSetBlendMode(int mode); // Set blending mode +RLAPI void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation); // Set blending mode factor and equation (using OpenGL factors) //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality //------------------------------------------------------------------------------------ +// rlgl initialization functions RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) RLAPI void rlglClose(void); // De-inititialize rlgl (buffers, shaders, textures) - +RLAPI void rlLoadExtensions(void* loader); // Load OpenGL extensions (loader function pointer required) RLAPI int rlGetVersion(void); // Returns current OpenGL version -RLAPI void rlCheckErrors(void); // Check and log OpenGL error codes -RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function pointer required) -RLAPI void rlSetBlendMode(int mode); // Set blending mode -RLAPI void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation); // Set blending mode factor and equation (using OpenGL factors) +RLAPI Shader rlGetShaderDefault(void); // Get default shader +RLAPI Texture2D rlGetTextureDefault(void); // Get default texture +RLAPI Texture2D rlGetShapesTexture(void); // Get texture to draw shapes +RLAPI Rectangle rlGetShapesTextureRec(void); // Get texture rectangle to draw shapes +RLAPI void rlSetShapesTexture(Texture2D texture, Rectangle source); // Define default texture used to draw shapes + +// Render batch management +// NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode +// but this render batch API is exposed in case of custom batches are required +RLAPI RenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements); // Load a render batch system +RLAPI void rlUnloadRenderBatch(RenderBatch batch); // Unload render batch system +RLAPI void rlDrawRenderBatch(RenderBatch *batch); // Draw render batch data (Update->Draw->Reset) +RLAPI void rlSetRenderBatchActive(RenderBatch *batch); // Set the active render batch for rlgl (NULL for default internal) +RLAPI void rlDrawRenderBatchActive(void); // Update and draw internal render batch +RLAPI bool rlCheckRenderBatchLimit(int vCount); // Check internal buffer overflow for a given number of vertex // Textures data management RLAPI unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU @@ -588,19 +596,10 @@ RLAPI unsigned int rlLoadTextureCubemap(void *data, int size, int format); RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update GPU texture with new data RLAPI void rlGetGlTextureFormats(int format, 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) -// OpenGL state management -RLAPI Matrix rlGetMatrixModelview(void); // Get internal modelview matrix -RLAPI Matrix rlGetMatrixProjection(void); // Get internal projection matrix -RLAPI void rlSetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) -RLAPI void rlSetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) -RLAPI void rlSetMatrixProjectionStereo(Matrix right, Matrix left); // Set eyes projection matrices for stereo rendering -RLAPI void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left); // Set eyes view offsets matrices for stereo rendering - // Framebuffer management (fbo) RLAPI unsigned int rlLoadFramebuffer(int width, int height); // Load an empty framebuffer RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType); // Attach texture/renderbuffer to a framebuffer @@ -608,40 +607,38 @@ RLAPI bool rlFramebufferComplete(unsigned int id); // Ver RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU // Vertex data management +// TODO: Avoid dealing with Mesh and Material structs? RLAPI void rlLoadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids +RLAPI unsigned int rlLoadVertexBuffer(unsigned int vaoId, int index, void *buffer, int size, bool dynamic); // Load a vertex buffer attribute RLAPI void rlUpdateMesh(Mesh mesh, int buffer, int count); // Update vertex or index data on GPU (upload new data to one buffer) RLAPI void rlUpdateMeshAt(Mesh mesh, int buffer, int count, int index); // Update vertex or index data on GPU, at index +RLAPI void rlUpdateBuffer(int bufferId, void *data, int dataSize); // Update GPU buffer with new data RLAPI void rlDrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform RLAPI void rlDrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int count); // Draw a 3d mesh with material and transform RLAPI void rlUnloadMesh(Mesh *mesh); // Unload mesh data from CPU and GPU -RLAPI unsigned int rlLoadVertexBuffer(unsigned int vaoId, int index, void *buffer, int size, bool dynamic); // Load a vertex buffer attribute -RLAPI void rlUpdateBuffer(int bufferId, void *data, int dataSize); // Update GPU buffer with new data - // Shaders management RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings +RLAPI unsigned int rlCompileShader(const char *shaderCode, int type); // Compile custom shader and return shader id (type: GL_VERTEX_SHADER, GL_FRAGMENT_SHADER) +RLAPI unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program +RLAPI void rlUnloadShaderProgram(unsigned int id); // Unload shader program RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName); // Get shader location uniform RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName); // Get shader location attribute RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count); // Set shader value uniform RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat); // Set shader value matrix RLAPI void rlSetUniformSampler(int locIndex, Texture2D texture); // Set shader value sampler +RLAPI void rlSetShaderActive(Shader shader); // Set shader currently active -// Render batch mangement -RLAPI bool rlCheckRenderBatchLimit(int vCount); // Check internal buffer overflow for a given number of vertex -RLAPI RenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements); // Load a render batch system -RLAPI void rlUnloadRenderBatch(RenderBatch batch); // Unload render batch system -RLAPI void rlDrawRenderBatch(RenderBatch *batch); // Draw render batch data (Update->Draw->Reset) -RLAPI void rlSetRenderBatchActive(RenderBatch *batch); // Set the active render batch for rlgl -RLAPI void rlSetRenderBatchDefault(void); // Set default render batch for rlgl - -RLAPI Shader rlGetShaderDefault(void); // Get default shader -RLAPI Texture2D rlGetTextureDefault(void); // Get default texture -RLAPI Texture2D rlGetShapesTexture(void); // Get texture to draw shapes -RLAPI Rectangle rlGetShapesTextureRec(void); // Get texture rectangle to draw shapes -RLAPI void rlSetShapesTexture(Texture2D texture, Rectangle source); // Define default texture used to draw shapes +// Matrix state management +RLAPI Matrix rlGetMatrixModelview(void); // Get internal modelview matrix +RLAPI Matrix rlGetMatrixProjection(void); // Get internal projection matrix +RLAPI void rlSetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) +RLAPI void rlSetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) +RLAPI void rlSetMatrixProjectionStereo(Matrix right, Matrix left); // Set eyes projection matrices for stereo rendering +RLAPI void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left); // Set eyes view offsets matrices for stereo rendering // Texture maps generation (PBR) -// NOTE: Required shaders should be provided +// TODO: Redesign or remove to avoid requiring external shaders RLAPI TextureCubemap rlGenTextureCubemap(Shader shader, Texture2D panorama, int size, int format); // Generate cubemap (6 faces) from equirectangular (panorama) texture RLAPI TextureCubemap rlGenTextureIrradiance(Shader shader, TextureCubemap cubemap, int size); // Generate irradiance cubemap using cubemap texture RLAPI TextureCubemap rlGenTexturePrefilter(Shader shader, TextureCubemap cubemap, int size); // Generate prefilter cubemap using cubemap texture @@ -891,21 +888,16 @@ static PFNGLVERTEXATTRIBDIVISOREXTPROC glVertexAttribDivisor = NULL; // 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 Shader rlLoadShaderDefault(void); // Load default shader (just vertex positioning and texture coloring) +static void rlUnloadShaderDefault(void); // Unload default shader -static void GenDrawCube(void); // Generate and draw cube -static void GenDrawQuad(void); // Generate and draw quad +static void rlGenDrawCube(void); // Generate and draw cube +static void rlGenDrawQuad(void); // Generate and draw quad #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); +static int rlGenerateMipmapsData(unsigned char *data, int baseWidth, int baseHeight); // Generate mipmaps data on CPU side +static Color *rlGenNextMipmapData(Color *srcData, int srcWidth, int srcHeight); // Geenrate next mipmap level on CPU side #endif - static int rlGetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) //---------------------------------------------------------------------------------- @@ -1253,9 +1245,9 @@ void rlColor3f(float x, float y, float z) #endif -//---------------------------------------------------------------------------------- -// Module Functions Definition - OpenGL equivalent functions (common to 1.1, 3.3+, ES2) -//---------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------- +// Module Functions Definition - OpenGL style functions (common to 1.1, 3.3+, ES2) +//-------------------------------------------------------------------------------------- // Enable texture usage void rlEnableTexture(unsigned int id) @@ -1459,38 +1451,16 @@ void rlDisableSmoothLines(void) // Enable stereo rendering void rlEnableStereoRender(void) { +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) RLGL.State.stereoRender = true; +#endif } // Disable stereo rendering void rlDisableStereoRender(void) { +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) RLGL.State.stereoRender = false; -} - -// Unload framebuffer from GPU memory -// NOTE: All attached textures/cubemaps/renderbuffers are also deleted -void rlUnloadFramebuffer(unsigned int id) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(SUPPORT_RENDER_TEXTURES_HINT) - - // Query depth attachment to automatically delete texture/renderbuffer - int depthType = 0, depthId = 0; - glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType); - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId); - - unsigned int depthIdU = (unsigned int)depthId; - if (depthType == GL_RENDERBUFFER) glDeleteRenderbuffers(1, &depthIdU); - else if (depthType == GL_RENDERBUFFER) glDeleteTextures(1, &depthIdU); - - // NOTE: If a texture object is deleted while its image is attached to the *currently bound* framebuffer, - // the texture image is automatically detached from the currently bound framebuffer. - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &id); - - TRACELOG(LOG_INFO, "FBO: [ID %i] Unloaded framebuffer from VRAM (GPU)", id); #endif } @@ -1513,17 +1483,66 @@ void rlClearScreenBuffers(void) //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) +// Check and log OpenGL error codes +void rlCheckErrors() +{ +#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int check = 1; + while (check) + { + const GLenum err = glGetError(); + switch (err) + { + case GL_NO_ERROR: check = 0; break; + case 0x0500: TRACELOG(LOG_WARNING, "GL: Error detected: GL_INVALID_ENUM"); break; + case 0x0501: TRACELOG(LOG_WARNING, "GL: Error detected: GL_INVALID_VALUE"); break; + case 0x0502: TRACELOG(LOG_WARNING, "GL: Error detected: GL_INVALID_OPERATION"); break; + case 0x0503: TRACELOG(LOG_WARNING, "GL: Error detected: GL_STACK_OVERFLOW"); break; + case 0x0504: TRACELOG(LOG_WARNING, "GL: Error detected: GL_STACK_UNDERFLOW"); break; + case 0x0505: TRACELOG(LOG_WARNING, "GL: Error detected: GL_OUT_OF_MEMORY"); break; + case 0x0506: TRACELOG(LOG_WARNING, "GL: Error detected: GL_INVALID_FRAMEBUFFER_OPERATION"); break; + default: TRACELOG(LOG_WARNING, "GL: Error detected: Unknown error code: %x", err); break; + } + } +#endif +} + +// Set blend mode +void rlSetBlendMode(int mode) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindBuffer(GL_ARRAY_BUFFER, bufferId); - glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, data); + if (RLGL.State.currentBlendMode != mode) + { + rlDrawRenderBatch(RLGL.currentBatch); + + switch (mode) + { + case BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; + case BLEND_ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; + case BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; + case BLEND_ADD_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; + case BLEND_SUBTRACT_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_SUBTRACT); break; + case BLEND_CUSTOM: glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation); break; + default: break; + } + + RLGL.State.currentBlendMode = mode; + } +#endif +} + +// Set blending mode factor and equation +void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.glBlendSrcFactor = glSrcFactor; + RLGL.State.glBlendDstFactor = glDstFactor; + RLGL.State.glBlendEquation = glEquation; #endif } //---------------------------------------------------------------------------------- -// Module Functions Definition - rlgl Functions +// Module Functions Definition - rlgl functionality //---------------------------------------------------------------------------------- // Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states @@ -1722,7 +1741,7 @@ void rlglInit(int width, int height) else TRACELOG(LOG_WARNING, "TEXTURE: Failed to load default texture"); // Init default Shader (customized for GL 3.3 and ES2) - RLGL.State.defaultShader = LoadShaderDefault(); + RLGL.State.defaultShader = rlLoadShaderDefault(); RLGL.State.currentShader = RLGL.State.defaultShader; // Init default vertex arrays buffers @@ -1791,42 +1810,34 @@ void rlglClose(void) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) rlUnloadRenderBatch(RLGL.defaultBatch); - UnloadShaderDefault(); // Unload default shader + rlUnloadShaderDefault(); // Unload default shader glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Unloaded default texture data from VRAM (GPU)", RLGL.State.defaultTextureId); #endif } -// Update and draw internal render batch -void rlDrawRenderBatchActive(void) +// Load OpenGL extensions +// NOTE: External loader function could be passed as a pointer +void rlLoadExtensions(void *loader) { -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside -#endif -} +#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"); -// Check and log OpenGL error codes -void rlCheckErrors() -{ -#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - int check = 1; - while (check) - { - const GLenum err = glGetError(); - switch (err) - { - case GL_NO_ERROR: check = 0; break; - case 0x0500: TRACELOG(LOG_WARNING, "GL: Error detected: GL_INVALID_ENUM"); break; - case 0x0501: TRACELOG(LOG_WARNING, "GL: Error detected: GL_INVALID_VALUE"); break; - case 0x0502: TRACELOG(LOG_WARNING, "GL: Error detected: GL_INVALID_OPERATION"); break; - case 0x0503: TRACELOG(LOG_WARNING, "GL: Error detected: GL_STACK_OVERFLOW"); break; - case 0x0504: TRACELOG(LOG_WARNING, "GL: Error detected: GL_STACK_UNDERFLOW"); break; - case 0x0505: TRACELOG(LOG_WARNING, "GL: Error detected: GL_OUT_OF_MEMORY"); break; - case 0x0506: TRACELOG(LOG_WARNING, "GL: Error detected: GL_INVALID_FRAMEBUFFER_OPERATION"); break; - default: TRACELOG(LOG_WARNING, "GL: Error detected: Unknown error code: %x", err); break; - } - } + #if defined(GRAPHICS_API_OPENGL_21) + if (GLAD_GL_VERSION_2_1) TRACELOG(LOG_INFO, "GL: OpenGL 2.1 profile supported"); + #endif + #if defined(GRAPHICS_API_OPENGL_33) + if (GLAD_GL_VERSION_3_3) TRACELOG(LOG_INFO, "GL: OpenGL 3.3 Core profile supported"); + else TRACELOG(LOG_ERROR, "GL: 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 } @@ -1851,1161 +1862,889 @@ int rlGetVersion(void) #endif } -// Check internal buffer overflow for a given number of vertex -// and force a RenderBatch draw call if required -bool rlCheckRenderBatchLimit(int vCount) +// Get default internal shader (simple texture + tint color) +Shader rlGetShaderDefault(void) { - bool overflow = false; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vCounter + vCount) >= - (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementsCount*4)) - { - overflow = true; - rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside - } + return RLGL.State.defaultShader; +#else + Shader shader = { 0 }; + return shader; #endif - return overflow; } -// Set blending mode factor and equation -void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation) +// Get default internal texture (white texture) +Texture2D rlGetTextureDefault(void) { + Texture2D texture = { 0 }; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - RLGL.State.glBlendSrcFactor = glSrcFactor; - RLGL.State.glBlendDstFactor = glDstFactor; - RLGL.State.glBlendEquation = glEquation; + texture.id = RLGL.State.defaultTextureId; + texture.width = 1; + texture.height = 1; + texture.mipmaps = 1; + texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; #endif + return texture; } -// Load OpenGL extensions -// NOTE: External loader function could be passed as a pointer -void rlLoadExtensions(void *loader) +// Get texture to draw shapes +Texture2D rlGetShapesTexture(void) { -#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, "GL: OpenGL 2.1 profile supported"); - #endif - #if defined(GRAPHICS_API_OPENGL_33) - if (GLAD_GL_VERSION_3_3) TRACELOG(LOG_INFO, "GL: OpenGL 3.3 Core profile supported"); - else TRACELOG(LOG_ERROR, "GL: 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 +#if defined(GRAPHICS_API_OPENGL_11) + Texture2D texture = { 0 }; + return texture; +#else + return RLGL.State.shapesTexture; #endif } -// Convert image data to OpenGL texture (returns OpenGL valid Id) -unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount) +// Get texture rectangle to draw shapes +Rectangle rlGetShapesTextureRec(void) { - 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 >= PIXELFORMAT_COMPRESSED_DXT1_RGB) - { - TRACELOG(LOG_WARNING, "GL: OpenGL 1.1 does not support GPU compressed texture formats"); - return id; - } + Rectangle rec = { 0 }; + return rec; #else - if ((!RLGL.ExtSupported.texCompDXT) && ((format == PIXELFORMAT_COMPRESSED_DXT1_RGB) || (format == PIXELFORMAT_COMPRESSED_DXT1_RGBA) || - (format == PIXELFORMAT_COMPRESSED_DXT3_RGBA) || (format == PIXELFORMAT_COMPRESSED_DXT5_RGBA))) - { - TRACELOG(LOG_WARNING, "GL: DXT compressed texture format not supported"); - return id; - } + return RLGL.State.shapesTextureRec; +#endif +} + +// Define default texture used to draw shapes +void rlSetShapesTexture(Texture2D texture, Rectangle source) +{ #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((!RLGL.ExtSupported.texCompETC1) && (format == PIXELFORMAT_COMPRESSED_ETC1_RGB)) - { - TRACELOG(LOG_WARNING, "GL: ETC1 compressed texture format not supported"); - return id; - } + RLGL.State.shapesTexture = texture; + RLGL.State.shapesTextureRec = source; +#endif +} - if ((!RLGL.ExtSupported.texCompETC2) && ((format == PIXELFORMAT_COMPRESSED_ETC2_RGB) || (format == PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA))) - { - TRACELOG(LOG_WARNING, "GL: ETC2 compressed texture format not supported"); - return id; - } +// Render batch management +//------------------------------------------------------------------------------------------------ +// Load render batch +RenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements) +{ + RenderBatch batch = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Initialize CPU (RAM) vertex buffers (position, texcoord, color data and indexes) + //-------------------------------------------------------------------------------------------- + batch.vertexBuffer = (VertexBuffer *)RL_MALLOC(sizeof(VertexBuffer)*numBuffers); - if ((!RLGL.ExtSupported.texCompPVRT) && ((format == PIXELFORMAT_COMPRESSED_PVRT_RGB) || (format == PIXELFORMAT_COMPRESSED_PVRT_RGBA))) + for (int i = 0; i < numBuffers; i++) { - TRACELOG(LOG_WARNING, "GL: PVRT compressed texture format not supported"); - return id; - } + batch.vertexBuffer[i].elementsCount = bufferElements; - if ((!RLGL.ExtSupported.texCompASTC) && ((format == PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA) || (format == PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA))) - { - TRACELOG(LOG_WARNING, "GL: ASTC compressed texture format not supported"); - return id; - } + batch.vertexBuffer[i].vertices = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float)); // 3 float by vertex, 4 vertex by quad + batch.vertexBuffer[i].texcoords = (float *)RL_MALLOC(bufferElements*2*4*sizeof(float)); // 2 float by texcoord, 4 texcoord by quad + batch.vertexBuffer[i].colors = (unsigned char *)RL_MALLOC(bufferElements*4*4*sizeof(unsigned char)); // 4 float by color, 4 colors by quad +#if defined(GRAPHICS_API_OPENGL_33) + batch.vertexBuffer[i].indices = (unsigned int *)RL_MALLOC(bufferElements*6*sizeof(unsigned int)); // 6 int by quad (indices) #endif -#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) +#if defined(GRAPHICS_API_OPENGL_ES2) + batch.vertexBuffer[i].indices = (unsigned short *)RL_MALLOC(bufferElements*6*sizeof(unsigned short)); // 6 int by quad (indices) #endif - glBindTexture(GL_TEXTURE_2D, id); + for (int j = 0; j < (3*4*bufferElements); j++) batch.vertexBuffer[i].vertices[j] = 0.0f; + for (int j = 0; j < (2*4*bufferElements); j++) batch.vertexBuffer[i].texcoords[j] = 0.0f; + for (int j = 0; j < (4*4*bufferElements); j++) batch.vertexBuffer[i].colors[j] = 0; - int mipWidth = width; - int mipHeight = height; - int mipOffset = 0; // Mipmap data offset + int k = 0; - // Load the different mipmap levels - for (int i = 0; i < mipmapCount; i++) - { - unsigned int mipSize = rlGetPixelDataSize(mipWidth, mipHeight, format); + // Indices can be initialized right now + for (int j = 0; j < (6*bufferElements); j += 6) + { + batch.vertexBuffer[i].indices[j] = 4*k; + batch.vertexBuffer[i].indices[j + 1] = 4*k + 1; + batch.vertexBuffer[i].indices[j + 2] = 4*k + 2; + batch.vertexBuffer[i].indices[j + 3] = 4*k; + batch.vertexBuffer[i].indices[j + 4] = 4*k + 2; + batch.vertexBuffer[i].indices[j + 5] = 4*k + 3; - unsigned int glInternalFormat, glFormat, glType; - rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + k++; + } - TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); + batch.vertexBuffer[i].vCounter = 0; + batch.vertexBuffer[i].tcCounter = 0; + batch.vertexBuffer[i].cCounter = 0; + } - if (glInternalFormat != -1) - { - if (format < PIXELFORMAT_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 + TRACELOG(LOG_INFO, "RLGL: Internal vertex buffers initialized successfully in RAM (CPU)"); + //-------------------------------------------------------------------------------------------- - #if defined(GRAPHICS_API_OPENGL_33) - if (format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) - { - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - } - else if (format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) - { - #if defined(GRAPHICS_API_OPENGL_21) - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; - #endif - #if 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 + // Upload to GPU (VRAM) vertex data and initialize VAOs/VBOs + //-------------------------------------------------------------------------------------------- + for (int i = 0; i < numBuffers; i++) + { + if (RLGL.ExtSupported.vao) + { + // Initialize Quads VAO + glGenVertexArrays(1, &batch.vertexBuffer[i].vaoId); + glBindVertexArray(batch.vertexBuffer[i].vaoId); } - mipWidth /= 2; - mipHeight /= 2; - mipOffset += mipSize; + // Quads - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[0]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*3*4*sizeof(float), batch.vertexBuffer[i].vertices, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_POSITION]); + glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - // Security check for NPOT textures - if (mipWidth < 1) mipWidth = 1; - if (mipHeight < 1) mipHeight = 1; - } + // Vertex texcoord buffer (shader-location = 1) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[1]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*2*4*sizeof(float), batch.vertexBuffer[i].texcoords, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); + glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); - // Texture parameters configuration - // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[2]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*4*4*sizeof(unsigned char), batch.vertexBuffer[i].colors, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_COLOR]); + glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + // Fill index buffer + glGenBuffers(1, &batch.vertexBuffer[i].vboId[3]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[3]); +#if defined(GRAPHICS_API_OPENGL_33) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(int), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); +#endif #if defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used - if (RLGL.ExtSupported.texNPOT) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis - } - else - { - // NOTE: If using negative texture coordinates (LoadOBJ()), it does not work! - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis - } -#else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(short), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); #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 + TRACELOG(LOG_INFO, "RLGL: Render batch vertex buffers loaded successfully"); -#if defined(GRAPHICS_API_OPENGL_33) - if (mipmapCount > 1) + // Unbind the current VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(0); + //-------------------------------------------------------------------------------------------- + + // Init draw calls tracking system + //-------------------------------------------------------------------------------------------- + batch.draws = (DrawCall *)RL_MALLOC(DEFAULT_BATCH_DRAWCALLS*sizeof(DrawCall)); + + for (int i = 0; i < DEFAULT_BATCH_DRAWCALLS; i++) { - // 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); + batch.draws[i].mode = RL_QUADS; + batch.draws[i].vertexCount = 0; + batch.draws[i].vertexAlignment = 0; + //batch.draws[i].vaoId = 0; + //batch.draws[i].shaderId = 0; + batch.draws[i].textureId = RLGL.State.defaultTextureId; + //batch.draws[i].RLGL.State.projection = MatrixIdentity(); + //batch.draws[i].RLGL.State.modelview = MatrixIdentity(); } + + batch.buffersCount = numBuffers; // Record buffer count + batch.drawsCounter = 1; // Reset draws counter + batch.currentDepth = -1.0f; // Reset depth value + //-------------------------------------------------------------------------------------------- #endif - // At this point we have the texture loaded in GPU and texture parameters configured + return batch; +} - // NOTE: If mipmaps were not in data, they are not generated automatically +// Unload default internal buffers vertex data from CPU and GPU +void rlUnloadRenderBatch(RenderBatch batch) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Unbind everything + if (RLGL.ExtSupported.vao) glBindVertexArray(0); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - // Unbind current texture - glBindTexture(GL_TEXTURE_2D, 0); + // Unload all vertex buffers data + for (int i = 0; i < batch.buffersCount; i++) + { + // Delete VBOs from GPU (VRAM) + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[0]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[1]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[2]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[3]); - if (id > 0) TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Texture created successfully (%ix%i - %i mipmaps)", id, width, height, mipmapCount); - else TRACELOG(LOG_WARNING, "TEXTURE: Failed to load texture"); + // Delete VAOs from GPU (VRAM) + if (RLGL.ExtSupported.vao) glDeleteVertexArrays(1, &batch.vertexBuffer[i].vaoId); - return id; + // Free vertex arrays memory from CPU (RAM) + RL_FREE(batch.vertexBuffer[i].vertices); + RL_FREE(batch.vertexBuffer[i].texcoords); + RL_FREE(batch.vertexBuffer[i].colors); + RL_FREE(batch.vertexBuffer[i].indices); + } + + // Unload arrays + RL_FREE(batch.vertexBuffer); + RL_FREE(batch.draws); +#endif } -// 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, bool useRenderBuffer) +// Draw render batch +// NOTE: We require a pointer to reset batch and increase current buffer (multi-buffer) +void rlDrawRenderBatch(RenderBatch *batch) { - unsigned int id = 0; - #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // In case depth textures not supported, we force renderbuffer usage - if (!RLGL.ExtSupported.texDepth) useRenderBuffer = true; + // Update batch vertex buffers + //------------------------------------------------------------------------------------------------------------ + // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) + // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) + if (batch->vertexBuffer[batch->currentBuffer].vCounter > 0) + { + // Activate elements VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); - // NOTE: We let the implementation to choose the best bit-depth - // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F - unsigned int glInternalFormat = GL_DEPTH_COMPONENT; + // Vertex positions buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, batch->vertexBuffer[batch->currentBuffer].vCounter*3*sizeof(float), batch->vertexBuffer[batch->currentBuffer].vertices); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*batch->vertexBuffer[batch->currentBuffer].elementsCount, batch->vertexBuffer[batch->currentBuffer].vertices, GL_DYNAMIC_DRAW); // Update all buffer -#if defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.ExtSupported.maxDepthBits == 32) glInternalFormat = GL_DEPTH_COMPONENT32_OES; - else if (RLGL.ExtSupported.maxDepthBits == 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES; - else glInternalFormat = GL_DEPTH_COMPONENT16; -#endif + // Texture coordinates buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); + glBufferSubData(GL_ARRAY_BUFFER, 0, batch->vertexBuffer[batch->currentBuffer].vCounter*2*sizeof(float), batch->vertexBuffer[batch->currentBuffer].texcoords); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*batch->vertexBuffer[batch->currentBuffer].elementsCount, batch->vertexBuffer[batch->currentBuffer].texcoords, GL_DYNAMIC_DRAW); // Update all buffer - if (!useRenderBuffer && RLGL.ExtSupported.texDepth) - { - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_2D, id); - glTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + // Colors buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); + glBufferSubData(GL_ARRAY_BUFFER, 0, batch->vertexBuffer[batch->currentBuffer].vCounter*4*sizeof(unsigned char), batch->vertexBuffer[batch->currentBuffer].colors); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*batch->vertexBuffer[batch->currentBuffer].elementsCount, batch->vertexBuffer[batch->currentBuffer].colors, GL_DYNAMIC_DRAW); // Update all buffer - 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); + // 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. - glBindTexture(GL_TEXTURE_2D, 0); + // Another option: map the buffer object into client's memory + // Probably this code could be moved somewhere else... + // batch->vertexBuffer[batch->currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // if (batch->vertexBuffer[batch->currentBuffer].vertices) + // { + // Update vertex data + // } + // glUnmapBuffer(GL_ARRAY_BUFFER); - TRACELOG(LOG_INFO, "TEXTURE: Depth texture loaded successfully"); + // Unbind the current VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(0); } - else + //------------------------------------------------------------------------------------------------------------ + + // Draw batch vertex buffers (considering VR stereo if required) + //------------------------------------------------------------------------------------------------------------ + Matrix matProjection = RLGL.State.projection; + Matrix matModelView = RLGL.State.modelview; + + int eyesCount = 1; + if (RLGL.State.stereoRender) eyesCount = 2; + + for (int eye = 0; eye < eyesCount; eye++) { - // 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); + if (eyesCount == 2) + { + // Setup current eye viewport (half screen width) + rlViewport(eye*RLGL.State.framebufferWidth/2, 0, RLGL.State.framebufferWidth/2, RLGL.State.framebufferHeight); - glBindRenderbuffer(GL_RENDERBUFFER, 0); + // Set current eye view offset to modelview matrix + rlSetMatrixModelview(MatrixMultiply(matModelView, RLGL.State.eyesViewOffset[eye])); + // Set current eye projection matrix + rlSetMatrixProjection(RLGL.State.eyesProjection[eye]); + } - TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Depth renderbuffer loaded successfully (%i bits)", id, (RLGL.ExtSupported.maxDepthBits >= 24)? RLGL.ExtSupported.maxDepthBits : 16); - } -#endif + // Draw buffers + if (batch->vertexBuffer[batch->currentBuffer].vCounter > 0) + { + // Set current shader and upload current MVP matrix + glUseProgram(RLGL.State.currentShader.id); - return id; -} + // Create modelview-projection matrix and upload to shader + Matrix matMVP = MatrixMultiply(RLGL.State.modelview, RLGL.State.projection); + glUniformMatrix4fv(RLGL.State.currentShader.locs[SHADER_LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); -// Load texture cubemap -// NOTE: Cubemap data is expected to be 6 images in a single data array (one after the other), -// expected the following convention: +X, -X, +Y, -Y, +Z, -Z -unsigned int rlLoadTextureCubemap(void *data, int size, int format) -{ - unsigned int id = 0; + if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); + glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_POSITION]); -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - unsigned int dataSize = rlGetPixelDataSize(size, size, format); + // Bind vertex attrib: texcoord (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); + glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_CUBE_MAP, id); + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); + glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_COLOR]); - unsigned int glInternalFormat, glFormat, glType; - rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[3]); + } - if (glInternalFormat != -1) - { - // Load cubemap faces - for (unsigned int i = 0; i < 6; i++) - { - if (data == NULL) + // Setup some default shader values + glUniform4f(RLGL.State.currentShader.locs[SHADER_LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1i(RLGL.State.currentShader.locs[SHADER_LOC_MAP_DIFFUSE], 0); // Active default sampler2D: texture0 + + // Activate additional sampler textures + // Those additional textures will be common for all draw calls of the batch + for (int i = 0; i < MAX_BATCH_ACTIVE_TEXTURES; i++) { - if (format < PIXELFORMAT_COMPRESSED_DXT1_RGB) + if (RLGL.State.activeTextureId[i] > 0) { - if (format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) - { - // Instead of using a sized internal texture format (GL_RGB16F, GL_RGB32F), we let the driver to choose the better format for us (GL_RGB) - if (RLGL.ExtSupported.texFloat32) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); - else TRACELOG(LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); - } - else if ((format == PIXELFORMAT_UNCOMPRESSED_R32) || (format == PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); - else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, NULL); + glActiveTexture(GL_TEXTURE0 + 1 + i); + glBindTexture(GL_TEXTURE_2D, RLGL.State.activeTextureId[i]); } - else TRACELOG(LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format"); } - else + + // Activate default sampler2D texture0 (one texture is always active for default batch shader) + // NOTE: Batch system accumulates calls by texture0 changes, additional textures are enabled for all the draw calls + glActiveTexture(GL_TEXTURE0); + + for (int i = 0, vertexOffset = 0; i < batch->drawsCounter; i++) { - if (format < PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize); - else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, dataSize, (unsigned char *)data + i*dataSize); - } + // Bind current draw call texture, activated as GL_TEXTURE0 and binded to sampler2D texture0 by default + glBindTexture(GL_TEXTURE_2D, batch->draws[i].textureId); + if ((batch->draws[i].mode == RL_LINES) || (batch->draws[i].mode == RL_TRIANGLES)) glDrawArrays(batch->draws[i].mode, vertexOffset, batch->draws[i].vertexCount); + else + { #if defined(GRAPHICS_API_OPENGL_33) - if (format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) - { - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; - glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - } - else if (format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) - { -#if defined(GRAPHICS_API_OPENGL_21) - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; + // 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, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(vertexOffset/4*6*sizeof(GLuint))); #endif -#if defined(GRAPHICS_API_OPENGL_33) - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; +#if defined(GRAPHICS_API_OPENGL_ES2) + glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(vertexOffset/4*6*sizeof(GLushort))); #endif - glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + + vertexOffset += (batch->draws[i].vertexCount + batch->draws[i].vertexAlignment); } -#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 + if (!RLGL.ExtSupported.vao) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); -#endif + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + } - if (id > 0) TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Cubemap texture created successfully (%ix%i)", id, size, size); - else TRACELOG(LOG_WARNING, "TEXTURE: Failed to load cubemap texture"); + if (RLGL.ExtSupported.vao) glBindVertexArray(0); // Unbind VAO - return id; -} + glUseProgram(0); // Unbind shader program + } + //------------------------------------------------------------------------------------------------------------ -// Update already loaded texture in GPU with new data -// NOTE: We don't know safely if internal texture format is the expected one... -void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data) -{ - glBindTexture(GL_TEXTURE_2D, id); + // Reset batch buffers + //------------------------------------------------------------------------------------------------------------ + // Reset vertex counters for next frame + batch->vertexBuffer[batch->currentBuffer].vCounter = 0; + batch->vertexBuffer[batch->currentBuffer].tcCounter = 0; + batch->vertexBuffer[batch->currentBuffer].cCounter = 0; - unsigned int glInternalFormat, glFormat, glType; - rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + // Reset depth for next draw + batch->currentDepth = -1.0f; - if ((glInternalFormat != -1) && (format < PIXELFORMAT_COMPRESSED_DXT1_RGB)) + // Restore projection/modelview matrices + RLGL.State.projection = matProjection; + RLGL.State.modelview = matModelView; + + // Reset RLGL.currentBatch->draws array + for (int i = 0; i < DEFAULT_BATCH_DRAWCALLS; i++) { - glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, (unsigned char *)data); + batch->draws[i].mode = RL_QUADS; + batch->draws[i].vertexCount = 0; + batch->draws[i].textureId = RLGL.State.defaultTextureId; } - else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to update for current texture format (%i)", id, format); -} -// Get OpenGL internal formats and data type from raylib PixelFormat -void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType) -{ - *glInternalFormat = -1; - *glFormat = -1; - *glType = -1; + // Reset active texture units for next batch + for (int i = 0; i < MAX_BATCH_ACTIVE_TEXTURES; i++) RLGL.State.activeTextureId[i] = 0; - switch (format) - { - #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; - case PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; - case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; - case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; - #if !defined(GRAPHICS_API_OPENGL_11) - case PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - #endif - #endif - #if defined(GRAPHICS_API_OPENGL_33) - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; - case PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; - case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; - case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; - case PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; - case PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; - case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; - #endif - #if !defined(GRAPHICS_API_OPENGL_11) - case PIXELFORMAT_COMPRESSED_DXT1_RGB: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; - case PIXELFORMAT_COMPRESSED_DXT1_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; - case PIXELFORMAT_COMPRESSED_DXT3_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; - case PIXELFORMAT_COMPRESSED_DXT5_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; - case PIXELFORMAT_COMPRESSED_ETC1_RGB: if (RLGL.ExtSupported.texCompETC1) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case PIXELFORMAT_COMPRESSED_ETC2_RGB: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case PIXELFORMAT_COMPRESSED_PVRT_RGB: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU - case PIXELFORMAT_COMPRESSED_PVRT_RGBA: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU - case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - #endif - default: TRACELOG(LOG_WARNING, "TEXTURE: Current format not supported (%i)", format); break; - } + // Reset draws counter to one draw for the batch + batch->drawsCounter = 1; + //------------------------------------------------------------------------------------------------------------ + + // Change to next buffer in the list (in case of multi-buffering) + batch->currentBuffer++; + if (batch->currentBuffer >= batch->buffersCount) batch->currentBuffer = 0; +#endif } -// Unload texture from GPU memory -void rlUnloadTexture(unsigned int id) +// Set the active render batch for rlgl +void rlSetRenderBatchActive(RenderBatch *batch) { - glDeleteTextures(1, &id); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlDrawRenderBatch(RLGL.currentBatch); + + if (batch != NULL) RLGL.currentBatch = batch; + else RLGL.currentBatch = &RLGL.defaultBatch; +#endif } -// Load a framebuffer to be used for rendering -// NOTE: No textures attached -unsigned int rlLoadFramebuffer(int width, int height) +// Update and draw internal render batch +void rlDrawRenderBatchActive(void) { - unsigned int fboId = 0; - -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(SUPPORT_RENDER_TEXTURES_HINT) - glGenFramebuffers(1, &fboId); // Create the framebuffer object - glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside #endif - - return fboId; } -// Attach color buffer texture to an fbo (unloads previous attachment) -// NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture -void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType) +// Check internal buffer overflow for a given number of vertex +// and force a RenderBatch draw call if required +bool rlCheckRenderBatchLimit(int vCount) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(SUPPORT_RENDER_TEXTURES_HINT) - glBindFramebuffer(GL_FRAMEBUFFER, fboId); + bool overflow = false; - switch (attachType) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vCounter + vCount) >= + (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementsCount*4)) { - case RL_ATTACHMENT_COLOR_CHANNEL0: - case RL_ATTACHMENT_COLOR_CHANNEL1: - case RL_ATTACHMENT_COLOR_CHANNEL2: - case RL_ATTACHMENT_COLOR_CHANNEL3: - case RL_ATTACHMENT_COLOR_CHANNEL4: - case RL_ATTACHMENT_COLOR_CHANNEL5: - case RL_ATTACHMENT_COLOR_CHANNEL6: - case RL_ATTACHMENT_COLOR_CHANNEL7: - { - if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, texId, 0); - else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_RENDERBUFFER, texId); - else if (texType >= RL_ATTACHMENT_CUBEMAP_POSITIVE_X) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_CUBE_MAP_POSITIVE_X + texType, texId, 0); - - } break; - case RL_ATTACHMENT_DEPTH: - { - if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texId, 0); - else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, texId); - - } break; - case RL_ATTACHMENT_STENCIL: - { - if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, 0); - else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, texId); - - } break; - default: break; + overflow = true; + rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside } - - glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif + + return overflow; } -// Verify render texture is complete -bool rlFramebufferComplete(unsigned int id) +// Textures data management +//----------------------------------------------------------------------------------------- +// Convert image data to OpenGL texture (returns OpenGL valid Id) +unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount) { - bool result = false; + glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(SUPPORT_RENDER_TEXTURES_HINT) - glBindFramebuffer(GL_FRAMEBUFFER, id); + unsigned int id = 0; - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + // Check texture format support by OpenGL 1.1 (compressed textures not supported) +#if defined(GRAPHICS_API_OPENGL_11) + if (format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) + { + TRACELOG(LOG_WARNING, "GL: OpenGL 1.1 does not support GPU compressed texture formats"); + return id; + } +#else + if ((!RLGL.ExtSupported.texCompDXT) && ((format == PIXELFORMAT_COMPRESSED_DXT1_RGB) || (format == PIXELFORMAT_COMPRESSED_DXT1_RGBA) || + (format == PIXELFORMAT_COMPRESSED_DXT3_RGBA) || (format == PIXELFORMAT_COMPRESSED_DXT5_RGBA))) + { + TRACELOG(LOG_WARNING, "GL: DXT compressed texture format not supported"); + return id; + } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((!RLGL.ExtSupported.texCompETC1) && (format == PIXELFORMAT_COMPRESSED_ETC1_RGB)) + { + TRACELOG(LOG_WARNING, "GL: ETC1 compressed texture format not supported"); + return id; + } - if (status != GL_FRAMEBUFFER_COMPLETE) + if ((!RLGL.ExtSupported.texCompETC2) && ((format == PIXELFORMAT_COMPRESSED_ETC2_RGB) || (format == PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA))) { - switch (status) - { - case GL_FRAMEBUFFER_UNSUPPORTED: TRACELOG(LOG_WARNING, "FBO: [ID %i] Framebuffer is unsupported", id); break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TRACELOG(LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete attachment", id); break; -#if defined(GRAPHICS_API_OPENGL_ES2) - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TRACELOG(LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete dimensions", id); break; -#endif - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TRACELOG(LOG_WARNING, "FBO: [ID %i] Framebuffer has a missing attachment", id); break; - default: break; - } + TRACELOG(LOG_WARNING, "GL: ETC2 compressed texture format not supported"); + return id; } - glBindFramebuffer(GL_FRAMEBUFFER, 0); + if ((!RLGL.ExtSupported.texCompPVRT) && ((format == PIXELFORMAT_COMPRESSED_PVRT_RGB) || (format == PIXELFORMAT_COMPRESSED_PVRT_RGBA))) + { + TRACELOG(LOG_WARNING, "GL: PVRT compressed texture format not supported"); + return id; + } - result = (status == GL_FRAMEBUFFER_COMPLETE); + if ((!RLGL.ExtSupported.texCompASTC) && ((format == PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA) || (format == PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA))) + { + TRACELOG(LOG_WARNING, "GL: ASTC compressed texture format not supported"); + return id; + } #endif +#endif // GRAPHICS_API_OPENGL_11 - return result; -} + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); -// Generate mipmap data for selected texture -void rlGenerateMipmaps(Texture2D *texture) -{ - glBindTexture(GL_TEXTURE_2D, texture->id); + glGenTextures(1, &id); // Generate texture id - // Check if texture is power-of-two (POT) - bool texIsPOT = false; +#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 - if (((texture->width > 0) && ((texture->width & (texture->width - 1)) == 0)) && - ((texture->height > 0) && ((texture->height & (texture->height - 1)) == 0))) texIsPOT = true; + glBindTexture(GL_TEXTURE_2D, id); -#if defined(GRAPHICS_API_OPENGL_11) - if (texIsPOT) + int mipWidth = width; + int mipHeight = height; + int mipOffset = 0; // Mipmap data offset + + // Load the different mipmap levels + for (int i = 0; i < mipmapCount; i++) { - // WARNING: Manual mipmap generation only works for RGBA 32bit textures! - if (texture->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - // Retrieve texture data from VRAM - void *texData = rlReadTexturePixels(*texture); + unsigned int mipSize = rlGetPixelDataSize(mipWidth, mipHeight, format); - // NOTE: Texture data size is reallocated to fit mipmaps data - // NOTE: CPU mipmap generation only supports RGBA 32bit data - int mipmapCount = GenerateMipmaps(texData, texture->width, texture->height); + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - int size = texture->width*texture->height*4; - int offset = size; + TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); - int mipWidth = texture->width/2; - int mipHeight = texture->height/2; + if (glInternalFormat != -1) + { + if (format < PIXELFORMAT_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 - // Load the mipmaps - for (int level = 1; level < mipmapCount; level++) + #if defined(GRAPHICS_API_OPENGL_33) + if (format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) { - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)texData + offset); - - size = mipWidth*mipHeight*4; - offset += size; - - mipWidth /= 2; - mipHeight /= 2; + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + else if (format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) + { + #if defined(GRAPHICS_API_OPENGL_21) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; + #elif defined(GRAPHICS_API_OPENGL_33) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; + #endif + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); } + #endif + } - texture->mipmaps = mipmapCount + 1; - RL_FREE(texData); // Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data + mipWidth /= 2; + mipHeight /= 2; + mipOffset += mipSize; - TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Mipmaps generated manually on CPU side, total: %i", texture->id, texture->mipmaps); - } - else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps for provided texture format", texture->id); + // Security check for NPOT textures + if (mipWidth < 1) mipWidth = 1; + if (mipHeight < 1) mipHeight = 1; } -#endif -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((texIsPOT) || (RLGL.ExtSupported.texNPOT)) - { - //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorythm: GL_FASTEST, GL_NICEST, GL_DONT_CARE - glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps - - #define MIN(a,b) (((a)<(b))?(a):(b)) - #define MAX(a,b) (((a)>(b))?(a):(b)) - texture->mipmaps = 1 + (int)floor(log(MAX(texture->width, texture->height))/log(2)); - TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Mipmaps generated automatically, total: %i", texture->id, texture->mipmaps); + // Texture parameters configuration + // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used +#if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used + if (RLGL.ExtSupported.texNPOT) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis + } + else + { + // NOTE: If using negative texture coordinates (LoadOBJ()), it does not work! + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis } +#else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis #endif - else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps", texture->id); - glBindTexture(GL_TEXTURE_2D, 0); -} + // 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 -// Upload vertex data into a VAO (if supported) and VBO -void rlLoadMesh(Mesh *mesh, bool dynamic) -{ - if (mesh->vaoId > 0) +#if defined(GRAPHICS_API_OPENGL_33) + if (mipmapCount > 1) { - // Check if mesh has already been loaded in GPU - TRACELOG(LOG_WARNING, "VAO: [ID %i] Trying to re-load an already loaded mesh", mesh->vaoId); - return; + // 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 - mesh->vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); + // At this point we have the texture loaded in GPU and texture parameters configured - 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 + // NOTE: If mipmaps were not in data, they are not generated automatically -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - int drawHint = GL_STATIC_DRAW; - if (dynamic) drawHint = GL_DYNAMIC_DRAW; + // Unbind current texture + glBindTexture(GL_TEXTURE_2D, 0); - if (RLGL.ExtSupported.vao) - { - // Initialize Quads VAO (Buffer A) - glGenVertexArrays(1, &mesh->vaoId); - glBindVertexArray(mesh->vaoId); - } + if (id > 0) TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Texture created successfully (%ix%i - %i mipmaps)", id, width, height, mipmapCount); + else TRACELOG(LOG_WARNING, "TEXTURE: Failed to load texture"); - // NOTE: Attributes must be uploaded considering default locations points + return id; +} - // Enable vertex attributes: position (shader-location = 0) - glGenBuffers(1, &mesh->vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[0]); - glBufferData(GL_ARRAY_BUFFER, mesh->vertexCount*3*sizeof(float), mesh->vertices, drawHint); - glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(0); +// 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, bool useRenderBuffer) +{ + unsigned int id = 0; - // Enable vertex attributes: texcoords (shader-location = 1) - glGenBuffers(1, &mesh->vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[1]); - glBufferData(GL_ARRAY_BUFFER, mesh->vertexCount*2*sizeof(float), mesh->texcoords, drawHint); - glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(1); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // In case depth textures not supported, we force renderbuffer usage + if (!RLGL.ExtSupported.texDepth) useRenderBuffer = true; - // 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, mesh->vertexCount*3*sizeof(float), 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); - } + // NOTE: We let the implementation to choose the best bit-depth + // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F + unsigned int glInternalFormat = GL_DEPTH_COMPONENT; - // 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, mesh->vertexCount*4*sizeof(unsigned char), 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); - } +#if defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.maxDepthBits == 32) glInternalFormat = GL_DEPTH_COMPONENT32_OES; + else if (RLGL.ExtSupported.maxDepthBits == 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES; + else glInternalFormat = GL_DEPTH_COMPONENT16; +#endif - // 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, mesh->vertexCount*4*sizeof(float), mesh->tangents, drawHint); - glVertexAttribPointer(4, 4, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(4); - } - else + if (!useRenderBuffer && RLGL.ExtSupported.texDepth) { - // Default tangents vertex attribute - glVertexAttrib4f(4, 0.0f, 0.0f, 0.0f, 0.0f); - glDisableVertexAttribArray(4); - } + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + glTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); - // 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, mesh->vertexCount*2*sizeof(float), 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); - } + 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); - if (mesh->indices != NULL) - { - glGenBuffers(1, &mesh->vboId[6]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->vboId[6]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->triangleCount*3*sizeof(unsigned short), mesh->indices, drawHint); - } + glBindTexture(GL_TEXTURE_2D, 0); - if (RLGL.ExtSupported.vao) - { - if (mesh->vaoId > 0) TRACELOG(LOG_INFO, "VAO: [ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId); - else TRACELOG(LOG_WARNING, "VAO: Failed to load mesh to VRAM (GPU)"); + TRACELOG(LOG_INFO, "TEXTURE: Depth texture loaded successfully"); } else { - TRACELOG(LOG_INFO, "VBO: Mesh uploaded successfully to VRAM (GPU)"); - } -#endif -} - -// Load a new attributes buffer -unsigned int rlLoadVertexBuffer(unsigned int vaoId, int index, 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 (RLGL.ExtSupported.vao) glBindVertexArray(vaoId); + // 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); - glGenBuffers(1, &id); - glBindBuffer(GL_ARRAY_BUFFER, id); - glBufferData(GL_ARRAY_BUFFER, size, buffer, drawHint); - glVertexAttribPointer(index, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(index); + glBindRenderbuffer(GL_RENDERBUFFER, 0); - if (RLGL.ExtSupported.vao) glBindVertexArray(0); + TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Depth renderbuffer loaded successfully (%i bits)", id, (RLGL.ExtSupported.maxDepthBits >= 24)? RLGL.ExtSupported.maxDepthBits : 16); + } #endif return id; } -// Update vertex or index data on GPU (upload new data to one buffer) -void rlUpdateMesh(Mesh mesh, int buffer, int count) +// Load texture cubemap +// NOTE: Cubemap data is expected to be 6 images in a single data array (one after the other), +// expected the following convention: +X, -X, +Y, -Y, +Z, -Z +unsigned int rlLoadTextureCubemap(void *data, int size, int format) { - rlUpdateMeshAt(mesh, buffer, count, 0); -} + unsigned int id = 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 count, int index) -{ #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Activate mesh VAO - if (RLGL.ExtSupported.vao) glBindVertexArray(mesh.vaoId); + unsigned int dataSize = rlGetPixelDataSize(size, size, format); - switch (buffer) - { - case 0: // Update vertices (vertex position) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); - if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*3*sizeof(float), mesh.vertices, GL_DYNAMIC_DRAW); - else if (index + count >= mesh.vertexCount) break; - else glBufferSubData(GL_ARRAY_BUFFER, index*3*sizeof(float), count*3*sizeof(float), mesh.vertices); + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_CUBE_MAP, id); - } break; - case 1: // Update texcoords (vertex texture coordinates) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); - if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*2*sizeof(float), mesh.texcoords, GL_DYNAMIC_DRAW); - else if (index + count >= mesh.vertexCount) break; - else glBufferSubData(GL_ARRAY_BUFFER, index*2*sizeof(float), count*2*sizeof(float), mesh.texcoords); + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - } break; - case 2: // Update normals (vertex normals) + if (glInternalFormat != -1) + { + // Load cubemap faces + for (unsigned int i = 0; i < 6; i++) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); - if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*3*sizeof(float), mesh.normals, GL_DYNAMIC_DRAW); - else if (index + count >= mesh.vertexCount) break; - else glBufferSubData(GL_ARRAY_BUFFER, index*3*sizeof(float), count*3*sizeof(float), mesh.normals); - - } break; - case 3: // Update colors (vertex colors) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); - if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*4*sizeof(unsigned char), mesh.colors, GL_DYNAMIC_DRAW); - else if (index + count >= mesh.vertexCount) break; - else glBufferSubData(GL_ARRAY_BUFFER, index*4*sizeof(unsigned char), count*4*sizeof(unsigned char), mesh.colors); - - } break; - case 4: // Update tangents (vertex tangents) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); - if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*4*sizeof(float), mesh.tangents, GL_DYNAMIC_DRAW); - else if (index + count >= mesh.vertexCount) break; - else glBufferSubData(GL_ARRAY_BUFFER, index*4*sizeof(float), count*4*sizeof(float), mesh.tangents); - - } break; - case 5: // Update texcoords2 (vertex second texture coordinates) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); - if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*2*sizeof(float), mesh.texcoords2, GL_DYNAMIC_DRAW); - else if (index + count >= mesh.vertexCount) break; - else glBufferSubData(GL_ARRAY_BUFFER, index*2*sizeof(float), count*2*sizeof(float), 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 && count >= mesh.triangleCount) glBufferData(GL_ELEMENT_ARRAY_BUFFER, count*3*sizeof(*indices), indices, GL_DYNAMIC_DRAW); - else if (index + count >= mesh.triangleCount) break; - else glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, index*3*sizeof(*indices), count*3*sizeof(*indices), indices); - - } break; - default: break; - } - - // Unbind the current VAO - if (RLGL.ExtSupported.vao) 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[MATERIAL_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[MATERIAL_MAP_DIFFUSE].color.r, material.maps[MATERIAL_MAP_DIFFUSE].color.g, material.maps[MATERIAL_MAP_DIFFUSE].color.b, material.maps[MATERIAL_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[SHADER_LOC_MATRIX_MODEL] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[SHADER_LOC_MATRIX_MODEL], transform); - - // Upload to shader material.colDiffuse - if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) - glUniform4f(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f, - (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f, - (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f, - (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f); - - // Upload to shader material.colSpecular (if available) - if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1) - glUniform4f(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], (float)material.maps[MATERIAL_MAP_SPECULAR].color.r/255.0f, - (float)material.maps[MATERIAL_MAP_SPECULAR].color.g/255.0f, - (float)material.maps[MATERIAL_MAP_SPECULAR].color.b/255.0f, - (float)material.maps[MATERIAL_MAP_SPECULAR].color.a/255.0f); - - if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[SHADER_LOC_MATRIX_VIEW], RLGL.State.modelview); - if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], RLGL.State.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 = RLGL.State.modelview; // View matrix (camera) - Matrix matProjection = RLGL.State.projection; // Projection matrix (perspective) - - // TODO: Consider possible transform matrices in the RLGL.State.stack - // Is this the right order? or should we start with the first stored matrix instead of the last one? - //Matrix matStackTransform = MatrixIdentity(); - //for (int i = RLGL.State.stackCounter; i > 0; i--) matStackTransform = MatrixMultiply(RLGL.State.stack[i], matStackTransform); - - // Transform to camera-space coordinates - Matrix matModelView = MatrixMultiply(transform, MatrixMultiply(RLGL.State.transform, 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 == MATERIAL_MAP_IRRADIANCE) || (i == MATERIAL_MAP_PREFILTER) || (i == MATERIAL_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[SHADER_LOC_MAP_DIFFUSE + i], i); - } - } - - // Bind vertex array objects (or VBOs) - if (RLGL.ExtSupported.vao) glBindVertexArray(mesh.vaoId); - else - { - // Bind mesh VBO data: vertex position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); - glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_POSITION]); - - // Bind mesh VBO data: vertex texcoords (shader-location = 1) - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); - glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); - - // Bind mesh VBO data: vertex normals (shader-location = 2, if available) - if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); - glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]); - } - - // Bind mesh VBO data: vertex colors (shader-location = 3, if available) - if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1) - { - if (mesh.vboId[3] != 0) + if (data == NULL) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); - glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); + if (format < PIXELFORMAT_COMPRESSED_DXT1_RGB) + { + if (format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) + { + // Instead of using a sized internal texture format (GL_RGB16F, GL_RGB32F), we let the driver to choose the better format for us (GL_RGB) + if (RLGL.ExtSupported.texFloat32) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); + else TRACELOG(LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); + } + else if ((format == PIXELFORMAT_UNCOMPRESSED_R32) || (format == PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); + else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, NULL); + } + else TRACELOG(LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format"); } else { - // Set default value for unused attribute - // NOTE: Required when using default shader and no VAO support - glVertexAttrib4f(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 1.0f, 1.0f, 1.0f, 1.0f); - glDisableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); + if (format < PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize); + else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, dataSize, (unsigned char *)data + i*dataSize); } - } - // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) - if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); - glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]); - } - - // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) - if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1) - { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); - glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); +#if defined(GRAPHICS_API_OPENGL_33) + if (format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) + { + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + else if (format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) + { +#if defined(GRAPHICS_API_OPENGL_21) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; +#elif defined(GRAPHICS_API_OPENGL_33) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; +#endif + glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } +#endif } - - if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vboId[6]); } - int eyesCount = 1; - if (RLGL.State.stereoRender) eyesCount = 2; - - for (int eye = 0; eye < eyesCount; eye++) - { - if (eyesCount == 1) RLGL.State.modelview = matModelView; - else - { - // Setup current eye viewport (half screen width) - rlViewport(eye*RLGL.State.framebufferWidth/2, 0, RLGL.State.framebufferWidth/2, RLGL.State.framebufferHeight); + // 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 - // Set current eye view offset to modelview matrix - rlSetMatrixModelview(MatrixMultiply(matModelView, RLGL.State.eyesViewOffset[eye])); - // Set current eye projection matrix - rlSetMatrixProjection(RLGL.State.eyesProjection[eye]); - } + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif - // Calculate model-view-projection matrix (MVP) - Matrix matMVP = MatrixMultiply(RLGL.State.modelview, RLGL.State.projection); // Transform to screen-space coordinates + if (id > 0) TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Cubemap texture created successfully (%ix%i)", id, size, size); + else TRACELOG(LOG_WARNING, "TEXTURE: Failed to load cubemap texture"); - // Send combined model-view-projection matrix to shader - glUniformMatrix4fv(material.shader.locs[SHADER_LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); + return id; +} - // 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); - } +// Update already loaded texture in GPU with new data +// NOTE: We don't know safely if internal texture format is the expected one... +void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data) +{ + glBindTexture(GL_TEXTURE_2D, id); - // Unbind all binded texture maps - for (int i = 0; i < MAX_MATERIAL_MAPS; i++) - { - glActiveTexture(GL_TEXTURE0 + i); // Set shader active texture - if ((i == MATERIAL_MAP_IRRADIANCE) || (i == MATERIAL_MAP_PREFILTER) || (i == MATERIAL_MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - else glBindTexture(GL_TEXTURE_2D, 0); // Unbind current active texture - } + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - // Unind vertex array objects (or VBOs) - if (RLGL.ExtSupported.vao) glBindVertexArray(0); - else + if ((glInternalFormat != -1) && (format < PIXELFORMAT_COMPRESSED_DXT1_RGB)) { - glBindBuffer(GL_ARRAY_BUFFER, 0); - if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, (unsigned char *)data); } - - // Unbind shader program - glUseProgram(0); - - // Restore RLGL.State.projection/RLGL.State.modelview matrices - // NOTE: In stereo rendering matrices are being modified to fit every eye - RLGL.State.projection = matProjection; - RLGL.State.modelview = matView; -#endif + else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to update for current texture format (%i)", id, format); } -// Draw a 3d mesh with material and transform -void rlDrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int count) +// Get OpenGL internal formats and data type from raylib PixelFormat +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType) { -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.ExtSupported.instancing) - { - // Bind shader program - glUseProgram(material.shader.id); - - // Upload to shader material.colDiffuse - if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) - glUniform4f(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f, - (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f, - (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f, - (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f); - - // Upload to shader material.colSpecular (if available) - if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1) - glUniform4f(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], (float)material.maps[MATERIAL_MAP_SPECULAR].color.r/255.0f, - (float)material.maps[MATERIAL_MAP_SPECULAR].color.g/255.0f, - (float)material.maps[MATERIAL_MAP_SPECULAR].color.b/255.0f, - (float)material.maps[MATERIAL_MAP_SPECULAR].color.a/255.0f); - - // 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 == MATERIAL_MAP_IRRADIANCE) || (i == MATERIAL_MAP_PREFILTER) || (i == MATERIAL_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[SHADER_LOC_MAP_DIFFUSE + i], i); - } - } - - // Bind vertex array objects (or VBOs) - glBindVertexArray(mesh.vaoId); - - // At this point the modelview matrix just contains the view matrix (camera) - // For instanced shaders "mvp" is not premultiplied by any instance transform, only RLGL.State.transform - glUniformMatrix4fv(material.shader.locs[SHADER_LOC_MATRIX_MVP], 1, false, - MatrixToFloat(MatrixMultiply(MatrixMultiply(RLGL.State.transform, RLGL.State.modelview), RLGL.State.projection))); - - float16* instanceTransforms = RL_MALLOC(count*sizeof(float16)); - - for (int i = 0; i < count; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]); - - // This could alternatively use a static VBO and either glMapBuffer or glBufferSubData. - // It isn't clear which would be reliably faster in all cases and on all platforms, and - // anecdotally glMapBuffer seems very slow (syncs) while glBufferSubData seems no faster - // since we're transferring all the transform matrices anyway. - unsigned int instancesB = 0; - glGenBuffers(1, &instancesB); - glBindBuffer(GL_ARRAY_BUFFER, instancesB); - glBufferData(GL_ARRAY_BUFFER, count*sizeof(float16), instanceTransforms, GL_STATIC_DRAW); - - // Instances are put in SHADER_LOC_MATRIX_MODEL attribute location with space for 4x Vector4, eg: - // layout (location = 12) in mat4 instance; - unsigned int instanceA = material.shader.locs[SHADER_LOC_MATRIX_MODEL]; - - for (unsigned int i = 0; i < 4; i++) - { - glEnableVertexAttribArray(instanceA+i); - glVertexAttribPointer(instanceA + i, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix), (void *)(i*sizeof(Vector4))); - glVertexAttribDivisor(instanceA + i, 1); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - // Draw instanced - if (mesh.indices != NULL) glDrawElementsInstanced(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0, count); - else glDrawArraysInstanced(GL_TRIANGLES, 0, mesh.vertexCount, count); - - glDeleteBuffers(1, &instancesB); - RL_FREE(instanceTransforms); - - // Unbind all binded texture maps - for (int i = 0; i < MAX_MATERIAL_MAPS; i++) - { - glActiveTexture(GL_TEXTURE0 + i); // Set shader active texture - if ((i == MATERIAL_MAP_IRRADIANCE) || (i == MATERIAL_MAP_PREFILTER) || (i == MATERIAL_MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - else glBindTexture(GL_TEXTURE_2D, 0); // Unbind current active texture - } - - // Unind vertex array objects (or VBOs) - glBindVertexArray(0); + *glInternalFormat = -1; + *glFormat = -1; + *glType = -1; - // Unbind shader program - glUseProgram(0); + switch (format) + { + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; + case PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + #if !defined(GRAPHICS_API_OPENGL_11) + case PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + #endif + #elif defined(GRAPHICS_API_OPENGL_33) + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; + case PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + case PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + #endif + #if !defined(GRAPHICS_API_OPENGL_11) + case PIXELFORMAT_COMPRESSED_DXT1_RGB: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; + case PIXELFORMAT_COMPRESSED_DXT1_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case PIXELFORMAT_COMPRESSED_DXT3_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case PIXELFORMAT_COMPRESSED_DXT5_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case PIXELFORMAT_COMPRESSED_ETC1_RGB: if (RLGL.ExtSupported.texCompETC1) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case PIXELFORMAT_COMPRESSED_ETC2_RGB: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case PIXELFORMAT_COMPRESSED_PVRT_RGB: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case PIXELFORMAT_COMPRESSED_PVRT_RGBA: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + #endif + default: TRACELOG(LOG_WARNING, "TEXTURE: Current format not supported (%i)", format); break; } -#endif } -// Unload mesh data from CPU and GPU -void rlUnloadMesh(Mesh *mesh) +// Unload texture from GPU memory +void rlUnloadTexture(unsigned int id) { -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - for (int i = 0; i < 7; i++) glDeleteBuffers(1, &mesh->vboId[i]); // DEFAULT_MESH_VERTEX_BUFFERS (model.c) - if (RLGL.ExtSupported.vao) - { - glBindVertexArray(0); - glDeleteVertexArrays(1, &mesh->vaoId); - TRACELOG(LOG_INFO, "VAO: [ID %i] Unloaded vertex data from VRAM (GPU)", mesh->vaoId); - } - else TRACELOG(LOG_INFO, "VBO: Unloaded vertex data from VRAM (GPU)"); -#endif - - RL_FREE(mesh->vboId); - mesh->vboId = NULL; + glDeleteTextures(1, &id); } -// Read screen pixel data (color buffer) -unsigned char *rlReadScreenPixels(int width, int height) + +// Generate mipmap data for selected texture +void rlGenerateMipmaps(Texture2D *texture) { - unsigned char *screenData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char)); + glBindTexture(GL_TEXTURE_2D, texture->id); - // 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); + // Check if texture is power-of-two (POT) + bool texIsPOT = false; - // Flip image vertically! - unsigned char *imgData = (unsigned char *)RL_MALLOC(width*height*4*sizeof(unsigned char)); + if (((texture->width > 0) && ((texture->width & (texture->width - 1)) == 0)) && + ((texture->height > 0) && ((texture->height & (texture->height - 1)) == 0))) texIsPOT = true; - for (int y = height - 1; y >= 0; y--) +#if defined(GRAPHICS_API_OPENGL_11) + if (texIsPOT) { - for (int x = 0; x < (width*4); x++) + // WARNING: Manual mipmap generation only works for RGBA 32bit textures! + if (texture->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) { - imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; // Flip line + // Retrieve texture data from VRAM + void *texData = rlReadTexturePixels(*texture); - // 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; + // NOTE: Texture data size is reallocated to fit mipmaps data + // NOTE: CPU mipmap generation only supports RGBA 32bit data + int mipmapCount = rlGenerateMipmapsData(texData, 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 *)texData + offset); + + size = mipWidth*mipHeight*4; + offset += size; + + mipWidth /= 2; + mipHeight /= 2; + } + + texture->mipmaps = mipmapCount + 1; + RL_FREE(texData); // Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data + + TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Mipmaps generated manually on CPU side, total: %i", texture->id, texture->mipmaps); } + else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps for provided texture format", texture->id); } +#endif +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((texIsPOT) || (RLGL.ExtSupported.texNPOT)) + { + //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorythm: GL_FASTEST, GL_NICEST, GL_DONT_CARE + glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically - RL_FREE(screenData); + 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 - return imgData; // NOTE: image data should be freed + #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)); + TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Mipmaps generated automatically, total: %i", texture->id, texture->mipmaps); + } +#endif + else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps", texture->id); + + glBindTexture(GL_TEXTURE_2D, 0); } + // Read texture pixel data void *rlReadTexturePixels(Texture2D texture) { @@ -3072,619 +2811,793 @@ void *rlReadTexturePixels(Texture2D texture) return pixels; } -// Set eyes projection matrices for stereo rendering -void rlSetMatrixProjectionStereo(Matrix right, Matrix left) -{ - RLGL.State.eyesProjection[0] = right; - RLGL.State.eyesProjection[1] = left; -} -// Set eyes view offsets matrices for stereo rendering -void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left) +// Read screen pixel data (color buffer) +unsigned char *rlReadScreenPixels(int width, int height) { - RLGL.State.eyesViewOffset[0] = right; - RLGL.State.eyesViewOffset[1] = left; -} + unsigned char *screenData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char)); -// Get default internal texture (white texture) -Texture2D rlGetTextureDefault(void) -{ - Texture2D texture = { 0 }; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - texture.id = RLGL.State.defaultTextureId; - texture.width = 1; - texture.height = 1; - texture.mipmaps = 1; - texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; -#endif - return texture; + // NOTE 1: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer + // NOTE 2: We are getting alpha channel! Be careful, it can be transparent if not cleared properly! + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData); + + // Flip image vertically! + unsigned char *imgData = (unsigned char *)RL_MALLOC(width*height*4*sizeof(unsigned char)); + + for (int y = height - 1; y >= 0; y--) + { + for (int x = 0; x < (width*4); x++) + { + imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; // Flip line + + // Set alpha component value to 255 (no trasparent image retrieval) + // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it! + if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255; + } + } + + RL_FREE(screenData); + + return imgData; // NOTE: image data should be freed } -// Get texture to draw shapes (RAII) -Texture2D rlGetShapesTexture(void) +// Framebuffer management (fbo) +//----------------------------------------------------------------------------------------- +// Load a framebuffer to be used for rendering +// NOTE: No textures attached +unsigned int rlLoadFramebuffer(int width, int height) { -#if defined(GRAPHICS_API_OPENGL_11) - Texture2D texture = { 0 }; - return texture; -#else - return RLGL.State.shapesTexture; + unsigned int fboId = 0; + +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(SUPPORT_RENDER_TEXTURES_HINT) + glGenFramebuffers(1, &fboId); // Create the framebuffer object + glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer #endif + + return fboId; } -// Get texture rectangle to draw shapes -Rectangle rlGetShapesTextureRec(void) +// Attach color buffer texture to an fbo (unloads previous attachment) +// NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture +void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType) { -#if defined(GRAPHICS_API_OPENGL_11) - Rectangle rec = { 0 }; - return rec; -#else - return RLGL.State.shapesTextureRec; +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(SUPPORT_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, fboId); + + switch (attachType) + { + case RL_ATTACHMENT_COLOR_CHANNEL0: + case RL_ATTACHMENT_COLOR_CHANNEL1: + case RL_ATTACHMENT_COLOR_CHANNEL2: + case RL_ATTACHMENT_COLOR_CHANNEL3: + case RL_ATTACHMENT_COLOR_CHANNEL4: + case RL_ATTACHMENT_COLOR_CHANNEL5: + case RL_ATTACHMENT_COLOR_CHANNEL6: + case RL_ATTACHMENT_COLOR_CHANNEL7: + { + if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, texId, 0); + else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_RENDERBUFFER, texId); + else if (texType >= RL_ATTACHMENT_CUBEMAP_POSITIVE_X) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_CUBE_MAP_POSITIVE_X + texType, texId, 0); + + } break; + case RL_ATTACHMENT_DEPTH: + { + if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texId, 0); + else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, texId); + + } break; + case RL_ATTACHMENT_STENCIL: + { + if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, 0); + else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, texId); + + } break; + default: break; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif } -// Define default texture used to draw shapes -void rlSetShapesTexture(Texture2D texture, Rectangle source) +// Verify render texture is complete +bool rlFramebufferComplete(unsigned int id) { -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - RLGL.State.shapesTexture = texture; - RLGL.State.shapesTextureRec = source; + bool result = false; + +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(SUPPORT_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, id); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + switch (status) + { + case GL_FRAMEBUFFER_UNSUPPORTED: TRACELOG(LOG_WARNING, "FBO: [ID %i] Framebuffer is unsupported", id); break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TRACELOG(LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete attachment", id); break; +#if defined(GRAPHICS_API_OPENGL_ES2) + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TRACELOG(LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete dimensions", id); break; #endif -} + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TRACELOG(LOG_WARNING, "FBO: [ID %i] Framebuffer has a missing attachment", id); break; + default: break; + } + } -// Get default shader -Shader rlGetShaderDefault(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - return RLGL.State.defaultShader; -#else - Shader shader = { 0 }; - return shader; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + result = (status == GL_FRAMEBUFFER_COMPLETE); #endif + + return result; } -// Load shader from code strings -// NOTE: If shader string is NULL, using default vertex/fragment shaders -unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) +// Unload framebuffer from GPU memory +// NOTE: All attached textures/cubemaps/renderbuffers are also deleted +void rlUnloadFramebuffer(unsigned int id) { - unsigned int id = 0; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - unsigned int vertexShaderId = RLGL.State.defaultVShaderId; - unsigned int fragmentShaderId = RLGL.State.defaultFShaderId; +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(SUPPORT_RENDER_TEXTURES_HINT) - if (vsCode != NULL) vertexShaderId = CompileShader(vsCode, GL_VERTEX_SHADER); - if (fsCode != NULL) fragmentShaderId = CompileShader(fsCode, GL_FRAGMENT_SHADER); + // Query depth attachment to automatically delete texture/renderbuffer + int depthType = 0, depthId = 0; + glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId); - if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShader.id; - else - { - id = LoadShaderProgram(vertexShaderId, fragmentShaderId); + unsigned int depthIdU = (unsigned int)depthId; + if (depthType == GL_RENDERBUFFER) glDeleteRenderbuffers(1, &depthIdU); + else if (depthType == GL_RENDERBUFFER) glDeleteTextures(1, &depthIdU); - if (vertexShaderId != RLGL.State.defaultVShaderId) - { - // Detach shader before deletion to make sure memory is freed - glDetachShader(id, vertexShaderId); - glDeleteShader(vertexShaderId); - } - if (fragmentShaderId != RLGL.State.defaultFShaderId) - { - // Detach shader before deletion to make sure memory is freed - glDetachShader(id, fragmentShaderId); - glDeleteShader(fragmentShaderId); - } + // NOTE: If a texture object is deleted while its image is attached to the *currently bound* framebuffer, + // the texture image is automatically detached from the currently bound framebuffer. - if (id == 0) - { - TRACELOG(LOG_WARNING, "SHADER: Failed to load custom shader code"); - id = RLGL.State.defaultShader.id; - } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &id); + + TRACELOG(LOG_INFO, "FBO: [ID %i] Unloaded framebuffer from VRAM (GPU)", id); +#endif +} + +// Vertex data management +//----------------------------------------------------------------------------------------- +// 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, "VAO: [ID %i] Trying to re-load an already loaded mesh", mesh->vaoId); + return; } - // Get available shader uniforms - // NOTE: This information is useful for debug... - int uniformCount = -1; + mesh->vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); - glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); + 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 - for (int i = 0; i < uniformCount; i++) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; + + if (RLGL.ExtSupported.vao) { - int namelen = -1; - int num = -1; - char name[256]; // Assume no variable names longer than 256 - GLenum type = GL_ZERO; + // Initialize Quads VAO (Buffer A) + glGenVertexArrays(1, &mesh->vaoId); + glBindVertexArray(mesh->vaoId); + } - // Get the name of the uniforms - glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name); + // NOTE: Attributes must be uploaded considering default locations points - name[namelen] = 0; + // Enable vertex attributes: position (shader-location = 0) + glGenBuffers(1, &mesh->vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[0]); + glBufferData(GL_ARRAY_BUFFER, mesh->vertexCount*3*sizeof(float), mesh->vertices, drawHint); + glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(0); - TRACELOGD("SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); - } -#endif + // Enable vertex attributes: texcoords (shader-location = 1) + glGenBuffers(1, &mesh->vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[1]); + glBufferData(GL_ARRAY_BUFFER, mesh->vertexCount*2*sizeof(float), mesh->texcoords, drawHint); + glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(1); - return id; -} + // 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, mesh->vertexCount*3*sizeof(float), 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); + } -// Unload shader program -void rlUnloadShaderProgram(unsigned int id) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDeleteProgram(id); -#endif -} + // 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, mesh->vertexCount*4*sizeof(unsigned char), 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); + } -// Set current shader -void rlSetShaderCurrent(Shader shader) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.State.currentShader.id != shader.id) + // Default tangent vertex attribute (shader-location = 4) + if (mesh->tangents != NULL) { - rlDrawRenderBatch(RLGL.currentBatch); - RLGL.State.currentShader = shader; + glGenBuffers(1, &mesh->vboId[4]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[4]); + glBufferData(GL_ARRAY_BUFFER, mesh->vertexCount*4*sizeof(float), 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); } -#endif -} -// Set blend mode -void rlSetBlendMode(int mode) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.State.currentBlendMode != mode) + // Default texcoord2 vertex attribute (shader-location = 5) + if (mesh->texcoords2 != NULL) { - rlDrawRenderBatch(RLGL.currentBatch); + glGenBuffers(1, &mesh->vboId[5]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[5]); + glBufferData(GL_ARRAY_BUFFER, mesh->vertexCount*2*sizeof(float), 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); + } - switch (mode) - { - case BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; - case BLEND_ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; - case BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; - case BLEND_ADD_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; - case BLEND_SUBTRACT_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_SUBTRACT); break; - case BLEND_CUSTOM: glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation); break; - default: break; - } + if (mesh->indices != NULL) + { + glGenBuffers(1, &mesh->vboId[6]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->vboId[6]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->triangleCount*3*sizeof(unsigned short), mesh->indices, drawHint); + } - RLGL.State.currentBlendMode = mode; + if (RLGL.ExtSupported.vao) + { + if (mesh->vaoId > 0) TRACELOG(LOG_INFO, "VAO: [ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId); + else TRACELOG(LOG_WARNING, "VAO: Failed to load mesh to VRAM (GPU)"); + } + else + { + TRACELOG(LOG_INFO, "VBO: Mesh uploaded successfully to VRAM (GPU)"); } #endif } -// Get shader location uniform -int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) +// Load a new attributes buffer +unsigned int rlLoadVertexBuffer(unsigned int vaoId, int index, void *buffer, int size, bool dynamic) { - int location = -1; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - location = glGetUniformLocation(shaderId, uniformName); -#endif - return location; -} + unsigned int id = 0; -// Get shader location attribute -int rlGetLocationAttrib(unsigned int shaderId, const char *attribName) -{ - int location = -1; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - location = glGetAttribLocation(shaderId, attribName); -#endif - return location; -} + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; -// Set shader value uniform -void rlSetUniform(int locIndex, const void *value, int uniformType, int count) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - switch (uniformType) - { - case SHADER_UNIFORM_FLOAT: glUniform1fv(locIndex, count, (float *)value); break; - case SHADER_UNIFORM_VEC2: glUniform2fv(locIndex, count, (float *)value); break; - case SHADER_UNIFORM_VEC3: glUniform3fv(locIndex, count, (float *)value); break; - case SHADER_UNIFORM_VEC4: glUniform4fv(locIndex, count, (float *)value); break; - case SHADER_UNIFORM_INT: glUniform1iv(locIndex, count, (int *)value); break; - case SHADER_UNIFORM_IVEC2: glUniform2iv(locIndex, count, (int *)value); break; - case SHADER_UNIFORM_IVEC3: glUniform3iv(locIndex, count, (int *)value); break; - case SHADER_UNIFORM_IVEC4: glUniform4iv(locIndex, count, (int *)value); break; - case SHADER_UNIFORM_SAMPLER2D: glUniform1iv(locIndex, count, (int *)value); break; - default: TRACELOG(LOG_WARNING, "SHADER: Failed to set uniform, data type not recognized"); - } + if (RLGL.ExtSupported.vao) glBindVertexArray(vaoId); + + glGenBuffers(1, &id); + glBindBuffer(GL_ARRAY_BUFFER, id); + glBufferData(GL_ARRAY_BUFFER, size, buffer, drawHint); + glVertexAttribPointer(index, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(index); + + if (RLGL.ExtSupported.vao) glBindVertexArray(0); #endif + + return id; } -// Set shader value uniform matrix -void rlSetUniformMatrix(int locIndex, Matrix mat) +// Update vertex or index data on GPU (upload new data to one buffer) +void rlUpdateMesh(Mesh mesh, int buffer, int count) { -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glUniformMatrix4fv(locIndex, 1, false, MatrixToFloat(mat)); -#endif + rlUpdateMeshAt(mesh, buffer, count, 0); } -// Set shader value uniform sampler -void rlSetUniformSampler(int locIndex, Texture2D texture) +// 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 count, int index) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Check if texture is already active - for (int i = 0; i < MAX_BATCH_ACTIVE_TEXTURES; i++) if (RLGL.State.activeTextureId[i] == texture.id) return; + // Activate mesh VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(mesh.vaoId); - // Register a new active texture for the internal batch system - // NOTE: Default texture is always activated as GL_TEXTURE0 - for (int i = 0; i < MAX_BATCH_ACTIVE_TEXTURES; i++) + switch (buffer) { - if (RLGL.State.activeTextureId[i] == 0) + case 0: // Update vertices (vertex position) { - glUniform1i(locIndex, 1 + i); // Activate new texture unit - RLGL.State.activeTextureId[i] = texture.id; // Save texture id for binding on drawing - break; - } + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*3*sizeof(float), mesh.vertices, GL_DYNAMIC_DRAW); + else if (index + count >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, index*3*sizeof(float), count*3*sizeof(float), mesh.vertices); + + } break; + case 1: // Update texcoords (vertex texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*2*sizeof(float), mesh.texcoords, GL_DYNAMIC_DRAW); + else if (index + count >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, index*2*sizeof(float), count*2*sizeof(float), mesh.texcoords); + + } break; + case 2: // Update normals (vertex normals) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*3*sizeof(float), mesh.normals, GL_DYNAMIC_DRAW); + else if (index + count >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, index*3*sizeof(float), count*3*sizeof(float), mesh.normals); + + } break; + case 3: // Update colors (vertex colors) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*4*sizeof(unsigned char), mesh.colors, GL_DYNAMIC_DRAW); + else if (index + count >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, index*4*sizeof(unsigned char), count*4*sizeof(unsigned char), mesh.colors); + + } break; + case 4: // Update tangents (vertex tangents) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); + if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*4*sizeof(float), mesh.tangents, GL_DYNAMIC_DRAW); + else if (index + count >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, index*4*sizeof(float), count*4*sizeof(float), mesh.tangents); + + } break; + case 5: // Update texcoords2 (vertex second texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); + if (index == 0 && count >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, count*2*sizeof(float), mesh.texcoords2, GL_DYNAMIC_DRAW); + else if (index + count >= mesh.vertexCount) break; + else glBufferSubData(GL_ARRAY_BUFFER, index*2*sizeof(float), count*2*sizeof(float), 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 && count >= mesh.triangleCount) glBufferData(GL_ELEMENT_ARRAY_BUFFER, count*3*sizeof(*indices), indices, GL_DYNAMIC_DRAW); + else if (index + count >= mesh.triangleCount) break; + else glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, index*3*sizeof(*indices), count*3*sizeof(*indices), indices); + + } break; + default: break; } -#endif -} -// Set a custom projection matrix (replaces internal projection matrix) -void rlSetMatrixProjection(Matrix projection) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - RLGL.State.projection = projection; -#endif -} + // Unbind the current VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(0); -// Return internal projection matrix -Matrix rlGetMatrixProjection(void) -{ -#if defined(GRAPHICS_API_OPENGL_11) - float mat[16]; - glGetFloatv(GL_PROJECTION_MATRIX,mat); - Matrix m; - m.m0 = mat[0]; m.m1 = mat[1]; m.m2 = mat[2]; m.m3 = mat[3]; - m.m4 = mat[4]; m.m5 = mat[5]; m.m6 = mat[6]; m.m7 = mat[7]; - m.m8 = mat[8]; m.m9 = mat[9]; m.m10 = mat[10]; m.m11 = mat[11]; - m.m12 = mat[12]; m.m13 = mat[13]; m.m14 = mat[14]; m.m15 = mat[15]; - return m; -#else - return RLGL.State.projection; + // 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 -# } -// Set a custom modelview matrix (replaces internal modelview matrix) -void rlSetMatrixModelview(Matrix view) +// 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) - RLGL.State.modelview = view; + glBindBuffer(GL_ARRAY_BUFFER, bufferId); + glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, data); #endif } -// Return internal modelview matrix -Matrix rlGetMatrixModelview(void) +// Draw a 3d mesh with material and transform +void rlDrawMesh(Mesh mesh, Material material, Matrix transform) { - 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 = RLGL.State.modelview; -#endif - return matrix; -} + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, material.maps[MATERIAL_MAP_DIFFUSE].texture.id); -// Generate cubemap texture from HDR texture -TextureCubemap rlGenTextureCubemap(Shader shader, Texture2D panorama, int size, int format) -{ - TextureCubemap cubemap = { 0 }; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube + // 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 - // STEP 1: Setup framebuffer - //------------------------------------------------------------------------------------------ - unsigned int rbo = rlLoadTextureDepth(size, size, true); - cubemap.id = rlLoadTextureCubemap(NULL, size, format); + 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 - unsigned int fbo = rlLoadFramebuffer(size, size); - rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER); - rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X); + rlPushMatrix(); + rlMultMatrixf(MatrixToFloat(transform)); + rlColor4ub(material.maps[MATERIAL_MAP_DIFFUSE].color.r, material.maps[MATERIAL_MAP_DIFFUSE].color.g, material.maps[MATERIAL_MAP_DIFFUSE].color.b, material.maps[MATERIAL_MAP_DIFFUSE].color.a); - // Check if framebuffer is complete with attachments (valid) - if (rlFramebufferComplete(fbo)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", fbo); - //------------------------------------------------------------------------------------------ + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, mesh.indices); + else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + rlPopMatrix(); - // STEP 2: Draw to framebuffer - //------------------------------------------------------------------------------------------ - // NOTE: Shader is used to convert HDR equirectangular environment map to cubemap equivalent (6 faces) - rlEnableShader(shader.id); + 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 - // Define projection matrix and send it to shader - Matrix matFboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR); - rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matFboProjection); + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +#endif - // Define view matrix for every side of the cubemap - 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 }) - }; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Bind shader program + glUseProgram(material.shader.id); -#if !defined(GENTEXTURECUBEMAP_USE_BATCH_SYSTEM) - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, panorama.id); -#endif + // Matrices and other values required by shader + //----------------------------------------------------- + // Calculate and send to shader model matrix (used by PBR shader) + if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[SHADER_LOC_MATRIX_MODEL], transform); - rlViewport(0, 0, size, size); // Set viewport to current fbo dimensions + // Upload to shader material.colDiffuse + if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) + glUniform4f(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f, + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f, + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f, + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f); - for (int i = 0; i < 6; i++) - { - rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]); - rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i); + // Upload to shader material.colSpecular (if available) + if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1) + glUniform4f(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], (float)material.maps[MATERIAL_MAP_SPECULAR].color.r/255.0f, + (float)material.maps[MATERIAL_MAP_SPECULAR].color.g/255.0f, + (float)material.maps[MATERIAL_MAP_SPECULAR].color.b/255.0f, + (float)material.maps[MATERIAL_MAP_SPECULAR].color.a/255.0f); - rlEnableFramebuffer(fbo); -#if defined(GENTEXTURECUBEMAP_USE_BATCH_SYSTEM) - rlEnableTexture(panorama.id); // WARNING: It must be called after enabling current framebuffer if using internal batch system! -#endif - rlClearScreenBuffers(); - GenDrawCube(); + if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[SHADER_LOC_MATRIX_VIEW], RLGL.State.modelview); + if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], RLGL.State.projection); -#if defined(GENTEXTURECUBEMAP_USE_BATCH_SYSTEM) - // Using internal batch system instead of raw OpenGL cube creating+drawing - // NOTE: DrawCubeV() is actually provided by models.c! -> rlGenTextureCubemap() should be moved to user code! - DrawCubeV(Vector3Zero(), Vector3One(), WHITE); - rlDrawRenderBatch(RLGL.currentBatch); -#endif + // 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 = RLGL.State.modelview; // View matrix (camera) + Matrix matProjection = RLGL.State.projection; // Projection matrix (perspective) + + // TODO: Consider possible transform matrices in the RLGL.State.stack + // Is this the right order? or should we start with the first stored matrix instead of the last one? + //Matrix matStackTransform = MatrixIdentity(); + //for (int i = RLGL.State.stackCounter; i > 0; i--) matStackTransform = MatrixMultiply(RLGL.State.stack[i], matStackTransform); + + // Transform to camera-space coordinates + Matrix matModelView = MatrixMultiply(transform, MatrixMultiply(RLGL.State.transform, 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 == MATERIAL_MAP_IRRADIANCE) || (i == MATERIAL_MAP_PREFILTER) || (i == MATERIAL_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[SHADER_LOC_MAP_DIFFUSE + i], i); + } } - //------------------------------------------------------------------------------------------ - // STEP 3: Unload framebuffer and reset state - //------------------------------------------------------------------------------------------ - rlDisableShader(); // Unbind shader - rlDisableTexture(); // Unbind texture - rlDisableFramebuffer(); // Unbind framebuffer - rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer) + // Bind vertex array objects (or VBOs) + if (RLGL.ExtSupported.vao) glBindVertexArray(mesh.vaoId); + else + { + // Bind mesh VBO data: vertex position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_POSITION]); - // Reset viewport dimensions to default - rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); - rlEnableBackfaceCulling(); - //------------------------------------------------------------------------------------------ + // Bind mesh VBO data: vertex texcoords (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); - cubemap.width = size; - cubemap.height = size; - cubemap.mipmaps = 1; - cubemap.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; -#endif - return cubemap; -} + // Bind mesh VBO data: vertex normals (shader-location = 2, if available) + if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]); + } -// Generate irradiance texture using cubemap data -TextureCubemap rlGenTextureIrradiance(Shader shader, TextureCubemap cubemap, int size) -{ - TextureCubemap irradiance = { 0 }; + // Bind mesh VBO data: vertex colors (shader-location = 3, if available) + if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1) + { + if (mesh.vboId[3] != 0) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); + } + else + { + // Set default value for unused attribute + // NOTE: Required when using default shader and no VAO support + glVertexAttrib4f(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 1.0f, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); + } + } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube + // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) + if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); + glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]); + } - // STEP 1: Setup framebuffer - //------------------------------------------------------------------------------------------ - unsigned int rbo = rlLoadTextureDepth(size, size, true); - irradiance.id = rlLoadTextureCubemap(NULL, size, PIXELFORMAT_UNCOMPRESSED_R32G32B32); + // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) + if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); + glVertexAttribPointer(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); + } - unsigned int fbo = rlLoadFramebuffer(size, size); - rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER); - rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X); - //------------------------------------------------------------------------------------------ + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vboId[6]); + } - // STEP 2: Draw to framebuffer - //------------------------------------------------------------------------------------------ - // NOTE: Shader is used to solve diffuse integral by convolution to create an irradiance cubemap - rlEnableShader(shader.id); + int eyesCount = 1; + if (RLGL.State.stereoRender) eyesCount = 2; - // Define projection matrix and send it to shader - Matrix matFboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR); - rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matFboProjection); + for (int eye = 0; eye < eyesCount; eye++) + { + if (eyesCount == 1) RLGL.State.modelview = matModelView; + else + { + // Setup current eye viewport (half screen width) + rlViewport(eye*RLGL.State.framebufferWidth/2, 0, RLGL.State.framebufferWidth/2, RLGL.State.framebufferHeight); - // Define view matrix for every side of the cubemap - 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 }) - }; + // Set current eye view offset to modelview matrix + rlSetMatrixModelview(MatrixMultiply(matModelView, RLGL.State.eyesViewOffset[eye])); + // Set current eye projection matrix + rlSetMatrixProjection(RLGL.State.eyesProjection[eye]); + } - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + // Calculate model-view-projection matrix (MVP) + Matrix matMVP = MatrixMultiply(RLGL.State.modelview, RLGL.State.projection); // Transform to screen-space coordinates - rlViewport(0, 0, size, size); // Set viewport to current fbo dimensions + // Send combined model-view-projection matrix to shader + glUniformMatrix4fv(material.shader.locs[SHADER_LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); - for (int i = 0; i < 6; i++) - { - rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]); - rlFramebufferAttach(fbo, irradiance.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i); + // 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); + } - rlEnableFramebuffer(fbo); - rlClearScreenBuffers(); - GenDrawCube(); + // Unbind all binded texture maps + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + { + glActiveTexture(GL_TEXTURE0 + i); // Set shader active texture + if ((i == MATERIAL_MAP_IRRADIANCE) || (i == MATERIAL_MAP_PREFILTER) || (i == MATERIAL_MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + else glBindTexture(GL_TEXTURE_2D, 0); // Unbind current active texture } - //------------------------------------------------------------------------------------------ - // STEP 3: Unload framebuffer and reset state - //------------------------------------------------------------------------------------------ - rlDisableShader(); // Unbind shader - rlDisableTexture(); // Unbind texture - rlDisableFramebuffer(); // Unbind framebuffer - rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer) + // Unind vertex array objects (or VBOs) + if (RLGL.ExtSupported.vao) glBindVertexArray(0); + else + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } - // Reset viewport dimensions to default - rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); - rlEnableBackfaceCulling(); - //------------------------------------------------------------------------------------------ + // Unbind shader program + glUseProgram(0); - irradiance.width = size; - irradiance.height = size; - irradiance.mipmaps = 1; - irradiance.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; + // Restore RLGL.State.projection/RLGL.State.modelview matrices + // NOTE: In stereo rendering matrices are being modified to fit every eye + RLGL.State.projection = matProjection; + RLGL.State.modelview = matView; #endif - return irradiance; } -// Generate prefilter texture using cubemap data -TextureCubemap rlGenTexturePrefilter(Shader shader, TextureCubemap cubemap, int size) +// Draw a 3d mesh with material and transform +void rlDrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int count) { - TextureCubemap prefilter = { 0 }; - -#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) - rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.instancing) + { + // Bind shader program + glUseProgram(material.shader.id); - // STEP 1: Setup framebuffer - //------------------------------------------------------------------------------------------ - unsigned int rbo = rlLoadTextureDepth(size, size, true); - prefilter.id = rlLoadTextureCubemap(NULL, size, PIXELFORMAT_UNCOMPRESSED_R32G32B32); - rlTextureParameters(prefilter.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_MIP_LINEAR); + // Upload to shader material.colDiffuse + if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) + glUniform4f(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f, + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f, + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f, + (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f); - unsigned int fbo = rlLoadFramebuffer(size, size); - rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER); - rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X); - //------------------------------------------------------------------------------------------ + // Upload to shader material.colSpecular (if available) + if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1) + glUniform4f(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], (float)material.maps[MATERIAL_MAP_SPECULAR].color.r/255.0f, + (float)material.maps[MATERIAL_MAP_SPECULAR].color.g/255.0f, + (float)material.maps[MATERIAL_MAP_SPECULAR].color.b/255.0f, + (float)material.maps[MATERIAL_MAP_SPECULAR].color.a/255.0f); - // Generate mipmaps for the prefiltered HDR texture - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + // 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 == MATERIAL_MAP_IRRADIANCE) || (i == MATERIAL_MAP_PREFILTER) || (i == MATERIAL_MAP_CUBEMAP)) + glBindTexture(GL_TEXTURE_CUBE_MAP, material.maps[i].texture.id); + else glBindTexture(GL_TEXTURE_2D, material.maps[i].texture.id); - // STEP 2: Draw to framebuffer - //------------------------------------------------------------------------------------------ - // NOTE: Shader is used to prefilter HDR and store data into mipmap levels + glUniform1i(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], i); + } + } - // Define projection matrix and send it to shader - Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR); - SetShaderValueMatrix(shader, shader.locs[SHADER_LOC_MATRIX_PROJECTION], fboProjection); + // Bind vertex array objects (or VBOs) + glBindVertexArray(mesh.vaoId); - // Define view matrix for every side of the cubemap - 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 }) - }; + // At this point the modelview matrix just contains the view matrix (camera) + // For instanced shaders "mvp" is not premultiplied by any instance transform, only RLGL.State.transform + glUniformMatrix4fv(material.shader.locs[SHADER_LOC_MATRIX_MVP], 1, false, + MatrixToFloat(MatrixMultiply(MatrixMultiply(RLGL.State.transform, RLGL.State.modelview), RLGL.State.projection))); - rlEnableShader(shader.id); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + float16* instanceTransforms = RL_MALLOC(count*sizeof(float16)); - // TODO: Locations should be taken out of this function... too shader dependant... - int roughnessLoc = GetShaderLocation(shader, "roughness"); + for (int i = 0; i < count; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]); - rlEnableFramebuffer(fbo); + // This could alternatively use a static VBO and either glMapBuffer or glBufferSubData. + // It isn't clear which would be reliably faster in all cases and on all platforms, and + // anecdotally glMapBuffer seems very slow (syncs) while glBufferSubData seems no faster + // since we're transferring all the transform matrices anyway. + unsigned int instancesB = 0; + glGenBuffers(1, &instancesB); + glBindBuffer(GL_ARRAY_BUFFER, instancesB); + glBufferData(GL_ARRAY_BUFFER, count*sizeof(float16), instanceTransforms, GL_STATIC_DRAW); - #define MAX_MIPMAP_LEVELS 5 // Max number of prefilter texture mipmaps + // Instances are put in SHADER_LOC_MATRIX_MODEL attribute location with space for 4x Vector4, eg: + // layout (location = 12) in mat4 instance; + unsigned int instanceA = material.shader.locs[SHADER_LOC_MATRIX_MODEL]; - 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); + for (unsigned int i = 0; i < 4; i++) + { + glEnableVertexAttribArray(instanceA+i); + glVertexAttribPointer(instanceA + i, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix), (void *)(i*sizeof(Vector4))); + glVertexAttribDivisor(instanceA + i, 1); + } - rlViewport(0, 0, mipWidth, mipHeight); + glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindRenderbuffer(GL_RENDERBUFFER, rbo); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight); + // Draw instanced + if (mesh.indices != NULL) glDrawElementsInstanced(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0, count); + else glDrawArraysInstanced(GL_TRIANGLES, 0, mesh.vertexCount, count); - float roughness = (float)mip/(float)(MAX_MIPMAP_LEVELS - 1); - glUniform1f(roughnessLoc, roughness); + glDeleteBuffers(1, &instancesB); + RL_FREE(instanceTransforms); - for (int i = 0; i < 6; i++) + // Unbind all binded texture maps + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) { - SetShaderValueMatrix(shader, shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilter.id, mip); - //rlFramebufferAttach(fbo, irradiance.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i); // TODO: Support mip levels? - - rlEnableFramebuffer(fbo); - rlClearScreenBuffers(); - GenDrawCube(); + glActiveTexture(GL_TEXTURE0 + i); // Set shader active texture + if ((i == MATERIAL_MAP_IRRADIANCE) || (i == MATERIAL_MAP_PREFILTER) || (i == MATERIAL_MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + else glBindTexture(GL_TEXTURE_2D, 0); // Unbind current active texture } - } - //------------------------------------------------------------------------------------------ - // STEP 3: Unload framebuffer and reset state - //------------------------------------------------------------------------------------------ - rlDisableShader(); // Unbind shader - rlDisableTexture(); // Unbind texture - rlDisableFramebuffer(); // Unbind framebuffer - rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer) + // Unind vertex array objects (or VBOs) + glBindVertexArray(0); - // Reset viewport dimensions to default - rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); - rlEnableBackfaceCulling(); - //------------------------------------------------------------------------------------------ + // Unbind shader program + glUseProgram(0); + } +#endif +} - prefilter.width = size; - prefilter.height = size; - //prefilter.mipmaps = 1 + (int)floor(log(size)/log(2)); // MAX_MIPMAP_LEVELS - //prefilter.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; +// Unload mesh data from CPU and GPU +void rlUnloadMesh(Mesh *mesh) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + for (int i = 0; i < 7; i++) glDeleteBuffers(1, &mesh->vboId[i]); // DEFAULT_MESH_VERTEX_BUFFERS (model.c) + if (RLGL.ExtSupported.vao) + { + glBindVertexArray(0); + glDeleteVertexArrays(1, &mesh->vaoId); + TRACELOG(LOG_INFO, "VAO: [ID %i] Unloaded vertex data from VRAM (GPU)", mesh->vaoId); + } + else TRACELOG(LOG_INFO, "VBO: Unloaded vertex data from VRAM (GPU)"); #endif - return prefilter; + + RL_FREE(mesh->vboId); + mesh->vboId = NULL; } -// Generate BRDF texture using cubemap data -// TODO: Review implementation: https://github.com/HectorMF/BRDFGenerator -Texture2D rlGenTextureBRDF(Shader shader, int size) +// Shaders management +//----------------------------------------------------------------------------------------------- +// Load shader from code strings +// NOTE: If shader string is NULL, using default vertex/fragment shaders +unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) { - Texture2D brdf = { 0 }; + unsigned int id = 0; + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // STEP 1: Setup framebuffer - //------------------------------------------------------------------------------------------ - unsigned int rbo = rlLoadTextureDepth(size, size, true); - brdf.id = rlLoadTexture(NULL, size, size, PIXELFORMAT_UNCOMPRESSED_R32G32B32, 1); + unsigned int vertexShaderId = RLGL.State.defaultVShaderId; + unsigned int fragmentShaderId = RLGL.State.defaultFShaderId; - unsigned int fbo = rlLoadFramebuffer(size, size); - rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER); - rlFramebufferAttach(fbo, brdf.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D); - //------------------------------------------------------------------------------------------ + if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER); + if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER); - // STEP 2: Draw to framebuffer - //------------------------------------------------------------------------------------------ - // NOTE: Render BRDF LUT into a quad using FBO + if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShader.id; + else + { + id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId); - rlEnableShader(shader.id); + if (vertexShaderId != RLGL.State.defaultVShaderId) + { + // Detach shader before deletion to make sure memory is freed + glDetachShader(id, vertexShaderId); + glDeleteShader(vertexShaderId); + } + if (fragmentShaderId != RLGL.State.defaultFShaderId) + { + // Detach shader before deletion to make sure memory is freed + glDetachShader(id, fragmentShaderId); + glDeleteShader(fragmentShaderId); + } - rlViewport(0, 0, size, size); + if (id == 0) + { + TRACELOG(LOG_WARNING, "SHADER: Failed to load custom shader code"); + id = RLGL.State.defaultShader.id; + } + } - rlEnableFramebuffer(fbo); - rlClearScreenBuffers(); - GenDrawQuad(); - //------------------------------------------------------------------------------------------ + // Get available shader uniforms + // NOTE: This information is useful for debug... + int uniformCount = -1; - // STEP 3: Unload framebuffer and reset state - //------------------------------------------------------------------------------------------ - rlDisableShader(); // Unbind shader - rlDisableTexture(); // Unbind texture - rlDisableFramebuffer(); // Unbind framebuffer - rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer) + glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); - // Reset viewport dimensions to default - rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); - //------------------------------------------------------------------------------------------ + 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; - brdf.width = size; - brdf.height = size; - brdf.mipmaps = 1; - brdf.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; + // Get the name of the uniforms + glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name); + + name[namelen] = 0; + + TRACELOGD("SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); + } #endif - return brdf; -} -//---------------------------------------------------------------------------------- -// Module specific Functions Definition -//---------------------------------------------------------------------------------- + return id; +} -#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 rlCompileShader(const char *shaderCode, int type) { - unsigned int shader = glCreateShader(type); - glShaderSource(shader, 1, &shaderStr, NULL); + unsigned int shader = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + shader = glCreateShader(type); + glShaderSource(shader, 1, &shaderCode, NULL); GLint success = 0; glCompileShader(shader); @@ -3707,12 +3620,13 @@ static unsigned int CompileShader(const char *shaderStr, int type) } } else TRACELOG(LOG_INFO, "SHADER: [ID %i] Compiled successfully", shader); +#endif return shader; } // Load custom shader strings and return program id -static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) +unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) { unsigned int program = 0; @@ -3764,523 +3678,602 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad return program; } - -// Load default shader (just vertex positioning and texture coloring) -// NOTE: This shader program is used for internal buffers -static Shader LoadShaderDefault(void) +// Unload shader program +void rlUnloadShaderProgram(unsigned int id) { - 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) + glDeleteProgram(id); - // Vertex shader directly defined, no external file required - const char *defaultVShaderStr = -#if defined(GRAPHICS_API_OPENGL_21) - "#version 120 \n" -#endif -#if 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" -#endif -#if 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" + TRACELOG(LOG_INFO, "SHADER: [ID %i] Unloaded shader program data from VRAM (GPU)", id); #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" -#endif -#if 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" -#endif -#if 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" +// Get shader location uniform +int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) +{ + int location = -1; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + location = glGetUniformLocation(shaderId, uniformName); + + if (location == -1) TRACELOG(LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName); + else TRACELOG(LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location); #endif -#if defined(GRAPHICS_API_OPENGL_33) - " vec4 texelColor = texture(texture0, fragTexCoord); \n" - " finalColor = texelColor*colDiffuse*fragColor; \n" + return location; +} + +// Get shader location attribute +int rlGetLocationAttrib(unsigned int shaderId, const char *attribName) +{ + int location = -1; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + location = glGetAttribLocation(shaderId, attribName); + + if (location == -1) TRACELOG(LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName); + else TRACELOG(LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location); #endif - "} \n"; - - // NOTE: Compiled vertex/fragment shaders are kept for re-use - RLGL.State.defaultVShaderId = CompileShader(defaultVShaderStr, GL_VERTEX_SHADER); // Compile default vertex shader - RLGL.State.defaultFShaderId = CompileShader(defaultFShaderStr, GL_FRAGMENT_SHADER); // Compile default fragment shader - - shader.id = LoadShaderProgram(RLGL.State.defaultVShaderId, RLGL.State.defaultFShaderId); + return location; +} - if (shader.id > 0) +// Set shader value uniform +void rlSetUniform(int locIndex, const void *value, int uniformType, int count) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + switch (uniformType) { - TRACELOG(LOG_INFO, "SHADER: [ID %i] Default shader loaded successfully", shader.id); - - // Set default shader locations: attributes locations - shader.locs[SHADER_LOC_VERTEX_POSITION] = glGetAttribLocation(shader.id, "vertexPosition"); - shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(shader.id, "vertexTexCoord"); - shader.locs[SHADER_LOC_VERTEX_COLOR] = glGetAttribLocation(shader.id, "vertexColor"); - - // Set default shader locations: uniform locations - shader.locs[SHADER_LOC_MATRIX_MVP] = glGetUniformLocation(shader.id, "mvp"); - shader.locs[SHADER_LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader.id, "colDiffuse"); - shader.locs[SHADER_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); + case SHADER_UNIFORM_FLOAT: glUniform1fv(locIndex, count, (float *)value); break; + case SHADER_UNIFORM_VEC2: glUniform2fv(locIndex, count, (float *)value); break; + case SHADER_UNIFORM_VEC3: glUniform3fv(locIndex, count, (float *)value); break; + case SHADER_UNIFORM_VEC4: glUniform4fv(locIndex, count, (float *)value); break; + case SHADER_UNIFORM_INT: glUniform1iv(locIndex, count, (int *)value); break; + case SHADER_UNIFORM_IVEC2: glUniform2iv(locIndex, count, (int *)value); break; + case SHADER_UNIFORM_IVEC3: glUniform3iv(locIndex, count, (int *)value); break; + case SHADER_UNIFORM_IVEC4: glUniform4iv(locIndex, count, (int *)value); break; + case SHADER_UNIFORM_SAMPLER2D: glUniform1iv(locIndex, count, (int *)value); break; + default: TRACELOG(LOG_WARNING, "SHADER: Failed to set uniform, data type not recognized"); } - else TRACELOG(LOG_WARNING, "SHADER: [ID %i] Failed to load default shader", shader.id); - - return shader; +#endif } -// 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) +// Set shader value uniform matrix +void rlSetUniformMatrix(int locIndex, Matrix mat) { - // 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[SHADER_LOC_VERTEX_POSITION] = glGetAttribLocation(shader->id, DEFAULT_SHADER_ATTRIB_NAME_POSITION); - shader->locs[SHADER_LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(shader->id, DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); - shader->locs[SHADER_LOC_VERTEX_TEXCOORD02] = glGetAttribLocation(shader->id, DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2); - shader->locs[SHADER_LOC_VERTEX_NORMAL] = glGetAttribLocation(shader->id, DEFAULT_SHADER_ATTRIB_NAME_NORMAL); - shader->locs[SHADER_LOC_VERTEX_TANGENT] = glGetAttribLocation(shader->id, DEFAULT_SHADER_ATTRIB_NAME_TANGENT); - shader->locs[SHADER_LOC_VERTEX_COLOR] = glGetAttribLocation(shader->id, DEFAULT_SHADER_ATTRIB_NAME_COLOR); - - // Get handles to GLSL uniform locations (vertex shader) - shader->locs[SHADER_LOC_MATRIX_MVP] = glGetUniformLocation(shader->id, "mvp"); - shader->locs[SHADER_LOC_MATRIX_PROJECTION] = glGetUniformLocation(shader->id, "projection"); - shader->locs[SHADER_LOC_MATRIX_VIEW] = glGetUniformLocation(shader->id, "view"); - - // Get handles to GLSL uniform locations (fragment shader) - shader->locs[SHADER_LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader->id, "colDiffuse"); - shader->locs[SHADER_LOC_MAP_DIFFUSE] = glGetUniformLocation(shader->id, "texture0"); - shader->locs[SHADER_LOC_MAP_SPECULAR] = glGetUniformLocation(shader->id, "texture1"); - shader->locs[SHADER_LOC_MAP_NORMAL] = glGetUniformLocation(shader->id, "texture2"); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUniformMatrix4fv(locIndex, 1, false, MatrixToFloat(mat)); +#endif } -// Unload default shader -static void UnloadShaderDefault(void) +// Set shader value uniform sampler +void rlSetUniformSampler(int locIndex, Texture2D texture) { - glUseProgram(0); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Check if texture is already active + for (int i = 0; i < MAX_BATCH_ACTIVE_TEXTURES; i++) if (RLGL.State.activeTextureId[i] == texture.id) return; - glDetachShader(RLGL.State.defaultShader.id, RLGL.State.defaultVShaderId); - glDetachShader(RLGL.State.defaultShader.id, RLGL.State.defaultFShaderId); - glDeleteShader(RLGL.State.defaultVShaderId); - glDeleteShader(RLGL.State.defaultFShaderId); + // Register a new active texture for the internal batch system + // NOTE: Default texture is always activated as GL_TEXTURE0 + for (int i = 0; i < MAX_BATCH_ACTIVE_TEXTURES; i++) + { + if (RLGL.State.activeTextureId[i] == 0) + { + glUniform1i(locIndex, 1 + i); // Activate new texture unit + RLGL.State.activeTextureId[i] = texture.id; // Save texture id for binding on drawing + break; + } + } +#endif +} - glDeleteProgram(RLGL.State.defaultShader.id); +// Set shader currently active +void rlSetShaderActive(Shader shader) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.State.currentShader.id != shader.id) + { + rlDrawRenderBatch(RLGL.currentBatch); + RLGL.State.currentShader = shader; + } +#endif +} - RL_FREE(RLGL.State.defaultShader.locs); +// Matrix state management +//----------------------------------------------------------------------------------------- +// Return internal modelview matrix +Matrix rlGetMatrixModelview(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 = RLGL.State.modelview; +#endif + return matrix; } -// Load render batch -RenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements) +// Return internal projection matrix +Matrix rlGetMatrixProjection(void) { - RenderBatch batch = { 0 }; +#if defined(GRAPHICS_API_OPENGL_11) + float mat[16]; + glGetFloatv(GL_PROJECTION_MATRIX,mat); + Matrix m; + m.m0 = mat[0]; m.m1 = mat[1]; m.m2 = mat[2]; m.m3 = mat[3]; + m.m4 = mat[4]; m.m5 = mat[5]; m.m6 = mat[6]; m.m7 = mat[7]; + m.m8 = mat[8]; m.m9 = mat[9]; m.m10 = mat[10]; m.m11 = mat[11]; + m.m12 = mat[12]; m.m13 = mat[13]; m.m14 = mat[14]; m.m15 = mat[15]; + return m; +#else + return RLGL.State.projection; +#endif +# +} - // Initialize CPU (RAM) vertex buffers (position, texcoord, color data and indexes) - //-------------------------------------------------------------------------------------------- - batch.vertexBuffer = (VertexBuffer *)RL_MALLOC(sizeof(VertexBuffer)*numBuffers); +// Set a custom modelview matrix (replaces internal modelview matrix) +void rlSetMatrixModelview(Matrix view) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.modelview = view; +#endif +} - for (int i = 0; i < numBuffers; i++) - { - batch.vertexBuffer[i].elementsCount = bufferElements; +// Set a custom projection matrix (replaces internal projection matrix) +void rlSetMatrixProjection(Matrix projection) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.projection = projection; +#endif +} - batch.vertexBuffer[i].vertices = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float)); // 3 float by vertex, 4 vertex by quad - batch.vertexBuffer[i].texcoords = (float *)RL_MALLOC(bufferElements*2*4*sizeof(float)); // 2 float by texcoord, 4 texcoord by quad - batch.vertexBuffer[i].colors = (unsigned char *)RL_MALLOC(bufferElements*4*4*sizeof(unsigned char)); // 4 float by color, 4 colors by quad -#if defined(GRAPHICS_API_OPENGL_33) - batch.vertexBuffer[i].indices = (unsigned int *)RL_MALLOC(bufferElements*6*sizeof(unsigned int)); // 6 int by quad (indices) +// Set eyes projection matrices for stereo rendering +void rlSetMatrixProjectionStereo(Matrix right, Matrix left) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.eyesProjection[0] = right; + RLGL.State.eyesProjection[1] = left; #endif -#if defined(GRAPHICS_API_OPENGL_ES2) - batch.vertexBuffer[i].indices = (unsigned short *)RL_MALLOC(bufferElements*6*sizeof(unsigned short)); // 6 int by quad (indices) +} + +// Set eyes view offsets matrices for stereo rendering +void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.eyesViewOffset[0] = right; + RLGL.State.eyesViewOffset[1] = left; #endif +} - for (int j = 0; j < (3*4*bufferElements); j++) batch.vertexBuffer[i].vertices[j] = 0.0f; - for (int j = 0; j < (2*4*bufferElements); j++) batch.vertexBuffer[i].texcoords[j] = 0.0f; - for (int j = 0; j < (4*4*bufferElements); j++) batch.vertexBuffer[i].colors[j] = 0; +// Texture maps generation (PBR) +//------------------------------------------------------------------------------------------- +// Generate cubemap texture from HDR texture +TextureCubemap rlGenTextureCubemap(Shader shader, Texture2D panorama, int size, int format) +{ + TextureCubemap cubemap = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube - int k = 0; + // STEP 1: Setup framebuffer + //------------------------------------------------------------------------------------------ + unsigned int rbo = rlLoadTextureDepth(size, size, true); + cubemap.id = rlLoadTextureCubemap(NULL, size, format); - // Indices can be initialized right now - for (int j = 0; j < (6*bufferElements); j += 6) - { - batch.vertexBuffer[i].indices[j] = 4*k; - batch.vertexBuffer[i].indices[j + 1] = 4*k + 1; - batch.vertexBuffer[i].indices[j + 2] = 4*k + 2; - batch.vertexBuffer[i].indices[j + 3] = 4*k; - batch.vertexBuffer[i].indices[j + 4] = 4*k + 2; - batch.vertexBuffer[i].indices[j + 5] = 4*k + 3; + unsigned int fbo = rlLoadFramebuffer(size, size); + rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER); + rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X); - k++; - } + // Check if framebuffer is complete with attachments (valid) + if (rlFramebufferComplete(fbo)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", fbo); + //------------------------------------------------------------------------------------------ - batch.vertexBuffer[i].vCounter = 0; - batch.vertexBuffer[i].tcCounter = 0; - batch.vertexBuffer[i].cCounter = 0; - } + // STEP 2: Draw to framebuffer + //------------------------------------------------------------------------------------------ + // NOTE: Shader is used to convert HDR equirectangular environment map to cubemap equivalent (6 faces) + rlEnableShader(shader.id); - TRACELOG(LOG_INFO, "RLGL: Internal vertex buffers initialized successfully in RAM (CPU)"); - //-------------------------------------------------------------------------------------------- + // Define projection matrix and send it to shader + Matrix matFboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR); + rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matFboProjection); - // Upload to GPU (VRAM) vertex data and initialize VAOs/VBOs - //-------------------------------------------------------------------------------------------- - for (int i = 0; i < numBuffers; i++) - { - if (RLGL.ExtSupported.vao) - { - // Initialize Quads VAO - glGenVertexArrays(1, &batch.vertexBuffer[i].vaoId); - glBindVertexArray(batch.vertexBuffer[i].vaoId); - } + // Define view matrix for every side of the cubemap + 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 }) + }; - // Quads - Vertex buffers binding and attributes enable - // Vertex position buffer (shader-location = 0) - glGenBuffers(1, &batch.vertexBuffer[i].vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[0]); - glBufferData(GL_ARRAY_BUFFER, bufferElements*3*4*sizeof(float), batch.vertexBuffer[i].vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_POSITION]); - glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); +#if !defined(GENTEXTURECUBEMAP_USE_BATCH_SYSTEM) + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, panorama.id); +#endif - // Vertex texcoord buffer (shader-location = 1) - glGenBuffers(1, &batch.vertexBuffer[i].vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[1]); - glBufferData(GL_ARRAY_BUFFER, bufferElements*2*4*sizeof(float), batch.vertexBuffer[i].texcoords, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); - glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + rlViewport(0, 0, size, size); // Set viewport to current fbo dimensions - // Vertex color buffer (shader-location = 3) - glGenBuffers(1, &batch.vertexBuffer[i].vboId[2]); - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[2]); - glBufferData(GL_ARRAY_BUFFER, bufferElements*4*4*sizeof(unsigned char), batch.vertexBuffer[i].colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_COLOR]); - glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + for (int i = 0; i < 6; i++) + { + rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]); + rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i); - // Fill index buffer - glGenBuffers(1, &batch.vertexBuffer[i].vboId[3]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[3]); -#if defined(GRAPHICS_API_OPENGL_33) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(int), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); + rlEnableFramebuffer(fbo); +#if defined(GENTEXTURECUBEMAP_USE_BATCH_SYSTEM) + rlEnableTexture(panorama.id); // WARNING: It must be called after enabling current framebuffer if using internal batch system! #endif -#if defined(GRAPHICS_API_OPENGL_ES2) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(short), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); + rlClearScreenBuffers(); + rlGenDrawCube(); + +#if defined(GENTEXTURECUBEMAP_USE_BATCH_SYSTEM) + // Using internal batch system instead of raw OpenGL cube creating+drawing + // NOTE: DrawCubeV() is actually provided by models.c! -> rlGenTextureCubemap() should be moved to user code! + DrawCubeV(Vector3Zero(), Vector3One(), WHITE); + rlDrawRenderBatch(RLGL.currentBatch); #endif } + //------------------------------------------------------------------------------------------ - TRACELOG(LOG_INFO, "RLGL: Render batch vertex buffers loaded successfully"); + // STEP 3: Unload framebuffer and reset state + //------------------------------------------------------------------------------------------ + rlDisableShader(); // Unbind shader + rlDisableTexture(); // Unbind texture + rlDisableFramebuffer(); // Unbind framebuffer + rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer) - // Unbind the current VAO - if (RLGL.ExtSupported.vao) glBindVertexArray(0); - //-------------------------------------------------------------------------------------------- + // Reset viewport dimensions to default + rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); + rlEnableBackfaceCulling(); + //------------------------------------------------------------------------------------------ - // Init draw calls tracking system - //-------------------------------------------------------------------------------------------- - batch.draws = (DrawCall *)RL_MALLOC(DEFAULT_BATCH_DRAWCALLS*sizeof(DrawCall)); + cubemap.width = size; + cubemap.height = size; + cubemap.mipmaps = 1; + cubemap.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; +#endif + return cubemap; +} - for (int i = 0; i < DEFAULT_BATCH_DRAWCALLS; i++) - { - batch.draws[i].mode = RL_QUADS; - batch.draws[i].vertexCount = 0; - batch.draws[i].vertexAlignment = 0; - //batch.draws[i].vaoId = 0; - //batch.draws[i].shaderId = 0; - batch.draws[i].textureId = RLGL.State.defaultTextureId; - //batch.draws[i].RLGL.State.projection = MatrixIdentity(); - //batch.draws[i].RLGL.State.modelview = MatrixIdentity(); - } +// Generate irradiance texture using cubemap data +TextureCubemap rlGenTextureIrradiance(Shader shader, TextureCubemap cubemap, int size) +{ + TextureCubemap irradiance = { 0 }; - batch.buffersCount = numBuffers; // Record buffer count - batch.drawsCounter = 1; // Reset draws counter - batch.currentDepth = -1.0f; // Reset depth value - //-------------------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube - return batch; -} + // STEP 1: Setup framebuffer + //------------------------------------------------------------------------------------------ + unsigned int rbo = rlLoadTextureDepth(size, size, true); + irradiance.id = rlLoadTextureCubemap(NULL, size, PIXELFORMAT_UNCOMPRESSED_R32G32B32); -// Draw render batch -// NOTE: We require a pointer to reset batch and increase current buffer (multi-buffer) -void rlDrawRenderBatch(RenderBatch *batch) -{ - // Update batch vertex buffers - //------------------------------------------------------------------------------------------------------------ - // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) - // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) - if (batch->vertexBuffer[batch->currentBuffer].vCounter > 0) - { - // Activate elements VAO - if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); + unsigned int fbo = rlLoadFramebuffer(size, size); + rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER); + rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X); + //------------------------------------------------------------------------------------------ - // Vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); - glBufferSubData(GL_ARRAY_BUFFER, 0, batch->vertexBuffer[batch->currentBuffer].vCounter*3*sizeof(float), batch->vertexBuffer[batch->currentBuffer].vertices); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*batch->vertexBuffer[batch->currentBuffer].elementsCount, batch->vertexBuffer[batch->currentBuffer].vertices, GL_DYNAMIC_DRAW); // Update all buffer + // STEP 2: Draw to framebuffer + //------------------------------------------------------------------------------------------ + // NOTE: Shader is used to solve diffuse integral by convolution to create an irradiance cubemap + rlEnableShader(shader.id); - // Texture coordinates buffer - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); - glBufferSubData(GL_ARRAY_BUFFER, 0, batch->vertexBuffer[batch->currentBuffer].vCounter*2*sizeof(float), batch->vertexBuffer[batch->currentBuffer].texcoords); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*batch->vertexBuffer[batch->currentBuffer].elementsCount, batch->vertexBuffer[batch->currentBuffer].texcoords, GL_DYNAMIC_DRAW); // Update all buffer + // Define projection matrix and send it to shader + Matrix matFboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR); + rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matFboProjection); - // Colors buffer - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); - glBufferSubData(GL_ARRAY_BUFFER, 0, batch->vertexBuffer[batch->currentBuffer].vCounter*4*sizeof(unsigned char), batch->vertexBuffer[batch->currentBuffer].colors); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*batch->vertexBuffer[batch->currentBuffer].elementsCount, batch->vertexBuffer[batch->currentBuffer].colors, GL_DYNAMIC_DRAW); // Update all buffer + // Define view matrix for every side of the cubemap + 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 }) + }; - // 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. + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); - // Another option: map the buffer object into client's memory - // Probably this code could be moved somewhere else... - // batch->vertexBuffer[batch->currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); - // if (batch->vertexBuffer[batch->currentBuffer].vertices) - // { - // Update vertex data - // } - // glUnmapBuffer(GL_ARRAY_BUFFER); + rlViewport(0, 0, size, size); // Set viewport to current fbo dimensions - // Unbind the current VAO - if (RLGL.ExtSupported.vao) glBindVertexArray(0); + for (int i = 0; i < 6; i++) + { + rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]); + rlFramebufferAttach(fbo, irradiance.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i); + + rlEnableFramebuffer(fbo); + rlClearScreenBuffers(); + rlGenDrawCube(); } - //------------------------------------------------------------------------------------------------------------ + //------------------------------------------------------------------------------------------ - // Draw batch vertex buffers (considering VR stereo if required) - //------------------------------------------------------------------------------------------------------------ - Matrix matProjection = RLGL.State.projection; - Matrix matModelView = RLGL.State.modelview; + // STEP 3: Unload framebuffer and reset state + //------------------------------------------------------------------------------------------ + rlDisableShader(); // Unbind shader + rlDisableTexture(); // Unbind texture + rlDisableFramebuffer(); // Unbind framebuffer + rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer) - int eyesCount = 1; - if (RLGL.State.stereoRender) eyesCount = 2; + // Reset viewport dimensions to default + rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); + rlEnableBackfaceCulling(); + //------------------------------------------------------------------------------------------ - for (int eye = 0; eye < eyesCount; eye++) - { - if (eyesCount == 2) - { - // Setup current eye viewport (half screen width) - rlViewport(eye*RLGL.State.framebufferWidth/2, 0, RLGL.State.framebufferWidth/2, RLGL.State.framebufferHeight); + irradiance.width = size; + irradiance.height = size; + irradiance.mipmaps = 1; + irradiance.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; +#endif + return irradiance; +} - // Set current eye view offset to modelview matrix - rlSetMatrixModelview(MatrixMultiply(matModelView, RLGL.State.eyesViewOffset[eye])); - // Set current eye projection matrix - rlSetMatrixProjection(RLGL.State.eyesProjection[eye]); - } +// Generate prefilter texture using cubemap data +TextureCubemap rlGenTexturePrefilter(Shader shader, TextureCubemap cubemap, int size) +{ + TextureCubemap prefilter = { 0 }; - // Draw buffers - if (batch->vertexBuffer[batch->currentBuffer].vCounter > 0) - { - // Set current shader and upload current MVP matrix - glUseProgram(RLGL.State.currentShader.id); +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube - // Create modelview-projection matrix and upload to shader - Matrix matMVP = MatrixMultiply(RLGL.State.modelview, RLGL.State.projection); - glUniformMatrix4fv(RLGL.State.currentShader.locs[SHADER_LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); + // STEP 1: Setup framebuffer + //------------------------------------------------------------------------------------------ + unsigned int rbo = rlLoadTextureDepth(size, size, true); + prefilter.id = rlLoadTextureCubemap(NULL, size, PIXELFORMAT_UNCOMPRESSED_R32G32B32); + rlTextureParameters(prefilter.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_MIP_LINEAR); - if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); - else - { - // Bind vertex attrib: position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); - glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_POSITION]); + unsigned int fbo = rlLoadFramebuffer(size, size); + rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER); + rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X); + //------------------------------------------------------------------------------------------ - // Bind vertex attrib: texcoord (shader-location = 1) - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); - glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); + // Generate mipmaps for the prefiltered HDR texture + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - // Bind vertex attrib: color (shader-location = 3) - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); - glVertexAttribPointer(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(RLGL.State.currentShader.locs[SHADER_LOC_VERTEX_COLOR]); + // STEP 2: Draw to framebuffer + //------------------------------------------------------------------------------------------ + // NOTE: Shader is used to prefilter HDR and store data into mipmap levels - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[3]); - } + // Define projection matrix and send it to shader + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR); + SetShaderValueMatrix(shader, shader.locs[SHADER_LOC_MATRIX_PROJECTION], fboProjection); - // Setup some default shader values - glUniform4f(RLGL.State.currentShader.locs[SHADER_LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); - glUniform1i(RLGL.State.currentShader.locs[SHADER_LOC_MAP_DIFFUSE], 0); // Active default sampler2D: texture0 + // Define view matrix for every side of the cubemap + 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 }) + }; - // Activate additional sampler textures - // Those additional textures will be common for all draw calls of the batch - for (int i = 0; i < MAX_BATCH_ACTIVE_TEXTURES; i++) - { - if (RLGL.State.activeTextureId[i] > 0) - { - glActiveTexture(GL_TEXTURE0 + 1 + i); - glBindTexture(GL_TEXTURE_2D, RLGL.State.activeTextureId[i]); - } - } + rlEnableShader(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); - // Activate default sampler2D texture0 (one texture is always active for default batch shader) - // NOTE: Batch system accumulates calls by texture0 changes, additional textures are enabled for all the draw calls - glActiveTexture(GL_TEXTURE0); + // TODO: Locations should be taken out of this function... too shader dependant... + int roughnessLoc = GetShaderLocation(shader, "roughness"); - for (int i = 0, vertexOffset = 0; i < batch->drawsCounter; i++) - { - // Bind current draw call texture, activated as GL_TEXTURE0 and binded to sampler2D texture0 by default - glBindTexture(GL_TEXTURE_2D, batch->draws[i].textureId); + rlEnableFramebuffer(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); + + rlViewport(0, 0, mipWidth, mipHeight); - if ((batch->draws[i].mode == RL_LINES) || (batch->draws[i].mode == RL_TRIANGLES)) glDrawArrays(batch->draws[i].mode, vertexOffset, batch->draws[i].vertexCount); - else - { -#if defined(GRAPHICS_API_OPENGL_33) - // We need to define the number of indices to be processed: 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, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(vertexOffset/4*6*sizeof(GLuint))); -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) - glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(vertexOffset/4*6*sizeof(GLushort))); -#endif - } + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight); - vertexOffset += (batch->draws[i].vertexCount + batch->draws[i].vertexAlignment); - } + float roughness = (float)mip/(float)(MAX_MIPMAP_LEVELS - 1); + glUniform1f(roughnessLoc, roughness); - if (!RLGL.ExtSupported.vao) - { - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } + for (int i = 0; i < 6; i++) + { + SetShaderValueMatrix(shader, shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilter.id, mip); + //rlFramebufferAttach(fbo, irradiance.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i); // TODO: Support mip levels? - glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + rlEnableFramebuffer(fbo); + rlClearScreenBuffers(); + rlGenDrawCube(); } + } + //------------------------------------------------------------------------------------------ - if (RLGL.ExtSupported.vao) glBindVertexArray(0); // Unbind VAO + // STEP 3: Unload framebuffer and reset state + //------------------------------------------------------------------------------------------ + rlDisableShader(); // Unbind shader + rlDisableTexture(); // Unbind texture + rlDisableFramebuffer(); // Unbind framebuffer + rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer) - glUseProgram(0); // Unbind shader program - } - //------------------------------------------------------------------------------------------------------------ + // Reset viewport dimensions to default + rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); + rlEnableBackfaceCulling(); + //------------------------------------------------------------------------------------------ - // Reset batch buffers - //------------------------------------------------------------------------------------------------------------ - // Reset vertex counters for next frame - batch->vertexBuffer[batch->currentBuffer].vCounter = 0; - batch->vertexBuffer[batch->currentBuffer].tcCounter = 0; - batch->vertexBuffer[batch->currentBuffer].cCounter = 0; + prefilter.width = size; + prefilter.height = size; + //prefilter.mipmaps = 1 + (int)floor(log(size)/log(2)); // MAX_MIPMAP_LEVELS + //prefilter.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; +#endif + return prefilter; +} - // Reset depth for next draw - batch->currentDepth = -1.0f; +// Generate BRDF texture using cubemap data +// TODO: Review implementation: https://github.com/HectorMF/BRDFGenerator +Texture2D rlGenTextureBRDF(Shader shader, int size) +{ + Texture2D brdf = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // STEP 1: Setup framebuffer + //------------------------------------------------------------------------------------------ + unsigned int rbo = rlLoadTextureDepth(size, size, true); + brdf.id = rlLoadTexture(NULL, size, size, PIXELFORMAT_UNCOMPRESSED_R32G32B32, 1); - // Restore projection/modelview matrices - RLGL.State.projection = matProjection; - RLGL.State.modelview = matModelView; + unsigned int fbo = rlLoadFramebuffer(size, size); + rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER); + rlFramebufferAttach(fbo, brdf.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D); + //------------------------------------------------------------------------------------------ - // Reset RLGL.currentBatch->draws array - for (int i = 0; i < DEFAULT_BATCH_DRAWCALLS; i++) - { - batch->draws[i].mode = RL_QUADS; - batch->draws[i].vertexCount = 0; - batch->draws[i].textureId = RLGL.State.defaultTextureId; - } + // STEP 2: Draw to framebuffer + //------------------------------------------------------------------------------------------ + // NOTE: Render BRDF LUT into a quad using FBO - // Reset active texture units for next batch - for (int i = 0; i < MAX_BATCH_ACTIVE_TEXTURES; i++) RLGL.State.activeTextureId[i] = 0; + rlEnableShader(shader.id); - // Reset draws counter to one draw for the batch - batch->drawsCounter = 1; - //------------------------------------------------------------------------------------------------------------ + rlViewport(0, 0, size, size); - // Change to next buffer in the list (in case of multi-buffering) - batch->currentBuffer++; - if (batch->currentBuffer >= batch->buffersCount) batch->currentBuffer = 0; + rlEnableFramebuffer(fbo); + rlClearScreenBuffers(); + rlGenDrawQuad(); + //------------------------------------------------------------------------------------------ + + // STEP 3: Unload framebuffer and reset state + //------------------------------------------------------------------------------------------ + rlDisableShader(); // Unbind shader + rlDisableTexture(); // Unbind texture + rlDisableFramebuffer(); // Unbind framebuffer + rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer) + + // Reset viewport dimensions to default + rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); + //------------------------------------------------------------------------------------------ + + brdf.width = size; + brdf.height = size; + brdf.mipmaps = 1; + brdf.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; +#endif + return brdf; } -// Unload default internal buffers vertex data from CPU and GPU -void rlUnloadRenderBatch(RenderBatch batch) +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Load default shader (just vertex positioning and texture coloring) +// NOTE: This shader program is used for internal buffers +static Shader rlLoadShaderDefault(void) { - // Unbind everything - if (RLGL.ExtSupported.vao) glBindVertexArray(0); - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(2); - glDisableVertexAttribArray(3); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + Shader shader = { 0 }; + shader.locs = (int *)RL_CALLOC(MAX_SHADER_LOCATIONS, sizeof(int)); - // Unload all vertex buffers data - for (int i = 0; i < batch.buffersCount; i++) + // 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" +#endif +#if 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" +#endif +#if 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" +#endif +#if 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" +#endif +#if 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" +#endif +#if 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 + RLGL.State.defaultVShaderId = rlCompileShader(defaultVShaderStr, GL_VERTEX_SHADER); // Compile default vertex shader + RLGL.State.defaultFShaderId = rlCompileShader(defaultFShaderStr, GL_FRAGMENT_SHADER); // Compile default fragment shader + + shader.id = rlLoadShaderProgram(RLGL.State.defaultVShaderId, RLGL.State.defaultFShaderId); + + if (shader.id > 0) { - // Delete VBOs from GPU (VRAM) - glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[0]); - glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[1]); - glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[2]); - glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[3]); + TRACELOG(LOG_INFO, "SHADER: [ID %i] Default shader loaded successfully", shader.id); - // Delete VAOs from GPU (VRAM) - if (RLGL.ExtSupported.vao) glDeleteVertexArrays(1, &batch.vertexBuffer[i].vaoId); + // Set default shader locations: attributes locations + shader.locs[SHADER_LOC_VERTEX_POSITION] = glGetAttribLocation(shader.id, "vertexPosition"); + shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(shader.id, "vertexTexCoord"); + shader.locs[SHADER_LOC_VERTEX_COLOR] = glGetAttribLocation(shader.id, "vertexColor"); - // Free vertex arrays memory from CPU (RAM) - RL_FREE(batch.vertexBuffer[i].vertices); - RL_FREE(batch.vertexBuffer[i].texcoords); - RL_FREE(batch.vertexBuffer[i].colors); - RL_FREE(batch.vertexBuffer[i].indices); + // Set default shader locations: uniform locations + shader.locs[SHADER_LOC_MATRIX_MVP] = glGetUniformLocation(shader.id, "mvp"); + shader.locs[SHADER_LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader.id, "colDiffuse"); + shader.locs[SHADER_LOC_MAP_DIFFUSE] = glGetUniformLocation(shader.id, "texture0"); } + else TRACELOG(LOG_WARNING, "SHADER: [ID %i] Failed to load default shader", shader.id); - // Unload arrays - RL_FREE(batch.vertexBuffer); - RL_FREE(batch.draws); + return shader; } -// Set the active render batch for rlgl -void rlSetRenderBatchActive(RenderBatch *batch) +// Unload default shader +static void rlUnloadShaderDefault(void) { - rlDrawRenderBatch(RLGL.currentBatch); - RLGL.currentBatch = batch; -} + glUseProgram(0); -// Set default render batch for rlgl -void rlSetRenderBatchDefault(void) -{ - rlDrawRenderBatch(RLGL.currentBatch); - RLGL.currentBatch = &RLGL.defaultBatch; + glDetachShader(RLGL.State.defaultShader.id, RLGL.State.defaultVShaderId); + glDetachShader(RLGL.State.defaultShader.id, RLGL.State.defaultFShaderId); + glDeleteShader(RLGL.State.defaultVShaderId); + glDeleteShader(RLGL.State.defaultFShaderId); + + glDeleteProgram(RLGL.State.defaultShader.id); + + RL_FREE(RLGL.State.defaultShader.locs); } // Renders a 1x1 XY quad in NDC -static void GenDrawQuad(void) +static void rlGenDrawQuad(void) { unsigned int quadVAO = 0; unsigned int quadVBO = 0; @@ -4319,7 +4312,7 @@ static void GenDrawQuad(void) } // Renders a 1x1 3D cube in NDC -static void GenDrawCube(void) +static void rlGenDrawCube(void) { unsigned int cubeVAO = 0; unsigned int cubeVBO = 0; @@ -4398,7 +4391,7 @@ static void GenDrawCube(void) #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) +static int rlGenerateMipmapsData(unsigned char *data, int baseWidth, int baseHeight) { int mipmapCount = 1; // Required mipmap levels count (including base level) int width = baseWidth; @@ -4450,7 +4443,7 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) for (int mip = 1; mip < mipmapCount; mip++) { - mipmap = GenNextMipmap(image, width, height); + mipmap = rlGenNextMipmapData(image, width, height); offset += (width*height*4); // Size of last mipmap j = 0; @@ -4481,7 +4474,7 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) } // Manual mipmap generation (basic scaling algorithm) -static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight) +static Color *rlGenNextMipmapData(Color *srcData, int srcWidth, int srcHeight) { int x2, y2; Color prow, pcol; diff --git a/src/textures.c b/src/textures.c index b353c5b8..f98a4ad2 100644 --- a/src/textures.c +++ b/src/textures.c @@ -2950,6 +2950,12 @@ Image GetScreenData(void) return image; } +// Define default texture used to draw shapes +void SetShapesTexture(Texture2D texture, Rectangle source) +{ + rlSetShapesTexture(texture, source); +} + //------------------------------------------------------------------------------------ // Texture configuration functions //------------------------------------------------------------------------------------ -- cgit v1.2.3