From eaa0b9102b3751617b9856d3a60eed26354c4140 Mon Sep 17 00:00:00 2001 From: murilluhenrique <90881619+murilluhenrique@users.noreply.github.com> Date: Thu, 15 Sep 2022 19:06:25 -0300 Subject: [PATCH 01/20] Fix typo (#2696) --- examples/core/core_loading_thread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 614adb690..09ec76460 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [cpre] example - loading thread +* raylib [core] example - loading thread * * NOTE: This example requires linking with pthreads library on MinGW, * it can be accomplished passing -static parameter to compiler From cb9b8f73c08e720ef6b8762192c4efe9521a1185 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 17 Sep 2022 13:39:49 +0200 Subject: [PATCH 02/20] ADDED: `GenImagePerlinNoise()` --- src/external/stb_perlin.h | 428 ++++++++++++++++++++++++++++++++++++++ src/raylib.h | 1 + src/rtextures.c | 51 ++++- 3 files changed, 475 insertions(+), 5 deletions(-) create mode 100644 src/external/stb_perlin.h diff --git a/src/external/stb_perlin.h b/src/external/stb_perlin.h new file mode 100644 index 000000000..47cb9a437 --- /dev/null +++ b/src/external/stb_perlin.h @@ -0,0 +1,428 @@ +// stb_perlin.h - v0.5 - perlin noise +// public domain single-file C implementation by Sean Barrett +// +// LICENSE +// +// See end of file. +// +// +// to create the implementation, +// #define STB_PERLIN_IMPLEMENTATION +// in *one* C/CPP file that includes this file. +// +// +// Documentation: +// +// float stb_perlin_noise3( float x, +// float y, +// float z, +// int x_wrap=0, +// int y_wrap=0, +// int z_wrap=0) +// +// This function computes a random value at the coordinate (x,y,z). +// Adjacent random values are continuous but the noise fluctuates +// its randomness with period 1, i.e. takes on wholly unrelated values +// at integer points. Specifically, this implements Ken Perlin's +// revised noise function from 2002. +// +// The "wrap" parameters can be used to create wraparound noise that +// wraps at powers of two. The numbers MUST be powers of two. Specify +// 0 to mean "don't care". (The noise always wraps every 256 due +// details of the implementation, even if you ask for larger or no +// wrapping.) +// +// float stb_perlin_noise3_seed( float x, +// float y, +// float z, +// int x_wrap=0, +// int y_wrap=0, +// int z_wrap=0, +// int seed) +// +// As above, but 'seed' selects from multiple different variations of the +// noise function. The current implementation only uses the bottom 8 bits +// of 'seed', but possibly in the future more bits will be used. +// +// +// Fractal Noise: +// +// Three common fractal noise functions are included, which produce +// a wide variety of nice effects depending on the parameters +// provided. Note that each function will call stb_perlin_noise3 +// 'octaves' times, so this parameter will affect runtime. +// +// float stb_perlin_ridge_noise3(float x, float y, float z, +// float lacunarity, float gain, float offset, int octaves) +// +// float stb_perlin_fbm_noise3(float x, float y, float z, +// float lacunarity, float gain, int octaves) +// +// float stb_perlin_turbulence_noise3(float x, float y, float z, +// float lacunarity, float gain, int octaves) +// +// Typical values to start playing with: +// octaves = 6 -- number of "octaves" of noise3() to sum +// lacunarity = ~ 2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) +// gain = 0.5 -- relative weighting applied to each successive octave +// offset = 1.0? -- used to invert the ridges, may need to be larger, not sure +// +// +// Contributors: +// Jack Mott - additional noise functions +// Jordan Peck - seeded noise +// + + +#ifdef __cplusplus +extern "C" { +#endif +extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap); +extern float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed); +extern float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves); +extern float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); +extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); +extern float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed); +#ifdef __cplusplus +} +#endif + +#ifdef STB_PERLIN_IMPLEMENTATION + +#include // fabs() + +// not same permutation table as Perlin's reference to avoid copyright issues; +// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/ +static unsigned char stb__perlin_randtab[512] = +{ + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, + + // and a second copy so we don't need an extra mask or static initializer + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, +}; + + +// perlin's gradient has 12 cases so some get used 1/16th of the time +// and some 2/16ths. We reduce bias by changing those fractions +// to 5/64ths and 6/64ths + +// this array is designed to match the previous implementation +// of gradient hash: indices[stb__perlin_randtab[i]&63] +static unsigned char stb__perlin_randtab_grad_idx[512] = +{ + 7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7, + 8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8, + 7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8, + 8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5, + 5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1, + 2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4, + 9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11, + 1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6, + 10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0, + 6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2, + 4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3, + 11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11, + 10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1, + 3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10, + 11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7, + 9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5, + + // and a second copy so we don't need an extra mask or static initializer + 7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7, + 8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8, + 7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8, + 8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5, + 5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1, + 2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4, + 9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11, + 1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6, + 10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0, + 6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2, + 4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3, + 11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11, + 10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1, + 3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10, + 11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7, + 9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5, +}; + +static float stb__perlin_lerp(float a, float b, float t) +{ + return a + (b-a) * t; +} + +static int stb__perlin_fastfloor(float a) +{ + int ai = (int) a; + return (a < ai) ? ai-1 : ai; +} + +// different grad function from Perlin's, but easy to modify to match reference +static float stb__perlin_grad(int grad_idx, float x, float y, float z) +{ + static float basis[12][4] = + { + { 1, 1, 0 }, + { -1, 1, 0 }, + { 1,-1, 0 }, + { -1,-1, 0 }, + { 1, 0, 1 }, + { -1, 0, 1 }, + { 1, 0,-1 }, + { -1, 0,-1 }, + { 0, 1, 1 }, + { 0,-1, 1 }, + { 0, 1,-1 }, + { 0,-1,-1 }, + }; + + float *grad = basis[grad_idx]; + return grad[0]*x + grad[1]*y + grad[2]*z; +} + +float stb_perlin_noise3_internal(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + unsigned int x_mask = (x_wrap-1) & 255; + unsigned int y_mask = (y_wrap-1) & 255; + unsigned int z_mask = (z_wrap-1) & 255; + int px = stb__perlin_fastfloor(x); + int py = stb__perlin_fastfloor(y); + int pz = stb__perlin_fastfloor(z); + int x0 = px & x_mask, x1 = (px+1) & x_mask; + int y0 = py & y_mask, y1 = (py+1) & y_mask; + int z0 = pz & z_mask, z1 = (pz+1) & z_mask; + int r0,r1, r00,r01,r10,r11; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0+seed]; + r1 = stb__perlin_randtab[x1+seed]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} + +float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap) +{ + return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap,0); +} + +float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed) +{ + return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap, (unsigned char) seed); +} + +float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves) +{ + int i; + float frequency = 1.0f; + float prev = 1.0f; + float amplitude = 0.5f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i); + r = offset - (float) fabs(r); + r = r*r; + sum += r*amplitude*prev; + prev = r; + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves) +{ + int i; + float frequency = 1.0f; + float amplitude = 1.0f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + sum += stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude; + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves) +{ + int i; + float frequency = 1.0f; + float amplitude = 1.0f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude; + sum += (float) fabs(r); + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + int px = stb__perlin_fastfloor(x); + int py = stb__perlin_fastfloor(y); + int pz = stb__perlin_fastfloor(z); + int x_wrap2 = (x_wrap ? x_wrap : 256); + int y_wrap2 = (y_wrap ? y_wrap : 256); + int z_wrap2 = (z_wrap ? z_wrap : 256); + int x0 = px % x_wrap2, x1; + int y0 = py % y_wrap2, y1; + int z0 = pz % z_wrap2, z1; + int r0,r1, r00,r01,r10,r11; + + if (x0 < 0) x0 += x_wrap2; + if (y0 < 0) y0 += y_wrap2; + if (z0 < 0) z0 += z_wrap2; + x1 = (x0+1) % x_wrap2; + y1 = (y0+1) % y_wrap2; + z1 = (z0+1) % z_wrap2; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0]; + r0 = stb__perlin_randtab[r0+seed]; + r1 = stb__perlin_randtab[x1]; + r1 = stb__perlin_randtab[r1+seed]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} +#endif // STB_PERLIN_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/raylib.h b/src/raylib.h index 693da1409..ac5d6e6af 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1241,6 +1241,7 @@ RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise +RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells // Image manipulation functions diff --git a/src/rtextures.c b/src/rtextures.c index 24fbafd2a..d602081ba 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -70,12 +70,12 @@ #if defined(SUPPORT_MODULE_RTEXTURES) -#include "utils.h" // Required for: TRACELOG() and fopen() Android mapping +#include "utils.h" // Required for: TRACELOG() #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2 #include // Required for: malloc(), free() #include // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()] -#include // Required for: fabsf() +#include // Required for: fabsf() [Used in DrawTextureRec()] #include // Required for: sprintf() [Used in ExportImageAsCode()] // Support only desired texture formats on stb_image @@ -147,6 +147,11 @@ #include "external/stb_image_write.h" // Required for: stbi_write_*() #endif +#if defined(SUPPORT_IMAGE_GENERATION) + #define STB_PERLIN_IMPLEMENTATION + #include "external/stb_perlin.h" // Required for: stb_perlin_fbm_noise3 +#endif + #if defined(SUPPORT_IMAGE_MANIPULATION) #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size)) #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr)) @@ -760,6 +765,42 @@ Image GenImageWhiteNoise(int width, int height, float factor) return image; } +// Generate image: perlin noise +Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale) +{ + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + float nx = (float)(x + offsetX)*scale/(float)width; + float ny = (float)(y + offsetY)*scale/(float)height; + + // Typical values to start playing with: + // lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) + // gain = 0.5 -- relative weighting applied to each successive octave + // octaves = 6 -- number of "octaves" of noise3() to sum + + // NOTE: We need to translate the data from [-1..1] to [0..1] + float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6) + 1.0f)/2.0f; + + int intensity = (int)(p*255.0f); + pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 }; + } + } + + Image image = { + .data = pixels, + .width = width, + .height = height, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, + .mipmaps = 1 + }; + + return image; +} + // Generate image: cellular algorithm. Bigger tileSize means bigger cells Image GenImageCellular(int width, int height, int tileSize) { @@ -3181,19 +3222,19 @@ void SetTextureWrap(Texture2D texture, int wrap) //------------------------------------------------------------------------------------ // Texture drawing functions //------------------------------------------------------------------------------------ -// Draw a Texture2D +// Draw a texture void DrawTexture(Texture2D texture, int posX, int posY, Color tint) { DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0.0f, 1.0f, tint); } -// Draw a Texture2D with position defined as Vector2 +// Draw a texture with position defined as Vector2 void DrawTextureV(Texture2D texture, Vector2 position, Color tint) { DrawTextureEx(texture, position, 0, 1.0f, tint); } -// Draw a Texture2D with extended parameters +// Draw a texture with extended parameters void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint) { Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height }; From c328f09efccce43293644c8fad0ea0bd7e7c94a9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 17 Sep 2022 19:26:23 +0200 Subject: [PATCH 03/20] Move compressed textures loading to a separate self-contained library --- src/external/rl_gputex.h | 818 +++++++++++++++++++++++++++++++++ src/rtextures.c | 945 +++++++-------------------------------- 2 files changed, 969 insertions(+), 794 deletions(-) create mode 100644 src/external/rl_gputex.h diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h new file mode 100644 index 000000000..22bb72816 --- /dev/null +++ b/src/external/rl_gputex.h @@ -0,0 +1,818 @@ +/********************************************************************************************** +* +* rl_gputex - GPU compressed textures loading and saving +* +* DESCRIPTION: +* +* Load GPU compressed image data from image files provided as memory data arrays, +* data is loaded compressed, ready to be loaded into GPU. +* +* Note that some file formats (DDS, PVR, KTX) also support uncompressed data storage. +* In those cases data is loaded uncompressed and format is returned. +* +* TODO: +* - Implement raylib function: rlGetGlTextureFormats(), required by rl_save_ktx_to_memory() +* - Review rl_load_ktx_from_memory() to support KTX v2.2 specs +* +* CONFIGURATION: +* +* #define RL_GPUTEX_SUPPORT_DDS +* #define RL_GPUTEX_SUPPORT_PKM +* #define RL_GPUTEX_SUPPORT_KTX +* #define RL_GPUTEX_SUPPORT_PVR +* #define RL_GPUTEX_SUPPORT_ASTC +* Define desired file formats to be supported +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RL_GPUTEX_H +#define RL_GPUTEX_H + +#ifndef RLAPI + #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +// Load image data from memory data files +RLAPI void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); +RLAPI void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); +RLAPI void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); +RLAPI void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); +RLAPI void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); + +RLAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int width, int height, int format, int mipmaps); // Save image data as KTX file + +#if defined(__cplusplus) +} +#endif + +#endif // RL_GPUTEX_H + + +/*********************************************************************************** +* +* RL_GPUTEX IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RL_GPUTEX_IMPLEMENTATION) + +// Simple log system to avoid RPNG_LOG() calls if required +// NOTE: Avoiding those calls, also avoids const strings memory usage +#define RL_GPUTEX_SHOW_LOG_INFO +#if defined(RL_GPUTEX_SHOW_LOG_INFO) && !defined(LOG) +#define LOG(...) printf(__VA_ARGS__) +#else +#define LOG(...) +#endif + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +// Get pixel data size in bytes for certain pixel format +static int get_pixel_data_size(int width, int height, int format); + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +#if defined(RL_GPUTEX_SUPPORT_DDS) +// Loading DDS from memory image data (compressed or uncompressed) +void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) +{ + void *image_data = NULL; // Image data pointer + int image_pixel_size = 0; // Image pixel size + + unsigned char *file_data_ptr = (unsigned char *)file_data; + + // Required extension: + // GL_EXT_texture_compression_s3tc + + // Supported tokens (defined by extensions) + // GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 + // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + + #define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII + #define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII + #define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII + + // DDS Pixel Format + typedef struct { + unsigned int size; + unsigned int flags; + unsigned int fourcc; + unsigned int rgb_bit_count; + unsigned int r_bit_mask; + unsigned int g_bit_mask; + unsigned int b_bit_mask; + unsigned int a_bit_mask; + } dds_pixel_format; + + // DDS Header (124 bytes) + typedef struct { + unsigned int size; + unsigned int flags; + unsigned int height; + unsigned int width; + unsigned int pitch_or_linear_size; + unsigned int depth; + unsigned int mipmap_count; + unsigned int reserved1[11]; + dds_pixel_format ddspf; + unsigned int caps; + unsigned int caps2; + unsigned int caps3; + unsigned int caps4; + unsigned int reserved2; + } dds_header; + + if (file_data_ptr != NULL) + { + // Verify the type of file + unsigned char *dds_header_id = file_data_ptr; + file_data_ptr += 4; + + if ((dds_header_id[0] != 'D') || (dds_header_id[1] != 'D') || (dds_header_id[2] != 'S') || (dds_header_id[3] != ' ')) + { + LOG("WARNING: IMAGE: DDS file data not valid"); + } + else + { + dds_header *header = (dds_header *)file_data_ptr; + + file_data_ptr += sizeof(dds_header); // Skip header + + *width = header->width; + *height = header->height; + image_pixel_size = header->width*header->height; + + if (header->mipmap_count == 0) *mips = 1; // Parameter not used + else *mips = header->mipmap_count; + + if (header->ddspf.rgb_bit_count == 16) // 16bit mode, no compressed + { + if (header->ddspf.flags == 0x40) // No alpha channel + { + int data_size = image_pixel_size*sizeof(unsigned short); + image_data = RL_MALLOC(data_size); + + memcpy(image_data, file_data_ptr, data_size); + + *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; + } + else if (header->ddspf.flags == 0x41) // With alpha channel + { + if (header->ddspf.a_bit_mask == 0x8000) // 1bit alpha + { + int data_size = image_pixel_size*sizeof(unsigned short); + image_data = RL_MALLOC(data_size); + + memcpy(image_data, file_data_ptr, data_size); + + unsigned char alpha = 0; + + // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1 + for (int i = 0; i < image_pixel_size; i++) + { + alpha = ((unsigned short *)image_data)[i] >> 15; + ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 1; + ((unsigned short *)image_data)[i] += alpha; + } + + *format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; + } + else if (header->ddspf.a_bit_mask == 0xf000) // 4bit alpha + { + int data_size = image_pixel_size*sizeof(unsigned short); + image_data = RL_MALLOC(data_size); + + memcpy(image_data, file_data_ptr, data_size); + + unsigned char alpha = 0; + + // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4 + for (int i = 0; i < image_pixel_size; i++) + { + alpha = ((unsigned short *)image_data)[i] >> 12; + ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 4; + ((unsigned short *)image_data)[i] += alpha; + } + + *format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; + } + } + } + else if (header->ddspf.flags == 0x40 && header->ddspf.rgb_bit_count == 24) // DDS_RGB, no compressed + { + int data_size = image_pixel_size*3*sizeof(unsigned char); + image_data = RL_MALLOC(data_size); + + memcpy(image_data, file_data_ptr, data_size); + + *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; + } + else if (header->ddspf.flags == 0x41 && header->ddspf.rgb_bit_count == 32) // DDS_RGBA, no compressed + { + int data_size = image_pixel_size*4*sizeof(unsigned char); + image_data = RL_MALLOC(data_size); + + memcpy(image_data, file_data_ptr, data_size); + + unsigned char blue = 0; + + // NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment) + // DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA + // So, we must realign B8G8R8A8 to R8G8B8A8 + for (int i = 0; i < image_pixel_size*4; i += 4) + { + blue = ((unsigned char *)image_data)[i]; + ((unsigned char *)image_data)[i] = ((unsigned char *)image_data)[i + 2]; + ((unsigned char *)image_data)[i + 2] = blue; + } + + *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + } + else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed + { + int data_size = 0; + + // Calculate data size, including all mipmaps + if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size*2; + else data_size = header->pitch_or_linear_size; + + image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + + memcpy(image_data, file_data_ptr, data_size); + + switch (header->ddspf.fourcc) + { + case FOURCC_DXT1: + { + if (header->ddspf.flags == 0x04) *format = PIXELFORMAT_COMPRESSED_DXT1_RGB; + else *format = PIXELFORMAT_COMPRESSED_DXT1_RGBA; + } break; + case FOURCC_DXT3: *format = PIXELFORMAT_COMPRESSED_DXT3_RGBA; break; + case FOURCC_DXT5: *format = PIXELFORMAT_COMPRESSED_DXT5_RGBA; break; + default: break; + } + } + } + } + + return image_data; +} +#endif + +#if defined(RL_GPUTEX_SUPPORT_PKM) +// Loading PKM image data (ETC1/ETC2 compression) +// NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps) +// PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps) +void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) +{ + void *image_data = NULL; // Image data pointer + + unsigned char *file_data_ptr = (unsigned char *)file_data; + + // Required extensions: + // GL_OES_compressed_ETC1_RGB8_texture (ETC1) (OpenGL ES 2.0) + // GL_ARB_ES3_compatibility (ETC2/EAC) (OpenGL ES 3.0) + + // Supported tokens (defined by extensions) + // GL_ETC1_RGB8_OES 0x8D64 + // GL_COMPRESSED_RGB8_ETC2 0x9274 + // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 + + // PKM file (ETC1) Header (16 bytes) + typedef struct { + char id[4]; // "PKM " + char version[2]; // "10" or "20" + unsigned short format; // Data format (big-endian) (Check list below) + unsigned short width; // Texture width (big-endian) (orig_width rounded to multiple of 4) + unsigned short height; // Texture height (big-endian) (orig_height rounded to multiple of 4) + unsigned short orig_width; // Original width (big-endian) + unsigned short orig_height; // Original height (big-endian) + } pkm_header; + + // Formats list + // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used) + // version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R + + // NOTE: The extended width and height are the widths rounded up to a multiple of 4. + // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels) + + if (file_data_ptr != NULL) + { + pkm_header *header = (pkm_header *)file_data_ptr; + + if ((header->id[0] != 'P') || (header->id[1] != 'K') || (header->id[2] != 'M') || (header->id[3] != ' ')) + { + LOG("WARNING: IMAGE: PKM file data not valid"); + } + else + { + file_data_ptr += sizeof(pkm_header); // Skip header + + // NOTE: format, width and height come as big-endian, data must be swapped to little-endian + header->format = ((header->format & 0x00FF) << 8) | ((header->format & 0xFF00) >> 8); + header->width = ((header->width & 0x00FF) << 8) | ((header->width & 0xFF00) >> 8); + header->height = ((header->height & 0x00FF) << 8) | ((header->height & 0xFF00) >> 8); + + *width = header->width; + *height = header->height; + *mips = 1; + + int bpp = 4; + if (header->format == 3) bpp = 8; + + int data_size = (*width)*(*height)*bpp/8; // Total data size in bytes + + image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + + memcpy(image_data, file_data_ptr, data_size); + + if (header->format == 0) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB; + else if (header->format == 1) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB; + else if (header->format == 3) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; + } + } + + return image_data; +} +#endif + +#if defined(RL_GPUTEX_SUPPORT_KTX) +// Load KTX compressed image data (ETC1/ETC2 compression) +// TODO: Review KTX loading, many things changed! +void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) +{ + void *image_data = NULL; // Image data pointer + + unsigned char *file_data_ptr = (unsigned char *)file_data; + + // Required extensions: + // GL_OES_compressed_ETC1_RGB8_texture (ETC1) + // GL_ARB_ES3_compatibility (ETC2/EAC) + + // Supported tokens (defined by extensions) + // GL_ETC1_RGB8_OES 0x8D64 + // GL_COMPRESSED_RGB8_ETC2 0x9274 + // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 + + // KTX file Header (64 bytes) + // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + // v2.0 - http://github.khronos.org/KTX-Specification/ + + // KTX 1.1 Header + // TODO: Support KTX 2.2 specs! + typedef struct { + char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" + unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 + unsigned int gl_type; // For compressed textures, glType must equal 0 + unsigned int gl_type_size; // For compressed texture data, usually 1 + unsigned int gl_format; // For compressed textures is 0 + unsigned int gl_internal_format; // Compressed internal format + unsigned int gl_base_internal_format; // Same as glFormat (RGB, RGBA, ALPHA...) + unsigned int width; // Texture image width in pixels + unsigned int height; // Texture image height in pixels + unsigned int depth; // For 2D textures is 0 + unsigned int elements; // Number of array elements, usually 0 + unsigned int faces; // Cubemap faces, for no-cubemap = 1 + unsigned int mipmap_levels; // Non-mipmapped textures = 1 + unsigned int key_value_data_size; // Used to encode any arbitrary data... + } ktx_header; + + // NOTE: Before start of every mipmap data block, we have: unsigned int data_size + + if (file_data_ptr != NULL) + { + ktx_header *header = (ktx_header *)file_data_ptr; + + if ((header->id[1] != 'K') || (header->id[2] != 'T') || (header->id[3] != 'X') || + (header->id[4] != ' ') || (header->id[5] != '1') || (header->id[6] != '1')) + { + LOG("WARNING: IMAGE: KTX file data not valid"); + } + else + { + file_data_ptr += sizeof(ktx_header); // Move file data pointer + + *width = header->width; + *height = header->height; + *mips = header->mipmap_levels; + + file_data_ptr += header->key_value_data_size; // Skip value data size + + int data_size = ((int *)file_data_ptr)[0]; + file_data_ptr += sizeof(int); + + image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + + memcpy(image_data, file_data_ptr, data_size); + + if (header->gl_internal_format == 0x8D64) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB; + else if (header->gl_internal_format == 0x9274) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB; + else if (header->gl_internal_format == 0x9278) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; + + // TODO: Support uncompressed data formats? Right now it returns format = 0! + } + } + + return image_data; +} + +// Save image data as KTX file +// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018) +// TODO: Review KTX saving, many things changed! +int rl_save_ktx(const char *file_name, void *data, int width, int height, int format, int mipmaps) +{ + // KTX file Header (64 bytes) + // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + // v2.0 - http://github.khronos.org/KTX-Specification/ - Final specs by 2021-04-18 + typedef struct { + char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n" + unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 + unsigned int gl_type; // For compressed textures, glType must equal 0 + unsigned int gl_type_size; // For compressed texture data, usually 1 + unsigned int gl_format; // For compressed textures is 0 + unsigned int gl_internal_format; // Compressed internal format + unsigned int gl_base_internal_format; // Same as glFormat (RGB, RGBA, ALPHA...) // KTX 2.0: UInt32 vkFormat + unsigned int width; // Texture image width in pixels + unsigned int height; // Texture image height in pixels + unsigned int depth; // For 2D textures is 0 + unsigned int elements; // Number of array elements, usually 0 + unsigned int faces; // Cubemap faces, for no-cubemap = 1 + unsigned int mipmap_levels; // Non-mipmapped textures = 1 + unsigned int key_value_data_size; // Used to encode any arbitrary data... // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0 + // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)... + // KTX 2.0 defines additional header elements... + } ktx_header; + + // Calculate file data_size required + int data_size = sizeof(ktx_header); + + for (int i = 0, width = width, height = height; i < mipmaps; i++) + { + data_size += get_pixel_data_size(width, height, format); + width /= 2; height /= 2; + } + + unsigned char *file_data = RL_CALLOC(data_size, 1); + unsigned char *file_data_ptr = file_data; + + ktx_header header = { 0 }; + + // KTX identifier (v1.1) + //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' }; + //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; + + const char ktx_identifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' }; + + // Get the image header + memcpy(header.id, ktx_identifier, 12); // KTX 1.1 signature + header.endianness = 0; + header.gl_type = 0; // Obtained from format + header.gl_type_size = 1; + header.gl_format = 0; // Obtained from format + header.gl_internal_format = 0; // Obtained from format + header.gl_base_internal_format = 0; + header.width = width; + header.height = height; + header.depth = 0; + header.elements = 0; + header.faces = 1; + header.mipmap_levels = mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats) + header.key_value_data_size = 0; // No extra data after the header + + rlGetGlTextureFormats(format, &header.gl_internal_format, &header.gl_format, &header.gl_type); // rlgl module function + header.gl_base_internal_format = header.gl_format; // KTX 1.1 only + + // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC + + if (header.gl_format == -1) LOG("WARNING: IMAGE: GL format not supported for KTX export (%i)", header.gl_format); + else + { + memcpy(file_data_ptr, &header, sizeof(ktx_header)); + file_data_ptr += sizeof(ktx_header); + + int temp_width = width; + int temp_height = height; + int data_offset = 0; + + // Save all mipmaps data + for (int i = 0; i < mipmaps; i++) + { + unsigned int data_size = get_pixel_data_size(temp_width, temp_height, format); + + memcpy(file_data_ptr, &data_size, sizeof(unsigned int)); + memcpy(file_data_ptr + 4, (unsigned char *)data + data_offset, data_size); + + temp_width /= 2; + temp_height /= 2; + data_offset += data_size; + file_data_ptr += (4 + data_size); + } + } + + // Save file data to file + int success = false; + FILE *file = fopen(file_name, "wb"); + + if (file != NULL) + { + unsigned int count = (unsigned int)fwrite(file_data, sizeof(unsigned char), data_size, file); + + if (count == 0) LOG("WARNING: FILEIO: [%s] Failed to write file", file_name); + else if (count != data_size) LOG("WARNING: FILEIO: [%s] File partially written", file_name); + else LOG("INFO: FILEIO: [%s] File saved successfully", file_name); + + int result = fclose(file); + if (result == 0) success = true; + } + else LOG("WARNING: FILEIO: [%s] Failed to open file", file_name); + + RL_FREE(file_data); // Free file data buffer + + // If all data has been written correctly to file, success = 1 + return success; +} +#endif + +#if defined(RL_GPUTEX_SUPPORT_PVR) +// Loading PVR image data (uncompressed or PVRT compression) +// NOTE: PVR v2 not supported, use PVR v3 instead +void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) +{ + void *image_data = NULL; // Image data pointer + + unsigned char *file_data_ptr = (unsigned char *)file_data; + + // Required extension: + // GL_IMG_texture_compression_pvrtc + + // Supported tokens (defined by extensions) + // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 + // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 + +#if 0 // Not used... + // PVR file v2 Header (52 bytes) + typedef struct { + unsigned int headerLength; + unsigned int height; + unsigned int width; + unsigned int numMipmaps; + unsigned int flags; + unsigned int dataLength; + unsigned int bpp; + unsigned int bitmaskRed; + unsigned int bitmaskGreen; + unsigned int bitmaskBlue; + unsigned int bitmaskAlpha; + unsigned int pvrTag; + unsigned int numSurfs; + } PVRHeaderV2; +#endif + + // PVR file v3 Header (52 bytes) + // NOTE: After it could be metadata (15 bytes?) + typedef struct { + char id[4]; + unsigned int flags; + unsigned char channels[4]; // pixelFormat high part + unsigned char channel_depth[4]; // pixelFormat low part + unsigned int color_space; + unsigned int channel_type; + unsigned int height; + unsigned int width; + unsigned int depth; + unsigned int num_surfaces; + unsigned int num_faces; + unsigned int num_mipmaps; + unsigned int metadata_size; + } pvr_header; + +#if 0 // Not used... + // Metadata (usually 15 bytes) + typedef struct { + unsigned int devFOURCC; + unsigned int key; + unsigned int data_size; // Not used? + unsigned char *data; // Not used? + } PVRMetadata; +#endif + + if (file_data_ptr != NULL) + { + // Check PVR image version + unsigned char pvr_version = file_data_ptr[0]; + + // Load different PVR data formats + if (pvr_version == 0x50) + { + pvr_header *header = (pvr_header *)file_data_ptr; + + if ((header->id[0] != 'P') || (header->id[1] != 'V') || (header->id[2] != 'R') || (header->id[3] != 3)) + { + LOG("WARNING: IMAGE: PVR file data not valid"); + } + else + { + file_data_ptr += sizeof(pvr_header); // Skip header + + *width = header->width; + *height = header->height; + *mips = header->num_mipmaps; + + // Check data format + if (((header->channels[0] == 'l') && (header->channels[1] == 0)) && (header->channel_depth[0] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + else if (((header->channels[0] == 'l') && (header->channels[1] == 'a')) && ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8))) *format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; + else if ((header->channels[0] == 'r') && (header->channels[1] == 'g') && (header->channels[2] == 'b')) + { + if (header->channels[3] == 'a') + { + if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 5) && (header->channel_depth[2] == 5) && (header->channel_depth[3] == 1)) *format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; + else if ((header->channel_depth[0] == 4) && (header->channel_depth[1] == 4) && (header->channel_depth[2] == 4) && (header->channel_depth[3] == 4)) *format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; + else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8) && (header->channel_depth[3] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + } + else if (header->channels[3] == 0) + { + if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 6) && (header->channel_depth[2] == 5)) *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; + else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; + } + } + else if (header->channels[0] == 2) *format = PIXELFORMAT_COMPRESSED_PVRT_RGB; + else if (header->channels[0] == 3) *format = PIXELFORMAT_COMPRESSED_PVRT_RGBA; + + file_data_ptr += header->metadata_size; // Skip meta data header + + // Calculate data size (depends on format) + int bpp = 0; + switch (*format) + { + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; + case PIXELFORMAT_COMPRESSED_PVRT_RGB: + case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; + default: break; + } + + int data_size = (*width)*(*height)*bpp/8; // Total data size in bytes + image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + + memcpy(image_data, file_data_ptr, data_size); + } + } + else if (pvr_version == 52) LOG("INFO: IMAGE: PVRv2 format not supported, update your files to PVRv3"); + } + + return image_data; +} +#endif + +#if defined(RL_GPUTEX_SUPPORT_ASTC) +// Load ASTC compressed image data (ASTC compression) +void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) +{ + void *image_data = NULL; // Image data pointer + + unsigned char *file_data_ptr = (unsigned char *)file_data; + + // Required extensions: + // GL_KHR_texture_compression_astc_hdr + // GL_KHR_texture_compression_astc_ldr + + // Supported tokens (defined by extensions) + // GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 + // GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 + + // ASTC file Header (16 bytes) + typedef struct { + unsigned char id[4]; // Signature: 0x13 0xAB 0xA1 0x5C + unsigned char blockX; // Block X dimensions + unsigned char blockY; // Block Y dimensions + unsigned char blockZ; // Block Z dimensions (1 for 2D images) + unsigned char width[3]; // void *width in pixels (24bit value) + unsigned char height[3]; // void *height in pixels (24bit value) + unsigned char length[3]; // void *Z-size (1 for 2D images) + } astc_header; + + if (file_data_ptr != NULL) + { + astc_header *header = (astc_header *)file_data_ptr; + + if ((header->id[3] != 0x5c) || (header->id[2] != 0xa1) || (header->id[1] != 0xab) || (header->id[0] != 0x13)) + { + LOG("WARNING: IMAGE: ASTC file data not valid"); + } + else + { + file_data_ptr += sizeof(astc_header); // Skip header + + // NOTE: Assuming Little Endian (could it be wrong?) + *width = 0x00000000 | ((int)header->width[2] << 16) | ((int)header->width[1] << 8) | ((int)header->width[0]); + *height = 0x00000000 | ((int)header->height[2] << 16) | ((int)header->height[1] << 8) | ((int)header->height[0]); + *mips = 1; // NOTE: ASTC format only contains one mipmap level + + // NOTE: Each block is always stored in 128bit so we can calculate the bpp + int bpp = 128/(header->blockX*header->blockY); + + // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8 + if ((bpp == 8) || (bpp == 2)) + { + int data_size = (*width)*(*height)*bpp/8; // Data size in bytes + + image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + + memcpy(image_data, file_data_ptr, data_size); + + if (bpp == 8) *format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA; + else if (bpp == 2) *format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA; + } + else LOG("WARNING: IMAGE: ASTC block size configuration not supported"); + } + } + + return image_data; +} +#endif + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- +// Get pixel data size in bytes for certain pixel format +static int get_pixel_data_size(int width, int height, int format) +{ + int data_size = 0; // Size in bytes + int bpp = 0; // Bits per pixel + + switch (format) + { + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; + case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; + case PIXELFORMAT_COMPRESSED_DXT1_RGB: + case PIXELFORMAT_COMPRESSED_DXT1_RGBA: + case PIXELFORMAT_COMPRESSED_ETC1_RGB: + case PIXELFORMAT_COMPRESSED_ETC2_RGB: + case PIXELFORMAT_COMPRESSED_PVRT_RGB: + case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; + case PIXELFORMAT_COMPRESSED_DXT3_RGBA: + case PIXELFORMAT_COMPRESSED_DXT5_RGBA: + case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: + case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break; + case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; + default: break; + } + + data_size = width*height*bpp/8; // Total data size in bytes + + // Most compressed formats works on 4x4 blocks, + // if texture is smaller, minimum dataSize is 8 or 16 + if ((width < 4) && (height < 4)) + { + if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) data_size = 8; + else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) data_size = 16; + } + + return data_size; +} + +#endif // RL_GPUTEX_IMPLEMENTATION diff --git a/src/rtextures.c b/src/rtextures.c index d602081ba..12ba4923b 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -104,6 +104,22 @@ #define STBI_NO_HDR #endif +#if defined(SUPPORT_FILEFORMAT_DDS) + #define RL_GPUTEX_SUPPORT_DDS +#endif +#if defined(SUPPORT_FILEFORMAT_PKM) + #define RL_GPUTEX_SUPPORT_PKM +#endif +#if defined(SUPPORT_FILEFORMAT_KTX) + #define RL_GPUTEX_SUPPORT_KTX +#endif +#if defined(SUPPORT_FILEFORMAT_PVR) + #define RL_GPUTEX_SUPPORT_PVR +#endif +#if defined(SUPPORT_FILEFORMAT_ASTC) + #define RL_GPUTEX_SUPPORT_ASTC +#endif + // Image fileformats not supported by default #define STBI_NO_PIC #define STBI_NO_PNM // Image format .ppm and .pgm @@ -130,6 +146,17 @@ // NOTE: Used to read image data (multiple formats support) #endif +#if (defined(SUPPORT_FILEFORMAT_DDS) || \ + defined(SUPPORT_FILEFORMAT_PKM) || \ + defined(SUPPORT_FILEFORMAT_KTX) || \ + defined(SUPPORT_FILEFORMAT_PVR) || \ + defined(SUPPORT_FILEFORMAT_ASTC)) + + #define RL_GPUTEX_IMPLEMENTATION + #include "external/rl_gputex.h" // Required for: rl_load_xxx_from_memory() + // NOTE: Used to read compressed textures data (multiple formats support) +#endif + #if defined(SUPPORT_FILEFORMAT_QOI) #define QOI_MALLOC RL_MALLOC #define QOI_FREE RL_FREE @@ -185,23 +212,6 @@ //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -#if defined(SUPPORT_FILEFORMAT_DDS) -static Image LoadDDS(const unsigned char *fileData, unsigned int fileSize); // Load DDS file data -#endif -#if defined(SUPPORT_FILEFORMAT_PKM) -static Image LoadPKM(const unsigned char *fileData, unsigned int fileSize); // Load PKM file data -#endif -#if defined(SUPPORT_FILEFORMAT_KTX) -static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize); // Load KTX file data -static int SaveKTX(Image image, const char *fileName); // Save image data as KTX file -#endif -#if defined(SUPPORT_FILEFORMAT_PVR) -static Image LoadPVR(const unsigned char *fileData, unsigned int fileSize); // Load PVR file data -#endif -#if defined(SUPPORT_FILEFORMAT_ASTC) -static Image LoadASTC(const unsigned char *fileData, unsigned int fileSize); // Load ASTC file data -#endif - static Vector4 *LoadImageDataNormalized(Image image); // Load pixel data from image as Vector4 array (float normalized) //---------------------------------------------------------------------------------- @@ -333,7 +343,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i #if defined(SUPPORT_FILEFORMAT_PSD) || (strcmp(fileType, ".psd") == 0) #endif - ) + ) { #if defined(STBI_REQUIRED) // NOTE: Using stb_image to load images (Supports multiple image formats) @@ -390,19 +400,35 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i } #endif #if defined(SUPPORT_FILEFORMAT_DDS) - else if (strcmp(fileType, ".dds") == 0) image = LoadDDS(fileData, dataSize); + else if (strcmp(fileType, ".dds") == 0) + { + int format = 0; + image.data = rl_load_dds_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif #if defined(SUPPORT_FILEFORMAT_PKM) - else if (strcmp(fileType, ".pkm") == 0) image = LoadPKM(fileData, dataSize); + else if (strcmp(fileType, ".pkm") == 0) + { + image.data = rl_load_pkm_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif #if defined(SUPPORT_FILEFORMAT_KTX) - else if (strcmp(fileType, ".ktx") == 0) image = LoadKTX(fileData, dataSize); + else if (strcmp(fileType, ".ktx") == 0) + { + image.data = rl_load_ktx_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif #if defined(SUPPORT_FILEFORMAT_PVR) - else if (strcmp(fileType, ".pvr") == 0) image = LoadPVR(fileData, dataSize); + else if (strcmp(fileType, ".pvr") == 0) + { + image.data = rl_load_pvr_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif #if defined(SUPPORT_FILEFORMAT_ASTC) - else if (strcmp(fileType, ".astc") == 0) image = LoadASTC(fileData, dataSize); + else if (strcmp(fileType, ".astc") == 0) + { + image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported"); @@ -528,7 +554,10 @@ bool ExportImage(Image image, const char *fileName) } #endif #if defined(SUPPORT_FILEFORMAT_KTX) - else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName); + else if (IsFileExtension(fileName, ".ktx")) + { + success = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps); + } #endif else if (IsFileExtension(fileName, ".raw")) { @@ -3253,104 +3282,6 @@ void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color DrawTexturePro(texture, source, dest, origin, 0.0f, tint); } -// Draw texture quad with tiling and offset parameters -// NOTE: Tiling and offset should be provided considering normalized texture values [0..1] -// i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center -void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint) -{ - // WARNING: This solution only works if TEXTURE_WRAP_REPEAT is supported, - // NPOT textures supported is required and OpenGL ES 2.0 could not support it - Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height }; - Vector2 origin = { 0.0f, 0.0f }; - - DrawTexturePro(texture, source, quad, origin, 0.0f, tint); -} - -// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. -// NOTE: For tilling a whole texture DrawTextureQuad() is better -void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint) -{ - if ((texture.id <= 0) || (scale <= 0.0f)) return; // Wanna see a infinite loop?!...just delete this line! - if ((source.width == 0) || (source.height == 0)) return; - - int tileWidth = (int)(source.width*scale), tileHeight = (int)(source.height*scale); - if ((dest.width < tileWidth) && (dest.height < tileHeight)) - { - // Can fit only one tile - DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height}, - (Rectangle){dest.x, dest.y, dest.width, dest.height}, origin, rotation, tint); - } - else if (dest.width <= tileWidth) - { - // Tiled vertically (one column) - int dy = 0; - for (;dy+tileHeight < dest.height; dy += tileHeight) - { - DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, source.height}, (Rectangle){dest.x, dest.y + dy, dest.width, (float)tileHeight}, origin, rotation, tint); - } - - // Fit last tile - if (dy < dest.height) - { - DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height}, - (Rectangle){dest.x, dest.y + dy, dest.width, dest.height - dy}, origin, rotation, tint); - } - } - else if (dest.height <= tileHeight) - { - // Tiled horizontally (one row) - int dx = 0; - for (;dx+tileWidth < dest.width; dx += tileWidth) - { - DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)dest.height/tileHeight)*source.height}, (Rectangle){dest.x + dx, dest.y, (float)tileWidth, dest.height}, origin, rotation, tint); - } - - // Fit last tile - if (dx < dest.width) - { - DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height}, - (Rectangle){dest.x + dx, dest.y, dest.width - dx, dest.height}, origin, rotation, tint); - } - } - else - { - // Tiled both horizontally and vertically (rows and columns) - int dx = 0; - for (;dx+tileWidth < dest.width; dx += tileWidth) - { - int dy = 0; - for (;dy+tileHeight < dest.height; dy += tileHeight) - { - DrawTexturePro(texture, source, (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, (float)tileHeight}, origin, rotation, tint); - } - - if (dy < dest.height) - { - DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)(dest.height - dy)/tileHeight)*source.height}, - (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, dest.height - dy}, origin, rotation, tint); - } - } - - // Fit last column of tiles - if (dx < dest.width) - { - int dy = 0; - for (;dy+tileHeight < dest.height; dy += tileHeight) - { - DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, source.height}, - (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, (float)tileHeight}, origin, rotation, tint); - } - - // Draw final tile in the bottom right corner - if (dy < dest.height) - { - DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height}, - (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, dest.height - dy}, origin, rotation, tint); - } - } - } -} - // Draw a part of a texture (defined by a rectangle) with 'pro' parameters // NOTE: origin is relative to destination rectangle size void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint) @@ -3474,6 +3405,104 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 } } +// Draw texture quad with tiling and offset parameters +// NOTE: Tiling and offset should be provided considering normalized texture values [0..1] +// i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center +void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint) +{ + // WARNING: This solution only works if TEXTURE_WRAP_REPEAT is supported, + // NPOT textures supported is required and OpenGL ES 2.0 could not support it + Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height }; + Vector2 origin = { 0.0f, 0.0f }; + + DrawTexturePro(texture, source, quad, origin, 0.0f, tint); +} + +// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. +// NOTE: For tilling a whole texture DrawTextureQuad() is better +void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint) +{ + if ((texture.id <= 0) || (scale <= 0.0f)) return; // Wanna see a infinite loop?!...just delete this line! + if ((source.width == 0) || (source.height == 0)) return; + + int tileWidth = (int)(source.width*scale), tileHeight = (int)(source.height*scale); + if ((dest.width < tileWidth) && (dest.height < tileHeight)) + { + // Can fit only one tile + DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height}, + (Rectangle){dest.x, dest.y, dest.width, dest.height}, origin, rotation, tint); + } + else if (dest.width <= tileWidth) + { + // Tiled vertically (one column) + int dy = 0; + for (;dy+tileHeight < dest.height; dy += tileHeight) + { + DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, source.height}, (Rectangle){dest.x, dest.y + dy, dest.width, (float)tileHeight}, origin, rotation, tint); + } + + // Fit last tile + if (dy < dest.height) + { + DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height}, + (Rectangle){dest.x, dest.y + dy, dest.width, dest.height - dy}, origin, rotation, tint); + } + } + else if (dest.height <= tileHeight) + { + // Tiled horizontally (one row) + int dx = 0; + for (;dx+tileWidth < dest.width; dx += tileWidth) + { + DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)dest.height/tileHeight)*source.height}, (Rectangle){dest.x + dx, dest.y, (float)tileWidth, dest.height}, origin, rotation, tint); + } + + // Fit last tile + if (dx < dest.width) + { + DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height}, + (Rectangle){dest.x + dx, dest.y, dest.width - dx, dest.height}, origin, rotation, tint); + } + } + else + { + // Tiled both horizontally and vertically (rows and columns) + int dx = 0; + for (;dx+tileWidth < dest.width; dx += tileWidth) + { + int dy = 0; + for (;dy+tileHeight < dest.height; dy += tileHeight) + { + DrawTexturePro(texture, source, (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, (float)tileHeight}, origin, rotation, tint); + } + + if (dy < dest.height) + { + DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)(dest.height - dy)/tileHeight)*source.height}, + (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, dest.height - dy}, origin, rotation, tint); + } + } + + // Fit last column of tiles + if (dx < dest.width) + { + int dy = 0; + for (;dy+tileHeight < dest.height; dy += tileHeight) + { + DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, source.height}, + (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, (float)tileHeight}, origin, rotation, tint); + } + + // Draw final tile in the bottom right corner + if (dy < dest.height) + { + DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height}, + (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, dest.height - dy}, origin, rotation, tint); + } + } + } +} + // Draws a texture (or part of it) that stretches or shrinks nicely using n-patch info void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint) { @@ -4106,678 +4135,6 @@ int GetPixelDataSize(int width, int height, int format) //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- -#if defined(SUPPORT_FILEFORMAT_DDS) -// Loading DDS image data (compressed or uncompressed) -static Image LoadDDS(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // Required extension: - // GL_EXT_texture_compression_s3tc - - // Supported tokens (defined by extensions) - // GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 - // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 - // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 - // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 - - #define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII - #define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII - #define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII - - // DDS Pixel Format - typedef struct { - unsigned int size; - unsigned int flags; - unsigned int fourCC; - unsigned int rgbBitCount; - unsigned int rBitMask; - unsigned int gBitMask; - unsigned int bBitMask; - unsigned int aBitMask; - } DDSPixelFormat; - - // DDS Header (124 bytes) - typedef struct { - unsigned int size; - unsigned int flags; - unsigned int height; - unsigned int width; - unsigned int pitchOrLinearSize; - unsigned int depth; - unsigned int mipmapCount; - unsigned int reserved1[11]; - DDSPixelFormat ddspf; - unsigned int caps; - unsigned int caps2; - unsigned int caps3; - unsigned int caps4; - unsigned int reserved2; - } DDSHeader; - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - // Verify the type of file - unsigned char *ddsHeaderId = fileDataPtr; - fileDataPtr += 4; - - if ((ddsHeaderId[0] != 'D') || (ddsHeaderId[1] != 'D') || (ddsHeaderId[2] != 'S') || (ddsHeaderId[3] != ' ')) - { - TRACELOG(LOG_WARNING, "IMAGE: DDS file data not valid"); - } - else - { - DDSHeader *ddsHeader = (DDSHeader *)fileDataPtr; - - TRACELOGD("IMAGE: DDS file data info:"); - TRACELOGD(" > Header size: %i", sizeof(DDSHeader)); - TRACELOGD(" > Pixel format size: %i", ddsHeader->ddspf.size); - TRACELOGD(" > Pixel format flags: 0x%x", ddsHeader->ddspf.flags); - TRACELOGD(" > File format: 0x%x", ddsHeader->ddspf.fourCC); - TRACELOGD(" > File bit count: 0x%x", ddsHeader->ddspf.rgbBitCount); - - fileDataPtr += sizeof(DDSHeader); // Skip header - - image.width = ddsHeader->width; - image.height = ddsHeader->height; - - if (ddsHeader->mipmapCount == 0) image.mipmaps = 1; // Parameter not used - else image.mipmaps = ddsHeader->mipmapCount; - - if (ddsHeader->ddspf.rgbBitCount == 16) // 16bit mode, no compressed - { - if (ddsHeader->ddspf.flags == 0x40) // no alpha channel - { - int dataSize = image.width*image.height*sizeof(unsigned short); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - image.format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; - } - else if (ddsHeader->ddspf.flags == 0x41) // with alpha channel - { - if (ddsHeader->ddspf.aBitMask == 0x8000) // 1bit alpha - { - int dataSize = image.width*image.height*sizeof(unsigned short); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - unsigned char alpha = 0; - - // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1 - for (int i = 0; i < image.width*image.height; i++) - { - alpha = ((unsigned short *)image.data)[i] >> 15; - ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 1; - ((unsigned short *)image.data)[i] += alpha; - } - - image.format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; - } - else if (ddsHeader->ddspf.aBitMask == 0xf000) // 4bit alpha - { - int dataSize = image.width*image.height*sizeof(unsigned short); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - unsigned char alpha = 0; - - // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4 - for (int i = 0; i < image.width*image.height; i++) - { - alpha = ((unsigned short *)image.data)[i] >> 12; - ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 4; - ((unsigned short *)image.data)[i] += alpha; - } - - image.format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; - } - } - } - else if (ddsHeader->ddspf.flags == 0x40 && ddsHeader->ddspf.rgbBitCount == 24) // DDS_RGB, no compressed - { - int dataSize = image.width*image.height*3*sizeof(unsigned char); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; - } - else if (ddsHeader->ddspf.flags == 0x41 && ddsHeader->ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed - { - int dataSize = image.width*image.height*4*sizeof(unsigned char); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - unsigned char blue = 0; - - // NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment) - // DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA - // So, we must realign B8G8R8A8 to R8G8B8A8 - for (int i = 0; i < image.width*image.height*4; i += 4) - { - blue = ((unsigned char *)image.data)[i]; - ((unsigned char *)image.data)[i] = ((unsigned char *)image.data)[i + 2]; - ((unsigned char *)image.data)[i + 2] = blue; - } - - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - } - else if (((ddsHeader->ddspf.flags == 0x04) || (ddsHeader->ddspf.flags == 0x05)) && (ddsHeader->ddspf.fourCC > 0)) // Compressed - { - int dataSize = 0; - - // Calculate data size, including all mipmaps - if (ddsHeader->mipmapCount > 1) dataSize = ddsHeader->pitchOrLinearSize*2; - else dataSize = ddsHeader->pitchOrLinearSize; - - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - - switch (ddsHeader->ddspf.fourCC) - { - case FOURCC_DXT1: - { - if (ddsHeader->ddspf.flags == 0x04) image.format = PIXELFORMAT_COMPRESSED_DXT1_RGB; - else image.format = PIXELFORMAT_COMPRESSED_DXT1_RGBA; - } break; - case FOURCC_DXT3: image.format = PIXELFORMAT_COMPRESSED_DXT3_RGBA; break; - case FOURCC_DXT5: image.format = PIXELFORMAT_COMPRESSED_DXT5_RGBA; break; - default: break; - } - } - } - } - - return image; -} -#endif - -#if defined(SUPPORT_FILEFORMAT_PKM) -// Loading PKM image data (ETC1/ETC2 compression) -// NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps) -// PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps) -static Image LoadPKM(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // Required extensions: - // GL_OES_compressed_ETC1_RGB8_texture (ETC1) (OpenGL ES 2.0) - // GL_ARB_ES3_compatibility (ETC2/EAC) (OpenGL ES 3.0) - - // Supported tokens (defined by extensions) - // GL_ETC1_RGB8_OES 0x8D64 - // GL_COMPRESSED_RGB8_ETC2 0x9274 - // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 - - // PKM file (ETC1) Header (16 bytes) - typedef struct { - char id[4]; // "PKM " - char version[2]; // "10" or "20" - unsigned short format; // Data format (big-endian) (Check list below) - unsigned short width; // Texture width (big-endian) (origWidth rounded to multiple of 4) - unsigned short height; // Texture height (big-endian) (origHeight rounded to multiple of 4) - unsigned short origWidth; // Original width (big-endian) - unsigned short origHeight; // Original height (big-endian) - } PKMHeader; - - // Formats list - // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used) - // version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R - - // NOTE: The extended width and height are the widths rounded up to a multiple of 4. - // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels) - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - PKMHeader *pkmHeader = (PKMHeader *)fileDataPtr; - - if ((pkmHeader->id[0] != 'P') || (pkmHeader->id[1] != 'K') || (pkmHeader->id[2] != 'M') || (pkmHeader->id[3] != ' ')) - { - TRACELOG(LOG_WARNING, "IMAGE: PKM file data not valid"); - } - else - { - fileDataPtr += sizeof(PKMHeader); // Skip header - - // NOTE: format, width and height come as big-endian, data must be swapped to little-endian - pkmHeader->format = ((pkmHeader->format & 0x00FF) << 8) | ((pkmHeader->format & 0xFF00) >> 8); - pkmHeader->width = ((pkmHeader->width & 0x00FF) << 8) | ((pkmHeader->width & 0xFF00) >> 8); - pkmHeader->height = ((pkmHeader->height & 0x00FF) << 8) | ((pkmHeader->height & 0xFF00) >> 8); - - TRACELOGD("IMAGE: PKM file data info:"); - TRACELOGD(" > Image width: %i", pkmHeader->width); - TRACELOGD(" > Image height: %i", pkmHeader->height); - TRACELOGD(" > Image format: %i", pkmHeader->format); - - image.width = pkmHeader->width; - image.height = pkmHeader->height; - image.mipmaps = 1; - - int bpp = 4; - if (pkmHeader->format == 3) bpp = 8; - - int dataSize = image.width*image.height*bpp/8; // Total data size in bytes - - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - - if (pkmHeader->format == 0) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB; - else if (pkmHeader->format == 1) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB; - else if (pkmHeader->format == 3) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; - } - } - - return image; -} -#endif - -#if defined(SUPPORT_FILEFORMAT_KTX) -// Load KTX compressed image data (ETC1/ETC2 compression) -// TODO: Review KTX loading, many things changed! -static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // Required extensions: - // GL_OES_compressed_ETC1_RGB8_texture (ETC1) - // GL_ARB_ES3_compatibility (ETC2/EAC) - - // Supported tokens (defined by extensions) - // GL_ETC1_RGB8_OES 0x8D64 - // GL_COMPRESSED_RGB8_ETC2 0x9274 - // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 - - // KTX file Header (64 bytes) - // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ - // v2.0 - http://github.khronos.org/KTX-Specification/ - - // TODO: Support KTX 2.2 specs! - - typedef struct { - char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" - unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 - unsigned int glType; // For compressed textures, glType must equal 0 - unsigned int glTypeSize; // For compressed texture data, usually 1 - unsigned int glFormat; // For compressed textures is 0 - unsigned int glInternalFormat; // Compressed internal format - unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) - unsigned int width; // Texture image width in pixels - unsigned int height; // Texture image height in pixels - unsigned int depth; // For 2D textures is 0 - unsigned int elements; // Number of array elements, usually 0 - unsigned int faces; // Cubemap faces, for no-cubemap = 1 - unsigned int mipmapLevels; // Non-mipmapped textures = 1 - unsigned int keyValueDataSize; // Used to encode any arbitrary data... - } KTXHeader; - - // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - KTXHeader *ktxHeader = (KTXHeader *)fileDataPtr; - - if ((ktxHeader->id[1] != 'K') || (ktxHeader->id[2] != 'T') || (ktxHeader->id[3] != 'X') || - (ktxHeader->id[4] != ' ') || (ktxHeader->id[5] != '1') || (ktxHeader->id[6] != '1')) - { - TRACELOG(LOG_WARNING, "IMAGE: KTX file data not valid"); - } - else - { - fileDataPtr += sizeof(KTXHeader); // Move file data pointer - - image.width = ktxHeader->width; - image.height = ktxHeader->height; - image.mipmaps = ktxHeader->mipmapLevels; - - TRACELOGD("IMAGE: KTX file data info:"); - TRACELOGD(" > Image width: %i", ktxHeader->width); - TRACELOGD(" > Image height: %i", ktxHeader->height); - TRACELOGD(" > Image format: 0x%x", ktxHeader->glInternalFormat); - - fileDataPtr += ktxHeader->keyValueDataSize; // Skip value data size - - int dataSize = ((int *)fileDataPtr)[0]; - fileDataPtr += sizeof(int); - - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - - if (ktxHeader->glInternalFormat == 0x8D64) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB; - else if (ktxHeader->glInternalFormat == 0x9274) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB; - else if (ktxHeader->glInternalFormat == 0x9278) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; - - // TODO: Support uncompressed data formats? Right now it returns format = 0! - } - } - - return image; -} - -// Save image data as KTX file -// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018) -// TODO: Review KTX saving, many things changed! -static int SaveKTX(Image image, const char *fileName) -{ - // KTX file Header (64 bytes) - // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ - // v2.0 - http://github.khronos.org/KTX-Specification/ - Final specs by 2021-04-18 - typedef struct { - char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n" - unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 - unsigned int glType; // For compressed textures, glType must equal 0 - unsigned int glTypeSize; // For compressed texture data, usually 1 - unsigned int glFormat; // For compressed textures is 0 - unsigned int glInternalFormat; // Compressed internal format - unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) // KTX 2.0: UInt32 vkFormat - unsigned int width; // Texture image width in pixels - unsigned int height; // Texture image height in pixels - unsigned int depth; // For 2D textures is 0 - unsigned int elements; // Number of array elements, usually 0 - unsigned int faces; // Cubemap faces, for no-cubemap = 1 - unsigned int mipmapLevels; // Non-mipmapped textures = 1 - unsigned int keyValueDataSize; // Used to encode any arbitrary data... // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0 - // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)... - // KTX 2.0 defines additional header elements... - } KTXHeader; - - // Calculate file dataSize required - int dataSize = sizeof(KTXHeader); - - for (int i = 0, width = image.width, height = image.height; i < image.mipmaps; i++) - { - dataSize += GetPixelDataSize(width, height, image.format); - width /= 2; height /= 2; - } - - unsigned char *fileData = RL_CALLOC(dataSize, 1); - unsigned char *fileDataPtr = fileData; - - KTXHeader ktxHeader = { 0 }; - - // KTX identifier (v1.1) - //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' }; - //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; - - const char ktxIdentifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' }; - - // Get the image header - memcpy(ktxHeader.id, ktxIdentifier, 12); // KTX 1.1 signature - ktxHeader.endianness = 0; - ktxHeader.glType = 0; // Obtained from image.format - ktxHeader.glTypeSize = 1; - ktxHeader.glFormat = 0; // Obtained from image.format - ktxHeader.glInternalFormat = 0; // Obtained from image.format - ktxHeader.glBaseInternalFormat = 0; - ktxHeader.width = image.width; - ktxHeader.height = image.height; - ktxHeader.depth = 0; - ktxHeader.elements = 0; - ktxHeader.faces = 1; - ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats) - ktxHeader.keyValueDataSize = 0; // No extra data after the header - - rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function - ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only - - // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC - - if (ktxHeader.glFormat == -1) TRACELOG(LOG_WARNING, "IMAGE: GL format not supported for KTX export (%i)", ktxHeader.glFormat); - else - { - memcpy(fileDataPtr, &ktxHeader, sizeof(KTXHeader)); - fileDataPtr += sizeof(KTXHeader); - - int width = image.width; - int height = image.height; - int dataOffset = 0; - - // Save all mipmaps data - for (int i = 0; i < image.mipmaps; i++) - { - unsigned int dataSize = GetPixelDataSize(width, height, image.format); - - memcpy(fileDataPtr, &dataSize, sizeof(unsigned int)); - memcpy(fileDataPtr + 4, (unsigned char *)image.data + dataOffset, dataSize); - - width /= 2; - height /= 2; - dataOffset += dataSize; - fileDataPtr += (4 + dataSize); - } - } - - int success = SaveFileData(fileName, fileData, dataSize); - - RL_FREE(fileData); // Free file data buffer - - // If all data has been written correctly to file, success = 1 - return success; -} -#endif - -#if defined(SUPPORT_FILEFORMAT_PVR) -// Loading PVR image data (uncompressed or PVRT compression) -// NOTE: PVR v2 not supported, use PVR v3 instead -static Image LoadPVR(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // Required extension: - // GL_IMG_texture_compression_pvrtc - - // Supported tokens (defined by extensions) - // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 - // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 - -#if 0 // Not used... - // PVR file v2 Header (52 bytes) - typedef struct { - unsigned int headerLength; - unsigned int height; - unsigned int width; - unsigned int numMipmaps; - unsigned int flags; - unsigned int dataLength; - unsigned int bpp; - unsigned int bitmaskRed; - unsigned int bitmaskGreen; - unsigned int bitmaskBlue; - unsigned int bitmaskAlpha; - unsigned int pvrTag; - unsigned int numSurfs; - } PVRHeaderV2; -#endif - - // PVR file v3 Header (52 bytes) - // NOTE: After it could be metadata (15 bytes?) - typedef struct { - char id[4]; - unsigned int flags; - unsigned char channels[4]; // pixelFormat high part - unsigned char channelDepth[4]; // pixelFormat low part - unsigned int colorSpace; - unsigned int channelType; - unsigned int height; - unsigned int width; - unsigned int depth; - unsigned int numSurfaces; - unsigned int numFaces; - unsigned int numMipmaps; - unsigned int metaDataSize; - } PVRHeaderV3; - -#if 0 // Not used... - // Metadata (usually 15 bytes) - typedef struct { - unsigned int devFOURCC; - unsigned int key; - unsigned int dataSize; // Not used? - unsigned char *data; // Not used? - } PVRMetadata; -#endif - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - // Check PVR image version - unsigned char pvrVersion = fileDataPtr[0]; - - // Load different PVR data formats - if (pvrVersion == 0x50) - { - PVRHeaderV3 *pvrHeader = (PVRHeaderV3 *)fileDataPtr; - - if ((pvrHeader->id[0] != 'P') || (pvrHeader->id[1] != 'V') || (pvrHeader->id[2] != 'R') || (pvrHeader->id[3] != 3)) - { - TRACELOG(LOG_WARNING, "IMAGE: PVR file data not valid"); - } - else - { - fileDataPtr += sizeof(PVRHeaderV3); // Skip header - - image.width = pvrHeader->width; - image.height = pvrHeader->height; - image.mipmaps = pvrHeader->numMipmaps; - - // Check data format - if (((pvrHeader->channels[0] == 'l') && (pvrHeader->channels[1] == 0)) && (pvrHeader->channelDepth[0] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; - else if (((pvrHeader->channels[0] == 'l') && (pvrHeader->channels[1] == 'a')) && ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8))) image.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; - else if ((pvrHeader->channels[0] == 'r') && (pvrHeader->channels[1] == 'g') && (pvrHeader->channels[2] == 'b')) - { - if (pvrHeader->channels[3] == 'a') - { - if ((pvrHeader->channelDepth[0] == 5) && (pvrHeader->channelDepth[1] == 5) && (pvrHeader->channelDepth[2] == 5) && (pvrHeader->channelDepth[3] == 1)) image.format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; - else if ((pvrHeader->channelDepth[0] == 4) && (pvrHeader->channelDepth[1] == 4) && (pvrHeader->channelDepth[2] == 4) && (pvrHeader->channelDepth[3] == 4)) image.format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; - else if ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8) && (pvrHeader->channelDepth[2] == 8) && (pvrHeader->channelDepth[3] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - } - else if (pvrHeader->channels[3] == 0) - { - if ((pvrHeader->channelDepth[0] == 5) && (pvrHeader->channelDepth[1] == 6) && (pvrHeader->channelDepth[2] == 5)) image.format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; - else if ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8) && (pvrHeader->channelDepth[2] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; - } - } - else if (pvrHeader->channels[0] == 2) image.format = PIXELFORMAT_COMPRESSED_PVRT_RGB; - else if (pvrHeader->channels[0] == 3) image.format = PIXELFORMAT_COMPRESSED_PVRT_RGBA; - - fileDataPtr += pvrHeader->metaDataSize; // Skip meta data header - - // Calculate data size (depends on format) - int bpp = 0; - switch (image.format) - { - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - case PIXELFORMAT_UNCOMPRESSED_R5G6B5: - case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; - case PIXELFORMAT_COMPRESSED_PVRT_RGB: - case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; - default: break; - } - - int dataSize = image.width*image.height*bpp/8; // Total data size in bytes - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - } - } - else if (pvrVersion == 52) TRACELOG(LOG_INFO, "IMAGE: PVRv2 format not supported, update your files to PVRv3"); - } - - return image; -} -#endif - -#if defined(SUPPORT_FILEFORMAT_ASTC) -// Load ASTC compressed image data (ASTC compression) -static Image LoadASTC(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // Required extensions: - // GL_KHR_texture_compression_astc_hdr - // GL_KHR_texture_compression_astc_ldr - - // Supported tokens (defined by extensions) - // GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 - // GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 - - // ASTC file Header (16 bytes) - typedef struct { - unsigned char id[4]; // Signature: 0x13 0xAB 0xA1 0x5C - unsigned char blockX; // Block X dimensions - unsigned char blockY; // Block Y dimensions - unsigned char blockZ; // Block Z dimensions (1 for 2D images) - unsigned char width[3]; // Image width in pixels (24bit value) - unsigned char height[3]; // Image height in pixels (24bit value) - unsigned char length[3]; // Image Z-size (1 for 2D images) - } ASTCHeader; - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - ASTCHeader *astcHeader = (ASTCHeader *)fileDataPtr; - - if ((astcHeader->id[3] != 0x5c) || (astcHeader->id[2] != 0xa1) || (astcHeader->id[1] != 0xab) || (astcHeader->id[0] != 0x13)) - { - TRACELOG(LOG_WARNING, "IMAGE: ASTC file data not valid"); - } - else - { - fileDataPtr += sizeof(ASTCHeader); // Skip header - - // NOTE: Assuming Little Endian (could it be wrong?) - image.width = 0x00000000 | ((int)astcHeader->width[2] << 16) | ((int)astcHeader->width[1] << 8) | ((int)astcHeader->width[0]); - image.height = 0x00000000 | ((int)astcHeader->height[2] << 16) | ((int)astcHeader->height[1] << 8) | ((int)astcHeader->height[0]); - - TRACELOGD("IMAGE: ASTC file data info:"); - TRACELOGD(" > Image width: %i", image.width); - TRACELOGD(" > Image height: %i", image.height); - TRACELOGD(" > Image blocks: %ix%i", astcHeader->blockX, astcHeader->blockY); - - image.mipmaps = 1; // NOTE: ASTC format only contains one mipmap level - - // NOTE: Each block is always stored in 128bit so we can calculate the bpp - int bpp = 128/(astcHeader->blockX*astcHeader->blockY); - - // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8 - if ((bpp == 8) || (bpp == 2)) - { - int dataSize = image.width*image.height*bpp/8; // Data size in bytes - - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - - if (bpp == 8) image.format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA; - else if (bpp == 2) image.format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA; - } - else TRACELOG(LOG_WARNING, "IMAGE: ASTC block size configuration not supported"); - } - } - - return image; -} -#endif - // Get pixel data from image as Vector4 array (float normalized) static Vector4 *LoadImageDataNormalized(Image image) { From 4c3d577ddbe0c36412c146072b0dc813ea5d079c Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 17 Sep 2022 17:12:57 -0400 Subject: [PATCH 04/20] CMake: Project template fix to easily target raylib version (#2700) --- projects/CMake/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index 351b4809f..cfd924fba 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -5,12 +5,13 @@ project(example) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Dependencies -find_package(raylib 4.2.0 QUIET) # QUIET or REQUIRED +set(RAYLIB_VERSION 4.2.0) +find_package(raylib ${RAYLIB_VERSION} QUIET) # QUIET or REQUIRED if (NOT raylib_FOUND) # If there's none, fetch and build raylib include(FetchContent) FetchContent_Declare( raylib - URL https://github.com/raysan5/raylib/archive/refs/tags/4.0.0.tar.gz + URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz ) FetchContent_GetProperties(raylib) if (NOT raylib_POPULATED) # Have we downloaded raylib yet? From 494a581339721f5693654281b19d2b2e69dfd9df Mon Sep 17 00:00:00 2001 From: AQuantumPotato <113799046+AQuantumPotato@users.noreply.github.com> Date: Sat, 17 Sep 2022 21:14:00 +0000 Subject: [PATCH 05/20] Update BINDINGS.md (#2701) Updated the list of projects that support 4.2 --- BINDINGS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index e6d167596..fe15740fa 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -6,7 +6,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | name | raylib version | language | license | repo | |:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------| -| raylib | **4.0** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | +| raylib | **4.2** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo | | Raylib-cs | **4.0** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | **4.0** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | @@ -15,7 +15,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | **4.0** | [D](https://dlang.org/) | Apache-2.0 | https://github.com/xdrie/dray | -| raylib-d | **4.0** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | +| raylib-d | **4.2** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | | dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | @@ -29,7 +29,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | **4.2** | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | -| raylib-lua | **4.0** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | +| raylib-lua | **4.2** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | **4.0** | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | @@ -54,7 +54,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-smallBasic | 4.1-dev | [SmallBASIC](https://github.com/smallbasic/SmallBASIC) | GPLv3 | https://github.com/smallbasic/smallbasic.plugins/tree/master/raylib | | raylib.v | **4.2** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | -| raylib-zig | **4.0** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | +| raylib-zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | | raylib.zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | | hare-raylib | auto | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | From 8e57c8ace389135d1f4b92ced0bfb79920ed359e Mon Sep 17 00:00:00 2001 From: Denis Pobedrya Date: Sun, 18 Sep 2022 00:15:11 +0300 Subject: [PATCH 06/20] Fix touchscreen input related functions on Android (#2702) Fix display -> screen coordinate conversion for android platform and move it to the platform event handling code, simplifying GetTouchPosition() function implementation. Co-authored-by: Denis Pobedrya --- src/rcore.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 40581415f..daf39a93d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3835,22 +3835,7 @@ Vector2 GetTouchPosition(int index) // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages if (index == 0) position = GetMousePosition(); #endif -#if defined(PLATFORM_ANDROID) - if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; - else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); - - if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height)) - { - position.x = position.x*((float)CORE.Window.screen.width/(float)(CORE.Window.display.width - CORE.Window.renderOffset.x)) - CORE.Window.renderOffset.x/2; - position.y = position.y*((float)CORE.Window.screen.height/(float)(CORE.Window.display.height - CORE.Window.renderOffset.y)) - CORE.Window.renderOffset.y/2; - } - else - { - position.x = position.x*((float)CORE.Window.render.width/(float)CORE.Window.display.width) - CORE.Window.renderOffset.x/2; - position.y = position.y*((float)CORE.Window.render.height/(float)CORE.Window.display.height) - CORE.Window.renderOffset.y/2; - } -#endif -#if defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); #endif @@ -5675,9 +5660,11 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) // Register touch points position CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; - // Normalize CORE.Input.Touch.position[x] for screenWidth and screenHeight - CORE.Input.Touch.position[i].x /= (float)GetScreenWidth(); - CORE.Input.Touch.position[i].y /= (float)GetScreenHeight(); + // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height + float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x) / (float)CORE.Window.display.width; + float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y) / (float)CORE.Window.display.height; + CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x * widthRatio - (float)CORE.Window.renderOffset.x / 2; + CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y * heightRatio - (float)CORE.Window.renderOffset.y / 2; } int32_t action = AMotionEvent_getAction(event); From ff25402f689113da07f0077354f32073fc070c2e Mon Sep 17 00:00:00 2001 From: Denis Pobedrya Date: Sun, 18 Sep 2022 12:28:55 +0300 Subject: [PATCH 07/20] Fix viewport scaling bug on android after context rebind (#2703) On android after rebinding context (which happens when you minimize and navigate back to an app, or when you turn a screen off and back on) there's a bug that viewport has a wrong scale and part of it is off screen. The change fixes it on devices I tried, but the solution feels hacky to me. However when I attempted instead to call SetupViewport() again which feels like a more proper solution, it didn't fix it. --- src/rcore.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index daf39a93d..f94687074 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5484,7 +5484,14 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Reset screen scaling to full display size EGLint displayFormat = 0; eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - ANativeWindow_setBuffersGeometry(app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + + // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the + // context rebinding if the screen is scaled unless offsets are added. There's probably a more + // appropriate way to fix this + ANativeWindow_setBuffersGeometry(app->window, + CORE.Window.render.width + CORE.Window.renderOffset.x, + CORE.Window.render.height + CORE.Window.renderOffset.y, + displayFormat); // Recreate display surface and re-attach OpenGL context CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); From 8f597b3cc379258215cb6cbdedada7e8389ad1df Mon Sep 17 00:00:00 2001 From: Denis Pobedrya Date: Sun, 18 Sep 2022 20:58:52 +0300 Subject: [PATCH 08/20] Basic gamepad support for Android (#2709) Currently assumes a single gamepad, has no code specific to gamepad detection (gamepad is "detected" when an event related to gamepad arrives). Also assumes that all gamepads look roughly like an xbox/ps controller. Both assumptions are not strictly true, but an implementation like that probably covers 85% of usecases. Also it doesn't update previousButtonState so functions IsGamepadButtonPressed() and IsGamepadButtonReleased() don't work, but they didn't work previously on Android anyway, and they are flaky on desktop as they are now, so the mechanism for these two functions probably should be reworked anyway. It's certainly an improvement compared to the previous android gamepad handling code, which put gamepad events into touch related structs. --- src/rcore.c | 108 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index f94687074..e46751d27 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5580,6 +5580,32 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } } +static GamepadButton AndroidTranslateGamepadButton(int button) +{ + switch (button) + { + case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN; + case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; + case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT; + case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP; + case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1; + case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1; + case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2; + case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB; + case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB; + case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT; + case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT; + case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE; + // On some (most?) gamepads dpad events are reported as axis motion instead + case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN; + case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT; + case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT; + case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP; + default: return GAMEPAD_BUTTON_UNKNOWN; + } +} + // ANDROID: Get input events static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) { @@ -5595,26 +5621,59 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) { - // Get first touch position - CORE.Input.Touch.position[0].x = AMotionEvent_getX(event, 0); - CORE.Input.Touch.position[0].y = AMotionEvent_getY(event, 0); + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; - // Get second touch position - CORE.Input.Touch.position[1].x = AMotionEvent_getX(event, 1); - CORE.Input.Touch.position[1].y = AMotionEvent_getY(event, 1); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_X, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Y, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Z, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_RZ, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f; + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f; - int32_t keycode = AKeyEvent_getKeyCode(event); - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + // dpad is reported as an axis on android + float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); + float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0); + + if (dpadX == 1.0f) { - CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; - CORE.Input.Keyboard.keyPressedQueueCount++; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; + } + else if (dpadX == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; } - else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up - // Stop processing gamepad buttons - return 1; + if (dpadY == 1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + else if (dpadY == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + + return 1; // Handled gamepad axis motion } } else if (type == AINPUT_EVENT_TYPE_KEY) @@ -5622,6 +5681,25 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) int32_t keycode = AKeyEvent_getKeyCode(event); //int32_t AKeyEvent_getMetaState(event); + // Handle gamepad button presses and releases + if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || + ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) + { + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; + + GamepadButton button = AndroidTranslateGamepadButton(keycode); + if (button == GAMEPAD_BUTTON_UNKNOWN) + return 1; + + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + CORE.Input.Gamepad.currentButtonState[0][button] = 1; + } + else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up + return 1; // Handled gamepad button + } + // Save current button and its state // NOTE: Android key action is 0 for down and 1 for up if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) From 42ecd72547b699c0f3008d49831e504e4eb3dfb2 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Sun, 18 Sep 2022 21:08:51 +0200 Subject: [PATCH 09/20] [rlgl] Check for extensions before enabling them (#2706) * [rlgl] Check for extensions before enabling them * Shift to glad on macOS * #undef CORE_OPENGL_33 * Remove version hack and fix ASTC compression assumption * Remove loader from glad * Use GLAD_MALLOC/FREE instead of malloc/free * More explicit extension checking --- src/external/glad.h | 170 +++++++++++++++++++++++++++----------------- src/rcore.c | 6 +- src/rlgl.h | 62 ++++++++-------- 3 files changed, 139 insertions(+), 99 deletions(-) diff --git a/src/external/glad.h b/src/external/glad.h index 5ca3ead32..5bb79d40d 100644 --- a/src/external/glad.h +++ b/src/external/glad.h @@ -1,9 +1,9 @@ /** - * Loader generated by glad 2.0.0-beta on Sun Oct 17 18:39:09 2021 + * Loader generated by glad 2.0.0-beta on Sun Sep 18 18:12:12 2022 * * Generator: C/C++ * Specification: gl - * Extensions: 110 + * Extensions: 116 * * APIs: * - gl:core=4.3 @@ -12,16 +12,16 @@ * - ALIAS = False * - DEBUG = False * - HEADER_ONLY = True - * - LOADER = True + * - LOADER = False * - MX = False * - MX_GLOBAL = False * - ON_DEMAND = False * * Commandline: - * --api='gl:core=4.3' --extensions='GL_ARB_ES2_compatibility,GL_ARB_ES3_1_compatibility,GL_ARB_ES3_2_compatibility,GL_ARB_ES3_compatibility,GL_ARB_blend_func_extended,GL_ARB_buffer_storage,GL_ARB_clear_buffer_object,GL_ARB_clear_texture,GL_ARB_color_buffer_float,GL_ARB_compatibility,GL_ARB_compressed_texture_pixel_storage,GL_ARB_compute_shader,GL_ARB_compute_variable_group_size,GL_ARB_copy_buffer,GL_ARB_copy_image,GL_ARB_debug_output,GL_ARB_depth_buffer_float,GL_ARB_depth_clamp,GL_ARB_depth_texture,GL_ARB_direct_state_access,GL_ARB_draw_buffers,GL_ARB_draw_buffers_blend,GL_ARB_draw_elements_base_vertex,GL_ARB_draw_indirect,GL_ARB_draw_instanced,GL_ARB_enhanced_layouts,GL_ARB_explicit_attrib_location,GL_ARB_explicit_uniform_location,GL_ARB_fragment_coord_conventions,GL_ARB_fragment_layer_viewport,GL_ARB_fragment_program,GL_ARB_fragment_program_shadow,GL_ARB_fragment_shader,GL_ARB_fragment_shader_interlock,GL_ARB_framebuffer_no_attachments,GL_ARB_framebuffer_object,GL_ARB_framebuffer_sRGB,GL_ARB_geometry_shader4,GL_ARB_get_program_binary,GL_ARB_get_texture_sub_image,GL_ARB_gl_spirv,GL_ARB_gpu_shader5,GL_ARB_gpu_shader_fp64,GL_ARB_gpu_shader_int64,GL_ARB_half_float_pixel,GL_ARB_half_float_vertex,GL_ARB_instanced_arrays,GL_ARB_internalformat_query,GL_ARB_internalformat_query2,GL_ARB_map_buffer_range,GL_ARB_multi_bind,GL_ARB_multi_draw_indirect,GL_ARB_multisample,GL_ARB_multitexture,GL_ARB_occlusion_query,GL_ARB_occlusion_query2,GL_ARB_pipeline_statistics_query,GL_ARB_query_buffer_object,GL_ARB_sample_locations,GL_ARB_sample_shading,GL_ARB_seamless_cube_map,GL_ARB_seamless_cubemap_per_texture,GL_ARB_shader_atomic_counter_ops,GL_ARB_shader_atomic_counters,GL_ARB_shader_bit_encoding,GL_ARB_shader_clock,GL_ARB_shader_image_load_store,GL_ARB_shader_image_size,GL_ARB_shader_objects,GL_ARB_shader_storage_buffer_object,GL_ARB_shader_texture_lod,GL_ARB_shading_language_100,GL_ARB_shading_language_420pack,GL_ARB_shading_language_include,GL_ARB_shading_language_packing,GL_ARB_spirv_extensions,GL_ARB_tessellation_shader,GL_ARB_texture_border_clamp,GL_ARB_texture_buffer_object_rgb32,GL_ARB_texture_compression,GL_ARB_texture_cube_map,GL_ARB_texture_cube_map_array,GL_ARB_texture_env_add,GL_ARB_texture_filter_anisotropic,GL_ARB_texture_filter_minmax,GL_ARB_texture_float,GL_ARB_texture_mirror_clamp_to_edge,GL_ARB_texture_mirrored_repeat,GL_ARB_texture_multisample,GL_ARB_texture_non_power_of_two,GL_ARB_texture_rg,GL_ARB_texture_storage,GL_ARB_texture_swizzle,GL_ARB_texture_view,GL_ARB_timer_query,GL_ARB_transpose_matrix,GL_ARB_uniform_buffer_object,GL_ARB_vertex_array_bgra,GL_ARB_vertex_array_object,GL_ARB_vertex_attrib_binding,GL_ARB_vertex_buffer_object,GL_ARB_vertex_program,GL_ARB_vertex_shader,GL_EXT_fog_coord,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_EXT_framebuffer_sRGB,GL_OES_compressed_paletted_texture,GL_OES_fixed_point' c --header-only --loader + * --api='gl:core=4.3' --extensions='GL_ARB_ES2_compatibility,GL_ARB_ES3_1_compatibility,GL_ARB_ES3_2_compatibility,GL_ARB_ES3_compatibility,GL_ARB_blend_func_extended,GL_ARB_buffer_storage,GL_ARB_clear_buffer_object,GL_ARB_clear_texture,GL_ARB_color_buffer_float,GL_ARB_compatibility,GL_ARB_compressed_texture_pixel_storage,GL_ARB_compute_shader,GL_ARB_compute_variable_group_size,GL_ARB_copy_buffer,GL_ARB_copy_image,GL_ARB_debug_output,GL_ARB_depth_buffer_float,GL_ARB_depth_clamp,GL_ARB_depth_texture,GL_ARB_direct_state_access,GL_ARB_draw_buffers,GL_ARB_draw_buffers_blend,GL_ARB_draw_elements_base_vertex,GL_ARB_draw_indirect,GL_ARB_draw_instanced,GL_ARB_enhanced_layouts,GL_ARB_explicit_attrib_location,GL_ARB_explicit_uniform_location,GL_ARB_fragment_coord_conventions,GL_ARB_fragment_layer_viewport,GL_ARB_fragment_program,GL_ARB_fragment_program_shadow,GL_ARB_fragment_shader,GL_ARB_fragment_shader_interlock,GL_ARB_framebuffer_no_attachments,GL_ARB_framebuffer_object,GL_ARB_framebuffer_sRGB,GL_ARB_geometry_shader4,GL_ARB_get_program_binary,GL_ARB_get_texture_sub_image,GL_ARB_gl_spirv,GL_ARB_gpu_shader5,GL_ARB_gpu_shader_fp64,GL_ARB_gpu_shader_int64,GL_ARB_half_float_pixel,GL_ARB_half_float_vertex,GL_ARB_instanced_arrays,GL_ARB_internalformat_query,GL_ARB_internalformat_query2,GL_ARB_map_buffer_range,GL_ARB_multi_bind,GL_ARB_multi_draw_indirect,GL_ARB_multisample,GL_ARB_multitexture,GL_ARB_occlusion_query,GL_ARB_occlusion_query2,GL_ARB_pipeline_statistics_query,GL_ARB_query_buffer_object,GL_ARB_sample_locations,GL_ARB_sample_shading,GL_ARB_seamless_cube_map,GL_ARB_seamless_cubemap_per_texture,GL_ARB_shader_atomic_counter_ops,GL_ARB_shader_atomic_counters,GL_ARB_shader_bit_encoding,GL_ARB_shader_clock,GL_ARB_shader_image_load_store,GL_ARB_shader_image_size,GL_ARB_shader_objects,GL_ARB_shader_storage_buffer_object,GL_ARB_shader_texture_lod,GL_ARB_shading_language_100,GL_ARB_shading_language_420pack,GL_ARB_shading_language_include,GL_ARB_shading_language_packing,GL_ARB_spirv_extensions,GL_ARB_tessellation_shader,GL_ARB_texture_border_clamp,GL_ARB_texture_buffer_object_rgb32,GL_ARB_texture_compression,GL_ARB_texture_cube_map,GL_ARB_texture_cube_map_array,GL_ARB_texture_env_add,GL_ARB_texture_filter_anisotropic,GL_ARB_texture_filter_minmax,GL_ARB_texture_float,GL_ARB_texture_mirror_clamp_to_edge,GL_ARB_texture_mirrored_repeat,GL_ARB_texture_multisample,GL_ARB_texture_non_power_of_two,GL_ARB_texture_rg,GL_ARB_texture_storage,GL_ARB_texture_swizzle,GL_ARB_texture_view,GL_ARB_timer_query,GL_ARB_transpose_matrix,GL_ARB_uniform_buffer_object,GL_ARB_vertex_array_bgra,GL_ARB_vertex_array_object,GL_ARB_vertex_attrib_binding,GL_ARB_vertex_buffer_object,GL_ARB_vertex_program,GL_ARB_vertex_shader,GL_EXT_draw_instanced,GL_EXT_fog_coord,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_EXT_framebuffer_sRGB,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_mirror_clamp,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_OES_compressed_paletted_texture,GL_OES_fixed_point' c --header-only * * Online: - * http://glad.sh/#api=gl%3Acore%3D4.3&generator=c&options=HEADER_ONLY%2CLOADER + * http://glad.sh/#api=gl%3Acore%3D4.3&generator=c&options=HEADER_ONLY * */ @@ -54,7 +54,6 @@ #define GLAD_GL #define GLAD_OPTION_GL_HEADER_ONLY -#define GLAD_OPTION_GL_LOADER #ifdef __cplusplus extern "C" { @@ -381,11 +380,25 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_COMPRESSED_RGBA 0x84EE #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 #define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 #define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C -#define GL_COMPRESSED_RGB_ARB 0x84ED #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#define GL_COMPRESSED_RGB_ARB 0x84ED #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 @@ -395,6 +408,20 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 #define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE #define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 #define GL_COMPRESSED_SRGB8_ETC2 0x9275 #define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 @@ -1067,6 +1094,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 #define GL_MAX_TEXTURE_LOD_BIAS 0x84FD #define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #define GL_MAX_TEXTURE_SIZE 0x0D33 #define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 #define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 @@ -1113,7 +1141,10 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_MIPMAP 0x8293 #define GL_MIRRORED_REPEAT 0x8370 #define GL_MIRRORED_REPEAT_ARB 0x8370 +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 #define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 #define GL_MULTISAMPLE 0x809D #define GL_MULTISAMPLE_ARB 0x809D #define GL_MULTISAMPLE_BIT_ARB 0x20000000 @@ -1749,6 +1780,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_TEXTURE_MAX_LEVEL 0x813D #define GL_TEXTURE_MAX_LOD 0x813B #define GL_TEXTURE_MIN_FILTER 0x2801 @@ -2159,6 +2191,20 @@ typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif #elif defined(__VMS ) || defined(__sgi) @@ -2241,14 +2287,21 @@ typedef unsigned short int khronos_uint16_t; * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears * to be the only LLP64 architecture in current use. */ -#ifdef _WIN64 +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; -typedef signed long long int khronos_ssize_t; -typedef unsigned long long int khronos_usize_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else typedef signed long int khronos_ssize_t; typedef unsigned long int khronos_usize_t; #endif @@ -2294,113 +2347,70 @@ typedef enum { } khronos_boolean_enum_t; #endif /* __khrplatform_h_ */ - typedef unsigned int GLenum; - typedef unsigned char GLboolean; - typedef unsigned int GLbitfield; - typedef void GLvoid; - typedef khronos_int8_t GLbyte; - typedef khronos_uint8_t GLubyte; - typedef khronos_int16_t GLshort; - typedef khronos_uint16_t GLushort; - typedef int GLint; - typedef unsigned int GLuint; - typedef khronos_int32_t GLclampx; - typedef int GLsizei; - typedef khronos_float_t GLfloat; - typedef khronos_float_t GLclampf; - typedef double GLdouble; - typedef double GLclampd; - typedef void *GLeglClientBufferEXT; - typedef void *GLeglImageOES; - typedef char GLchar; - typedef char GLcharARB; - #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif - typedef khronos_uint16_t GLhalf; - typedef khronos_uint16_t GLhalfARB; - typedef khronos_int32_t GLfixed; - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptr; #else typedef khronos_intptr_t GLintptr; #endif - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptrARB; #else typedef khronos_intptr_t GLintptrARB; #endif - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptr; #else typedef khronos_ssize_t GLsizeiptr; #endif - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptrARB; #else typedef khronos_ssize_t GLsizeiptrARB; #endif - typedef khronos_int64_t GLint64; - typedef khronos_int64_t GLint64EXT; - typedef khronos_uint64_t GLuint64; - typedef khronos_uint64_t GLuint64EXT; - typedef struct __GLsync *GLsync; - struct _cl_context; - struct _cl_event; - typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); - typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); - typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); - typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); - typedef unsigned short GLhalfNV; - typedef GLintptr GLvdpauSurfaceNV; - typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); - #define GL_VERSION_1_0 1 GLAD_API_CALL int GLAD_GL_VERSION_1_0; #define GL_VERSION_1_1 1 @@ -2593,8 +2603,6 @@ GLAD_API_CALL int GLAD_GL_ARB_texture_border_clamp; GLAD_API_CALL int GLAD_GL_ARB_texture_buffer_object_rgb32; #define GL_ARB_texture_compression 1 GLAD_API_CALL int GLAD_GL_ARB_texture_compression; -#define GL_EXT_texture_compression_s3tc 1 -GLAD_API_CALL int GLAD_GL_EXT_texture_compression_s3tc; #define GL_ARB_texture_cube_map 1 GLAD_API_CALL int GLAD_GL_ARB_texture_cube_map; #define GL_ARB_texture_cube_map_array 1 @@ -2641,6 +2649,8 @@ GLAD_API_CALL int GLAD_GL_ARB_vertex_buffer_object; GLAD_API_CALL int GLAD_GL_ARB_vertex_program; #define GL_ARB_vertex_shader 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_shader; +#define GL_EXT_draw_instanced 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_instanced; #define GL_EXT_fog_coord 1 GLAD_API_CALL int GLAD_GL_EXT_fog_coord; #define GL_EXT_framebuffer_blit 1 @@ -2651,6 +2661,16 @@ GLAD_API_CALL int GLAD_GL_EXT_framebuffer_multisample; GLAD_API_CALL int GLAD_GL_EXT_framebuffer_object; #define GL_EXT_framebuffer_sRGB 1 GLAD_API_CALL int GLAD_GL_EXT_framebuffer_sRGB; +#define GL_EXT_texture_compression_s3tc 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_s3tc; +#define GL_EXT_texture_filter_anisotropic 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_filter_anisotropic; +#define GL_EXT_texture_mirror_clamp 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_mirror_clamp; +#define GL_KHR_texture_compression_astc_hdr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_hdr; +#define GL_KHR_texture_compression_astc_ldr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_ldr; #define GL_OES_compressed_paletted_texture 1 GLAD_API_CALL int GLAD_GL_OES_compressed_paletted_texture; #define GL_OES_fixed_point 1 @@ -2848,6 +2868,7 @@ typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINDIRECTPROC)(GLenum mode, const void typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDARBPROC)(GLenum mode, GLint first, GLsizei count, GLsizei primcount); typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDEXTPROC)(GLenum mode, GLint start, GLsizei count, GLsizei primcount); typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERPROC)(GLenum buf); typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum * bufs); typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSARBPROC)(GLsizei n, const GLenum * bufs); @@ -2859,6 +2880,7 @@ typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDARBPROC)(GLenum mode, GLsi typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLuint baseinstance); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei primcount); typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices); typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKPROC)(GLenum mode, GLuint id); @@ -4045,6 +4067,8 @@ GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB; #define glDrawArraysInstancedARB glad_glDrawArraysInstancedARB GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance; #define glDrawArraysInstancedBaseInstance glad_glDrawArraysInstancedBaseInstance +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT; +#define glDrawArraysInstancedEXT glad_glDrawArraysInstancedEXT GLAD_API_CALL PFNGLDRAWBUFFERPROC glad_glDrawBuffer; #define glDrawBuffer glad_glDrawBuffer GLAD_API_CALL PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; @@ -4067,6 +4091,8 @@ GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstan #define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance; #define glDrawElementsInstancedBaseVertexBaseInstance glad_glDrawElementsInstancedBaseVertexBaseInstance +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT; +#define glDrawElementsInstancedEXT glad_glDrawElementsInstancedEXT GLAD_API_CALL PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; #define glDrawRangeElements glad_glDrawRangeElements GLAD_API_CALL PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex; @@ -5682,12 +5708,6 @@ GLAD_API_CALL int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr); GLAD_API_CALL int gladLoadGL( GLADloadfunc load); -#ifdef GLAD_GL - -GLAD_API_CALL int gladLoaderLoadGL(void); -GLAD_API_CALL void gladLoaderUnloadGL(void); - -#endif #ifdef __cplusplus } @@ -5813,7 +5833,6 @@ int GLAD_GL_ARB_tessellation_shader = 0; int GLAD_GL_ARB_texture_border_clamp = 0; int GLAD_GL_ARB_texture_buffer_object_rgb32 = 0; int GLAD_GL_ARB_texture_compression = 0; -int GLAD_GL_EXT_texture_compression_s3tc = 0; int GLAD_GL_ARB_texture_cube_map = 0; int GLAD_GL_ARB_texture_cube_map_array = 0; int GLAD_GL_ARB_texture_env_add = 0; @@ -5837,11 +5856,17 @@ int GLAD_GL_ARB_vertex_attrib_binding = 0; int GLAD_GL_ARB_vertex_buffer_object = 0; int GLAD_GL_ARB_vertex_program = 0; int GLAD_GL_ARB_vertex_shader = 0; +int GLAD_GL_EXT_draw_instanced = 0; int GLAD_GL_EXT_fog_coord = 0; int GLAD_GL_EXT_framebuffer_blit = 0; int GLAD_GL_EXT_framebuffer_multisample = 0; int GLAD_GL_EXT_framebuffer_object = 0; int GLAD_GL_EXT_framebuffer_sRGB = 0; +int GLAD_GL_EXT_texture_compression_s3tc = 0; +int GLAD_GL_EXT_texture_filter_anisotropic = 0; +int GLAD_GL_EXT_texture_mirror_clamp = 0; +int GLAD_GL_KHR_texture_compression_astc_hdr = 0; +int GLAD_GL_KHR_texture_compression_astc_ldr = 0; int GLAD_GL_OES_compressed_paletted_texture = 0; int GLAD_GL_OES_fixed_point = 0; @@ -6038,6 +6063,7 @@ PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect = NULL; PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; PFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB = NULL; PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance = NULL; +PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT = NULL; PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; PFNGLDRAWBUFFERSARBPROC glad_glDrawBuffersARB = NULL; @@ -6049,6 +6075,7 @@ PFNGLDRAWELEMENTSINSTANCEDARBPROC glad_glDrawElementsInstancedARB = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance = NULL; +PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT = NULL; PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL; PFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback = NULL; @@ -8122,6 +8149,11 @@ static void glad_gl_load_GL_ARB_vertex_shader( GLADuserptrloadfunc load, void* u glad_glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) load(userptr, "glVertexAttrib4usvARB"); glad_glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) load(userptr, "glVertexAttribPointerARB"); } +static void glad_gl_load_GL_EXT_draw_instanced( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_instanced) return; + glad_glDrawArraysInstancedEXT = (PFNGLDRAWARRAYSINSTANCEDEXTPROC) load(userptr, "glDrawArraysInstancedEXT"); + glad_glDrawElementsInstancedEXT = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC) load(userptr, "glDrawElementsInstancedEXT"); +} static void glad_gl_load_GL_EXT_fog_coord( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_EXT_fog_coord) return; glad_glFogCoordPointerEXT = (PFNGLFOGCOORDPOINTEREXTPROC) load(userptr, "glFogCoordPointerEXT"); @@ -8451,8 +8483,7 @@ static int glad_gl_find_extensions_gl( int version) { GLAD_GL_ARB_texture_border_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_border_clamp"); GLAD_GL_ARB_texture_buffer_object_rgb32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_buffer_object_rgb32"); GLAD_GL_ARB_texture_compression = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_compression"); - GLAD_GL_EXT_texture_compression_s3tc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_s3tc"); - GLAD_GL_ARB_texture_cube_map = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map"); + GLAD_GL_ARB_texture_cube_map = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map"); GLAD_GL_ARB_texture_cube_map_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map_array"); GLAD_GL_ARB_texture_env_add = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_env_add"); GLAD_GL_ARB_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_filter_anisotropic"); @@ -8475,11 +8506,17 @@ static int glad_gl_find_extensions_gl( int version) { GLAD_GL_ARB_vertex_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_buffer_object"); GLAD_GL_ARB_vertex_program = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_program"); GLAD_GL_ARB_vertex_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_shader"); + GLAD_GL_EXT_draw_instanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_instanced"); GLAD_GL_EXT_fog_coord = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_fog_coord"); GLAD_GL_EXT_framebuffer_blit = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_blit"); GLAD_GL_EXT_framebuffer_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_multisample"); GLAD_GL_EXT_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_object"); GLAD_GL_EXT_framebuffer_sRGB = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_sRGB"); + GLAD_GL_EXT_texture_compression_s3tc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_s3tc"); + GLAD_GL_EXT_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_filter_anisotropic"); + GLAD_GL_EXT_texture_mirror_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_mirror_clamp"); + GLAD_GL_KHR_texture_compression_astc_hdr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_hdr"); + GLAD_GL_KHR_texture_compression_astc_ldr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_ldr"); GLAD_GL_OES_compressed_paletted_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_compressed_paletted_texture"); GLAD_GL_OES_fixed_point = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_fixed_point"); @@ -8615,6 +8652,7 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) { glad_gl_load_GL_ARB_vertex_buffer_object(load, userptr); glad_gl_load_GL_ARB_vertex_program(load, userptr); glad_gl_load_GL_ARB_vertex_shader(load, userptr); + glad_gl_load_GL_EXT_draw_instanced(load, userptr); glad_gl_load_GL_EXT_fog_coord(load, userptr); glad_gl_load_GL_EXT_framebuffer_blit(load, userptr); glad_gl_load_GL_EXT_framebuffer_multisample(load, userptr); @@ -8632,6 +8670,10 @@ int gladLoadGL( GLADloadfunc load) { } + + + + #ifdef __cplusplus } #endif diff --git a/src/rcore.c b/src/rcore.c index e46751d27..5605a75f5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3986,9 +3986,9 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 } - // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version - // with forward compatibility to older OpenGL versions. - // For example, if using OpenGL 1.1, driver can provide a 4.3 context forward compatible. + // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version + // with backward compatibility to older OpenGL versions. + // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context. // Check selection OpenGL version if (rlGetVersion() == RL_OPENGL_21) diff --git a/src/rlgl.h b/src/rlgl.h index 6a0d715eb..1efd1106a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -444,13 +444,13 @@ typedef enum { RL_SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance RL_SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter RL_SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf -} rlShaderLocationIndex; - +} rlShaderLocationIndex; + #define RL_SHADER_LOC_MAP_DIFFUSE RL_SHADER_LOC_MAP_ALBEDO #define RL_SHADER_LOC_MAP_SPECULAR RL_SHADER_LOC_MAP_METALNESS - + // Shader uniform data type -typedef enum { +typedef enum { RL_SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float RL_SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) RL_SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) @@ -741,16 +741,11 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #endif #if defined(GRAPHICS_API_OPENGL_33) - #if defined(__APPLE__) - #include // OpenGL 3 library for OSX - #include // OpenGL 3 extensions library for OSX - #else - #define GLAD_MALLOC RL_MALLOC - #define GLAD_FREE RL_FREE + #define GLAD_MALLOC RL_MALLOC + #define GLAD_FREE RL_FREE - #define GLAD_GL_IMPLEMENTATION - #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers - #endif + #define GLAD_GL_IMPLEMENTATION + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers #endif #if defined(GRAPHICS_API_OPENGL_ES2) @@ -2001,10 +1996,8 @@ void rlLoadExtensions(void *loader) { #if defined(GRAPHICS_API_OPENGL_33) // Also defined for GRAPHICS_API_OPENGL_21 // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions (and lower versions) - #if !defined(__APPLE__) - if (gladLoadGL((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); - else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); - #endif + if (gladLoadGL((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); + else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); // Get number of supported extensions GLint numExt = 0; @@ -2018,6 +2011,18 @@ void rlLoadExtensions(void *loader) for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", glGetStringi(GL_EXTENSIONS, i)); #endif +#if defined(GRAPHICS_API_OPENGL_21) + // Register supported extensions flags + // Optional OpenGL 2.1 extensions + RLGL.ExtSupported.vao = GLAD_GL_ARB_vertex_array_object; + RLGL.ExtSupported.instancing = (GLAD_GL_EXT_draw_instanced && GLAD_GL_ARB_instanced_arrays); + RLGL.ExtSupported.texNPOT = GLAD_GL_ARB_texture_non_power_of_two; + RLGL.ExtSupported.texFloat32 = GLAD_GL_ARB_texture_float; + RLGL.ExtSupported.texDepth = GLAD_GL_ARB_depth_texture; + RLGL.ExtSupported.maxDepthBits = 32; + RLGL.ExtSupported.texAnisoFilter = GLAD_GL_EXT_texture_filter_anisotropic; + RLGL.ExtSupported.texMirrorClamp = GLAD_GL_EXT_texture_mirror_clamp; +#else // Register supported extensions flags // OpenGL 3.3 extensions supported by default (core) RLGL.ExtSupported.vao = true; @@ -2028,18 +2033,17 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.maxDepthBits = 32; RLGL.ExtSupported.texAnisoFilter = true; RLGL.ExtSupported.texMirrorClamp = true; +#endif + + // Optional OpenGL 3.3 extensions + RLGL.ExtSupported.texCompASTC = GLAD_GL_KHR_texture_compression_astc_hdr && GLAD_GL_KHR_texture_compression_astc_ldr; + RLGL.ExtSupported.texCompDXT = GLAD_GL_EXT_texture_compression_s3tc; // Texture compression: DXT + RLGL.ExtSupported.texCompETC2 = GLAD_GL_ARB_ES3_compatibility; // Texture compression: ETC2/EAC #if defined(GRAPHICS_API_OPENGL_43) RLGL.ExtSupported.computeShader = GLAD_GL_ARB_compute_shader; RLGL.ExtSupported.ssbo = GLAD_GL_ARB_shader_storage_buffer_object; #endif - #if defined(__APPLE__) - // Apple provides its own extension macros - RLGL.ExtSupported.texCompDXT = GL_EXT_texture_compression_s3tc; - #else - // NOTE: With GLAD, we can check if an extension is supported using the GLAD_GL_xxx booleans - RLGL.ExtSupported.texCompDXT = GLAD_GL_EXT_texture_compression_s3tc; // Texture compression: DXT - RLGL.ExtSupported.texCompETC2 = GLAD_GL_ARB_ES3_compatibility; // Texture compression: ETC2/EAC - #endif + #endif // GRAPHICS_API_OPENGL_33 #if defined(GRAPHICS_API_OPENGL_ES2) @@ -2205,12 +2209,10 @@ void rlLoadExtensions(void *loader) #else // RLGL_SHOW_GL_DETAILS_INFO // Show some basic info about GL supported features - #if defined(GRAPHICS_API_OPENGL_ES2) if (RLGL.ExtSupported.vao) TRACELOG(RL_LOG_INFO, "GL: VAO extension detected, VAO functions loaded successfully"); else TRACELOG(RL_LOG_WARNING, "GL: VAO extension not found, VAO not supported"); if (RLGL.ExtSupported.texNPOT) TRACELOG(RL_LOG_INFO, "GL: NPOT textures extension detected, full NPOT textures supported"); else TRACELOG(RL_LOG_WARNING, "GL: NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); - #endif if (RLGL.ExtSupported.texCompDXT) TRACELOG(RL_LOG_INFO, "GL: DXT compressed textures supported"); if (RLGL.ExtSupported.texCompETC1) TRACELOG(RL_LOG_INFO, "GL: ETC1 compressed textures supported"); if (RLGL.ExtSupported.texCompETC2) TRACELOG(RL_LOG_INFO, "GL: ETC2/EAC compressed textures supported"); @@ -2231,11 +2233,7 @@ int rlGetVersion(void) glVersion = RL_OPENGL_11; #endif #if defined(GRAPHICS_API_OPENGL_21) - #if defined(__APPLE__) - glVersion = RL_OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX - #else - glVersion = RL_OPENGL_21; - #endif + glVersion = RL_OPENGL_21; #elif defined(GRAPHICS_API_OPENGL_33) glVersion = RL_OPENGL_33; #endif From 7e3a50b4acea0bbedbf121aad4f36431c2e09907 Mon Sep 17 00:00:00 2001 From: Gunko Vadim Date: Mon, 19 Sep 2022 01:36:59 +0500 Subject: [PATCH 10/20] Update BINDINGS.md (#2710) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index fe15740fa..ec8e9d885 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -39,7 +39,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | | raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib-ocaml | **4.0** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | -| Ray4Laz | **4.2** | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language))| Zlib | https://github.com/GuvaCode/Ray4Laz | +| Ray4Laz | **4.2** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | | raylib-python-cffi | **4.2** | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | From 27781a4767afdb60a9bea452a910fa4d53d85461 Mon Sep 17 00:00:00 2001 From: Denis Pobedrya Date: Mon, 19 Sep 2022 00:01:10 +0300 Subject: [PATCH 11/20] Remove touch points on touch up events on Android (#2711) Fixes #2683 Remove elements from touch point related arrays when touch up and similar events are processed. This makes GetTouchPointCount() always report the actual count of touch points, and all positions returned by GetTouchPosition() correspond to positions of currently happening touches. --- src/rcore.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 5605a75f5..1b00b14f1 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5755,9 +5755,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) int32_t action = AMotionEvent_getAction(event); unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; - if (flags == AMOTION_EVENT_ACTION_DOWN || flags == AMOTION_EVENT_ACTION_MOVE) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; - else if (flags == AMOTION_EVENT_ACTION_UP) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; - #if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID GestureEvent gestureEvent = { 0 }; @@ -5779,6 +5776,25 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) ProcessGestureEvent(gestureEvent); #endif + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + + if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP) + { + // One of the touchpoints is released, remove it from touch point arrays + for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount-1) && (i < MAX_TOUCH_POINTS); i++) + { + CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; + CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; + } + CORE.Input.Touch.pointCount--; + } + + // When all touchpoints are tapped and released really quickly, this event is generated + if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; + + if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; + else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; + return 0; } #endif From 12e8cef9cfe7ba99696b1eed5454e58767d0a3d3 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 10:31:55 +0200 Subject: [PATCH 12/20] REMOVED: Mipmaps software generation for OpenGL 1.1 As generation is done in software, it's up to the user to do it. `ImageMipmaps()` is already provided for reference. --- src/rlgl.h | 175 ++--------------------------------------------------- 1 file changed, 4 insertions(+), 171 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 1efd1106a..da4882540 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -990,14 +990,12 @@ static void rlUnloadShaderDefault(void); // Unload default shader static char *rlGetCompressedFormatName(int format); // Get compressed format official GL identifier name #endif // RLGL_SHOW_GL_DETAILS_INFO #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -#if defined(GRAPHICS_API_OPENGL_11) -static int rlGenTextureMipmapsData(unsigned char *data, int baseWidth, int baseHeight); // Generate mipmaps data on CPU side -static unsigned char *rlGenNextMipmapData(unsigned char *srcData, int srcWidth, int srcHeight); // Generate 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) + // Auxiliar matrix math functions -static Matrix rlMatrixIdentity(void); // Get identity matrix -static Matrix rlMatrixMultiply(Matrix left, Matrix right); // Multiply two matrices +static Matrix rlMatrixIdentity(void); // Get identity matrix +static Matrix rlMatrixMultiply(Matrix left, Matrix right); // Multiply two matrices //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix operations @@ -3094,45 +3092,6 @@ void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; -#if defined(GRAPHICS_API_OPENGL_11) - if (texIsPOT) - { - // WARNING: Manual mipmap generation only works for RGBA 32bit textures! - if (format == RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - // Retrieve texture data from VRAM - void *texData = rlReadTexturePixels(id, width, height, format); - - // NOTE: Texture data size is reallocated to fit mipmaps data - // NOTE: CPU mipmap generation only supports RGBA 32bit data - int mipmapCount = rlGenTextureMipmapsData(texData, width, height); - - int size = width*height*4; - int offset = size; - - int mipWidth = width/2; - int mipHeight = 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; - } - - *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(RL_LOG_WARNING, "TEXTURE: [ID %i] Mipmaps generated manually on CPU side, total: %i", id, *mipmaps); - } - else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps for provided texture format", id); - } -#endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if ((texIsPOT) || (RLGL.ExtSupported.texNPOT)) { @@ -4522,132 +4481,6 @@ static char *rlGetCompressedFormatName(int format) #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -#if defined(GRAPHICS_API_OPENGL_11) -// Mipmaps data is generated after image data -// NOTE: Only works with RGBA (4 bytes) data! -static int rlGenTextureMipmapsData(unsigned char *data, int baseWidth, int baseHeight) -{ - int mipmapCount = 1; // Required mipmap levels count (including base level) - int width = baseWidth; - int height = baseHeight; - int size = baseWidth*baseHeight*4; // Size in bytes (will include mipmaps...), RGBA only - - // Count mipmap levels required - while ((width != 1) && (height != 1)) - { - width /= 2; - height /= 2; - - TRACELOGD("TEXTURE: Next mipmap size: %i x %i", width, height); - - mipmapCount++; - - size += (width*height*4); // Add mipmap size (in bytes) - } - - TRACELOGD("TEXTURE: Total mipmaps required: %i", mipmapCount); - TRACELOGD("TEXTURE: Total size of data required: %i", size); - - unsigned char *temp = RL_REALLOC(data, size); - - if (temp != NULL) data = temp; - else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to re-allocate required mipmaps memory"); - - width = baseWidth; - height = baseHeight; - size = (width*height*4); // RGBA: 4 bytes - - // Generate mipmaps - // NOTE: Every mipmap data is stored after data (RGBA - 4 bytes) - unsigned char *image = (unsigned char *)RL_MALLOC(width*height*4); - unsigned char *mipmap = NULL; - int offset = 0; - - for (int i = 0; i < size; i += 4) - { - image[i] = data[i]; - image[i + 1] = data[i + 1]; - image[i + 2] = data[i + 2]; - image[i + 3] = data[i + 3]; - } - - TRACELOGD("TEXTURE: Mipmap base size (%ix%i)", width, height); - - for (int mip = 1; mip < mipmapCount; mip++) - { - mipmap = rlGenNextMipmapData(image, width, height); - - offset += (width*height*4); // Size of last mipmap - - width /= 2; - height /= 2; - size = (width*height*4); // Mipmap size to store after offset - - // Add mipmap to data - for (int i = 0; i < size; i += 4) - { - data[offset + i] = mipmap[i]; - data[offset + i + 1] = mipmap[i + 1]; - data[offset + i + 2] = mipmap[i + 2]; - data[offset + i + 3] = mipmap[i + 3]; - } - - RL_FREE(image); - - image = mipmap; - mipmap = NULL; - } - - RL_FREE(mipmap); // free mipmap data - - return mipmapCount; -} - -// Manual mipmap generation (basic scaling algorithm) -static unsigned char *rlGenNextMipmapData(unsigned char *srcData, int srcWidth, int srcHeight) -{ - int x2 = 0; - int y2 = 0; - unsigned char prow[4] = { 0 }; - unsigned char pcol[4] = { 0 }; - - int width = srcWidth/2; - int height = srcHeight/2; - - unsigned char *mipmap = (unsigned char *)RL_MALLOC(width*height*4); - - // Scaling algorithm works perfectly (box-filter) - for (int y = 0; y < height; y++) - { - y2 = 2*y; - - for (int x = 0; x < width; x++) - { - x2 = 2*x; - - prow[0] = (srcData[(y2*srcWidth + x2)*4 + 0] + srcData[(y2*srcWidth + x2 + 1)*4 + 0])/2; - prow[1] = (srcData[(y2*srcWidth + x2)*4 + 1] + srcData[(y2*srcWidth + x2 + 1)*4 + 1])/2; - prow[2] = (srcData[(y2*srcWidth + x2)*4 + 2] + srcData[(y2*srcWidth + x2 + 1)*4 + 2])/2; - prow[3] = (srcData[(y2*srcWidth + x2)*4 + 3] + srcData[(y2*srcWidth + x2 + 1)*4 + 3])/2; - - pcol[0] = (srcData[((y2 + 1)*srcWidth + x2)*4 + 0] + srcData[((y2 + 1)*srcWidth + x2 + 1)*4 + 0])/2; - pcol[1] = (srcData[((y2 + 1)*srcWidth + x2)*4 + 1] + srcData[((y2 + 1)*srcWidth + x2 + 1)*4 + 1])/2; - pcol[2] = (srcData[((y2 + 1)*srcWidth + x2)*4 + 2] + srcData[((y2 + 1)*srcWidth + x2 + 1)*4 + 2])/2; - pcol[3] = (srcData[((y2 + 1)*srcWidth + x2)*4 + 3] + srcData[((y2 + 1)*srcWidth + x2 + 1)*4 + 3])/2; - - mipmap[(y*width + x)*4 + 0] = (prow[0] + pcol[0])/2; - mipmap[(y*width + x)*4 + 1] = (prow[1] + pcol[1])/2; - mipmap[(y*width + x)*4 + 2] = (prow[2] + pcol[2])/2; - mipmap[(y*width + x)*4 + 3] = (prow[3] + pcol[3])/2; - } - } - - TRACELOGD("TEXTURE: Mipmap generated successfully (%ix%i)", width, height); - - return mipmap; -} -#endif // GRAPHICS_API_OPENGL_11 - // Get pixel data size in bytes (image or texture) // NOTE: Size depends on pixel format static int rlGetPixelDataSize(int width, int height, int format) From 6e8f3b0f45c73005634e8c4cfe8fa0d61560fbb5 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 10:35:34 +0200 Subject: [PATCH 13/20] Update rlgl.h --- src/rlgl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rlgl.h b/src/rlgl.h index da4882540..ee3c3d91a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3082,6 +3082,7 @@ void rlUnloadTexture(unsigned int id) } // Generate mipmap data for selected texture +// NOTE: Only supports GPU mipmap generation void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps) { glBindTexture(GL_TEXTURE_2D, id); From 5530a3ceb88962066affa5db8e13b00b64444b37 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 18:20:36 +0200 Subject: [PATCH 14/20] REVIEWED: `ScanDirectoryFilesRecursively()`, fix #2704 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 1b00b14f1..8d5b4589d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5146,7 +5146,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const // Scan all files and directories recursively from a base path static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter) { - static char path[MAX_FILEPATH_LENGTH] = { 0 }; + char path[MAX_FILEPATH_LENGTH] = { 0 }; memset(path, 0, MAX_FILEPATH_LENGTH); struct dirent *dp = NULL; From 0e5cd442be3b90c041a5874fbec52659f214827e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 18:29:08 +0200 Subject: [PATCH 15/20] REVIEWED: Renamed some shaders, fix #2707 --- .../resources/shaders/glsl100/{base_lighting.vs => lighting.vs} | 0 .../{base_lighting_instanced.vs => lighting_instanced.vs} | 0 .../resources/shaders/glsl120/{base_lighting.vs => lighting.vs} | 0 .../resources/shaders/glsl330/{base_lighting.vs => lighting.vs} | 0 examples/shaders/shaders_basic_lighting.c | 2 +- examples/shaders/shaders_fog.c | 2 +- 6 files changed, 2 insertions(+), 2 deletions(-) rename examples/shaders/resources/shaders/glsl100/{base_lighting.vs => lighting.vs} (100%) rename examples/shaders/resources/shaders/glsl100/{base_lighting_instanced.vs => lighting_instanced.vs} (100%) rename examples/shaders/resources/shaders/glsl120/{base_lighting.vs => lighting.vs} (100%) rename examples/shaders/resources/shaders/glsl330/{base_lighting.vs => lighting.vs} (100%) diff --git a/examples/shaders/resources/shaders/glsl100/base_lighting.vs b/examples/shaders/resources/shaders/glsl100/lighting.vs similarity index 100% rename from examples/shaders/resources/shaders/glsl100/base_lighting.vs rename to examples/shaders/resources/shaders/glsl100/lighting.vs diff --git a/examples/shaders/resources/shaders/glsl100/base_lighting_instanced.vs b/examples/shaders/resources/shaders/glsl100/lighting_instanced.vs similarity index 100% rename from examples/shaders/resources/shaders/glsl100/base_lighting_instanced.vs rename to examples/shaders/resources/shaders/glsl100/lighting_instanced.vs diff --git a/examples/shaders/resources/shaders/glsl120/base_lighting.vs b/examples/shaders/resources/shaders/glsl120/lighting.vs similarity index 100% rename from examples/shaders/resources/shaders/glsl120/base_lighting.vs rename to examples/shaders/resources/shaders/glsl120/lighting.vs diff --git a/examples/shaders/resources/shaders/glsl330/base_lighting.vs b/examples/shaders/resources/shaders/glsl330/lighting.vs similarity index 100% rename from examples/shaders/resources/shaders/glsl330/base_lighting.vs rename to examples/shaders/resources/shaders/glsl330/lighting.vs diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index 42910a516..c557ecf73 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -57,7 +57,7 @@ int main(void) Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 4.0f, 2.0f)); // Load basic lighting shader - Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/base_lighting.vs", GLSL_VERSION), + Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/lighting.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/lighting.fs", GLSL_VERSION)); // Get some required shader locations shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index 5cd68d2c5..de5c23df5 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -63,7 +63,7 @@ int main(void) modelC.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Load shader and set up some uniforms - Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/base_lighting.vs", GLSL_VERSION), + Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/lighting.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/fog.fs", GLSL_VERSION)); shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel"); shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); From 2093fdcc536e68a131c15e196460e338609e1a76 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Mon, 19 Sep 2022 12:41:17 -0400 Subject: [PATCH 16/20] Added: `ImageDrawCircleLines`, `ImageDrawCircleLinesV` (#2713) This adds `ImageDrawCircleLines()` and `ImageDrawCircleLinesV()` to draw outlines of circles, and updates `ImageDrawCircle()` draw a filled circle to match the effect of `DrawCircle()` and `DrawCircleLines()`. --- examples/textures/textures_image_drawing.c | 2 +- src/raylib.h | 2 ++ src/rtextures.c | 37 +++++++++++++++++++--- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/examples/textures/textures_image_drawing.c b/examples/textures/textures_image_drawing.c index 203b3cd38..190fb8594 100644 --- a/examples/textures/textures_image_drawing.c +++ b/examples/textures/textures_image_drawing.c @@ -42,7 +42,7 @@ int main(void) // Draw on the image with a few image draw methods ImageDrawPixel(&parrots, 10, 10, RAYWHITE); - ImageDrawCircle(&parrots, 10, 10, 5, RAYWHITE); + ImageDrawCircleLines(&parrots, 10, 10, 5, RAYWHITE); ImageDrawRectangle(&parrots, 5, 20, 10, 10, RAYWHITE); UnloadImage(cat); // Unload image from RAM diff --git a/src/raylib.h b/src/raylib.h index ac5d6e6af..7e4fdf3fb 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1287,6 +1287,8 @@ RLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, RLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color); // Draw line within an image (Vector version) RLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle within an image RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw circle within an image (Vector version) +RLAPI void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle outline within an image +RLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color); // Draw circle outline within an image (Vector version) RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color); // Draw rectangle within an image RLAPI void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color); // Draw rectangle within an image (Vector version) RLAPI void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color); // Draw rectangle within an image diff --git a/src/rtextures.c b/src/rtextures.c index 12ba4923b..5a77ac7b2 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2716,7 +2716,36 @@ void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color) } // Draw circle within an image -void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color) +void ImageDrawCircle(Image* dst, int centerX, int centerY, int radius, Color color) +{ + int x = 0, y = radius; + int decesionParameter = 3 - 2 * radius; + + while (y >= x) + { + ImageDrawRectangle(dst, centerX - x, centerY + y, x * 2, 1, color); + ImageDrawRectangle(dst, centerX - x, centerY - y, x * 2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY + x, y * 2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY - x, y * 2, 1, color); + x++; + + if (decesionParameter > 0) { + y--; + decesionParameter = decesionParameter + 4 * (x - y) + 10; + } else { + decesionParameter = decesionParameter + 4 * x + 6; + } + } +} + +// Draw circle within an image (Vector version) +void ImageDrawCircleV(Image* dst, Vector2 center, int radius, Color color) +{ + ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color); +} + +// Draw circle outline within an image +void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color) { int x = 0, y = radius; int decesionParameter = 3 - 2*radius; @@ -2742,10 +2771,10 @@ void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color col } } -// Draw circle within an image (Vector version) -void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color) +// Draw circle outline within an image (Vector version) +void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color) { - ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color); + ImageDrawCircleLines(dst, (int)center.x, (int)center.y, radius, color); } // Draw rectangle within an image From 4311ffc9e13c4de3c519ce9b1afd2f52fc618914 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 18:47:16 +0200 Subject: [PATCH 17/20] REVIEWED: New functions coding conventions --- src/rtextures.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 5a77ac7b2..3badc349d 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2718,23 +2718,24 @@ void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color) // Draw circle within an image void ImageDrawCircle(Image* dst, int centerX, int centerY, int radius, Color color) { - int x = 0, y = radius; - int decesionParameter = 3 - 2 * radius; + int x = 0; + int y = radius; + int decesionParameter = 3 - 2*radius; while (y >= x) { - ImageDrawRectangle(dst, centerX - x, centerY + y, x * 2, 1, color); - ImageDrawRectangle(dst, centerX - x, centerY - y, x * 2, 1, color); - ImageDrawRectangle(dst, centerX - y, centerY + x, y * 2, 1, color); - ImageDrawRectangle(dst, centerX - y, centerY - x, y * 2, 1, color); + ImageDrawRectangle(dst, centerX - x, centerY + y, x*2, 1, color); + ImageDrawRectangle(dst, centerX - x, centerY - y, x*2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY + x, y*2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY - x, y*2, 1, color); x++; - if (decesionParameter > 0) { + if (decesionParameter > 0) + { y--; - decesionParameter = decesionParameter + 4 * (x - y) + 10; - } else { - decesionParameter = decesionParameter + 4 * x + 6; - } + decesionParameter = decesionParameter + 4*(x - y) + 10; + } + else decesionParameter = decesionParameter + 4*x + 6; } } @@ -2747,7 +2748,8 @@ void ImageDrawCircleV(Image* dst, Vector2 center, int radius, Color color) // Draw circle outline within an image void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color) { - int x = 0, y = radius; + int x = 0; + int y = radius; int decesionParameter = 3 - 2*radius; while (y >= x) From 907e9e1fe2943efdb253103995d1c10a4adc5c9c Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 22 Sep 2022 20:29:54 +0200 Subject: [PATCH 18/20] Update text_codepoints_loading.c --- examples/text/text_codepoints_loading.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index 3b6e11609..c68544030 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -57,6 +57,10 @@ int main(void) bool showFontAtlas = false; + int codepointSize = 0; + int codepoint = 0; + char *ptr = text; + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -66,6 +70,20 @@ int main(void) // Update //---------------------------------------------------------------------------------- if (IsKeyPressed(KEY_SPACE)) showFontAtlas = !showFontAtlas; + + // Testing code: getting next and previous codepoints on provided text + if (IsKeyPressed(KEY_RIGHT)) + { + // Get next codepoint in string and move pointer + codepoint = GetCodepointNext(ptr, &codepointSize); + ptr += codepointSize; + } + else if (IsKeyPressed(KEY_LEFT)) + { + // Get previous codepoint in string and move pointer + codepoint = GetCodepointPrevious(ptr, &codepointSize); + ptr -= codepointSize; + } //---------------------------------------------------------------------------------- // Draw From 4b1d4b4f6b669b133fab34d9f91ad2f5b898e97a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 22 Sep 2022 20:30:02 +0200 Subject: [PATCH 19/20] Update rcore.c --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 8d5b4589d..490ac4a10 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1934,7 +1934,7 @@ void EnableEventWaiting(void) } // Disable waiting for events on EndDrawing(), automatic events polling -RLAPI void DisableEventWaiting(void) +void DisableEventWaiting(void) { CORE.Window.eventWaiting = false; } @@ -2435,7 +2435,7 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName) } // Load shader from code strings and bind default locations -RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) +Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) { Shader shader = { 0 }; shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); From 810a0330abdf13c49843692407e2f77da08f8bdc Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 22 Sep 2022 20:35:55 +0200 Subject: [PATCH 20/20] WARNING: Several changes on UTF-8/Codepoints API - ADDED: `GetCodepointPrevious()` - RENAMED: `GetCodepoint()` -> `GetCodepointNext()`, actually, reimplemented - `GetCodepoint()` has been kept for the moment, for compatibility and also because implementation is different - RENAMED: `TextCodepointsToUTF8()` to `LoadUTF8()`, simpler name and more aligned with raylib conventions (functions loading memory start with Load*()), parameters should be descriptive of functionailty. - ADDED: `UnloadUTF8()`, aligned with `LoadUTF8()` to avoid allocators issues. --- src/raylib.h | 15 ++-- src/rtext.c | 219 +++++++++++++++++++++++++++++++----------------- src/rtextures.c | 4 +- 3 files changed, 151 insertions(+), 87 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 7e4fdf3fb..84a18ebe2 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1370,12 +1370,15 @@ RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found // Text codepoints management functions (unicode characters) -RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter -RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory -RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string -RLAPI int GetCodepoint(const char *text, int *bytesProcessed); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) -RLAPI char *TextCodepointsToUTF8(const int *codepoints, int length); // Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!) +RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array +RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array +RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter +RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory +RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string +RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) // Text strings management functions (no UTF-8 strings, only byte chars) // NOTE: Some strings allocate memory internally for returned strings, just be careful! diff --git a/src/rtext.c b/src/rtext.c index ef5debd5b..324ebe95a 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1043,7 +1043,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f { // Get next codepoint from byte string and glyph index in font int codepointByteCount = 0; - int codepoint = GetCodepoint(&text[i], &codepointByteCount); + int codepoint = GetCodepointNext(&text[i], &codepointByteCount); int index = GetGlyphIndex(font, codepoint); // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) @@ -1185,7 +1185,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing byteCounter++; int next = 0; - letter = GetCodepoint(&text[i], &next); + letter = GetCodepointNext(&text[i], &next); index = GetGlyphIndex(font, letter); // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) @@ -1627,7 +1627,7 @@ const char *TextToPascal(const char *text) // Encode text codepoint into UTF-8 text // REQUIRES: memcpy() // WARNING: Allocated memory must be manually freed -char *TextCodepointsToUTF8(const int *codepoints, int length) +char *LoadUTF8(const int *codepoints, int length) { // We allocate enough memory fo fit all possible codepoints // NOTE: 5 bytes for every codepoint should be enough @@ -1650,9 +1650,68 @@ char *TextCodepointsToUTF8(const int *codepoints, int length) return text; } +// Unload UTF-8 text encoded from codepoints array +void UnloadUTF8(char *text) +{ + RL_FREE(text); +} + +// Load all codepoints from a UTF-8 text string, codepoints count returned by parameter +int *LoadCodepoints(const char *text, int *count) +{ + int textLength = TextLength(text); + + int codepointSize = 0; + int codepointCount = 0; + + // Allocate a big enough buffer to store as many codepoints as text bytes + int *codepoints = RL_CALLOC(textLength, sizeof(int)); + + for (int i = 0; i < textLength; codepointCount++) + { + codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize); + i += codepointSize; + } + + // Re-allocate buffer to the actual number of codepoints loaded + void *temp = RL_REALLOC(codepoints, codepointCount*sizeof(int)); + if (temp != NULL) codepoints = temp; + + *count = codepointCount; + + return codepoints; +} + +// Unload codepoints data from memory +void UnloadCodepoints(int *codepoints) +{ + RL_FREE(codepoints); +} + +// Get total number of characters(codepoints) in a UTF-8 encoded text, until '\0' is found +// NOTE: If an invalid UTF-8 sequence is encountered a '?'(0x3f) codepoint is counted instead +int GetCodepointCount(const char *text) +{ + unsigned int length = 0; + char *ptr = (char *)&text[0]; + + while (*ptr != '\0') + { + int next = 0; + int letter = GetCodepointNext(ptr, &next); + + if (letter == 0x3f) ptr += 1; + else ptr += next; + + length++; + } + + return length; +} + // Encode codepoint into utf8 text (char array length returned as parameter) // NOTE: It uses a static array to store UTF-8 bytes -RLAPI const char *CodepointToUTF8(int codepoint, int *byteSize) +const char *CodepointToUTF8(int codepoint, int *utf8Size) { static char utf8[6] = { 0 }; int size = 0; // Byte size of codepoint @@ -1684,63 +1743,10 @@ RLAPI const char *CodepointToUTF8(int codepoint, int *byteSize) size = 4; } - *byteSize = size; + *utf8Size = size; return utf8; } - -// Load all codepoints from a UTF-8 text string, codepoints count returned by parameter -int *LoadCodepoints(const char *text, int *count) -{ - int textLength = TextLength(text); - - int bytesProcessed = 0; - int codepointCount = 0; - - // Allocate a big enough buffer to store as many codepoints as text bytes - int *codepoints = RL_CALLOC(textLength, sizeof(int)); - - for (int i = 0; i < textLength; codepointCount++) - { - codepoints[codepointCount] = GetCodepoint(text + i, &bytesProcessed); - i += bytesProcessed; - } - - // Re-allocate buffer to the actual number of codepoints loaded - void *temp = RL_REALLOC(codepoints, codepointCount*sizeof(int)); - if (temp != NULL) codepoints = temp; - - *count = codepointCount; - - return codepoints; -} - -// Unload codepoints data from memory -void UnloadCodepoints(int *codepoints) -{ - RL_FREE(codepoints); -} - -// Get total number of characters(codepoints) in a UTF-8 encoded text, until '\0' is found -// NOTE: If an invalid UTF-8 sequence is encountered a '?'(0x3f) codepoint is counted instead -int GetCodepointCount(const char *text) -{ - unsigned int length = 0; - char *ptr = (char *)&text[0]; - - while (*ptr != '\0') - { - int next = 0; - int letter = GetCodepoint(ptr, &next); - - if (letter == 0x3f) ptr += 1; - else ptr += next; - - length++; - } - - return length; -} #endif // SUPPORT_TEXT_MANIPULATION // Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found @@ -1748,7 +1754,7 @@ int GetCodepointCount(const char *text) // Total number of bytes processed are returned as a parameter // NOTE: The standard says U+FFFD should be returned in case of errors // but that character is not supported by the default font in raylib -int GetCodepoint(const char *text, int *bytesProcessed) +int GetCodepoint(const char *text, int *codepointSize) { /* UTF-8 specs from https://www.ietf.org/rfc/rfc3629.txt @@ -1763,14 +1769,14 @@ int GetCodepoint(const char *text, int *bytesProcessed) */ // NOTE: on decode errors we return as soon as possible - int code = 0x3f; // Codepoint (defaults to '?') + int codepoint = 0x3f; // Codepoint (defaults to '?') int octet = (unsigned char)(text[0]); // The first UTF8 octet - *bytesProcessed = 1; + *codepointSize = 1; if (octet <= 0x7f) { // Only one octet (ASCII range x00-7F) - code = text[0]; + codepoint = text[0]; } else if ((octet & 0xe0) == 0xc0) { @@ -1779,12 +1785,12 @@ int GetCodepoint(const char *text, int *bytesProcessed) // [0]xC2-DF [1]UTF8-tail(x80-BF) unsigned char octet1 = text[1]; - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *codepointSize = 2; return codepoint; } // Unexpected sequence if ((octet >= 0xc2) && (octet <= 0xdf)) { - code = ((octet & 0x1f) << 6) | (octet1 & 0x3f); - *bytesProcessed = 2; + codepoint = ((octet & 0x1f) << 6) | (octet1 & 0x3f); + *codepointSize = 2; } } else if ((octet & 0xf0) == 0xe0) @@ -1793,11 +1799,11 @@ int GetCodepoint(const char *text, int *bytesProcessed) unsigned char octet1 = text[1]; unsigned char octet2 = '\0'; - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *codepointSize = 2; return codepoint; } // Unexpected sequence octet2 = text[2]; - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence + if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *codepointSize = 3; return codepoint; } // Unexpected sequence // [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) // [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) @@ -1805,50 +1811,105 @@ int GetCodepoint(const char *text, int *bytesProcessed) // [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF) if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) || - ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; } + ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *codepointSize = 2; return codepoint; } if ((octet >= 0xe0) && (octet <= 0xef)) { - code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); - *bytesProcessed = 3; + codepoint = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); + *codepointSize = 3; } } else if ((octet & 0xf8) == 0xf0) { // Four octets - if (octet > 0xf4) return code; + if (octet > 0xf4) return codepoint; unsigned char octet1 = text[1]; unsigned char octet2 = '\0'; unsigned char octet3 = '\0'; - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *codepointSize = 2; return codepoint; } // Unexpected sequence octet2 = text[2]; - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence + if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *codepointSize = 3; return codepoint; } // Unexpected sequence octet3 = text[3]; - if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence + if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *codepointSize = 4; return codepoint; } // Unexpected sequence // [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail // [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail // [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) || - ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence + ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *codepointSize = 2; return codepoint; } // Unexpected sequence if (octet >= 0xf0) { - code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); - *bytesProcessed = 4; + codepoint = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); + *codepointSize = 4; } } - if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid + if (codepoint > 0x10ffff) codepoint = 0x3f; // Codepoints after U+10ffff are invalid - return code; + return codepoint; +} + +// Get next codepoint in a byte sequence and bytes processed +int GetCodepointNext(const char *text, int *codepointSize) +{ + const char *ptr = text; + int codepoint = 0x3f; // Codepoint (defaults to '?') + *codepointSize = 0; + + // Get current codepoint and bytes processed + if (0xf0 == (0xf8 & ptr[0])) + { + // 4 byte UTF-8 codepoint + codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]); + *codepointSize = 4; + } + else if (0xe0 == (0xf0 & ptr[0])) + { + // 3 byte UTF-8 codepoint */ + codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]); + *codepointSize = 3; + } + else if (0xc0 == (0xe0 & ptr[0])) + { + // 2 byte UTF-8 codepoint + codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); + *codepointSize = 2; + } + else + { + // 1 byte UTF-8 codepoint + codepoint = ptr[0]; + *codepointSize = 1; + } + + return codepoint; +} + +// Get previous codepoint in a byte sequence and bytes processed +int GetCodepointPrevious(const char *text, int *codepointSize) +{ + const char *ptr = text; + int codepoint = 0x3f; // Codepoint (defaults to '?') + int cpSize = 0; + *codepointSize = 0; + + // Move to previous codepoint + do ptr--; + while (((0x80 & ptr[0]) != 0) && ((0xc0 & ptr[0]) == 0x80)); + + codepoint = GetCodepointNext(ptr, &cpSize); + + if (codepoint != 0) *codepointSize = cpSize; + + return codepoint; } //---------------------------------------------------------------------------------- diff --git a/src/rtextures.c b/src/rtextures.c index 3badc349d..41bd3e184 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1238,8 +1238,8 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co { // Get next codepoint from byte string and glyph index in font int codepointByteCount = 0; - int codepoint = GetCodepoint(&text[i], &codepointByteCount); // WARNING: Module required: rtext - int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext + int codepoint = GetCodepointNext(&text[i], &codepointByteCount); // WARNING: Module required: rtext + int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // but we need to draw all of the bad bytes using the '?' symbol moving one byte