[rtextures/rlgl] Load mipmaps for cubemaps (#4429)

* [rlgl] Load cubemap mipmaps

* [rtextures] Only generate mipmaps that don't already exist

* [rtextures] ImageDraw(): Implement drawing to mipmaps

* [rtextures] Load cubemap mipmaps
This commit is contained in:
Nikolas 2024-10-26 12:09:38 +02:00 committed by GitHub
parent 91a4f04794
commit 7fedf9e0b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 78 additions and 20 deletions

View file

@ -191,7 +191,7 @@ static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int s
// STEP 1: Setup framebuffer // STEP 1: Setup framebuffer
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
unsigned int rbo = rlLoadTextureDepth(size, size, true); unsigned int rbo = rlLoadTextureDepth(size, size, true);
cubemap.id = rlLoadTextureCubemap(0, size, format); cubemap.id = rlLoadTextureCubemap(0, size, format, 1);
unsigned int fbo = rlLoadFramebuffer(); unsigned int fbo = rlLoadFramebuffer();
rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0); rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);

View file

@ -749,7 +749,7 @@ RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, const void
// Textures management // Textures management
RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount); // Load texture data RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount); // Load texture data
RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo) RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo)
RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format); // Load texture cubemap data RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mipmapCount); // Load texture cubemap data
RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update texture with new data on GPU RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update texture with new data on GPU
RLAPI void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats RLAPI void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats
RLAPI const char *rlGetPixelFormatName(unsigned int format); // Get name string for pixel format RLAPI const char *rlGetPixelFormatName(unsigned int format); // Get name string for pixel format
@ -3386,11 +3386,17 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer)
// Load texture cubemap // Load texture cubemap
// NOTE: Cubemap data is expected to be 6 images in a single data array (one after the other), // 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 // expected the following convention: +X, -X, +Y, -Y, +Z, -Z
unsigned int rlLoadTextureCubemap(const void *data, int size, int format) unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mipmapCount)
{ {
unsigned int id = 0; unsigned int id = 0;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
int mipSize = size;
// NOTE: Added pointer math separately from function to avoid UBSAN complaining
unsigned char *dataPtr = NULL;
if (data != NULL) dataPtr = (unsigned char *)data;
unsigned int dataSize = rlGetPixelDataSize(size, size, format); unsigned int dataSize = rlGetPixelDataSize(size, size, format);
glGenTextures(1, &id); glGenTextures(1, &id);
@ -3401,9 +3407,12 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format)
if (glInternalFormat != 0) if (glInternalFormat != 0)
{ {
// Load cubemap faces // Load cubemap faces/mipmaps
for (unsigned int i = 0; i < 6; i++) for (unsigned int i = 0; i < 6 * mipmapCount; i++)
{ {
int mipmapLevel = i / 6;
int face = i % 6;
if (data == NULL) if (data == NULL)
{ {
if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)
@ -3411,14 +3420,14 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format)
if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32) if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)
|| (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16))
TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); TRACELOG(RL_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 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, glFormat, glType, NULL);
} }
else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format"); else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format");
} }
else else
{ {
if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize); if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, glFormat, glType, (unsigned char *)dataPtr + face*dataSize);
else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, dataSize, (unsigned char *)data + i*dataSize); else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, dataSize, (unsigned char *)dataPtr + face*dataSize);
} }
#if defined(GRAPHICS_API_OPENGL_33) #if defined(GRAPHICS_API_OPENGL_33)
@ -3437,11 +3446,25 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format)
glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
} }
#endif #endif
if (face == 5) {
mipSize /= 2;
if (data != NULL)
dataPtr += dataSize * 6; // Increment data pointer to next mipmap
// Security check for NPOT textures
if (mipSize < 1) mipSize = 1;
dataSize = rlGetPixelDataSize(mipSize, mipSize, format);
}
} }
} }
// Set cubemap texture sampling parameters // Set cubemap texture sampling parameters
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); if (mipmapCount > 1) {
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
} else {
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

View file

@ -2390,22 +2390,16 @@ void ImageMipmaps(Image *image)
else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated"); else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated");
// Pointer to allocated memory point where store next mipmap level data // Pointer to allocated memory point where store next mipmap level data
unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format); unsigned char *nextmip = image->data;
mipWidth = image->width/2; mipWidth = image->width;
mipHeight = image->height/2; mipHeight = image->height;
mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format); mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
Image imCopy = ImageCopy(*image); Image imCopy = ImageCopy(*image);
for (int i = 1; i < mipCount; i++) for (int i = 1; i < mipCount; i++)
{ {
TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
memcpy(nextmip, imCopy.data, mipSize);
nextmip += mipSize; nextmip += mipSize;
image->mipmaps++;
mipWidth /= 2; mipWidth /= 2;
mipHeight /= 2; mipHeight /= 2;
@ -2415,9 +2409,20 @@ void ImageMipmaps(Image *image)
if (mipHeight < 1) mipHeight = 1; if (mipHeight < 1) mipHeight = 1;
mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format); mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
if (i < image->mipmaps)
continue;
TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
memcpy(nextmip, imCopy.data, mipSize);
} }
UnloadImage(imCopy); UnloadImage(imCopy);
image->mipmaps = mipCount;
} }
else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps already available"); else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps already available");
} }
@ -3906,7 +3911,6 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0) || if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0) ||
(src.data == NULL) || (src.width == 0) || (src.height == 0)) return; (src.data == NULL) || (src.width == 0) || (src.height == 0)) return;
if (dst->mipmaps > 1) TRACELOG(LOG_WARNING, "Image drawing only applied to base mipmap level");
if (dst->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image drawing not supported for compressed formats"); if (dst->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image drawing not supported for compressed formats");
else else
{ {
@ -4019,6 +4023,34 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
} }
if (useSrcMod) UnloadImage(srcMod); // Unload source modified image if (useSrcMod) UnloadImage(srcMod); // Unload source modified image
if (dst->mipmaps > 1 && src.mipmaps > 1) {
Image mipmapDst = *dst;
mipmapDst.data = (char *) mipmapDst.data + GetPixelDataSize(mipmapDst.width, mipmapDst.height, mipmapDst.format);
mipmapDst.width /= 2;
mipmapDst.height /= 2;
mipmapDst.mipmaps--;
Image mipmapSrc = src;
mipmapSrc.data = (char *) mipmapSrc.data + GetPixelDataSize(mipmapSrc.width, mipmapSrc.height, mipmapSrc.format);
mipmapSrc.width /= 2;
mipmapSrc.height /= 2;
mipmapSrc.mipmaps--;
Rectangle mipmapSrcRec = srcRec;
mipmapSrcRec.width /= 2;
mipmapSrcRec.height /= 2;
mipmapSrcRec.x /= 2;
mipmapSrcRec.y /= 2;
Rectangle mipmapDstRec = dstRec;
mipmapDstRec.width /= 2;
mipmapDstRec.height /= 2;
mipmapDstRec.x /= 2;
mipmapDstRec.y /= 2;
ImageDraw(&mipmapDst, mipmapSrc, mipmapSrcRec, mipmapDstRec, tint);
}
} }
} }
@ -4162,6 +4194,9 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
faces = GenImageColor(size, size*6, MAGENTA); faces = GenImageColor(size, size*6, MAGENTA);
ImageFormat(&faces, image.format); ImageFormat(&faces, image.format);
ImageMipmaps(&image);
ImageMipmaps(&faces);
// NOTE: Image formatting does not work with compressed textures // NOTE: Image formatting does not work with compressed textures
for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE); for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
@ -4169,7 +4204,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
// NOTE: Cubemap data is expected to be provided as 6 images in a single data array, // NOTE: Cubemap data is expected to be provided as 6 images in a single data array,
// one after the other (that's a vertical image), following convention: +X, -X, +Y, -Y, +Z, -Z // one after the other (that's a vertical image), following convention: +X, -X, +Y, -Y, +Z, -Z
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format); cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format, faces.mipmaps);
if (cubemap.id != 0) if (cubemap.id != 0)
{ {