Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rtextures/rlgl] Load mipmaps for cubemaps #4429

Merged
merged 4 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/models/models_skybox.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int s
// STEP 1: Setup framebuffer
//------------------------------------------------------------------------------------------
unsigned int rbo = rlLoadTextureDepth(size, size, true);
cubemap.id = rlLoadTextureCubemap(0, size, format);
cubemap.id = rlLoadTextureCubemap(0, size, format, 1);

unsigned int fbo = rlLoadFramebuffer();
rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
Expand Down
39 changes: 31 additions & 8 deletions src/rlgl.h
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, const void
// Textures management
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 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 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
Expand Down Expand Up @@ -3386,11 +3386,17 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer)
// 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(const void *data, int size, int format)
unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mipmapCount)
{
unsigned int id = 0;

#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);

glGenTextures(1, &id);
Expand All @@ -3401,24 +3407,27 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format)

if (glInternalFormat != 0)
{
// Load cubemap faces
for (unsigned int i = 0; i < 6; i++)
// Load cubemap faces/mipmaps
for (unsigned int i = 0; i < 6 * mipmapCount; i++)
{
int mipmapLevel = i / 6;
int face = i % 6;

if (data == NULL)
{
if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)
{
if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)
|| (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16))
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
{
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);
else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, dataSize, (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 + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, dataSize, (unsigned char *)dataPtr + face*dataSize);
}

#if defined(GRAPHICS_API_OPENGL_33)
Expand All @@ -3437,11 +3446,25 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format)
glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
}
#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
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_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Expand Down
57 changes: 46 additions & 11 deletions src/rtextures.c
Original file line number Diff line number Diff line change
Expand Up @@ -2395,22 +2395,16 @@ void ImageMipmaps(Image *image)
else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated");

// 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;
mipHeight = image->height/2;
mipWidth = image->width;
mipHeight = image->height;
mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
Image imCopy = ImageCopy(*image);

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;
image->mipmaps++;

mipWidth /= 2;
mipHeight /= 2;
Expand All @@ -2420,9 +2414,20 @@ void ImageMipmaps(Image *image)
if (mipHeight < 1) mipHeight = 1;

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);

image->mipmaps = mipCount;
}
else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps already available");
}
Expand Down Expand Up @@ -3911,7 +3916,6 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0) ||
(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");
else
{
Expand Down Expand Up @@ -4024,6 +4028,34 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
}

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);
}
}
}

Expand Down Expand Up @@ -4169,14 +4201,17 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
faces = GenImageColor(size, size*6, MAGENTA);
ImageFormat(&faces, image.format);

ImageMipmaps(&image);
ImageMipmaps(&faces);

// 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);
}

// 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
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format, faces.mipmaps);

if (cubemap.id != 0)
{
Expand Down
Loading