From fce48e85f441215192d188011b48a0f35828e4b3 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Feb 2019 13:27:36 +0100 Subject: [PATCH] ADDED: LoadTextureCubemap() Probably uncomplete, not tested yet... --- src/raylib.h | 14 +++++ src/rlgl.h | 135 +++++++++++++++++++++++++++++++++++++++---------- src/textures.c | 86 ++++++++++++++++++++++++++++++- 3 files changed, 207 insertions(+), 28 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 55ecbcd56d7a..94c9148456d6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -222,6 +222,9 @@ typedef struct Texture2D { // Texture type, same as Texture2D typedef Texture2D Texture; +// TextureCubemap type, actually, same as Texture2D +typedef Texture2D TextureCubemap; + // RenderTexture2D type, for texture rendering typedef struct RenderTexture2D { unsigned int id; // OpenGL Framebuffer Object (FBO) id @@ -749,6 +752,16 @@ typedef enum { FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x } TextureFilterMode; +// Cubemap layout type +typedef enum { + CUBEMAP_AUTO_DETECT = 0, // Automatically detect layout type + CUBEMAP_LINE_VERTICAL, // Layout is defined by a vertical line with faces + CUBEMAP_LINE_HORIZONTAL, // Layout is defined by an horizontal line with faces + CUBEMAP_CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces + CUBEMAP_CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces + CUBEMAP_PANORAMA // Layout is defined by a panorama image (equirectangular map) +} CubemapLayoutType; + // Texture parameters: wrap mode typedef enum { WRAP_REPEAT = 0, // Repeats texture in tiled mode @@ -1049,6 +1062,7 @@ RLAPI void ExportImage(Image image, const char *fileName); RLAPI void ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data +RLAPI TextureCubemap LoadTextureCubemap(Image image, int layoutType); // Load cubemap from image, multiple image cubemap layouts supported RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) diff --git a/src/rlgl.h b/src/rlgl.h index 253dddeb781c..69af55e6f4dc 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -450,14 +450,18 @@ Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coo // Textures data management unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU +unsigned int rlLoadTextureCubemap(void *data, int size, int format); // Load texture cubemap void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats -void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory +void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory void rlGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture void *rlReadTexturePixels(Texture2D texture); // Read texture pixel data unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) -RenderTexture2D rlLoadRenderTexture(int width, int height); // Load a texture to be used for rendering (fbo with color and depth attachments) +RenderTexture2D rlLoadRenderTexture(int width, int height); // Load a texture to be used for rendering (fbo with default color and depth attachments) +RenderTexture2D rlLoadRenderTextureEx(int width, int height, int colorFormat, int depthBits, bool useDepthTexture); +void rlAttachRenderTexture(RenderTexture target, unsigned int textureId); // Attach/detach color buffer texture to an FBO (returns previous attachment) +bool rlCompleteRenderTexture(RenderTexture target); // Verify rendertexture is complete // Vertex data management void rlLoadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids @@ -699,9 +703,9 @@ typedef struct DynamicBuffer { typedef struct DrawCall { int mode; // Drawing mode: LINES, TRIANGLES, QUADS int vertexCount; // Number of vertex of the draw - //GLuint vaoId; // Vertex Array id to be used on the draw - //GLuint shaderId; // Shader id to be used on the draw - GLuint textureId; // Texture id to be used on the draw + //unsigned int vaoId; // Vertex Array id to be used on the draw + //unsigned int shaderId; // Shader id to be used on the draw + unsigned int textureId; // Texture id to be used on the draw //Matrix projection; // Projection matrix for this draw //Matrix modelview; // Modelview matrix for this draw } DrawCall; @@ -713,7 +717,8 @@ typedef struct VrStereoConfig { Shader distortionShader; // VR stereo rendering distortion shader Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices - int eyesViewport[2][4]; // VR stereo rendering eyes viewports [x, y, w, h] + int eyeViewportRight[4]; // VR stereo rendering right eye viewport [x, y, w, h] + int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] } VrStereoConfig; #endif @@ -1816,7 +1821,7 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi { glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding - GLuint id = 0; + unsigned int id = 0; // Check texture format support by OpenGL 1.1 (compressed textures not supported) #if defined(GRAPHICS_API_OPENGL_11) @@ -2039,26 +2044,81 @@ void rlUnloadTexture(unsigned int id) if (id > 0) glDeleteTextures(1, &id); } -// Load a texture to be used for rendering (fbo with color and depth attachments) -RenderTexture2D rlLoadRenderTexture(int width, int height) +// Load texture cubemap +// NOTE: Cubemap data is expected to be 6 images in a single column, +// expected the following convention: +X, -X, +Y, -Y, +Z, -Z +unsigned int rlLoadTextureCubemap(void *data, int size, int format) { - RenderTexture2D target = { 0 }; + unsigned int cubemapId = 0; + unsigned int dataSize = GetPixelDataSize(size, size, format); - target.id = 0; + glGenTextures(1, &cubemapId); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapId); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + // Load cubemap faces + for (unsigned int i = 0; i < 6; i++) + { + if (glInternalFormat != -1) + { + if (format < COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize); +#if !defined(GRAPHICS_API_OPENGL_11) + else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, dataSize, (unsigned char *)data + i*dataSize); +#endif +#if defined(GRAPHICS_API_OPENGL_33) + if (format == UNCOMPRESSED_GRAYSCALE) + { + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + else if (format == UNCOMPRESSED_GRAY_ALPHA) + { +#if defined(GRAPHICS_API_OPENGL_21) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; +#elif defined(GRAPHICS_API_OPENGL_33) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; +#endif + glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } +#endif + } + } + + // Set cubemap texture sampling parameters + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#if defined(GRAPHICS_API_OPENGL_33) + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Flag not supported on OpenGL ES 2.0 +#endif + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + + return cubemapId; +} + +// Load a texture to be used for rendering (fbo with default color and depth attachments) +// NOTE: If colorFormat or depthBits are no supported, no attachment is done +RenderTexture2D rlLoadRenderTexture(int width, int height) //, int colorFormat, int depthBits, bool useDepthTexture); +{ + RenderTexture2D target = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Create the framebuffer object + glGenFramebuffers(1, &target.id); + glBindFramebuffer(GL_FRAMEBUFFER, target.id); + + // Create fbo color texture attachment + //----------------------------------------------------------------------------------------------------- target.texture.id = 0; target.texture.width = width; target.texture.height = height; target.texture.format = UNCOMPRESSED_R8G8B8A8; target.texture.mipmaps = 1; - target.depth.id = 0; - target.depth.width = width; - target.depth.height = height; - target.depth.format = 19; //DEPTH_COMPONENT_24BIT - target.depth.mipmaps = 1; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Create the texture that will serve as the color attachment for the framebuffer glGenTextures(1, &target.texture.id); glBindTexture(GL_TEXTURE_2D, target.texture.id); @@ -2068,7 +2128,16 @@ RenderTexture2D rlLoadRenderTexture(int width, int height) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glBindTexture(GL_TEXTURE_2D, 0); - + //----------------------------------------------------------------------------------------------------- + + // Create fbo depth renderbuffer/texture + //----------------------------------------------------------------------------------------------------- + target.depth.id = 0; + target.depth.width = width; + target.depth.height = height; + target.depth.format = 19; //DEPTH_COMPONENT_24BIT + target.depth.mipmaps = 1; + #if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) #define USE_DEPTH_RENDERBUFFER #else @@ -2092,19 +2161,20 @@ RenderTexture2D rlLoadRenderTexture(int width, int height) glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); glBindTexture(GL_TEXTURE_2D, 0); #endif - - // Create the framebuffer object - glGenFramebuffers(1, &target.id); - glBindFramebuffer(GL_FRAMEBUFFER, target.id); - + //----------------------------------------------------------------------------------------------------- + // Attach color texture and depth renderbuffer to FBO + //----------------------------------------------------------------------------------------------------- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target.texture.id, 0); #if defined(USE_DEPTH_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, target.depth.id); #elif defined(USE_DEPTH_TEXTURE) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, target.depth.id, 0); #endif + //----------------------------------------------------------------------------------------------------- + // Check if fbo is complete with attachments (valid) + //----------------------------------------------------------------------------------------------------- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) @@ -2135,6 +2205,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height) glDeleteFramebuffers(1, &target.id); } else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); + //----------------------------------------------------------------------------------------------------- glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif @@ -2142,6 +2213,16 @@ RenderTexture2D rlLoadRenderTexture(int width, int height) return target; } +// Improved FBO generation function to allow selecting the attached color buffer pixel format +// and the depth buffer type (Renderbuffer or Texture) and size +// NOTE: Be careful on supported pixel formats... +//RenderTexture rlLoadRenderTexture(int width, int height, int colorFormat, int depthBits, bool useDepthTexture); +// Attach/detach color buffer texture to an FBO (returns previous attachment) +//Texture2D rlAttachRenderTexture(RenderTexture target, Texture2D texture); + +//void rlRenderTextureColorAttach(RenderTexture target, Texture2D texture); +//bool rlRenderTextureComplete(RenderTexture target); // Verify valid rendertexture + // Generate mipmap data for selected texture void rlGenerateMipmaps(Texture2D *texture) { @@ -2855,7 +2936,7 @@ Shader LoadShaderCode(char *vsCode, char *fsCode) name[namelen] = 0; // Get the location of the named uniform - GLuint location = glGetUniformLocation(shader.id, name); + unsigned int location = glGetUniformLocation(shader.id, name); TraceLog(LOG_DEBUG, "[SHDR ID %i] Active uniform [%s] set at location: %i", shader.id, name, location); } @@ -4315,8 +4396,8 @@ static void SetStereoConfig(VrDeviceInfo hmd) vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); // Compute eyes Viewports - //vrConfig.eyesViewport[0] = { 0.0f, 0.0f, (float)hmd.hResolution/2, (float)hmd.vResolution }; - //vrConfig.eyesViewport[1] = { hmd.hResolution/2.0f, 0.0f, (float)hmd.hResolution/2, (float) hmd.vResolution }; + //vrConfig.eyeViewportRight[0] = (int[4]){ 0, 0, hmd.hResolution/2, hmd.vResolution }; + //vrConfig.eyeViewportLeft[0] = (int[4]){ hmd.hResolution/2, 0, hmd.hResolution/2, hmd.vResolution }; } // Set internal projection and modelview matrix depending on eyes tracking data diff --git a/src/textures.c b/src/textures.c index 726a2a847acd..988b94a323fe 100644 --- a/src/textures.c +++ b/src/textures.c @@ -398,7 +398,92 @@ Texture2D LoadTextureFromImage(Image image) return texture; } +// Load cubemap from image, multiple image cubemap layouts supported +TextureCubemap LoadTextureCubemap(Image image, int layoutType) +{ + TextureCubemap cubemap = { 0 }; + + if (layoutType == CUBEMAP_AUTO_DETECT) // Try to automatically guess layout type + { + // Check image width/height to determine the type of cubemap provided + if (image.width > image.height) + { + if ((image.width/6) == image.height) { layoutType = CUBEMAP_LINE_HORIZONTAL; cubemap.width = image.width/6; } + else if ((image.width/4) == (image.height/3)) { layoutType = CUBEMAP_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; } + else if (image.width >= (int)((float)image.height*1.85f)) { layoutType = CUBEMAP_PANORAMA; cubemap.width = image.width/4; } + } + else if (image.height > image.width) + { + if ((image.height/6) == image.width) { layoutType = CUBEMAP_LINE_VERTICAL; cubemap.width = image.height/6; } + else if ((image.width/3) == (image.height/4)) { layoutType = CUBEMAP_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; } + } + + cubemap.height = cubemap.width; + } + + int size = cubemap.width; + + if (layoutType != CUBEMAP_AUTO_DETECT) + { + //unsigned int dataSize = GetPixelDataSize(size, size, format); + //void *facesData = malloc(size*size*dataSize*6); // Get memory for 6 faces in a column + + Image faces = { 0 }; // Vertical column image + Rectangle faceRecs[6] = { 0 }; // Face source rectangles + for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, size, size }; + + if (layoutType == CUBEMAP_LINE_VERTICAL) + { + faces = image; + for (int i = 0; i < 6; i++) faceRecs[i].y = size*i; + } + else if (layoutType == CUBEMAP_PANORAMA) + { + // TODO: Convert panorama image to square faces... + } + else + { + if (layoutType == CUBEMAP_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = size*i; + else if (layoutType == CUBEMAP_CROSS_THREE_BY_FOUR) + { + faceRecs[0].x = size; faceRecs[0].y = size; + faceRecs[1].x = size; faceRecs[1].y = 3*size; + faceRecs[2].x = size; faceRecs[2].y = 0; + faceRecs[3].x = size; faceRecs[3].y = 2*size; + faceRecs[4].x = 0; faceRecs[4].y = size; + faceRecs[5].x = 2*size; faceRecs[5].y = size; + } + else if (layoutType == CUBEMAP_CROSS_FOUR_BY_THREE) + { + faceRecs[0].x = 2*size; faceRecs[0].y = size; + faceRecs[1].x = 0; faceRecs[1].y = size; + faceRecs[2].x = size; faceRecs[2].y = 0; + faceRecs[3].x = size; faceRecs[3].y = 2*size; + faceRecs[4].x = size; faceRecs[4].y = size; + faceRecs[5].x = 3*size; faceRecs[5].y = size; + } + + // Convert image data to 6 faces in a vertical column, that's the optimum layout for loading + faces = GenImageColor(size, size*6, MAGENTA); + ImageFormat(&faces, image.format); + + // TODO: Image formating does not work with compressed textures! + } + + for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, size*i, size, size }); + + cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format); + if (cubemap.id == 0) TraceLog(LOG_WARNING, "Cubemap image could not be loaded."); + + UnloadImage(faces); + } + else TraceLog(LOG_WARNING, "Cubemap image layout can not be detected."); + + return cubemap; +} + // Load texture for rendering (framebuffer) +// NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer RenderTexture2D LoadRenderTexture(int width, int height) { RenderTexture2D target = rlLoadRenderTexture(width, height); @@ -1666,7 +1751,6 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co int index; // Index position in sprite font unsigned char character; // Current character - // TODO: ISSUE: Measured text size does not seem to be correct... issue on ImageDraw() Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); TraceLog(LOG_DEBUG, "Text Image size: %f, %f", imSize.x, imSize.y);