diff --git a/BINDINGS.md b/BINDINGS.md index bdddf095f..f9c667748 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -12,6 +12,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [cray](https://github.com/tapgg/cray) : raylib **Crystal** binding - [Graphics::Raylib](https://metacpan.org/pod/Graphics::Raylib) : raylib **Perl** wrapper - [raylib-pascal](https://github.com/drezgames/raylib-pascal) - raylib **Pascal** binding + - [raylib-pas](https://github.com/tazdij/raylib-pas) - raylib **Pascal** binding (including rlgl & raymath) - [Raylib-cs](https://github.com/ChrisDill/Raylib-cs) : raylib **C#** binding - [RaylibSharp](https://github.com/TheLumaio/RaylibSharp) : raylib **C#** binding - [raylib-ruby-ffi](https://github.com/D3nX/raylib-ruby-ffi) : raylib **Ruby** binding diff --git a/examples/Makefile b/examples/Makefile index c4e7c57c6..c71758497 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -438,7 +438,7 @@ EXAMPLES = \ shaders/shaders_eratosthenes \ shaders/shaders_basic_lighting \ shaders/shaders_fog \ - shaders/shaders_simple \ + shaders/shaders_simple_mask \ audio/audio_module_playing \ audio/audio_music_stream \ audio/audio_raw_stream \ diff --git a/examples/shaders/resources/shaders/glsl100/raymarching.fs b/examples/shaders/resources/shaders/glsl100/raymarching.fs index 4ae71297d..c823e01eb 100644 --- a/examples/shaders/resources/shaders/glsl100/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl100/raymarching.fs @@ -8,8 +8,6 @@ varying vec4 fragColor; uniform vec3 viewEye; uniform vec3 viewCenter; -uniform vec3 viewUp; -uniform float deltaTime; uniform float runTime; uniform vec2 resolution; @@ -428,4 +426,4 @@ void main() #endif gl_FragColor = vec4( tot, 1.0 ); -} \ No newline at end of file +} diff --git a/examples/shaders/resources/shaders/glsl330/raymarching.fs b/examples/shaders/resources/shaders/glsl330/raymarching.fs index 7c9fbcb10..3cec58a2b 100644 --- a/examples/shaders/resources/shaders/glsl330/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl330/raymarching.fs @@ -9,8 +9,6 @@ out vec4 finalColor; uniform vec3 viewEye; uniform vec3 viewCenter; -uniform vec3 viewUp; -uniform float deltaTime; uniform float runTime; uniform vec2 resolution; @@ -429,4 +427,4 @@ void main() #endif finalColor = vec4( tot, 1.0 ); -} \ No newline at end of file +} diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index 34091792e..06ad0f92e 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -28,9 +28,10 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + int screenWidth = 800; + int screenHeight = 450; + SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(screenWidth, screenHeight, "raylib [shaders] example - raymarching shapes"); Camera camera = { 0 }; @@ -48,12 +49,10 @@ int main(void) // Get shader locations for required uniforms int viewEyeLoc = GetShaderLocation(shader, "viewEye"); int viewCenterLoc = GetShaderLocation(shader, "viewCenter"); - int viewUpLoc = GetShaderLocation(shader, "viewUp"); - int deltaTimeLoc = GetShaderLocation(shader, "deltaTime"); int runTimeLoc = GetShaderLocation(shader, "runTime"); int resolutionLoc = GetShaderLocation(shader, "resolution"); - float resolution[2] = { screenWidth, screenHeight }; + float resolution[2] = { (float)screenWidth, (float)screenHeight }; SetShaderValue(shader, resolutionLoc, resolution, UNIFORM_VEC2); float runTime = 0.0f; @@ -64,13 +63,22 @@ int main(void) // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { + // Check if screen is resized + //---------------------------------------------------------------------------------- + if(IsWindowResized()) + { + screenWidth = GetScreenWidth(); + screenHeight = GetScreenHeight(); + float resolution[2] = { (float)screenWidth, (float)screenHeight }; + SetShaderValue(shader, resolutionLoc, resolution, UNIFORM_VEC2); + } + // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera); // Update camera float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; float cameraTarget[3] = { camera.target.x, camera.target.y, camera.target.z }; - float cameraUp[3] = { camera.up.x, camera.up.y, camera.up.z }; float deltaTime = GetFrameTime(); runTime += deltaTime; @@ -78,8 +86,6 @@ int main(void) // Set shader required uniform values SetShaderValue(shader, viewEyeLoc, cameraPos, UNIFORM_VEC3); SetShaderValue(shader, viewCenterLoc, cameraTarget, UNIFORM_VEC3); - SetShaderValue(shader, viewUpLoc, cameraUp, UNIFORM_VEC3); - SetShaderValue(shader, deltaTimeLoc, &deltaTime, UNIFORM_FLOAT); SetShaderValue(shader, runTimeLoc, &runTime, UNIFORM_FLOAT); //---------------------------------------------------------------------------------- @@ -95,7 +101,7 @@ int main(void) DrawRectangle(0, 0, screenWidth, screenHeight, WHITE); EndShaderMode(); - DrawText("(c) Raymarching shader by Iñigo Quilez. MIT License.", screenWidth - 280, screenHeight - 20, 10, GRAY); + DrawText("(c) Raymarching shader by Iñigo Quilez. MIT License.", screenWidth - 280, screenHeight - 20, 10, BLACK); EndDrawing(); //---------------------------------------------------------------------------------- @@ -109,4 +115,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/src/camera.h b/src/camera.h index bc813b53b..51da4804f 100644 --- a/src/camera.h +++ b/src/camera.h @@ -8,7 +8,7 @@ * * #define CAMERA_IMPLEMENTATION * Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers +* If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation. * * #define CAMERA_STANDALONE @@ -77,7 +77,7 @@ } Camera3D; typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D - + // Camera system modes typedef enum { CAMERA_CUSTOM = 0, @@ -113,8 +113,8 @@ void UpdateCamera(Camera *camera); // Update camera pos void SetCameraPanControl(int panKey); // Set camera pan key to combine with mouse movement (free camera) void SetCameraAltControl(int altKey); // Set camera alt key to combine with mouse movement (free camera) void SetCameraSmoothZoomControl(int szoomKey); // Set camera smooth zoom key to combine with mouse (free camera) -void SetCameraMoveControls(int frontKey, int backKey, - int rightKey, int leftKey, +void SetCameraMoveControls(int frontKey, int backKey, + int rightKey, int leftKey, int upKey, int downKey); // Set camera move controls (1st person and 3rd person cameras) #endif @@ -188,21 +188,21 @@ void SetCameraMoveControls(int frontKey, int backKey, // Types and Structures Definition //---------------------------------------------------------------------------------- // Camera move modes (first person and third person cameras) -typedef enum { - MOVE_FRONT = 0, - MOVE_BACK, - MOVE_RIGHT, - MOVE_LEFT, - MOVE_UP, - MOVE_DOWN +typedef enum { + MOVE_FRONT = 0, + MOVE_BACK, + MOVE_RIGHT, + MOVE_LEFT, + MOVE_UP, + MOVE_DOWN } CameraMove; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- static Vector2 cameraAngle = { 0.0f, 0.0f }; // Camera angle in plane XZ -static float cameraTargetDistance = 0.0f; // Camera distance from position to target -static float playerEyesPosition = 1.85f; // Default player eyes position from ground (in meters) +static float cameraTargetDistance = 0.0f; // Camera distance from position to target +static float playerEyesPosition = 1.85f; // Default player eyes position from ground (in meters) static int cameraMoveControl[6] = { 'W', 'S', 'D', 'A', 'E', 'Q' }; static int cameraPanControlKey = 2; // raylib: MOUSE_MIDDLE_BUTTON @@ -236,21 +236,21 @@ void SetCameraMode(Camera camera, int mode) { Vector3 v1 = camera.position; Vector3 v2 = camera.target; - + float dx = v2.x - v1.x; float dy = v2.y - v1.y; float dz = v2.z - v1.z; - + cameraTargetDistance = sqrtf(dx*dx + dy*dy + dz*dz); - + Vector2 distance = { 0.0f, 0.0f }; distance.x = sqrtf(dx*dx + dz*dz); distance.y = sqrtf(dx*dx + dy*dy); - + // Camera angle calculation cameraAngle.x = asinf( (float)fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) cameraAngle.y = -asinf( (float)fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW) - + playerEyesPosition = camera.position.y; // Lock cursor for first person and third person cameras @@ -272,24 +272,24 @@ void UpdateCamera(Camera *camera) static Vector2 previousMousePosition = { 0.0f, 0.0f }; // TODO: Compute cameraTargetDistance and cameraAngle here - + // Mouse movement detection Vector2 mousePositionDelta = { 0.0f, 0.0f }; Vector2 mousePosition = GetMousePosition(); int mouseWheelMove = GetMouseWheelMove(); - + // Keys input detection bool panKey = IsMouseButtonDown(cameraPanControlKey); bool altKey = IsKeyDown(cameraAltControlKey); bool szoomKey = IsKeyDown(cameraSmoothZoomControlKey); - + bool direction[6] = { IsKeyDown(cameraMoveControl[MOVE_FRONT]), IsKeyDown(cameraMoveControl[MOVE_BACK]), IsKeyDown(cameraMoveControl[MOVE_RIGHT]), IsKeyDown(cameraMoveControl[MOVE_LEFT]), IsKeyDown(cameraMoveControl[MOVE_UP]), IsKeyDown(cameraMoveControl[MOVE_DOWN]) }; - + // TODO: Consider touch inputs for camera if (cameraMode != CAMERA_CUSTOM) @@ -384,7 +384,7 @@ void UpdateCamera(Camera *camera) camera->target.z += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.x)*sinf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER); } } - + // Update camera position with changes camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x; camera->position.y = ((cameraAngle.y <= 0.0f)? 1 : -1)*sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y; @@ -395,15 +395,15 @@ void UpdateCamera(Camera *camera) { cameraAngle.x += CAMERA_ORBITAL_SPEED; // Camera orbit angle cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom - + // Camera distance clamp if (cameraTargetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; - + // Update camera position with changes camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x; camera->position.y = ((cameraAngle.y <= 0.0f)? 1 : -1)*sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y; camera->position.z = cosf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.z; - + } break; case CAMERA_FIRST_PERSON: { @@ -411,11 +411,11 @@ void UpdateCamera(Camera *camera) sinf(cameraAngle.x)*direction[MOVE_FRONT] - cosf(cameraAngle.x)*direction[MOVE_LEFT] + cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; - + camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] - sinf(cameraAngle.y)*direction[MOVE_BACK] + 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; - + camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] - cosf(cameraAngle.x)*direction[MOVE_FRONT] + sinf(cameraAngle.x)*direction[MOVE_LEFT] - @@ -424,11 +424,11 @@ void UpdateCamera(Camera *camera) bool isMoving = false; // Required for swinging for (int i = 0; i < 6; i++) if (direction[i]) { isMoving = true; break; } - + // Camera orientation calculation cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); - + // Angle clamp if (cameraAngle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD; else if (cameraAngle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD; @@ -437,7 +437,7 @@ void UpdateCamera(Camera *camera) camera->target.x = camera->position.x - sinf(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; camera->target.y = camera->position.y + sinf(cameraAngle.y)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; camera->target.z = camera->position.z - cosf(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; - + if (isMoving) swingCounter++; // Camera position update @@ -446,8 +446,8 @@ void UpdateCamera(Camera *camera) camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; - - + + } break; case CAMERA_THIRD_PERSON: { @@ -455,11 +455,11 @@ void UpdateCamera(Camera *camera) sinf(cameraAngle.x)*direction[MOVE_FRONT] - cosf(cameraAngle.x)*direction[MOVE_LEFT] + cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; - + camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] - sinf(cameraAngle.y)*direction[MOVE_BACK] + 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; - + camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] - cosf(cameraAngle.x)*direction[MOVE_FRONT] + sinf(cameraAngle.x)*direction[MOVE_LEFT] - @@ -468,7 +468,7 @@ void UpdateCamera(Camera *camera) // Camera orientation calculation cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); - + // Angle clamp if (cameraAngle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD; else if (cameraAngle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD; @@ -487,7 +487,7 @@ void UpdateCamera(Camera *camera) } break; default: break; - } + } } // Set camera pan key to combine with mouse movement (free camera) diff --git a/src/core.c b/src/core.c index 305eb33cf..b247c1aa5 100644 --- a/src/core.c +++ b/src/core.c @@ -579,7 +579,7 @@ static void InitTerminal(void) } else { - + ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); } @@ -590,7 +590,7 @@ static void InitTerminal(void) static void RestoreTerminal(void) { TraceLog(LOG_INFO, "Restore Terminal ..."); - + // Reset to default keyboard settings tcsetattr(STDIN_FILENO, TCSANOW, &defaultKeyboardSettings); @@ -774,7 +774,7 @@ void CloseWindow(void) pthread_join(eventWorkers[i].threadId, NULL); } } - + if (gamepadThreadId) pthread_join(gamepadThreadId, NULL); #endif @@ -1207,7 +1207,7 @@ void EndDrawing(void) // we draw a small rectangle for user reference DrawRectangle(mousePosition.x, mousePosition.y, 3, 3, MAROON); #endif - + rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) #if defined(SUPPORT_GIF_RECORDING) @@ -1270,10 +1270,10 @@ void BeginMode2D(Camera2D camera) rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlLoadIdentity(); // Reset current matrix (MODELVIEW) - + // Apply screen scaling if required rlMultMatrixf(MatrixToFloat(screenScaling)); - + // Apply 2d camera transformation to modelview rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera))); } @@ -1465,7 +1465,7 @@ Matrix GetCameraMatrix(Camera camera) } // Returns camera 2d transform matrix -Matrix GetCameraMatrix2D(Camera2D camera) +Matrix GetCameraMatrix2D(Camera2D camera) { Matrix matTransform = { 0 }; // The camera in world-space is set by @@ -1486,9 +1486,9 @@ Matrix GetCameraMatrix2D(Camera2D camera) Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD); Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f); Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f); - + matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation); - + return matTransform; } @@ -1535,20 +1535,20 @@ Vector2 GetWorldToScreen(Vector3 position, Camera camera) } // Returns the screen space position for a 2d camera world space position -Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera) +Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera) { Matrix matCamera = GetCameraMatrix2D(camera); Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera); - + return (Vector2){ transform.x, transform.y }; } // Returns the world space position for a 2d camera screen space position -Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera) +Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera) { Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera)); Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera); - + return (Vector2){ transform.x, transform.y }; } @@ -1796,29 +1796,21 @@ bool FileExists(const char *fileName) bool IsFileExtension(const char *fileName, const char *ext) { bool result = false; - const char *fileExt; + const char *fileExt = GetExtension(fileName); - if ((fileExt = strrchr(fileName, '.')) != NULL) + if (fileExt != NULL) { -#if defined(_WIN32) - result = true; - int extLen = strlen(ext); + int extCount = 0; + const char **checkExts = TextSplit(ext, ';', &extCount); - if (strlen(fileExt) == extLen) + for (int i = 0; i < extCount; i++) { - for (int i = 0; i < extLen; i++) + if (strcmp(fileExt, checkExts[i] + 1) == 0) { - if (tolower(fileExt[i]) != tolower(ext[i])) - { - result = false; - break; - } + result = true; + break; } } - else result = false; -#else - if (strcmp(fileExt, ext) == 0) result = true; -#endif } return result; @@ -1916,9 +1908,9 @@ const char *GetPrevDirectoryPath(const char *dirPath) static char prevDirPath[MAX_FILEPATH_LENGTH]; memset(prevDirPath, 0, MAX_FILEPATH_LENGTH); int pathLen = strlen(dirPath); - + if (pathLen <= 3) strcpy(prevDirPath, dirPath); - + for (int i = (pathLen - 1); (i > 0) && (pathLen > 3); i--) { if ((dirPath[i] == '\\') || (dirPath[i] == '/')) @@ -1928,7 +1920,7 @@ const char *GetPrevDirectoryPath(const char *dirPath) break; } } - + return prevDirPath; } @@ -1986,11 +1978,12 @@ void ClearDirectoryFiles(void) { if (dirFilesCount > 0) { - for (int i = 0; i < dirFilesCount; i++) RL_FREE(dirFilesPath[i]); + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) RL_FREE(dirFilesPath[i]); RL_FREE(dirFilesPath); - dirFilesCount = 0; } + + dirFilesCount = 0; } // Change working directory, returns true if success @@ -2045,9 +2038,9 @@ long GetFileModTime(const char *fileName) unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLength) { #define COMPRESSION_QUALITY_DEFLATE 8 - + unsigned char *compData = NULL; - + #if defined(SUPPORT_COMPRESSION_API) compData = stbi_zlib_compress(data, dataLength, compDataLength, COMPRESSION_QUALITY_DEFLATE); #endif @@ -2059,7 +2052,7 @@ unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLe unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength) { char *data = NULL; - + #if defined(SUPPORT_COMPRESSION_API) data = stbi_zlib_decode_malloc((char *)compData, compDataLength, dataLength); #endif @@ -2664,9 +2657,9 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); #if defined(PLATFORM_DESKTOP) - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); #else - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); #endif } @@ -4931,7 +4924,7 @@ static void *EventThread(void *arg) // TODO: This fifo is not fully threadsafe with multiple writers, so multiple keyboards hitting a key at the exact same time could miss a key (double write to head before it was incremented) } */ - + currentKeyState[keycode] = event.value; if (event.value == 1) lastKeyPressed = keycode; // Register last key pressed @@ -4945,7 +4938,7 @@ static void *EventThread(void *arg) #endif if (currentKeyState[exitKey] == 1) windowShouldClose = true; - + TraceLog(LOG_DEBUG, "KEY%s ScanCode: %4i KeyCode: %4i",event.value == 0 ? "UP":"DOWN", event.code, keycode); } } diff --git a/src/external/stb_image.h b/src/external/stb_image.h index a6202a31f..196dfd5cc 100644 --- a/src/external/stb_image.h +++ b/src/external/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.22 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.23 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,7 @@ LICENSE RECENT REVISION HISTORY: + 2.23 (2019-08-11) fix clang static analysis warning 2.22 (2019-03-04) gif fixes, fix warnings 2.21 (2019-02-25) fix typo in comment 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs @@ -5079,7 +5080,7 @@ static int stbi__high_bit(unsigned int z) if (z >= 0x00100) { n += 8; z >>= 8; } if (z >= 0x00010) { n += 4; z >>= 4; } if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1; z >>= 1; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } @@ -5237,7 +5238,10 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req psize = (info.offset - 14 - info.hsz) >> 2; } - s->img_n = ma ? 4 : 3; + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else @@ -5547,6 +5551,8 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5710,6 +5716,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } @@ -6936,7 +6943,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) return 0; if (x) *x = s->img_x; if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } return 1; } #endif diff --git a/src/external/stb_image_write.h b/src/external/stb_image_write.h index 082eb69d5..a9bf66c14 100644 --- a/src/external/stb_image_write.h +++ b/src/external/stb_image_write.h @@ -1,4 +1,4 @@ -/* stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h +/* stb_image_write - v1.13 - public domain - http://nothings.org/stb writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk @@ -868,7 +868,7 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; - unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); if (hash_table == NULL) return NULL; if (quality < 5) quality = 5; @@ -1530,6 +1530,8 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.11 (2019-08-11) + 1.10 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 1.09 (2018-02-11) diff --git a/src/external/stb_perlin.h b/src/external/stb_perlin.h index d582d5a4a..941773f02 100644 --- a/src/external/stb_perlin.h +++ b/src/external/stb_perlin.h @@ -81,6 +81,7 @@ extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap 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 @@ -321,6 +322,66 @@ float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, 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 /* diff --git a/src/external/stb_truetype.h b/src/external/stb_truetype.h index 767f005ab..4a3ad33da 100644 --- a/src/external/stb_truetype.h +++ b/src/external/stb_truetype.h @@ -1,5 +1,5 @@ -// stb_truetype.h - v1.21 - public domain -// authored from 2009-2016 by Sean Barrett / RAD Game Tools +// stb_truetype.h - v1.22 - public domain +// authored from 2009-2019 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: // parse files @@ -46,9 +46,11 @@ // Rob Loach Cort Stratton // Kenney Phillis Jr. github:oyvindjam // Brian Costabile github:vassvik +// Ken Voskuil (kaesve) Ryan Griege // // VERSION HISTORY // +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined // 1.21 (2019-02-25) fix warning // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() // 1.19 (2018-02-11) GPOS kerning, STBTT_fmod @@ -2540,8 +2542,7 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int if (info->gpos) xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); - - if (info->kern) + else if (info->kern) xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); return xAdvance; @@ -3968,6 +3969,7 @@ static float stbtt__oversample_shift(int oversample) STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k; + int missing_glyph_added = 0; k=0; for (i=0; i < num_ranges; ++i) { @@ -3979,7 +3981,7 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb int x0,y0,x1,y1; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; int glyph = stbtt_FindGlyphIndex(info, codepoint); - if (glyph == 0 && spc->skip_missing) { + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { rects[k].w = rects[k].h = 0; } else { stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, @@ -3989,6 +3991,8 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb &x0,&y0,&x1,&y1); rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; } ++k; } @@ -4023,7 +4027,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info // rects array must be big enough to accommodate all characters in the given ranges STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { - int i,j,k, return_value = 1; + int i,j,k, missing_glyph = -1, return_value = 1; // save current values int old_h_over = spc->h_oversample; @@ -4088,6 +4092,13 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const bc->yoff = (float) y0 * recip_v + sub_y; bc->xoff2 = (x0 + r->w) * recip_h + sub_x; bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; } else { return_value = 0; // if any fail, report failure } @@ -4389,12 +4400,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc int w,h; unsigned char *data; - // if one scale is 0, use same scale for both - if (scale_x == 0) scale_x = scale_y; - if (scale_y == 0) { - if (scale_x == 0) return NULL; // if both scales are 0, return NULL - scale_y = scale_x; - } + if (scale == 0) return NULL; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); diff --git a/src/gestures.h b/src/gestures.h index 367753330..7b3d7f31b 100644 --- a/src/gestures.h +++ b/src/gestures.h @@ -8,7 +8,7 @@ * * #define GESTURES_IMPLEMENTATION * Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers +* If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation. * * #define GESTURES_STANDALONE @@ -216,8 +216,8 @@ static float pinchDistance = 0.0f; // PINCH displacement distance ( static int currentGesture = GESTURE_NONE; // Current detected gesture -// Enabled gestures flags, all gestures enabled by default -static unsigned int enabledGestures = 0b0000001111111111; +// Enabled gestures flags, all gestures enabled by default +static unsigned int enabledGestures = 0b0000001111111111; //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -251,13 +251,13 @@ void ProcessGestureEvent(GestureEvent event) { // Reset required variables pointCount = event.pointCount; // Required on UpdateGestures() - + if (pointCount < 2) { if (event.touchAction == TOUCH_DOWN) { tapCounter++; // Tap counter - + // Detect GESTURE_DOUBLE_TAP if ((currentGesture == GESTURE_NONE) && (tapCounter >= 2) && ((GetCurrentTime() - eventTime) < TAP_TIMEOUT) && (Vector2Distance(touchDownPosition, event.position[0]) < DOUBLETAP_RANGE)) { @@ -269,15 +269,15 @@ void ProcessGestureEvent(GestureEvent event) tapCounter = 1; currentGesture = GESTURE_TAP; } - + touchDownPosition = event.position[0]; touchDownDragPosition = event.position[0]; - + touchUpPosition = touchDownPosition; eventTime = GetCurrentTime(); - + firstTouchId = event.pointerId[0]; - + dragVector = (Vector2){ 0.0f, 0.0f }; } else if (event.touchAction == TOUCH_UP) @@ -287,15 +287,15 @@ void ProcessGestureEvent(GestureEvent event) // NOTE: dragIntensity dependend on the resolution of the screen dragDistance = Vector2Distance(touchDownPosition, touchUpPosition); dragIntensity = dragDistance/(float)((GetCurrentTime() - swipeTime)); - + startMoving = false; - + // Detect GESTURE_SWIPE if ((dragIntensity > FORCE_TO_SWIPE) && (firstTouchId == event.pointerId[0])) { // NOTE: Angle should be inverted in Y dragAngle = 360.0f - Vector2Angle(touchDownPosition, touchUpPosition); - + if ((dragAngle < 30) || (dragAngle > 330)) currentGesture = GESTURE_SWIPE_RIGHT; // Right else if ((dragAngle > 30) && (dragAngle < 120)) currentGesture = GESTURE_SWIPE_UP; // Up else if ((dragAngle > 120) && (dragAngle < 210)) currentGesture = GESTURE_SWIPE_LEFT; // Left @@ -307,31 +307,31 @@ void ProcessGestureEvent(GestureEvent event) dragDistance = 0.0f; dragIntensity = 0.0f; dragAngle = 0.0f; - + currentGesture = GESTURE_NONE; } - + touchDownDragPosition = (Vector2){ 0.0f, 0.0f }; pointCount = 0; } else if (event.touchAction == TOUCH_MOVE) { if (currentGesture == GESTURE_DRAG) eventTime = GetCurrentTime(); - + if (!startMoving) { swipeTime = GetCurrentTime(); startMoving = true; } - + moveDownPosition = event.position[0]; - + if (currentGesture == GESTURE_HOLD) { if (resetHold) touchDownPosition = event.position[0]; - + resetHold = false; - + // Detect GESTURE_DRAG if (Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_DRAG) { @@ -339,7 +339,7 @@ void ProcessGestureEvent(GestureEvent event) currentGesture = GESTURE_DRAG; } } - + dragVector.x = moveDownPosition.x - touchDownDragPosition.x; dragVector.y = moveDownPosition.y - touchDownDragPosition.y; } @@ -350,28 +350,28 @@ void ProcessGestureEvent(GestureEvent event) { touchDownPosition = event.position[0]; touchDownPosition2 = event.position[1]; - + //pinchDistance = Vector2Distance(touchDownPosition, touchDownPosition2); - + pinchVector.x = touchDownPosition2.x - touchDownPosition.x; pinchVector.y = touchDownPosition2.y - touchDownPosition.y; - + currentGesture = GESTURE_HOLD; timeHold = GetCurrentTime(); } else if (event.touchAction == TOUCH_MOVE) { pinchDistance = Vector2Distance(moveDownPosition, moveDownPosition2); - + touchDownPosition = moveDownPosition; touchDownPosition2 = moveDownPosition2; - + moveDownPosition = event.position[0]; moveDownPosition2 = event.position[1]; - + pinchVector.x = moveDownPosition2.x - moveDownPosition.x; pinchVector.y = moveDownPosition2.y - moveDownPosition.y; - + if ((Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_PINCH) || (Vector2Distance(touchDownPosition2, moveDownPosition2) >= MINIMUM_PINCH)) { if ((Vector2Distance(moveDownPosition, moveDownPosition2) - pinchDistance) < 0) currentGesture = GESTURE_PINCH_IN; @@ -382,7 +382,7 @@ void ProcessGestureEvent(GestureEvent event) currentGesture = GESTURE_HOLD; timeHold = GetCurrentTime(); } - + // NOTE: Angle should be inverted in Y pinchAngle = 360.0f - Vector2Angle(moveDownPosition, moveDownPosition2); } @@ -392,7 +392,7 @@ void ProcessGestureEvent(GestureEvent event) pinchAngle = 0.0f; pinchVector = (Vector2){ 0.0f, 0.0f }; pointCount = 0; - + currentGesture = GESTURE_NONE; } } @@ -409,14 +409,14 @@ void UpdateGestures(void) currentGesture = GESTURE_HOLD; timeHold = GetCurrentTime(); } - + if (((GetCurrentTime() - eventTime) > TAP_TIMEOUT) && (currentGesture == GESTURE_DRAG) && (pointCount < 2)) { currentGesture = GESTURE_HOLD; timeHold = GetCurrentTime(); resetHold = true; } - + // Detect GESTURE_NONE if ((currentGesture == GESTURE_SWIPE_RIGHT) || (currentGesture == GESTURE_SWIPE_UP) || (currentGesture == GESTURE_SWIPE_LEFT) || (currentGesture == GESTURE_SWIPE_DOWN)) { @@ -428,7 +428,7 @@ void UpdateGestures(void) int GetTouchPointsCount(void) { // NOTE: point count is calculated when ProcessGestureEvent(GestureEvent event) is called - + return pointCount; } @@ -443,11 +443,11 @@ int GetGestureDetected(void) float GetGestureHoldDuration(void) { // NOTE: time is calculated on current gesture HOLD - + double time = 0.0; - + if (currentGesture == GESTURE_HOLD) time = GetCurrentTime() - timeHold; - + return (float)time; } @@ -455,7 +455,7 @@ float GetGestureHoldDuration(void) Vector2 GetGestureDragVector(void) { // NOTE: drag vector is calculated on one touch points TOUCH_MOVE - + return dragVector; } @@ -464,7 +464,7 @@ Vector2 GetGestureDragVector(void) float GetGestureDragAngle(void) { // NOTE: drag angle is calculated on one touch points TOUCH_UP - + return dragAngle; } @@ -473,7 +473,7 @@ Vector2 GetGesturePinchVector(void) { // NOTE: The position values used for pinchDistance are not modified like the position values of [core.c]-->GetTouchPosition(int index) // NOTE: pinch distance is calculated on two touch points TOUCH_MOVE - + return pinchVector; } @@ -482,7 +482,7 @@ Vector2 GetGesturePinchVector(void) float GetGesturePinchAngle(void) { // NOTE: pinch angle is calculated on two touch points TOUCH_MOVE - + return pinchAngle; } @@ -494,7 +494,7 @@ float GetGesturePinchAngle(void) static float Vector2Angle(Vector2 v1, Vector2 v2) { float angle = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI); - + if (angle < 0) angle += 360.0f; return angle; @@ -518,13 +518,13 @@ static float Vector2Distance(Vector2 v1, Vector2 v2) static double GetCurrentTime(void) { double time = 0; - + #if defined(_WIN32) unsigned long long int clockFrequency, currentTime; - + QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation! QueryPerformanceCounter(¤tTime); - + time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds #endif @@ -533,24 +533,24 @@ static double GetCurrentTime(void) struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds - + time = ((double)nowTime/1000000.0); // Time in miliseconds #endif #if defined(__APPLE__) //#define CLOCK_REALTIME CALENDAR_CLOCK // returns UTC time since 1970-01-01 //#define CLOCK_MONOTONIC SYSTEM_CLOCK // returns the time since boot time - + clock_serv_t cclock; mach_timespec_t now; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); - + // NOTE: OS X does not have clock_gettime(), using clock_get_time() clock_get_time(cclock, &now); mach_port_deallocate(mach_task_self(), cclock); uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds - time = ((double)nowTime/1000000.0); // Time in miliseconds + time = ((double)nowTime/1000000.0); // Time in miliseconds #endif return time; diff --git a/src/models.c b/src/models.c index 2462190d9..501f1e706 100644 --- a/src/models.c +++ b/src/models.c @@ -2385,8 +2385,18 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota for (int i = 0; i < model.meshCount; i++) { - model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = tint; + // TODO: Review color + tint premultiplication mechanism + Color color = model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color; + + Color colorTint = WHITE; + colorTint.r = (((float)color.r/255.0)*((float)tint.r/255.0))*255; + colorTint.g = (((float)color.g/255.0)*((float)tint.g/255.0))*255; + colorTint.b = (((float)color.b/255.0)*((float)tint.b/255.0))*255; + colorTint.a = (((float)color.a/255.0)*((float)tint.a/255.0))*255; + + model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = colorTint; rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); + model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = color; } } @@ -3344,6 +3354,88 @@ static unsigned char *DecodeBase64(char *input, int *size) return buf; } +// Load texture from cgltf_image +static Texture LoadTextureFromCgltfImage(cgltf_image *image, const char *texPath, Color tint) +{ + Texture texture = { 0 }; + + if (image->uri) + { + if ((strlen(image->uri) > 5) && + (image->uri[0] == 'd') && + (image->uri[1] == 'a') && + (image->uri[2] == 't') && + (image->uri[3] == 'a') && + (image->uri[4] == ':')) + { + // Data URI + // Format: data:;base64, + + // Find the comma + int i = 0; + while ((image->uri[i] != ',') && (image->uri[i] != 0)) i++; + + if (image->uri[i] == 0) TraceLog(LOG_WARNING, "CGLTF Image: Invalid data URI"); + else + { + int size; + unsigned char *data = DecodeBase64(image->uri + i + 1, &size); + + int w, h; + unsigned char *raw = stbi_load_from_memory(data, size, &w, &h, NULL, 4); + + Image rimage = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8); + + // TODO: Tint shouldn't be applied here! + ImageColorTint(&rimage, tint); + texture = LoadTextureFromImage(rimage); + UnloadImage(rimage); + } + } + else + { + Image rimage = LoadImage(TextFormat("%s/%s", texPath, image->uri)); + + // TODO: Tint shouldn't be applied here! + ImageColorTint(&rimage, tint); + texture = LoadTextureFromImage(rimage); + UnloadImage(rimage); + } + } + else if (image->buffer_view) + { + unsigned char *data = RL_MALLOC(image->buffer_view->size); + int n = image->buffer_view->offset; + int stride = image->buffer_view->stride ? image->buffer_view->stride : 1; + + for (int i = 0; i < image->buffer_view->size; i++) + { + data[i] = ((unsigned char *)image->buffer_view->buffer->data)[n]; + n += stride; + } + + int w, h; + unsigned char *raw = stbi_load_from_memory(data, image->buffer_view->size, &w, &h, NULL, 4); + free(data); + + Image rimage = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8); + free(raw); + + // TODO: Tint shouldn't be applied here! + ImageColorTint(&rimage, tint); + texture = LoadTextureFromImage(rimage); + UnloadImage(rimage); + } + else + { + Image rimage = LoadImageEx(&tint, 1, 1); + texture = LoadTextureFromImage(rimage); + UnloadImage(rimage); + } + + return texture; +} + // Load glTF mesh data static Model LoadGLTF(const char *fileName) { @@ -3409,9 +3501,7 @@ static Model LoadGLTF(const char *fileName) // Read data buffers result = cgltf_load_buffers(&options, data, fileName); - if (result != cgltf_result_success) { - TraceLog(LOG_INFO, "[%s][%s] Error loading mesh/material buffers", fileName, (data->file_type == 2)? "glb" : "gltf"); - } + if (result != cgltf_result_success) TraceLog(LOG_INFO, "[%s][%s] Error loading mesh/material buffers", fileName, (data->file_type == 2)? "glb" : "gltf"); int primitivesCount = 0; @@ -3426,106 +3516,55 @@ static Model LoadGLTF(const char *fileName) for (int i = 0; i < model.meshCount; i++) model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); + //For each material for (int i = 0; i < model.materialCount - 1; i++) { - Color tint = WHITE; - Texture2D texture = { 0 }; + model.materials[i] = LoadMaterialDefault(); + Color tint = (Color){ 255, 255, 255, 255 }; const char *texPath = GetDirectoryPath(fileName); - if (data->materials[i].has_pbr_metallic_roughness) + //Ensure material follows raylib support for PBR (metallic/roughness flow) + if (data->materials[i].has_pbr_metallic_roughness) { - tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255.99f); - tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255.99f); - tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255.99f); - tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255.99f); - } - else - { - tint.r = 1.0f; - tint.g = 1.0f; - tint.b = 1.0f; - tint.a = 1.0f; - } + float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor; + float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor; - if (data->materials[i].has_pbr_metallic_roughness) - { - cgltf_image *img = data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image; + // NOTE: Material name not used for the moment + //if (model.materials[i].name && data->materials[i].name) strcpy(model.materials[i].name, data->materials[i].name); - if (img->uri) + // TODO: REview: shouldn't these be *255 ??? + tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255); + tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255); + tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255); + tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255); + + model.materials[i].maps[MAP_ROUGHNESS].color = tint; + + if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture) { - if ((strlen(img->uri) > 5) && - (img->uri[0] == 'd') && - (img->uri[1] == 'a') && - (img->uri[2] == 't') && - (img->uri[3] == 'a') && - (img->uri[4] == ':')) - { - // Data URI - // Format: data:;base64, - - // Find the comma - int i = 0; - while ((img->uri[i] != ',') && (img->uri[i] != 0)) i++; - - if (img->uri[i] == 0) TraceLog(LOG_WARNING, "[%s] Invalid data URI", fileName); - else - { - int size; - unsigned char *data = DecodeBase64(img->uri + i + 1, &size); - - int w, h; - unsigned char *raw = stbi_load_from_memory(data, size, &w, &h, NULL, 4); - - Image image = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8); - ImageColorTint(&image, tint); - texture = LoadTextureFromImage(image); - UnloadImage(image); - } - } - else - { - char *textureName = img->uri; - char *texturePath = RL_MALLOC(strlen(texPath) + strlen(textureName) + 2); - strcpy(texturePath, texPath); - strcat(texturePath, "/"); - strcat(texturePath, textureName); - - Image image = LoadImage(texturePath); - ImageColorTint(&image, tint); - texture = LoadTextureFromImage(image); - UnloadImage(image); - RL_FREE(texturePath); - } - } - else if (img->buffer_view) - { - unsigned char *data = RL_MALLOC(img->buffer_view->size); - int n = img->buffer_view->offset; - int stride = img->buffer_view->stride ? img->buffer_view->stride : 1; - - for (int i = 0; i < img->buffer_view->size; i++) - { - data[i] = ((unsigned char *)img->buffer_view->buffer->data)[n]; - n += stride; - } - - int w, h; - unsigned char *raw = stbi_load_from_memory(data, img->buffer_view->size, &w, &h, NULL, 4); - - Image image = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8); - ImageColorTint(&image, tint); - texture = LoadTextureFromImage(image); - UnloadImage(image); - } - else - { - Image image = LoadImageEx(&tint, 1, 1); - texture = LoadTextureFromImage(image); - UnloadImage(image); + model.materials[i].maps[MAP_ALBEDO].texture = LoadTextureFromCgltfImage(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, texPath, tint); } - model.materials[i] = LoadMaterialDefault(); - model.materials[i].maps[MAP_DIFFUSE].texture = texture; + // NOTE: Tint isn't need for other textures.. pass null or clear? + // Just set as white, multiplying by white has no effect + tint = WHITE; + + if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture) + { + model.materials[i].maps[MAP_ROUGHNESS].texture = LoadTextureFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath, tint); + } + model.materials[i].maps[MAP_ROUGHNESS].value = roughness; + model.materials[i].maps[MAP_METALNESS].value = metallic; + + if (data->materials[i].normal_texture.texture) + { + model.materials[i].maps[MAP_NORMAL].texture = LoadTextureFromCgltfImage(data->materials[i].normal_texture.texture->image, texPath, tint); + } + + if (data->materials[i].occlusion_texture.texture) + { + model.materials[i].maps[MAP_OCCLUSION].texture = LoadTextureFromCgltfImage(data->materials[i].occlusion_texture.texture->image, texPath, tint); + } } } diff --git a/src/physac.h b/src/physac.h index 42cb01984..4119feaed 100644 --- a/src/physac.h +++ b/src/physac.h @@ -1896,7 +1896,7 @@ static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) static void InitTimer(void) { srand(time(NULL)); // Initialize random seed - + #if defined(_WIN32) QueryPerformanceFrequency((unsigned long long int *) &frequency); #endif @@ -1911,7 +1911,7 @@ static void InitTimer(void) mach_timebase_info(&timebase); frequency = (timebase.denom*1e9)/timebase.numer; #endif - + baseTime = GetTimeCount(); // Get MONOTONIC clock time offset startTime = GetCurrentTime(); // Get current time } @@ -1920,7 +1920,7 @@ static void InitTimer(void) static uint64_t GetTimeCount(void) { uint64_t value = 0; - + #if defined(_WIN32) QueryPerformanceCounter((unsigned long long int *) &value); #endif diff --git a/src/raudio.c b/src/raudio.c index 447e50808..188c05329 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -198,10 +198,10 @@ typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioB // playback device depending on whether or not data is streamed struct rAudioBuffer { ma_pcm_converter dsp; // PCM data converter - + float volume; // Audio buffer volume float pitch; // Audio buffer pitch - + bool playing; // Audio buffer state: AUDIO_PLAYING bool paused; // Audio buffer state: AUDIO_PAUSED bool looping; // Audio buffer looping, always true for AudioStreams @@ -209,11 +209,11 @@ struct rAudioBuffer { bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer) unsigned int frameCursorPos; // Frame cursor position - unsigned int bufferSizeInFrames; // Total buffer size in frames + unsigned int bufferSizeInFrames; // Total buffer size in frames unsigned int totalFramesProcessed; // Total frames processed in this buffer (required for play timming) - + unsigned char *buffer; // Data buffer, on music stream keeps filling - + rAudioBuffer *next; // Next audio buffer on the list rAudioBuffer *prev; // Previous audio buffer on the list }; @@ -289,7 +289,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const if (!audioBuffer->playing || audioBuffer->paused) continue; ma_uint32 framesRead = 0; - + while (1) { if (framesRead > frameCount) @@ -302,7 +302,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const // Just read as much data as we can from the stream ma_uint32 framesToRead = (frameCount - framesRead); - + while (framesToRead > 0) { float tempBuffer[1024]; // 512 frames for stereo @@ -387,7 +387,7 @@ static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, { if (framesRead >= frameCount) break; } - else + else { if (isSubBufferProcessed[currentSubBufferIndex]) break; } @@ -465,7 +465,7 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr static void InitAudioBufferPool() { // Dummy buffers - for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) + for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) { audioBufferPool[i] = InitAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, 0, AUDIO_BUFFER_USAGE_STATIC); } @@ -474,7 +474,11 @@ static void InitAudioBufferPool() // Close the audio buffers pool static void CloseAudioBufferPool() { - for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) RL_FREE(audioBufferPool[i]); + for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) + { + RL_FREE(audioBufferPool[i]->buffer); + RL_FREE(audioBufferPool[i]); + } } //---------------------------------------------------------------------------------- @@ -486,7 +490,7 @@ void InitAudioDevice(void) // Init audio context ma_context_config contextConfig = ma_context_config_init(); contextConfig.logCallback = OnLog; - + ma_result result = ma_context_init(NULL, 0, &contextConfig, &context); if (result != MA_SUCCESS) { @@ -589,7 +593,7 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam { AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(1, sizeof(AudioBuffer)); audioBuffer->buffer = RL_CALLOC(bufferSizeInFrames*channels*ma_get_bytes_per_sample(format), 1); - + if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "InitAudioBuffer() : Failed to allocate memory for audio buffer"); @@ -608,7 +612,7 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam dspConfig.onRead = OnAudioBufferDSPRead; // Callback on data reading dspConfig.pUserData = audioBuffer; // Audio data pointer dspConfig.allowDynamicSampleRate = true; // Required for pitch shifting - + ma_result result = ma_pcm_converter_init(&dspConfig, &audioBuffer->dsp); if (result != MA_SUCCESS) @@ -655,7 +659,7 @@ void CloseAudioBuffer(AudioBuffer *buffer) bool IsAudioBufferPlaying(AudioBuffer *buffer) { bool result = false; - + if (buffer != NULL) result = (buffer->playing && !buffer->paused); else TraceLog(LOG_ERROR, "IsAudioBufferPlaying() : No audio buffer"); @@ -698,7 +702,7 @@ void StopAudioBuffer(AudioBuffer *buffer) void PauseAudioBuffer(AudioBuffer *buffer) { if (buffer != NULL) buffer->paused = true; - else TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer"); + else TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer"); } // Resume an audio buffer @@ -722,8 +726,8 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch) { float pitchMul = pitch/buffer->pitch; - // Pitching is just an adjustment of the sample rate. - // Note that this changes the duration of the sound: + // Pitching is just an adjustment of the sample rate. + // Note that this changes the duration of the sound: // - higher pitches will make the sound faster // - lower pitches make it slower ma_uint32 newOutputSampleRate = (ma_uint32)((float)buffer->dsp.src.config.sampleRateOut/pitchMul); @@ -816,7 +820,7 @@ Sound LoadSoundFromWave(Wave wave) if (wave.data != NULL) { - // When using miniaudio we need to do our own mixing. + // When using miniaudio we need to do our own mixing. // To simplify this we need convert the format of each sound to be consistent with // the format used to open the playback device. We can do this two ways: // @@ -909,7 +913,7 @@ void ExportWaveAsCode(Wave wave, const char *fileName) int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; FILE *txtFile = fopen(fileName, "wt"); - + if (txtFile != NULL) { fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n"); @@ -967,7 +971,7 @@ void PlaySoundMulti(Sound sound) oldAge = audioBufferPoolChannels[i]; oldIndex = i; } - + if (!IsAudioBufferPlaying(audioBufferPool[i])) { index = i; @@ -979,17 +983,17 @@ void PlaySoundMulti(Sound sound) if (index == -1) { TraceLog(LOG_WARNING,"pool age %i ended a sound early no room in buffer pool", audioBufferPoolCounter); - + if (oldIndex == -1) { // Shouldn't be able to get here... but just in case something odd happens! TraceLog(LOG_ERROR,"sound buffer pool couldn't determine oldest buffer not playing sound"); - + return; } - + index = oldIndex; - + // Just in case... StopAudioBuffer(audioBufferPool[index]); } @@ -1000,7 +1004,7 @@ void PlaySoundMulti(Sound sound) audioBufferPoolChannels[index] = audioBufferPoolCounter; audioBufferPoolCounter++; - + audioBufferPool[index]->volume = sound.stream.buffer->volume; audioBufferPool[index]->pitch = sound.stream.buffer->pitch; audioBufferPool[index]->looping = sound.stream.buffer->looping; @@ -1023,12 +1027,12 @@ void StopSoundMulti(void) int GetSoundsPlaying(void) { int counter = 0; - + for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) { if (IsAudioBufferPlaying(audioBufferPool[i])) counter++; } - + return counter; } @@ -1211,7 +1215,7 @@ Music LoadMusicStream(const char *fileName) { drmp3 *ctxMp3 = RL_MALLOC(sizeof(drmp3)); music.ctxData = ctxMp3; - + int result = drmp3_init_file(ctxMp3, fileName, NULL); if (result > 0) @@ -1242,7 +1246,7 @@ Music LoadMusicStream(const char *fileName) music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); music.loopCount = 0; // Infinite loop by default musicLoaded = true; - + music.ctxData = ctxXm; } } @@ -1252,7 +1256,7 @@ Music LoadMusicStream(const char *fileName) { jar_mod_context_t *ctxMod = RL_MALLOC(sizeof(jar_mod_context_t)); music.ctxData = ctxMod; - + jar_mod_init(ctxMod); int result = jar_mod_load_file(ctxMod, fileName); @@ -1335,7 +1339,7 @@ void PlayMusicStream(Music music) { // For music streams, we need to make sure we maintain the frame cursor position // This is a hack for this section of code in UpdateMusicStream() - // NOTE: In case window is minimized, music stream is stopped, just make sure to + // NOTE: In case window is minimized, music stream is stopped, just make sure to // play again on window restore: if (IsMusicPlaying(music)) PlayMusicStream(music); ma_uint32 frameCursorPos = audioBuffer->frameCursorPos; PlayAudioStream(music.stream); // WARNING: This resets the cursor position. @@ -1395,7 +1399,7 @@ void UpdateMusicStream(Music music) void *pcm = RL_CALLOC(subBufferSizeInFrames*music.stream.channels*music.stream.sampleSize/8, 1); int samplesCount = 0; // Total size of data streamed in L+R samples for xm floats, individual L or R for ogg shorts - + // TODO: Get the sampleLeft using totalFramesProcessed... but first, get total frames processed correctly... //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels; int sampleLeft = music.sampleCount - (music.stream.buffer->totalFramesProcessed*music.stream.channels); @@ -1449,7 +1453,7 @@ void UpdateMusicStream(Music music) } UpdateAudioStream(music.stream, pcm, samplesCount); - + if ((music.ctxType == MUSIC_MODULE_XM) || (music.ctxType == MUSIC_MODULE_MOD)) { if (samplesCount > 1) sampleLeft -= samplesCount/2; @@ -1549,11 +1553,11 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un // The size of a streaming buffer must be at least double the size of a period unsigned int periodSize = device.playback.internalBufferSizeInFrames/device.playback.internalPeriods; unsigned int subBufferSize = AUDIO_BUFFER_SIZE; - + if (subBufferSize < periodSize) subBufferSize = periodSize; stream.buffer = InitAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); - + if (stream.buffer != NULL) { stream.buffer->looping = true; // Always loop for streaming buffers @@ -1578,7 +1582,7 @@ void CloseAudioStream(AudioStream stream) void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { AudioBuffer *audioBuffer = stream.buffer; - + if (audioBuffer != NULL) { if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]) @@ -1587,7 +1591,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1]) { - // Both buffers are available for updating. + // Both buffers are available for updating. // Update the first one and make sure the cursor is moved back to the front. subBufferToUpdate = 0; audioBuffer->frameCursorPos = 0; @@ -1604,7 +1608,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) // TODO: Get total frames processed on this buffer... DOES NOT WORK. audioBuffer->totalFramesProcessed += subBufferSizeInFrames; - // Does this API expect a whole buffer to be updated in one go? + // Does this API expect a whole buffer to be updated in one go? // Assuming so, but if not will need to change this logic. if (subBufferSizeInFrames >= (ma_uint32)samplesCount/stream.channels) { diff --git a/src/raudio.h b/src/raudio.h index eadbf33b4..f53150052 100644 --- a/src/raudio.h +++ b/src/raudio.h @@ -110,7 +110,7 @@ typedef struct Sound { typedef struct Music { int ctxType; // Type of music context (audio filetype) void *ctxData; // Audio context data, depends on type - + unsigned int sampleCount; // Total number of samples unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop diff --git a/src/raylib.h b/src/raylib.h index be5bcaf0b..6c1e36f09 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -106,7 +106,7 @@ #ifndef RL_FREE #define RL_FREE(p) free(p) #endif - + // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. #if defined(__cplusplus) @@ -432,7 +432,7 @@ typedef struct Sound { typedef struct Music { int ctxType; // Type of music context (audio filetype) void *ctxData; // Audio context data, depends on type - + unsigned int sampleCount; // Total number of samples unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop @@ -1188,14 +1188,11 @@ RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontS RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font -RLAPI int GetNextCodepoint(const char *text, int *bytesProcessed); // Returns next codepoint in a UTF8 encoded string - // NOTE: 0x3f('?') is returned on failure -// Text strings management functions +// Text strings management functions (no utf8 strings, only byte chars) // NOTE: Some strings allocate memory internally for returned strings, just be careful! RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending -RLAPI unsigned int TextCountCodepoints(const char *text); // Get total number of characters (codepoints) in a UTF8 encoded string RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf style) RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory should be freed!) @@ -1208,6 +1205,12 @@ RLAPI const char *TextToUpper(const char *text); // Get upp RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) +RLAPI const char *TextToUtf8(int codepoint, int *byteLength); // Encode codepoint into utf8 text (char array length returned as parameter) + +// UTF8 text strings management functions +RLAPI int *GetCodepoints(const char *text, int *count); // Get all codepoints in a string, codepoints count returned by parameters +RLAPI int GetCodepointsCount(const char *text); // Get total number of characters (codepoints) in a UTF8 encoded string +RLAPI int GetNextCodepoint(const char *text, int *bytesProcessed); // Returns next codepoint in a UTF8 encoded string; 0x3f('?') is returned on failure //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) @@ -1319,6 +1322,7 @@ RLAPI void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D textur RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) RLAPI Matrix GetMatrixModelview(void); // Get internal modelview matrix +RLAPI Matrix GetMatrixProjection(void); // Get internal projection matrix // Texture maps generation (PBR) // NOTE: Required shaders should be provided @@ -1406,7 +1410,7 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check i RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) - + //------------------------------------------------------------------------------------ // Network (Module: network) //------------------------------------------------------------------------------------ diff --git a/src/rglfw.c b/src/rglfw.c index 8d331f9c6..b05ff3c74 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -2,7 +2,7 @@ * * rglfw - raylib GLFW single file compilation * -* This file includes latest GLFW sources (https://github.com/glfw/glfw) to be compiled together +* This file includes latest GLFW sources (https://github.com/glfw/glfw) to be compiled together * with raylib for all supported platforms, this way, no external dependencies are required. * * LICENSE: zlib/libpng diff --git a/src/rlgl.h b/src/rlgl.h index 70b0a5750..b83da6e8c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2967,7 +2967,7 @@ char *LoadText(const char *fileName) Shader LoadShader(const char *vsFileName, const char *fsFileName) { Shader shader = { 0 }; - + // NOTE: Shader.locs is allocated by LoadShaderCode() char *vShaderStr = NULL; @@ -3155,6 +3155,23 @@ void SetMatrixProjection(Matrix proj) #endif } +// Return internal projection matrix +Matrix GetMatrixProjection(void) { +#if defined(GRAPHICS_API_OPENGL_11) + float mat[16]; + glGetFloatv(GL_PROJECTION_MATRIX,mat); + Matrix m; + m.m0 = mat[0]; m.m1 = mat[1]; m.m2 = mat[2]; m.m3 = mat[3]; + m.m4 = mat[4]; m.m5 = mat[5]; m.m6 = mat[6]; m.m7 = mat[7]; + m.m8 = mat[8]; m.m9 = mat[9]; m.m10 = mat[10]; m.m11 = mat[11]; + m.m12 = mat[12]; m.m13 = mat[13]; m.m14 = mat[14]; m.m15 = mat[15]; + return m; +#else + return projection; +#endif +# +} + // Set a custom modelview matrix (replaces internal modelview matrix) void SetMatrixModelview(Matrix view) { @@ -3170,6 +3187,10 @@ Matrix GetMatrixModelview(void) #if defined(GRAPHICS_API_OPENGL_11) float mat[16]; glGetFloatv(GL_MODELVIEW_MATRIX, mat); + matrix.m0 = mat[0]; matrix.m1 = mat[1]; matrix.m2 = mat[2]; matrix.m3 = mat[3]; + matrix.m4 = mat[4]; matrix.m5 = mat[5]; matrix.m6 = mat[6]; matrix.m7 = mat[7]; + matrix.m8 = mat[8]; matrix.m9 = mat[9]; matrix.m10 = mat[10]; matrix.m11 = mat[11]; + matrix.m12 = mat[12]; matrix.m13 = mat[13]; matrix.m14 = mat[14]; matrix.m15 = mat[15]; #else matrix = modelview; #endif diff --git a/src/rmem.h b/src/rmem.h index 11e69588a..46e3b6532 100644 --- a/src/rmem.h +++ b/src/rmem.h @@ -54,7 +54,7 @@ #else #define RMEMAPI // We are building or using library as a static library (or Linux shared library) #endif - + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -139,9 +139,9 @@ RMEMAPI void ObjPoolCleanUp(ObjPool *objpool, void **ptrref); #if defined(RMEM_IMPLEMENTATION) -#include // Required for: -#include // Required for: -#include // Required for: +#include // Required for: +#include // Required for: +#include // Required for: //---------------------------------------------------------------------------------- // Defines and Macros @@ -163,8 +163,8 @@ RMEMAPI void ObjPoolCleanUp(ObjPool *objpool, void **ptrref); //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static inline size_t __AlignSize(const size_t size, const size_t align) -{ +static inline size_t __AlignSize(const size_t size, const size_t align) +{ return (size + (align - 1)) & -align; } @@ -175,9 +175,9 @@ static inline size_t __AlignSize(const size_t size, const size_t align) MemPool CreateMemPool(const size_t size) { MemPool mempool = { 0 }; - + if (size == 0UL) return mempool; - else + else { // Align the mempool size to at least the size of an alloc node. mempool.stack.size = size; @@ -188,7 +188,7 @@ MemPool CreateMemPool(const size_t size) mempool.stack.size = 0UL; return mempool; } - else + else { mempool.stack.base = mempool.stack.mem + mempool.stack.size; return mempool; @@ -199,9 +199,9 @@ MemPool CreateMemPool(const size_t size) MemPool CreateMemPoolFromBuffer(void *buf, const size_t size) { MemPool mempool = { 0 }; - + if ((size == 0UL) || (buf == NULL) || (size <= sizeof(MemNode))) return mempool; - else + else { mempool.stack.size = size; mempool.stack.mem = buf; @@ -213,7 +213,7 @@ MemPool CreateMemPoolFromBuffer(void *buf, const size_t size) void DestroyMemPool(MemPool *const mempool) { if ((mempool == NULL) || (mempool->stack.mem == NULL)) return; - else + else { free(mempool->stack.mem); *mempool = (MemPool){ 0 }; @@ -228,7 +228,7 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) MemNode *new_mem = NULL; const size_t ALLOC_SIZE = __AlignSize(size + sizeof *new_mem, sizeof(intptr_t)); const size_t BUCKET_INDEX = (ALLOC_SIZE >> MEMPOOL_BUCKET_BITS) - 1; - + // If the size is small enough, let's check if our buckets has a fitting memory block. if (BUCKET_INDEX < MEMPOOL_BUCKET_SIZE && mempool->buckets[BUCKET_INDEX] != NULL && mempool->buckets[BUCKET_INDEX]->size >= ALLOC_SIZE) { @@ -240,7 +240,7 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) else if (mempool->freeList.head != NULL) { const size_t MEM_SPLIT_THRESHOLD = 16; - + // If the freelist is valid, let's allocate FROM the freelist then! for (MemNode *inode = mempool->freeList.head; inode != NULL; inode = inode->next) { @@ -251,10 +251,10 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) new_mem = inode; (inode->prev != NULL)? (inode->prev->next = inode->next) : (mempool->freeList.head = inode->next); (inode->next != NULL)? (inode->next->prev = inode->prev) : (mempool->freeList.tail = inode->prev); - + if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL; else mempool->freeList.tail = NULL; - + if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL; mempool->freeList.len--; break; @@ -269,7 +269,7 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) } } } - + if (new_mem == NULL) { // not enough memory to support the size! @@ -279,13 +279,13 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) // Couldn't allocate from a freelist, allocate from available mempool. // Subtract allocation size from the mempool. mempool->stack.base -= ALLOC_SIZE; - + // Use the available mempool space as the new node. new_mem = (MemNode *)mempool->stack.base; new_mem->size = ALLOC_SIZE; } } - + // Visual of the allocation block. // -------------- // | mem size | lowest addr of block @@ -314,7 +314,7 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si MemNode *const node = (MemNode *)((uint8_t *)ptr - sizeof *node); const size_t NODE_SIZE = sizeof *node; uint8_t *const resized_block = MemPoolAlloc(mempool, size); - + if (resized_block == NULL) return NULL; else { @@ -329,16 +329,16 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si void MemPoolFree(MemPool *const restrict mempool, void *ptr) { if ((mempool == NULL) || (ptr == NULL) || ((uintptr_t)ptr - sizeof(MemNode) < (uintptr_t)mempool->stack.mem)) return; - else + else { // Behind the actual pointer data is the allocation info. MemNode *const mem_node = (MemNode *)((uint8_t *)ptr - sizeof *mem_node); const size_t BUCKET_INDEX = (mem_node->size >> MEMPOOL_BUCKET_BITS) - 1; - + // Make sure the pointer data is valid. - if (((uintptr_t)mem_node < (uintptr_t)mempool->stack.base) || - (((uintptr_t)mem_node - (uintptr_t)mempool->stack.mem) > mempool->stack.size) || - (mem_node->size == 0UL) || + if (((uintptr_t)mem_node < (uintptr_t)mempool->stack.base) || + (((uintptr_t)mem_node - (uintptr_t)mempool->stack.mem) > mempool->stack.size) || + (mem_node->size == 0UL) || (mem_node->size > mempool->stack.size)) return; // If the mem_node is right at the stack base ptr, then add it to the stack. else if ((uintptr_t)mem_node == (uintptr_t)mempool->stack.base) @@ -362,13 +362,13 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr) else /*if ((mempool->freeList.len == 0UL) || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size))*/ { for (MemNode *n = mempool->freeList.head; n != NULL; n = n->next) if (n == mem_node) return; - + // This code insertion sorts where largest size is last. if (mempool->freeList.head == NULL) { mempool->freeList.head = mempool->freeList.tail = mem_node; mempool->freeList.len++; - } + } else if (mempool->freeList.head->size >= mem_node->size) { mem_node->next = mempool->freeList.head; @@ -383,7 +383,7 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr) mempool->freeList.tail = mem_node; mempool->freeList.len++; } - + if (mempool->freeList.autoDefrag && (mempool->freeList.maxNodes != 0UL) && (mempool->freeList.len > mempool->freeList.maxNodes)) MemPoolDefrag(mempool); } } @@ -392,7 +392,7 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr) void MemPoolCleanUp(MemPool *const restrict mempool, void **ptrref) { if ((mempool == NULL) || (ptrref == NULL) || (*ptrref == NULL)) return; - else + else { MemPoolFree(mempool, *ptrref); *ptrref = NULL; @@ -402,11 +402,11 @@ void MemPoolCleanUp(MemPool *const restrict mempool, void **ptrref) size_t GetMemPoolFreeMemory(const MemPool mempool) { size_t total_remaining = (uintptr_t)mempool.stack.base - (uintptr_t)mempool.stack.mem; - + for (MemNode *n=mempool.freeList.head; n != NULL; n = n->next) total_remaining += n->size; - + for (size_t i=0; inext) total_remaining += n->size; - + return total_remaining; } @@ -423,12 +423,12 @@ bool MemPoolDefrag(MemPool *const mempool) for (size_t i = 0; i < MEMPOOL_BUCKET_SIZE; i++) mempool->buckets[i] = NULL; mempool->stack.base = mempool->stack.mem + mempool->stack.size; return true; - } + } else { for (size_t i=0; ibuckets[i] != NULL) + while (mempool->buckets[i] != NULL) { if ((uintptr_t)mempool->buckets[i] == (uintptr_t)mempool->stack.base) { @@ -440,42 +440,42 @@ bool MemPoolDefrag(MemPool *const mempool) else break; } } - + const size_t PRE_DEFRAG_LEN = mempool->freeList.len; MemNode **node = &mempool->freeList.head; - + while (*node != NULL) { - if ((uintptr_t)*node == (uintptr_t)mempool->stack.base) + if ((uintptr_t)*node == (uintptr_t)mempool->stack.base) { // If node is right at the stack, merge it back into the stack. mempool->stack.base += (*node)->size; (*node)->size = 0UL; ((*node)->prev != NULL)? ((*node)->prev->next = (*node)->next) : (mempool->freeList.head = (*node)->next); ((*node)->next != NULL)? ((*node)->next->prev = (*node)->prev) : (mempool->freeList.tail = (*node)->prev); - + if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL; else mempool->freeList.tail = NULL; - + if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL; mempool->freeList.len--; node = &mempool->freeList.head; - } + } else if (((uintptr_t)*node + (*node)->size) == (uintptr_t)(*node)->next) { // Next node is at a higher address. (*node)->size += (*node)->next->size; (*node)->next->size = 0UL; - + // <-[P Curr N]-> <-[P Next N]-> <-[P NextNext N]-> - // + // // |--------------------| // <-[P Curr N]-> <-[P Next N]-> [P NextNext N]-> if ((*node)->next->next != NULL) (*node)->next->next->prev = *node; - + // <-[P Curr N]-> <-[P NextNext N]-> (*node)->next = (*node)->next->next; - + mempool->freeList.len--; node = &mempool->freeList.head; } @@ -484,16 +484,16 @@ bool MemPoolDefrag(MemPool *const mempool) // Prev node is at a higher address. (*node)->size += (*node)->prev->size; (*node)->prev->size = 0UL; - + // <-[P PrevPrev N]-> <-[P Prev N]-> <-[P Curr N]-> // // |--------------------| // <-[P PrevPrev N] <-[P Prev N]-> <-[P Curr N]-> (*node)->prev->prev->next = *node; - + // <-[P PrevPrev N]-> <-[P Curr N]-> (*node)->prev = (*node)->prev->prev; - + mempool->freeList.len--; node = &mempool->freeList.head; } @@ -501,12 +501,12 @@ bool MemPoolDefrag(MemPool *const mempool) { // Next node is at a lower address. (*node)->next->size += (*node)->size; - + (*node)->size = 0UL; (*node)->next->prev = (*node)->prev; (*node)->prev->next = (*node)->next; *node = (*node)->next; - + mempool->freeList.len--; node = &mempool->freeList.head; } @@ -514,21 +514,21 @@ bool MemPoolDefrag(MemPool *const mempool) { // Prev node is at a lower address. (*node)->prev->size += (*node)->size; - + (*node)->size = 0UL; (*node)->next->prev = (*node)->prev; (*node)->prev->next = (*node)->next; *node = (*node)->prev; - + mempool->freeList.len--; node = &mempool->freeList.head; - } + } else { node = &(*node)->next; } } - + return PRE_DEFRAG_LEN > mempool->freeList.len; } } @@ -551,19 +551,19 @@ union ObjInfo { ObjPool CreateObjPool(const size_t objsize, const size_t len) { ObjPool objpool = { 0 }; - + if ((len == 0UL) || (objsize == 0UL)) return objpool; else { objpool.objSize = __AlignSize(objsize, sizeof(size_t)); objpool.stack.size = objpool.freeBlocks = len; objpool.stack.mem = calloc(objpool.stack.size, objpool.objSize); - + if (objpool.stack.mem == NULL) { objpool.stack.size = 0UL; return objpool; - } + } else { for (size_t i=0; istack.base }; objpool->freeBlocks--; - + // after allocating, we set head to the address of the index that *Head holds. // Head = &pool[*Head * pool.objsize]; objpool->stack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + (*ret.index*objpool->objSize) : NULL; diff --git a/src/rnet.h b/src/rnet.h index 6dbcb9253..6110bf849 100644 --- a/src/rnet.h +++ b/src/rnet.h @@ -95,10 +95,10 @@ // Platform type definitions // From: https://github.com/DFHack/clsocket/blob/master/src/Host.h //---------------------------------------------------------------------------------- - + #ifdef WIN32 typedef int socklen_t; -#endif +#endif #ifndef RESULT_SUCCESS # define RESULT_SUCCESS 0 @@ -171,7 +171,7 @@ typedef int socklen_t; #define SOCKET_MAX_QUEUE_SIZE (16) // Maximum socket queue size #define SOCKET_MAX_SOCK_OPTS (4) // Maximum socket options #define SOCKET_MAX_UDPCHANNELS (32) // Maximum UDP channels -#define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses +#define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses // Network address related defines @@ -386,7 +386,7 @@ int AddSocket(SocketSet *set, Socket *sock); int RemoveSocket(SocketSet *set, Socket *sock); int CheckSockets(SocketSet *set, unsigned int timeout); -// Packet API +// Packet API void PacketSend(Packet *packet); void PacketReceive(Packet *packet); void PacketWrite8(Packet *packet, uint16_t value); diff --git a/src/text.c b/src/text.c index 4eb1b4b04..66d3a7257 100644 --- a/src/text.c +++ b/src/text.c @@ -10,9 +10,19 @@ * supported by default, to remove support, just comment unrequired #define in this module * * #define SUPPORT_DEFAULT_FONT +* Load default raylib font on initialization to be used by DrawText() and MeasureText(). +* If no default font loaded, DrawTextEx() and MeasureTextEx() are required. +* +* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH +* TextSplit() function static buffer max size +* +* #define TEXTSPLIT_MAX_SUBSTRINGS_COUNT +* TextSplit() function static substrings pointers array (pointing to static buffer) +* * * DEPENDENCIES: -* stb_truetype - Load TTF file and rasterize characters data +* stb_truetype - Load TTF file and rasterize characters data +* stb_rect_pack - Rectangles packing algorythms, required for font atlas generation * * * LICENSE: zlib/libpng @@ -40,7 +50,7 @@ // Check if config flags have been externally provided on compilation line #if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags + #include "config.h" // Defines module configuration flags #endif #include // Required for: malloc(), free() @@ -63,7 +73,18 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers of some Text*() functions +#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: + // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal() + +#define MAX_TEXT_UNICODE_CHARS 512 // Maximum number of unicode codepoints + +#if !defined(TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH) + #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH 1024 // Size of static buffer: TextSplit() +#endif + +#if !defined(TEXTSPLIT_MAX_SUBSTRINGS_COUNT) + #define TEXTSPLIT_MAX_SUBSTRINGS_COUNT 128 // Size of static pointers array: TextSplit() +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -282,7 +303,7 @@ Font LoadFont(const char *fileName) Font font = { 0 }; #if defined(SUPPORT_FILEFORMAT_TTF) - if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, NULL, DEFAULT_TTF_NUMCHARS); + if (IsFileExtension(fileName, ".ttf;.otf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, NULL, DEFAULT_TTF_NUMCHARS); else #endif #if defined(SUPPORT_FILEFORMAT_FNT) @@ -321,7 +342,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou { Image atlas = GenImageFontAtlas(font.chars, &font.recs, font.charsCount, font.baseSize, 2, 0); font.texture = LoadTextureFromImage(atlas); - + // Update chars[i].image to use alpha, required to be used on ImageDrawText() for (int i = 0; i < font.charsCount; i++) { @@ -439,7 +460,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) for (int i = 0; i < spriteFont.charsCount; i++) { spriteFont.chars[i].value = tempCharValues[i]; - + // Get character rectangle in the font atlas texture spriteFont.recs[i] = tempCharRecs[i]; @@ -447,7 +468,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) spriteFont.chars[i].offsetX = 0; spriteFont.chars[i].offsetY = 0; spriteFont.chars[i].advanceX = 0; - + // Fill character image data from fontClear data spriteFont.chars[i].image = ImageFromImage(fontClear, tempCharRecs[i]); } @@ -587,7 +608,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo // In case no chars count provided we suppose default of 95 charsCount = (charsCount > 0)? charsCount : 95; - + // NOTE: Rectangles memory is loaded here! Rectangle *recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle)); @@ -711,7 +732,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo RL_FREE(atlas.data); atlas.data = dataGrayAlpha; atlas.format = UNCOMPRESSED_GRAY_ALPHA; - + *charRecs = recs; return atlas; @@ -756,120 +777,6 @@ void DrawFPS(int posX, int posY) DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, LIME); } -// Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found -// When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned -// 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 -// TODO: optimize this code for speed!! -int GetNextCodepoint(const char *text, int *bytesProcessed) -{ -/* - UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt - - Char. number range | UTF-8 octet sequence - (hexadecimal) | (binary) - --------------------+--------------------------------------------- - 0000 0000-0000 007F | 0xxxxxxx - 0000 0080-0000 07FF | 110xxxxx 10xxxxxx - 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx - 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx -*/ - - // NOTE: on decode errors we return as soon as possible - - int code = 0x3f; // Codepoint (defaults to '?') - int octet = (unsigned char)(text[0]); // The first UTF8 octet - *bytesProcessed = 1; - - if (octet <= 0x7f) - { - // Only one octet (ASCII range x00-7F) - code = text[0]; - } - else if ((octet & 0xe0) == 0xc0) - { - // Two octets - // [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 ((octet >= 0xc2) && (octet <= 0xdf)) - { - code = ((octet & 0x1f) << 6) | (octet1 & 0x3f); - *bytesProcessed = 2; - } - } - else if ((octet & 0xf0) == 0xe0) - { - // Three octets - unsigned char octet1 = text[1]; - unsigned char octet2 = '\0'; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - octet2 = text[2]; - - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence - - /* - [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) - [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) - [0]xED [1]x80-9F [2]UTF8-tail(x80-BF) - [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; } - - if ((octet >= 0xe0) && (0 <= 0xef)) - { - code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); - *bytesProcessed = 3; - } - } - else if ((octet & 0xf8) == 0xf0) - { - // Four octets - if (octet > 0xf4) return code; - - 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 - - octet2 = text[2]; - - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence - - octet3 = text[3]; - - if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // 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 - - if (octet >= 0xf0) - { - code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); - *bytesProcessed = 4; - } - } - - if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid - - return code; -} - - // Draw text (using default font) // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used // NOTE: chars spacing is proportional to fontSize @@ -907,12 +814,12 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f int next = 0; letter = GetNextCodepoint(&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) // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set 'next = 1' - if (letter == 0x3f) next = 1; + if (letter == 0x3f) next = 1; i += (next - 1); - + if (letter == '\n') { // NOTE: Fixed line spacing of 1.5 lines @@ -960,17 +867,17 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f int startLine = -1; // Index where to begin drawing (where a line begins) int endLine = -1; // Index where to stop drawing (where a line ends) int lastk = -1; // Holds last value of the character position - + for (int i = 0, k = 0; i < length; i++, k++) { int glyphWidth = 0; int next = 0; letter = GetNextCodepoint(&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) // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1 - if (letter == 0x3f) next = 1; + if (letter == 0x3f) next = 1; i += next - 1; if (letter != '\n') @@ -988,7 +895,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f if (state == MEASURE_STATE) { // TODO: there are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more - // See: http://jkorpela.fi/chars/spaces.html + // See: http://jkorpela.fi/chars/spaces.html if ((letter == ' ') || (letter == '\t') || (letter == '\n')) endLine = i; if ((textOffsetX + glyphWidth + 1) >= rec.width) @@ -1013,7 +920,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f textOffsetX = 0; i = startLine; glyphWidth = 0; - + // Save character position when we switch states int tmp = lastk; lastk = k - 1; @@ -1114,16 +1021,16 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing for (int i = 0; i < len; i++) { lenCounter++; - + int next = 0; letter = GetNextCodepoint(&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) // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1 - if (letter == 0x3f) next = 1; - i += next - 1; - + if (letter == 0x3f) next = 1; + i += next - 1; + if (letter != '\n') { if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX; @@ -1194,27 +1101,6 @@ unsigned int TextLength(const char *text) return length; } -// Returns total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found -// NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead -unsigned int TextCountCodepoints(const char *text) -{ - unsigned int len = 0; - char *ptr = (char *)&text[0]; - - while (*ptr != '\0') - { - int next = 0; - int letter = GetNextCodepoint(ptr, &next); - - if (letter == 0x3f) ptr += 1; - else ptr += next; - - len++; - } - - return len; -} - // Formatting of text with variables to 'embed' const char *TextFormat(const char *text, ...) { @@ -1338,14 +1224,14 @@ const char *TextJoin(const char **textList, int count, const char *delimiter) for (int i = 0; i < count; i++) { int textListLength = strlen(textList[i]); - + // Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH if ((totalLength + textListLength) < MAX_TEXT_BUFFER_LENGTH) { strcat(text, textList[i]); totalLength += textListLength; - - if ((delimiterLen > 0) && (i < (count - 1))) + + if ((delimiterLen > 0) && (i < (count - 1))) { strcat(text, delimiter); totalLength += delimiterLen; @@ -1362,22 +1248,20 @@ const char **TextSplit(const char *text, char delimiter, int *count) // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, // all used memory is static... it has some limitations: - // 1. Maximum number of possible split strings is set by MAX_SUBSTRINGS_COUNT - // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH + // 1. Maximum number of possible split strings is set by TEXTSPLIT_MAX_SUBSTRINGS_COUNT + // 2. Maximum size of text to split is TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH - #define MAX_SUBSTRINGS_COUNT 64 - - static const char *result[MAX_SUBSTRINGS_COUNT] = { NULL }; - static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; - memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); + static const char *result[TEXTSPLIT_MAX_SUBSTRINGS_COUNT] = { NULL }; + static char buffer[TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(buffer, 0, TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH); result[0] = buffer; int counter = 0; - + if (text != NULL) { counter = 1; - + // Count how many substrings we have on text and point to every one for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) { @@ -1389,7 +1273,7 @@ const char **TextSplit(const char *text, char delimiter, int *count) result[counter] = buffer + i + 1; counter++; - if (counter == MAX_SUBSTRINGS_COUNT) break; + if (counter == TEXTSPLIT_MAX_SUBSTRINGS_COUNT) break; } } } @@ -1492,6 +1376,200 @@ int TextToInteger(const char *text) return result; } + +// Encode codepoint into utf8 text (char array length returned as parameter) +RLAPI const char *TextToUtf8(int codepoint, int *byteLength) +{ + static char utf8[6] = { 0 }; + int length = 0; + + if (codepoint <= 0x7f) + { + utf8[0] = (char)codepoint; + length = 1; + } + else if (codepoint <= 0x7ff) + { + utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0); + utf8[1] = (char)((codepoint & 0x3f) | 0x80); + length = 2; + } + else if (codepoint <= 0xffff) + { + utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0); + utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[2] = (char)((codepoint & 0x3f) | 0x80); + length = 3; + } + else if (codepoint <= 0x10ffff) + { + utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0); + utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80); + utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[3] = (char)((codepoint & 0x3f) | 0x80); + length = 4; + } + + *byteLength = length; + + return utf8; +} + + +// Get all codepoints in a string, codepoints count returned by parameters +int *GetCodepoints(const char *text, int *count) +{ + static int codepoints[MAX_TEXT_UNICODE_CHARS] = { 0 }; + memset(codepoints, 0, MAX_TEXT_UNICODE_CHARS*sizeof(int)); + + int bytesProcessed = 0; + int textLength = strlen(text); + int codepointsCount = 0; + + for (int i = 0; i < textLength; codepointsCount++) + { + codepoints[codepointsCount] = GetNextCodepoint(text + i, &bytesProcessed); + i += bytesProcessed; + } + + *count = codepointsCount; + + return codepoints; +} + +// Returns total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found +// NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead +int GetCodepointsCount(const char *text) +{ + unsigned int len = 0; + char *ptr = (char *)&text[0]; + + while (*ptr != '\0') + { + int next = 0; + int letter = GetNextCodepoint(ptr, &next); + + if (letter == 0x3f) ptr += 1; + else ptr += next; + + len++; + } + + return len; +} + + +// Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found +// When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned +// 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 +// TODO: optimize this code for speed!! +int GetNextCodepoint(const char *text, int *bytesProcessed) +{ +/* + UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt + + Char. number range | UTF-8 octet sequence + (hexadecimal) | (binary) + --------------------+--------------------------------------------- + 0000 0000-0000 007F | 0xxxxxxx + 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx +*/ + // NOTE: on decode errors we return as soon as possible + + int code = 0x3f; // Codepoint (defaults to '?') + int octet = (unsigned char)(text[0]); // The first UTF8 octet + *bytesProcessed = 1; + + if (octet <= 0x7f) + { + // Only one octet (ASCII range x00-7F) + code = text[0]; + } + else if ((octet & 0xe0) == 0xc0) + { + // Two octets + // [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 ((octet >= 0xc2) && (octet <= 0xdf)) + { + code = ((octet & 0x1f) << 6) | (octet1 & 0x3f); + *bytesProcessed = 2; + } + } + else if ((octet & 0xf0) == 0xe0) + { + // Three octets + unsigned char octet1 = text[1]; + unsigned char octet2 = '\0'; + + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + + octet2 = text[2]; + + if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence + + /* + [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) + [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) + [0]xED [1]x80-9F [2]UTF8-tail(x80-BF) + [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; } + + if ((octet >= 0xe0) && (0 <= 0xef)) + { + code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); + *bytesProcessed = 3; + } + } + else if ((octet & 0xf8) == 0xf0) + { + // Four octets + if (octet > 0xf4) return code; + + 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 + + octet2 = text[2]; + + if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence + + octet3 = text[3]; + + if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // 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 + + if (octet >= 0xf0) + { + code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); + *bytesProcessed = 4; + } + } + + if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid + + return code; +} //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- @@ -1572,18 +1650,15 @@ static Font LoadBMFont(const char *fileName) TraceLog(LOG_DEBUG, "[%s] Font texture loading path: %s", fileName, texPath); Image imFont = LoadImage(texPath); - Image imFontAlpha = ImageCopy(imFont); if (imFont.format == UNCOMPRESSED_GRAYSCALE) { - for (int i = 0; i < imFontAlpha.width*imFontAlpha.height; i++) ((unsigned char *)imFontAlpha.data)[i] = 0xff; - - ImageAlphaMask(&imFontAlpha, imFont); - font.texture = LoadTextureFromImage(imFontAlpha); + // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel + ImageAlphaMask(&imFont, imFont); + for (int p = 0; p < (imFont.width*imFont.height*2); p += 2) ((unsigned char *)(imFont.data))[p] = 0xff; } - else font.texture = LoadTextureFromImage(imFont); - - UnloadImage(imFont); + + font.texture = LoadTextureFromImage(imFont); RL_FREE(texPath); @@ -1600,7 +1675,7 @@ static Font LoadBMFont(const char *fileName) fgets(buffer, MAX_BUFFER_SIZE, fntFile); sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); - + // Get character rectangle in the font atlas texture font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; @@ -1609,12 +1684,12 @@ static Font LoadBMFont(const char *fileName) font.chars[i].offsetX = charOffsetX; font.chars[i].offsetY = charOffsetY; font.chars[i].advanceX = charAdvanceX; - + // Fill character image data from imFont data - font.chars[i].image = ImageFromImage(imFontAlpha, font.recs[i]); + font.chars[i].image = ImageFromImage(imFont, font.recs[i]); } - UnloadImage(imFontAlpha); + UnloadImage(imFont); fclose(fntFile); diff --git a/src/textures.c b/src/textures.c index 1c7edac9e..ec08e3ac8 100644 --- a/src/textures.c +++ b/src/textures.c @@ -850,7 +850,7 @@ void ExportImageAsCode(Image image, const char *fileName) #define BYTES_TEXT_PER_LINE 20 FILE *txtFile = fopen(fileName, "wt"); - + if (txtFile != NULL) { char varFileName[256] = { 0 }; @@ -926,11 +926,11 @@ Image ImageCopy(Image image) Image ImageFromImage(Image image, Rectangle rec) { Image result = ImageCopy(image); - + #if defined(SUPPORT_IMAGE_MANIPULATION) ImageCrop(&result, rec); #endif - + return result; } @@ -1181,13 +1181,18 @@ void ImageAlphaMask(Image *image, Image alphaMask) // In case image is only grayscale, we just add alpha channel if (image->format == UNCOMPRESSED_GRAYSCALE) { - ImageFormat(image, UNCOMPRESSED_GRAY_ALPHA); + unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2); // Apply alpha mask to alpha channel - for (int i = 0, k = 1; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2) + for (int i = 0, k = 0; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2) { - ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i]; + data[k] = ((unsigned char *)image->data)[i]; + data[k + 1] = ((unsigned char *)mask.data)[i]; } + + RL_FREE(image->data); + image->data = data; + image->format = UNCOMPRESSED_GRAY_ALPHA; } else { @@ -1338,13 +1343,13 @@ void ImageCrop(Image *image, Rectangle crop) { // Security check to avoid program crash if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; - + // Security checks to validate crop rectangle if (crop.x < 0) { crop.width += crop.x; crop.x = 0; } if (crop.y < 0) { crop.height += crop.y; crop.y = 0; } if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x; if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y; - + if ((crop.x < image->width) && (crop.y < image->height)) { // Start the cropping process @@ -1818,7 +1823,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color } Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it - + // Crop source image to desired source rectangle (if required) if ((src.width != (int)srcRec.width) && (src.height != (int)srcRec.height)) ImageCrop(&srcCopy, srcRec); @@ -1827,7 +1832,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color { ImageResize(&srcCopy, (int)dstRec.width, (int)dstRec.height); } - + // Check that dstRec is inside dst image // Allow negative position within destination with cropping if (dstRec.x < 0) @@ -1836,7 +1841,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color dstRec.width = dstRec.width + dstRec.x; dstRec.x = 0; } - + if ((dstRec.x + dstRec.width) > dst->width) { ImageCrop(&srcCopy, (Rectangle) { 0, 0, dst->width - dstRec.x, dstRec.height }); @@ -1849,7 +1854,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color dstRec.height = dstRec.height + dstRec.y; dstRec.y = 0; } - + if ((dstRec.y + dstRec.height) > dst->height) { ImageCrop(&srcCopy, (Rectangle) { 0, 0, dstRec.width, dst->height - dstRec.y }); @@ -1875,7 +1880,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color fdst = ColorNormalize(dstPixels[j*(int)dst->width + i]); fsrc = ColorNormalize(srcPixels[(j - (int)dstRec.y)*(int)dstRec.width + (i - (int)dstRec.x)]); - + // Apply color tint to source image fsrc.x *= ftint.x; fsrc.y *= ftint.y; fsrc.z *= ftint.z; fsrc.w *= ftint.w; @@ -1938,16 +1943,16 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Create image to store text Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); - + for (int i = 0; i < length; i++) { int next = 0; letter = GetNextCodepoint(&text[i], &next); index = GetGlyphIndex(font, letter); - - if (letter == 0x3f) next = 1; + + if (letter == 0x3f) next = 1; i += (next - 1); - + if (letter == '\n') { // TODO: Support line break @@ -1957,7 +1962,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co if (letter != ' ') { ImageDraw(&imText, font.chars[index].image, (Rectangle){ 0, 0, font.chars[index].image.width, font.chars[index].image.height }, - (Rectangle){ (float)(positionX + font.chars[index].offsetX),(float)font.chars[index].offsetY, + (Rectangle){ (float)(positionX + font.chars[index].offsetX),(float)font.chars[index].offsetY, font.chars[index].image.width, font.chars[index].image.height }, tint); } @@ -1997,7 +2002,7 @@ void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color) ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y, rec.width, thick }, color); ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + thick, thick, rec.height - thick*2 }, color); ImageDrawRectangle(dst, (Rectangle){ rec.x + rec.width - thick, rec.y + thick, thick, rec.height - thick*2 }, color); - ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.height - thick, rec.width, thick }, color); + ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + rec.height - thick, rec.width, thick }, color); } // Draw text (default font) within an image (destination) @@ -2712,9 +2717,9 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V { float width = (float)texture.width; float height = (float)texture.height; - + bool flipX = false; - + if (sourceRec.width < 0) { flipX = true; sourceRec.width *= -1; } if (sourceRec.height < 0) sourceRec.y -= sourceRec.height; @@ -2976,12 +2981,12 @@ static Image LoadAnimatedGIF(const char *fileName, int *frames, int **delays) fseek(gifFile, 0L, SEEK_END); int size = ftell(gifFile); fseek(gifFile, 0L, SEEK_SET); - + unsigned char *buffer = (unsigned char *)RL_CALLOC(size, sizeof(char)); fread(buffer, sizeof(char), size, gifFile); - + fclose(gifFile); // Close file pointer - + int comp = 0; image.data = stbi_load_gif_from_memory(buffer, size, delays, &image.width, &image.height, frames, &comp, 4); diff --git a/src/utils.h b/src/utils.h index 1611b02ca..98c2f59c2 100644 --- a/src/utils.h +++ b/src/utils.h @@ -36,7 +36,7 @@ // Some basic Defines //---------------------------------------------------------------------------------- #if defined(PLATFORM_ANDROID) - #define fopen(name, mode) android_fopen(name, mode) + #define fopen(name, mode) android_fopen(name, mode) #endif //---------------------------------------------------------------------------------- @@ -86,8 +86,8 @@ typedef enum { typedef struct UWPMessage { UWPMessageType type; // Message type - - Vector2 paramVector0; // Vector parameters + + Vector2 paramVector0; // Vector parameters int paramInt0; // Int parameter int paramInt1; // Int parameter char paramChar0; // Char parameters