diff --git a/src/models.c b/src/models.c index 4943aa5ef..f7e39863c 100644 --- a/src/models.c +++ b/src/models.c @@ -557,11 +557,10 @@ void DrawGizmo(Vector3 position) // Load a 3d model (from file) Model LoadModel(const char *fileName) { - VertexData vData; - Model model; + VertexData vData = { 0 }; - // TODO: Initialize default data for model in case loading fails, maybe a cube? + // NOTE: Initialize default data for model in case loading fails, maybe a cube? if (strcmp(GetExtension(fileName),"obj") == 0) vData = LoadOBJ(fileName); else TraceLog(WARNING, "[%s] Model extension not recognized, it can't be loaded", fileName); @@ -1589,7 +1588,7 @@ static float GetHeightValue(Color pixel) // Load OBJ mesh data static VertexData LoadOBJ(const char *fileName) { - VertexData vData; + VertexData vData = { 0 }; char dataType; char comments[200]; diff --git a/src/raylib.h b/src/raylib.h index 419989d04..d86554517 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -157,8 +157,13 @@ // Mouse Buttons #define MOUSE_LEFT_BUTTON 0 -#define MOUSE_RIGHT_BUTTON 1 -#define MOUSE_MIDDLE_BUTTON 2 +#if defined(PLATFORM_WEB) + #define MOUSE_RIGHT_BUTTON 2 + #define MOUSE_MIDDLE_BUTTON 1 +#else + #define MOUSE_RIGHT_BUTTON 1 + #define MOUSE_MIDDLE_BUTTON 2 +#endif // Gamepad Number #define GAMEPAD_PLAYER1 0 @@ -579,7 +584,7 @@ Image LoadImageEx(Color *pixels, int width, int height); Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image data from RAW file Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource) Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory -Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount); // Load a texture from raw data into GPU memory +Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat); // Load a texture from raw data into GPU memory Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource) Texture2D LoadTextureFromImage(Image image); // Load a texture from image data void UnloadImage(Image image); // Unload image from CPU memory (RAM) @@ -602,6 +607,7 @@ void ImageColorGrayscale(Image *image); void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100) void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255) void GenTextureMipmaps(Texture2D texture); // Generate GPU mipmaps for a texture +void UpdateTexture(Texture2D texture, void *pixels); // Update GPU texture with new data void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 diff --git a/src/rlgl.c b/src/rlgl.c index ebff0d531..766a834d8 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -1029,7 +1029,7 @@ void rlglInit(void) else TraceLog(WARNING, "[EXTENSION] VAO extension not found, VAO usage not supported"); if (npotSupported) TraceLog(INFO, "[EXTENSION] NPOT textures extension detected, full NPOT textures supported"); - else TraceLog(WARNING, "[EXTENSION] NPOT textures extension not found, NPOT textures support is limited (no-mipmaps, no-repeat"); + else TraceLog(WARNING, "[EXTENSION] NPOT textures extension not found, NPOT textures support is limited (no-mipmaps, no-repeat)"); #endif if (texCompDXTSupported) TraceLog(INFO, "[EXTENSION] DXT compressed textures supported"); @@ -1610,7 +1610,7 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height) // NOTE: Don't confuse glViewport with the transformation matrix // NOTE: glViewport just defines the area of the context that you will actually draw to. - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set background color (black) + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black) //glClearDepth(1.0f); // Clear depth buffer (default) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers, depth buffer is used for 3D @@ -1880,6 +1880,38 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma return id; } +void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data) +{ + glBindTexture(GL_TEXTURE_2D, id); + +#if defined(GRAPHICS_API_OPENGL_33) + switch (format) + { + case UNCOMPRESSED_GRAYSCALE: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_GRAY_ALPHA: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RG, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G6B5: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G5B5A1: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; + case UNCOMPRESSED_R4G4B4A4: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8A8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + default: TraceLog(WARNING, "Texture format updating not supported"); break; + } +#elif defined(GRAPHICS_API_OPENGL_11) || 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 + switch (format) + { + case UNCOMPRESSED_GRAYSCALE: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_GRAY_ALPHA: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G6B5: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G5B5A1: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; + case UNCOMPRESSED_R4G4B4A4: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8A8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + default: TraceLog(WARNING, "Texture format updating not supported"); break; + } +#endif +} + // Generate mipmap data for selected texture void rlglGenerateMipmaps(unsigned int textureId) { @@ -2019,8 +2051,6 @@ Model rlglLoadModel(VertexData mesh) } // Read screen pixel data (color buffer) -// ISSUE: Non pre-multiplied alpha when reading from backbuffer! -// TODO: Multiply alpha unsigned char *rlglReadScreenPixels(int width, int height) { unsigned char *screenData = (unsigned char *)malloc(width*height*sizeof(unsigned char)*4); @@ -2031,11 +2061,16 @@ unsigned char *rlglReadScreenPixels(int width, int height) // Flip image vertically! unsigned char *imgData = (unsigned char *)malloc(width*height*sizeof(unsigned char)*4); - for (int y = height-1; y >= 0; y--) + for (int y = height - 1; y >= 0; y--) { for (int x = 0; x < (width*4); x++) { - imgData[x + (height - y - 1)*width*4] = screenData[x + (y*width*4)]; + // Flip line + imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; + + // 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; } } @@ -2045,27 +2080,30 @@ unsigned char *rlglReadScreenPixels(int width, int height) } // Read texture pixel data -// NOTE: Retrieving pixel data from GPU (glGetTexImage()) not supported on OpenGL ES 2.0 -void *rlglReadTexturePixels(unsigned int textureId, unsigned int format) +// NOTE: glGetTexImage() is not available on OpenGL ES 2.0 +// Texture2D width and height are required on OpenGL ES 2.0. There is no way to get it from texture id. +void *rlglReadTexturePixels(Texture2D texture) { void *pixels = NULL; #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - int width, height; - - glBindTexture(GL_TEXTURE_2D, textureId); + glBindTexture(GL_TEXTURE_2D, texture.id); + // NOTE: Using texture.id, we can retrieve some texture info (but not on OpenGL ES 2.0) + /* + int width, height, format; glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); - //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); - //GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); + // Other texture info: GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE + */ int glFormat = 0, glType = 0; - unsigned int size = width*height; + unsigned int size = texture.width*texture.height; // NOTE: GL_LUMINANCE and GL_LUMINANCE_ALPHA are removed since OpenGL 3.1 - // Must be replaced by GL_RED and GL_RG on Core OpenGL 3.3 and data must be swizzled + // Must be replaced by GL_RED and GL_RG on Core OpenGL 3.3 switch (format) { @@ -2073,27 +2111,15 @@ void *rlglReadTexturePixels(unsigned int textureId, unsigned int format) case UNCOMPRESSED_GRAYSCALE: pixels = (unsigned char *)malloc(size); glFormat = GL_LUMINANCE; glType = GL_UNSIGNED_BYTE; break; // 8 bit per pixel (no alpha) case UNCOMPRESSED_GRAY_ALPHA: pixels = (unsigned char *)malloc(size*2); glFormat = GL_LUMINANCE_ALPHA; glType = GL_UNSIGNED_BYTE; break; // 16 bpp (2 channels) #elif defined(GRAPHICS_API_OPENGL_33) - case UNCOMPRESSED_GRAYSCALE: // 8 bit per pixel (no alpha) - { - pixels = (unsigned char *)malloc(size); glFormat = GL_RED; glType = GL_UNSIGNED_BYTE; - - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - } break; - case UNCOMPRESSED_GRAY_ALPHA: // 16 bpp (2 channels) - { - pixels = (unsigned char *)malloc(size*2); glFormat = GL_RG; glType = GL_UNSIGNED_BYTE; - - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - } break; + case UNCOMPRESSED_GRAYSCALE: pixels = (unsigned char *)malloc(size); glFormat = GL_RED; glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_GRAY_ALPHA: pixels = (unsigned char *)malloc(size*2); glFormat = GL_RG; glType = GL_UNSIGNED_BYTE; break; #endif case UNCOMPRESSED_R5G6B5: pixels = (unsigned short *)malloc(size); glFormat = GL_RGB; glType = GL_UNSIGNED_SHORT_5_6_5; break; // 16 bpp case UNCOMPRESSED_R8G8B8: pixels = (unsigned char *)malloc(size*3); glFormat = GL_RGB; glType = GL_UNSIGNED_BYTE; break; // 24 bpp case UNCOMPRESSED_R5G5B5A1: pixels = (unsigned short *)malloc(size); glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_5_5_5_1; break; // 16 bpp (1 bit alpha) case UNCOMPRESSED_R4G4B4A4: pixels = (unsigned short *)malloc(size); glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_4_4_4_4; break; // 16 bpp (4 bit alpha) case UNCOMPRESSED_R8G8B8A8: pixels = (unsigned char *)malloc(size*4); glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; break; // 32 bpp - default: TraceLog(WARNING, "Texture format not suported"); break; + default: TraceLog(WARNING, "Texture data retrieval, format not suported"); break; } // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding. @@ -2108,16 +2134,36 @@ void *rlglReadTexturePixels(unsigned int textureId, unsigned int format) #endif #if defined(GRAPHICS_API_OPENGL_ES2) - // TODO: Look for some way to retrieve texture width and height from id - int width = 1024; - int height = 1024; + FBO fbo = rlglLoadFBO(texture.width, texture.height); + + // NOTE: Two possible Options: + // 1 - Bind texture to color fbo attachment and glReadPixels() + // 2 - Create an fbo, activate it, render quad with texture, glReadPixels() + +#define GET_TEXTURE_FBO_OPTION_1 // It works + +#if defined(GET_TEXTURE_FBO_OPTION_1) + glBindFramebuffer(GL_FRAMEBUFFER, fbo.id); + glBindTexture(GL_TEXTURE_2D, 0); - FBO fbo = rlglLoadFBO(width, height); + // Attach our texture to FBO -> Texture must be RGB + // NOTE: Previoust attached texture is automatically detached + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.id, 0); + + pixels = (unsigned char *)malloc(texture.width*texture.height*4*sizeof(unsigned char)); + + // NOTE: Despite FBO color texture is RGB, we read data as RGBA... reading as RGB doesn't work... o__O + glReadPixels(0, 0, texture.width, texture.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Re-attach internal FBO color texture before deleting it + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.colorTextureId, 0); - // NOTE: Altenatively we can bind texture to color fbo and glReadPixels() + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#elif defined(GET_TEXTURE_FBO_OPTION_2) // Render texture to fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo.id); + glClearColor(0.0, 0.0, 0.0, 0.0); glClearDepthf(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -2131,16 +2177,23 @@ void *rlglReadTexturePixels(unsigned int textureId, unsigned int format) //glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); - //Model quad = GenModelQuad(width, height); - //DrawModel(quad, (Vector3){ 0, 0, 0 }, 1.0f, WHITE); + Model quad; + //quad.mesh = GenMeshQuad(width, height); + quad.transform = MatrixIdentity(); + quad.shader = simpleShader; - pixels = (unsigned char *)malloc(width*height*4*sizeof(unsigned char)); + DrawModel(quad, (Vector3){ 0, 0, 0 }, 1.0f, WHITE); - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + pixels = (unsigned char *)malloc(texture.width*texture.height*3*sizeof(unsigned char)); + + glReadPixels(0, 0, texture.width, texture.height, GL_RGB, GL_UNSIGNED_BYTE, pixels); // Bind framebuffer 0, which means render to back buffer glBindFramebuffer(GL_FRAMEBUFFER, 0); + UnloadModel(quad); +#endif // GET_TEXTURE_FBO_OPTION + // Clean up temporal fbo rlglUnloadFBO(fbo); #endif diff --git a/src/rlgl.h b/src/rlgl.h index 272b245e5..0960fa835 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -242,7 +242,8 @@ void rlglClose(void); // De-init rlgl void rlglDraw(void); // Draw VAO/VBO void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) -unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount); // Load in GPU OpenGL texture +unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount); // Load texture in GPU +void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data); // Update GPU texture with new data void rlglGenerateMipmaps(unsigned int textureId); // Generate mipmap data for selected texture // NOTE: There is a set of shader related functions that are available to end user, @@ -254,10 +255,10 @@ void rlglDrawPostpro(void); // Draw with postprocessing shad Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires); -Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates +Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates -unsigned char *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) -void *rlglReadTexturePixels(unsigned int textureId, unsigned int format); // Read texture pixel data +unsigned char *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) +void *rlglReadTexturePixels(Texture2D texture); // Read texture pixel data #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) void PrintProjectionMatrix(void); // DEBUG: Print projection matrix diff --git a/src/text.c b/src/text.c index 5e9471879..7b9660ddc 100644 --- a/src/text.c +++ b/src/text.c @@ -649,7 +649,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) // 1) Generate sprite sheet image with characters from TTF // 2) Load image as SpriteFont - SpriteFont font; + SpriteFont font = { 0 }; Image image; image.width = 512; diff --git a/src/textures.c b/src/textures.c index 007547ca8..27046b613 100644 --- a/src/textures.c +++ b/src/textures.c @@ -338,16 +338,17 @@ Texture2D LoadTexture(const char *fileName) return texture; } -Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount) +// Load a texture from raw data into GPU memory +Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat) { Texture2D texture; texture.width = width; texture.height = height; - texture.mipmaps = mipmapCount; + texture.mipmaps = 1; texture.format = textureFormat; - texture.id = rlglLoadTexture(data, width, height, textureFormat, mipmapCount); + texture.id = rlglLoadTexture(data, width, height, textureFormat, 1); return texture; } @@ -501,26 +502,27 @@ Image GetTextureData(Texture2D texture) Image image; image.data = NULL; -#if defined(GRAPHICS_API_OPENGL_ES2) - TraceLog(WARNING, "Texture data retrieval not supported on OpenGL ES 2.0"); -#else if (texture.format < 8) { - image.data = rlglReadTexturePixels(texture.id, texture.format); + image.data = rlglReadTexturePixels(texture); if (image.data != NULL) { image.width = texture.width; image.height = texture.height; - image.format = texture.format; image.mipmaps = 1; - +#if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: Data retrieved on OpenGL ES 2.0 comes as RGB (from framebuffer) + image.format = UNCOMPRESSED_R8G8B8A8; +#else + image.format = texture.format; +#endif TraceLog(INFO, "Texture pixel data obtained successfully"); } else TraceLog(WARNING, "Texture pixel data could not be obtained"); } else TraceLog(WARNING, "Compressed texture data could not be obtained"); -#endif + return image; } @@ -1172,6 +1174,13 @@ void GenTextureMipmaps(Texture2D texture) #endif } +// Update GPU texture with new data +// NOTE: pixels data must match texture.format +void UpdateTexture(Texture2D texture, void *pixels) +{ + rlglUpdateTexture(texture.id, texture.width, texture.height, texture.format, pixels); +} + // Draw a Texture2D void DrawTexture(Texture2D texture, int posX, int posY, Color tint) {