diff --git a/.gitignore b/.gitignore index 7cf6671f5..4b847bd0d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ Thumbs.db [Bb]in [Dd]ebug/ [Dd]ebug.win32/ +[Dd]ebug.DLL/ *.sbr *.sdf obj/ diff --git a/.travis.yml b/.travis.yml index ec4a8ebd7..c27b6582c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -120,7 +120,6 @@ script: -DBUILD_EXAMPLES=ON -DBUILD_GAMES=ON -DUSE_EXTERNAL_GLFW=$USE_EXTERNAL_GLFW -DUSE_WAYLAND=$WAYLAND - -DUSE_OPENAL_BACKEND=$OPENAL -DINCLUDE_EVERYTHING=ON .. - $RUNNER make VERBOSE=1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 35be10099..94688ea0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,21 +59,23 @@ To open new issue for raylib (bug, enhancement, discussion...), just try to foll Some people ported raylib to other languages in form of bindings or wrappers to the library, here is a list with the ones I'm aware of: - - [raylib C/C++ version (default)](https://github.com/raysan5/raylib) - - [raylib Lua binding](https://github.com/raysan5/raylib-lua) - - [raylib Go binding](https://github.com/gen2brain/raylib-go) - - [raylib Nim binding](https://gitlab.com/define-private-public/raylib-Nim) - - [raylib Crystal binding](https://gitlab.com/Zatherz/cray) - - [raylib Perl wrapper](https://metacpan.org/pod/Graphics::Raylib) - - [raylib Pascal binding](https://github.com/drezgames/raylib-pascal) - - [raylib flat-assembler Usage example](http://forum.raylib.com/index.php?p=/discussion/comment/425/#Comment_425) - - [raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib) - - [raylib C# binding](https://github.com/ChrisDill/Raylib-cs) - - [raylib C# binding](https://github.com/TheLumaio/RaylibSharp) - - [raylib Ruby binding](https://github.com/D3nX/raylib-ruby-ffi) - - [raylib Rust binding](https://github.com/deltaphc/raylib-rs) - - [raylib Python binding](https://github.com/overdev/raylibpy) - - [raylib Haskell binding](https://github.com/DevJac/raylib-haskell) + - [raylib](https://github.com/raysan5/raylib) : raylib **C/C++** version (default) + - [raylib-lua](https://github.com/raysan5/raylib-lua) : raylib **Lua** binding + - [raylib-go](https://github.com/gen2brain/raylib-go) : raylib **Go** binding + - [raylib-Nim](https://gitlab.com/define-private-public/raylib-Nim) : raylib **Nim** binding + - [cray](https://gitlab.com/Zatherz/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-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 + - [raylib-rs](https://github.com/deltaphc/raylib-rs) : raylib **Rust** binding + - [raylib-rust](https://github.com/dtcristo/raylib-rust) : raylib **Rust** binding + - [raylib-py](https://github.com/overdev/raylib-py) : raylib **Python** binding + - [raylib-haskell](https://github.com/DevJac/raylib-haskell) : raylib **Haskell** binding + - [raylib-java]() : raylib **Java** binding + - *[raylib flat-assembler Usage example](http://forum.raylib.com/index.php?p=/discussion/comment/425/#Comment_425)* + - *[raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)* Usually, raylib bindings follow the convention: `raylib-{language}` @@ -85,7 +87,7 @@ provide the icon/logo for that new language binding. If you have any doubt, don't hesitate to [contact me](mailto:ray@raylib.com)!. You can write me a direct mail but you can also contact me on the following networks: - - [raylib forum](http://forum.raylib.com/) - A good place for discussions or to ask for help. + - [raylib reddit](https://www.reddit.com/r/raylib/) - A good place for discussions or to ask for help. - [raylib Discord](https://discord.gg/VkzNHUE) - A direct communication channel for project discussions. - [raylib twitter](https://twitter.com/raysan5) - My personal twitter account, I usually post about raylib, you can send me PMs. - [raylib web](http://www.raylib.com/) - On top-right corner there is a bunch of networks where you can find me. diff --git a/LICENSE.md b/LICENSE.md index d317c2f3e..b66f47832 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -7,7 +7,7 @@ source code raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software: -Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) +Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) This software is provided "as-is", without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. @@ -41,9 +41,9 @@ The following fonts [provided with raylib](https://github.com/raysan5/raylib/tre 2d art ------ -[scarfy spritesheet](https://github.com/raysan5/raylib/blob/master/examples/textures/resources/scarfy.png) and [fudesumi image](https://github.com/raysan5/raylib/blob/master/examples/textures/resources/fudesumi.png) have been created by [Eiden Marsal](https://www.artstation.com/artist/marshall_z) and licensed as [Creative Commons Attribution-NonCommercial 3.0](https://creativecommons.org/licenses/by-nc/3.0/legalcode) +[scarfy spritesheet](https://github.com/raysan5/raylib/blob/master/examples/textures/resources/scarfy.png) and [fudesumi image](https://github.com/raysan5/raylib/blob/master/examples/textures/resources/fudesumi.png) have been created by [Eiden Marsal](https://www.artstation.com/artist/marshall_z) and are licensed as [Creative Commons Attribution-NonCommercial 3.0](https://creativecommons.org/licenses/by-nc/3.0/legalcode) 3d models --------- -[medieval city 3d models and textures](https://github.com/raysan5/raylib/tree/master/examples/models/resources/medieval) have been created by Alberto Cano and licensed as [Creative Commons Attribution-NonCommercial 4.0](https://creativecommons.org/licenses/by-nc/4.0/legalcode) +[medieval city 3d models and textures](https://github.com/raysan5/raylib/tree/master/examples/models/resources/medieval) have been created by Alberto Cano and are licensed as [Creative Commons Attribution-NonCommercial 4.0](https://creativecommons.org/licenses/by-nc/4.0/legalcode) diff --git a/README.md b/README.md index 2c4c7601d..7dbaaa6b5 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ features - **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h) - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) - **VR stereo rendering** support with configurable HMD device parameters - - Bindings to **Lua** ([raylib-lua](https://github.com/raysan5/raylib-lua)), **Go** ([raylib-go](https://github.com/gen2brain/raylib-go)) and more! + - Bindings to **Lua** ([raylib-lua](https://github.com/raysan5/raylib-lua)), **Go** ([raylib-go](https://github.com/gen2brain/raylib-go)) and [more](https://github.com/raysan5/raylib/blob/master/CONTRIBUTING.md#raylib-bindings)! raylib uses on its [core](https://github.com/raysan5/raylib/blob/master/src/core.c) module the outstanding [GLFW3](http://www.glfw.org/) library, embedded inside raylib in the form of [rglfw](https://github.com/raysan5/raylib/blob/master/src/rglfw.c) module, avoiding that way external dependencies. diff --git a/examples/Makefile b/examples/Makefile index a7a85661c..a26e0bbbf 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -375,6 +375,7 @@ EXAMPLES = \ core/core_2d_camera \ core/core_world_screen \ core/core_vr_simulator \ + core/core_multitouch \ shapes/shapes_logo_raylib \ shapes/shapes_basic_shapes \ shapes/shapes_colors_palette \ diff --git a/examples/core/core_multitouch.c b/examples/core/core_multitouch.c new file mode 100644 index 000000000..c059ac035 --- /dev/null +++ b/examples/core/core_multitouch.c @@ -0,0 +1,90 @@ +/******************************************************************************************* +* +* raylib [core] example - Multitouch input +* +* This example has been created using raylib 2.1 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example by Berni +* +********************************************************************************************/ + +#include "raylib.h" +#include + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - multitouch input"); + + Vector2 ballPosition = { -100.0f, -100.0f }; + Color ballColor; + int PressedCounter = 0; + Vector2 TouchPos; + char Str[16]; + + SetTargetFPS(60); + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + ballPosition = GetMousePosition(); + + ballColor = BEIGE; + + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) ballColor = MAROON; + if (IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) ballColor = LIME; + if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) ballColor = DARKBLUE; + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) PressedCounter = 10; + if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) PressedCounter = 10; + if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) PressedCounter = 10; + if(PressedCounter > 0) + PressedCounter--; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Multitouch + for (int i = 0; i < MAX_TOUCH_POINTS; ++i) + { + TouchPos = GetTouchPosition(i); // Get the touch point + + if( (TouchPos.x >= 0) && (TouchPos.y >= 0) ) // Make sure point is not (-1,-1) as this means there is no touch for it + { + DrawCircleV(TouchPos, 34, ORANGE); // Draw a circle there + + sprintf(Str,"%d",i); + DrawText(Str, TouchPos.x - 10, TouchPos.y - 70, 40, BLACK); // Also show its index number + } + } + + // Draw the normal mouse location + DrawCircleV(ballPosition, 30 + (PressedCounter * 3), ballColor); + + DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, DARKGRAY); + DrawText("touch the screen at multiple locations to get multiple balls", 10, 30, 20, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/core/core_multitouch.png b/examples/core/core_multitouch.png new file mode 100644 index 000000000..74284f82a Binary files /dev/null and b/examples/core/core_multitouch.png differ diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index 0dcf8c70f..88b0a6109 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -5,8 +5,7 @@ * This example has been created using raylib 1.8 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Example based on Berni work on Raspberry Pi: -* http://forum.raylib.com/index.php?p=/discussion/124/line-versus-triangle-drawing-order +* Example based on Berni work on Raspberry Pi. * * Copyright (c) 2017 Ramon Santamaria (@raysan5) * diff --git a/examples/physac/physics_restitution.c b/examples/physac/physics_restitution.c index d2ec49db8..197e6eb51 100644 --- a/examples/physac/physics_restitution.c +++ b/examples/physac/physics_restitution.c @@ -51,6 +51,9 @@ int main() circleC->restitution = 1; SetTargetFPS(60); + + // Restitution demo needs a very tiny physics time step for a proper simulation + SetPhysicsTimeStep(1.0/60.0/100 * 1000); //-------------------------------------------------------------------------------------- // Main game loop diff --git a/examples/shapes/shapes_colors_palette.png b/examples/shapes/shapes_colors_palette.png index dd3cf4a57..4073b6701 100644 Binary files a/examples/shapes/shapes_colors_palette.png and b/examples/shapes/shapes_colors_palette.png differ diff --git a/examples/text/resources/custom_alagard.png b/examples/text/resources/custom_alagard.png index c3eb63b71..634265557 100644 Binary files a/examples/text/resources/custom_alagard.png and b/examples/text/resources/custom_alagard.png differ diff --git a/examples/text/resources/custom_jupiter_crash.png b/examples/text/resources/custom_jupiter_crash.png index 451b591f1..2deaaeff7 100644 Binary files a/examples/text/resources/custom_jupiter_crash.png and b/examples/text/resources/custom_jupiter_crash.png differ diff --git a/examples/text/resources/custom_mecha.png b/examples/text/resources/custom_mecha.png index 59caab2cc..a8c9e06e6 100644 Binary files a/examples/text/resources/custom_mecha.png and b/examples/text/resources/custom_mecha.png differ diff --git a/examples/text/resources/fonts/alagard.png b/examples/text/resources/fonts/alagard.png index 3ac4bf1cf..c0c542731 100644 Binary files a/examples/text/resources/fonts/alagard.png and b/examples/text/resources/fonts/alagard.png differ diff --git a/examples/text/resources/fonts/alpha_beta.png b/examples/text/resources/fonts/alpha_beta.png index c362bfb18..8a0c27338 100644 Binary files a/examples/text/resources/fonts/alpha_beta.png and b/examples/text/resources/fonts/alpha_beta.png differ diff --git a/examples/text/resources/fonts/jupiter_crash.png b/examples/text/resources/fonts/jupiter_crash.png index 1f5172fbe..4972c02e9 100644 Binary files a/examples/text/resources/fonts/jupiter_crash.png and b/examples/text/resources/fonts/jupiter_crash.png differ diff --git a/examples/text/resources/fonts/mecha.png b/examples/text/resources/fonts/mecha.png index 8022d18c4..9213fa2d1 100644 Binary files a/examples/text/resources/fonts/mecha.png and b/examples/text/resources/fonts/mecha.png differ diff --git a/examples/text/resources/fonts/pixantiqua.png b/examples/text/resources/fonts/pixantiqua.png index ce422e7e7..17ad1ab7d 100644 Binary files a/examples/text/resources/fonts/pixantiqua.png and b/examples/text/resources/fonts/pixantiqua.png differ diff --git a/examples/text/resources/fonts/pixelplay.png b/examples/text/resources/fonts/pixelplay.png index bf8f8818f..fbf6430d6 100644 Binary files a/examples/text/resources/fonts/pixelplay.png and b/examples/text/resources/fonts/pixelplay.png differ diff --git a/examples/text/resources/fonts/romulus.png b/examples/text/resources/fonts/romulus.png index 46ccc3273..648aa8b06 100644 Binary files a/examples/text/resources/fonts/romulus.png and b/examples/text/resources/fonts/romulus.png differ diff --git a/examples/text/resources/fonts/setback.png b/examples/text/resources/fonts/setback.png index 086f3e27b..1630a7330 100644 Binary files a/examples/text/resources/fonts/setback.png and b/examples/text/resources/fonts/setback.png differ diff --git a/examples/textures/resources/custom_jupiter_crash.png b/examples/textures/resources/custom_jupiter_crash.png index 451b591f1..2deaaeff7 100644 Binary files a/examples/textures/resources/custom_jupiter_crash.png and b/examples/textures/resources/custom_jupiter_crash.png differ diff --git a/examples/textures/resources/scarfy.png b/examples/textures/resources/scarfy.png index beb5ffa06..4803ef777 100644 Binary files a/examples/textures/resources/scarfy.png and b/examples/textures/resources/scarfy.png differ diff --git a/games/drturtle/resources/fish.png b/games/drturtle/resources/fish.png index 894dd1cac..dca5956e5 100644 Binary files a/games/drturtle/resources/fish.png and b/games/drturtle/resources/fish.png differ diff --git a/games/drturtle/resources/gamera.png b/games/drturtle/resources/gamera.png index 57127c100..9c2fea71c 100644 Binary files a/games/drturtle/resources/gamera.png and b/games/drturtle/resources/gamera.png differ diff --git a/games/drturtle/resources/gframe.png b/games/drturtle/resources/gframe.png index 87796af23..5946589e6 100644 Binary files a/games/drturtle/resources/gframe.png and b/games/drturtle/resources/gframe.png differ diff --git a/games/drturtle/resources/komika.png b/games/drturtle/resources/komika.png index 174d85b92..2c2ab7142 100644 Binary files a/games/drturtle/resources/komika.png and b/games/drturtle/resources/komika.png differ diff --git a/games/drturtle/resources/mountains.png b/games/drturtle/resources/mountains.png index 169177031..f193f5047 100644 Binary files a/games/drturtle/resources/mountains.png and b/games/drturtle/resources/mountains.png differ diff --git a/games/drturtle/resources/orca.png b/games/drturtle/resources/orca.png index 09aed5741..b580ef072 100644 Binary files a/games/drturtle/resources/orca.png and b/games/drturtle/resources/orca.png differ diff --git a/games/drturtle/resources/sea.png b/games/drturtle/resources/sea.png index ad8f22ef0..34593536d 100644 Binary files a/games/drturtle/resources/sea.png and b/games/drturtle/resources/sea.png differ diff --git a/games/drturtle/resources/shark.png b/games/drturtle/resources/shark.png index 26c05ea5d..24a569574 100644 Binary files a/games/drturtle/resources/shark.png and b/games/drturtle/resources/shark.png differ diff --git a/games/drturtle/resources/sky.png b/games/drturtle/resources/sky.png index 5af8b6f70..e75715c92 100644 Binary files a/games/drturtle/resources/sky.png and b/games/drturtle/resources/sky.png differ diff --git a/games/drturtle/resources/swhale.png b/games/drturtle/resources/swhale.png index be1d0c5c7..5652928c0 100644 Binary files a/games/drturtle/resources/swhale.png and b/games/drturtle/resources/swhale.png differ diff --git a/games/drturtle/resources/title.png b/games/drturtle/resources/title.png index abe0f5b0d..9ddd0de30 100644 Binary files a/games/drturtle/resources/title.png and b/games/drturtle/resources/title.png differ diff --git a/games/drturtle/resources/turtle.png b/games/drturtle/resources/turtle.png index 4abe9f51f..52ce9691f 100644 Binary files a/games/drturtle/resources/turtle.png and b/games/drturtle/resources/turtle.png differ diff --git a/games/just_do/screens/screen_level06.c b/games/just_do/screens/screen_level06.c index a5536aa4a..ec72d70c1 100644 --- a/games/just_do/screens/screen_level06.c +++ b/games/just_do/screens/screen_level06.c @@ -132,7 +132,7 @@ void DrawLevel06Screen(void) DrawRectangleRec(movingRecs[i], GRAY); } - if (!done & (mouseOverNum >= 0)) DrawRectangleLines(movingRecs[mouseOverNum].x - 5, movingRecs[mouseOverNum].y - 5, movingRecs[mouseOverNum].width + 10, movingRecs[mouseOverNum].height + 10, Fade(LIGHTGRAY, 0.8f)); + if (!done && (mouseOverNum >= 0)) DrawRectangleLines(movingRecs[mouseOverNum].x - 5, movingRecs[mouseOverNum].y - 5, movingRecs[mouseOverNum].width + 10, movingRecs[mouseOverNum].height + 10, Fade(LIGHTGRAY, 0.8f)); if (levelFinished) { diff --git a/games/koala_seasons/screens/screen_gameplay.c b/games/koala_seasons/screens/screen_gameplay.c index 6bbcfaafc..4d8ff04d6 100644 --- a/games/koala_seasons/screens/screen_gameplay.c +++ b/games/koala_seasons/screens/screen_gameplay.c @@ -954,7 +954,7 @@ void UpdateGameplayScreen(void) playerActive = false; killer = 5; } - else if (CheckCollisionRecs(bee, player) && (state == FINALFORM) && (state != KICK)) + else if (CheckCollisionRecs(bee, player) && (state == FINALFORM)) { isHitBee = true; beeVelocity = 8; diff --git a/games/light_my_ritual/resources/font_arcadian.png b/games/light_my_ritual/resources/font_arcadian.png index 5c3df51ac..7abcc197f 100644 Binary files a/games/light_my_ritual/resources/font_arcadian.png and b/games/light_my_ritual/resources/font_arcadian.png differ diff --git a/games/light_my_ritual/resources/textures/back_title.png b/games/light_my_ritual/resources/textures/back_title.png index 549212478..c6cf96984 100644 Binary files a/games/light_my_ritual/resources/textures/back_title.png and b/games/light_my_ritual/resources/textures/back_title.png differ diff --git a/games/light_my_ritual/resources/textures/background.png b/games/light_my_ritual/resources/textures/background.png index 367bdd30c..78dd3e212 100644 Binary files a/games/light_my_ritual/resources/textures/background.png and b/games/light_my_ritual/resources/textures/background.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_i_off.png b/games/light_my_ritual/resources/textures/circle_level_i_off.png index 7961af7d4..4fe966189 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_i_off.png and b/games/light_my_ritual/resources/textures/circle_level_i_off.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_i_on.png b/games/light_my_ritual/resources/textures/circle_level_i_on.png index 1f2177349..3ac21705c 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_i_on.png and b/games/light_my_ritual/resources/textures/circle_level_i_on.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_ii_off.png b/games/light_my_ritual/resources/textures/circle_level_ii_off.png index 642adc3be..d97b7df17 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_ii_off.png and b/games/light_my_ritual/resources/textures/circle_level_ii_off.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_ii_on.png b/games/light_my_ritual/resources/textures/circle_level_ii_on.png index 93b71b3a5..c4a1d710f 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_ii_on.png and b/games/light_my_ritual/resources/textures/circle_level_ii_on.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_iii_off.png b/games/light_my_ritual/resources/textures/circle_level_iii_off.png index daf50be95..fe92c4f90 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_iii_off.png and b/games/light_my_ritual/resources/textures/circle_level_iii_off.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_iii_on.png b/games/light_my_ritual/resources/textures/circle_level_iii_on.png index e2256e95c..2c986595e 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_iii_on.png and b/games/light_my_ritual/resources/textures/circle_level_iii_on.png differ diff --git a/games/light_my_ritual/resources/textures/enemy.png b/games/light_my_ritual/resources/textures/enemy.png index dc0a911d2..7f51ab59a 100644 Binary files a/games/light_my_ritual/resources/textures/enemy.png and b/games/light_my_ritual/resources/textures/enemy.png differ diff --git a/games/light_my_ritual/resources/textures/foreground_level_i.png b/games/light_my_ritual/resources/textures/foreground_level_i.png index 32b96740d..58a5797a9 100644 Binary files a/games/light_my_ritual/resources/textures/foreground_level_i.png and b/games/light_my_ritual/resources/textures/foreground_level_i.png differ diff --git a/games/light_my_ritual/resources/textures/foreground_level_ii.png b/games/light_my_ritual/resources/textures/foreground_level_ii.png index 44f26003a..b9e30648c 100644 Binary files a/games/light_my_ritual/resources/textures/foreground_level_ii.png and b/games/light_my_ritual/resources/textures/foreground_level_ii.png differ diff --git a/games/light_my_ritual/resources/textures/foreground_level_iii.png b/games/light_my_ritual/resources/textures/foreground_level_iii.png index 0fc039d72..054aa8dcf 100644 Binary files a/games/light_my_ritual/resources/textures/foreground_level_iii.png and b/games/light_my_ritual/resources/textures/foreground_level_iii.png differ diff --git a/games/light_my_ritual/resources/textures/light.png b/games/light_my_ritual/resources/textures/light.png index 5d08326f8..98829c76a 100644 Binary files a/games/light_my_ritual/resources/textures/light.png and b/games/light_my_ritual/resources/textures/light.png differ diff --git a/games/light_my_ritual/resources/textures/light_glow.png b/games/light_my_ritual/resources/textures/light_glow.png index d31356c6d..a0bc845b7 100644 Binary files a/games/light_my_ritual/resources/textures/light_glow.png and b/games/light_my_ritual/resources/textures/light_glow.png differ diff --git a/games/light_my_ritual/resources/textures/light_ray.png b/games/light_my_ritual/resources/textures/light_ray.png index f9f877fa4..68c9e766b 100644 Binary files a/games/light_my_ritual/resources/textures/light_ray.png and b/games/light_my_ritual/resources/textures/light_ray.png differ diff --git a/games/light_my_ritual/resources/textures/msg_ritual.png b/games/light_my_ritual/resources/textures/msg_ritual.png index 73f14d484..fa256cc70 100644 Binary files a/games/light_my_ritual/resources/textures/msg_ritual.png and b/games/light_my_ritual/resources/textures/msg_ritual.png differ diff --git a/games/light_my_ritual/resources/textures/player.png b/games/light_my_ritual/resources/textures/player.png index d6849478c..637f1a253 100644 Binary files a/games/light_my_ritual/resources/textures/player.png and b/games/light_my_ritual/resources/textures/player.png differ diff --git a/games/light_my_ritual/resources/textures/time_over.png b/games/light_my_ritual/resources/textures/time_over.png index 9f7935bea..971334ab2 100644 Binary files a/games/light_my_ritual/resources/textures/time_over.png and b/games/light_my_ritual/resources/textures/time_over.png differ diff --git a/games/light_my_ritual/resources/textures/title.png b/games/light_my_ritual/resources/textures/title.png index 55eb593dd..a1a0c86eb 100644 Binary files a/games/light_my_ritual/resources/textures/title.png and b/games/light_my_ritual/resources/textures/title.png differ diff --git a/games/skully_escape/resources/textures/background_aisle01.png b/games/skully_escape/resources/textures/background_aisle01.png index 482056f8d..32fe5b699 100644 Binary files a/games/skully_escape/resources/textures/background_aisle01.png and b/games/skully_escape/resources/textures/background_aisle01.png differ diff --git a/games/skully_escape/resources/textures/doors.png b/games/skully_escape/resources/textures/doors.png index f5a75d058..ff33169d9 100644 Binary files a/games/skully_escape/resources/textures/doors.png and b/games/skully_escape/resources/textures/doors.png differ diff --git a/games/skully_escape/resources/textures/monster_chair_left.png b/games/skully_escape/resources/textures/monster_chair_left.png index b3ac070f3..a47740324 100644 Binary files a/games/skully_escape/resources/textures/monster_chair_left.png and b/games/skully_escape/resources/textures/monster_chair_left.png differ diff --git a/games/skully_escape/resources/textures/monster_chair_right.png b/games/skully_escape/resources/textures/monster_chair_right.png index d29cc0211..8d3007a3c 100644 Binary files a/games/skully_escape/resources/textures/monster_chair_right.png and b/games/skully_escape/resources/textures/monster_chair_right.png differ diff --git a/games/skully_escape/resources/textures/monster_lamp_left.png b/games/skully_escape/resources/textures/monster_lamp_left.png index 3b31b77f5..d95dd2486 100644 Binary files a/games/skully_escape/resources/textures/monster_lamp_left.png and b/games/skully_escape/resources/textures/monster_lamp_left.png differ diff --git a/games/skully_escape/resources/textures/monster_lamp_right.png b/games/skully_escape/resources/textures/monster_lamp_right.png index 26491866b..81bbc6d6a 100644 Binary files a/games/skully_escape/resources/textures/monster_lamp_right.png and b/games/skully_escape/resources/textures/monster_lamp_right.png differ diff --git a/games/skully_escape/resources/textures/monster_picture.png b/games/skully_escape/resources/textures/monster_picture.png index 29549895b..f4f9d84be 100644 Binary files a/games/skully_escape/resources/textures/monster_picture.png and b/games/skully_escape/resources/textures/monster_picture.png differ diff --git a/games/skully_escape/resources/textures/monster_window.png b/games/skully_escape/resources/textures/monster_window.png index 47d70a793..4a20a5c64 100644 Binary files a/games/skully_escape/resources/textures/monster_window.png and b/games/skully_escape/resources/textures/monster_window.png differ diff --git a/games/skully_escape/resources/textures/skully.png b/games/skully_escape/resources/textures/skully.png index 9ce6cd583..5fec59397 100644 Binary files a/games/skully_escape/resources/textures/skully.png and b/games/skully_escape/resources/textures/skully.png differ diff --git a/games/transmission/resources/textures/ending_background.png b/games/transmission/resources/textures/ending_background.png index 6577be63b..208886588 100644 Binary files a/games/transmission/resources/textures/ending_background.png and b/games/transmission/resources/textures/ending_background.png differ diff --git a/games/transmission/resources/textures/message_background.png b/games/transmission/resources/textures/message_background.png index 78c00a597..fb544796e 100644 Binary files a/games/transmission/resources/textures/message_background.png and b/games/transmission/resources/textures/message_background.png differ diff --git a/games/transmission/resources/textures/message_vignette.png b/games/transmission/resources/textures/message_vignette.png index 72234c594..6ef64b6bc 100644 Binary files a/games/transmission/resources/textures/message_vignette.png and b/games/transmission/resources/textures/message_vignette.png differ diff --git a/games/transmission/resources/textures/mission_background.png b/games/transmission/resources/textures/mission_background.png index 4efe58155..da5f3f67a 100644 Binary files a/games/transmission/resources/textures/mission_background.png and b/games/transmission/resources/textures/mission_background.png differ diff --git a/games/transmission/resources/textures/mission_backline.png b/games/transmission/resources/textures/mission_backline.png index 2a89d80a6..6123a9845 100644 Binary files a/games/transmission/resources/textures/mission_backline.png and b/games/transmission/resources/textures/mission_backline.png differ diff --git a/games/transmission/resources/textures/title_background.png b/games/transmission/resources/textures/title_background.png index c90d880d5..e45bbec80 100644 Binary files a/games/transmission/resources/textures/title_background.png and b/games/transmission/resources/textures/title_background.png differ diff --git a/games/transmission/resources/textures/title_ribbon.png b/games/transmission/resources/textures/title_ribbon.png index 1526eba17..f75438dd6 100644 Binary files a/games/transmission/resources/textures/title_ribbon.png and b/games/transmission/resources/textures/title_ribbon.png differ diff --git a/games/transmission/resources/textures/words_base.png b/games/transmission/resources/textures/words_base.png index a21390301..e79e9815e 100644 Binary files a/games/transmission/resources/textures/words_base.png and b/games/transmission/resources/textures/words_base.png differ diff --git a/games/transmission/screens/screen_ending.c b/games/transmission/screens/screen_ending.c index 0aba5f014..d0cf7d899 100644 --- a/games/transmission/screens/screen_ending.c +++ b/games/transmission/screens/screen_ending.c @@ -110,10 +110,12 @@ void InitEndingScreen(void) { // WARNING: It fails if the last sentence word has a '.' after space char *title = StringReplace(headline, messageWords[i].text, codingWords[messageWords[i].id]); - - strcpy(headline, title); // Base headline updated - - if (title != NULL) free(title); + + if (title != NULL) + { + strcpy(headline, title); // Base headline updated + free(title); + } } } diff --git a/games/wave_collector/resources/textures/background.png b/games/wave_collector/resources/textures/background.png index f8613f829..b4f767f75 100644 Binary files a/games/wave_collector/resources/textures/background.png and b/games/wave_collector/resources/textures/background.png differ diff --git a/games/wave_collector/resources/textures/background_gameplay.png b/games/wave_collector/resources/textures/background_gameplay.png index c6a510705..9be7d17de 100644 Binary files a/games/wave_collector/resources/textures/background_gameplay.png and b/games/wave_collector/resources/textures/background_gameplay.png differ diff --git a/games/wave_collector/resources/textures/background_title.png b/games/wave_collector/resources/textures/background_title.png index 01fc46d3e..19546d863 100644 Binary files a/games/wave_collector/resources/textures/background_title.png and b/games/wave_collector/resources/textures/background_title.png differ diff --git a/games/wave_collector/resources/textures/icon_synchro.png b/games/wave_collector/resources/textures/icon_synchro.png index b71728237..841f826b7 100644 Binary files a/games/wave_collector/resources/textures/icon_synchro.png and b/games/wave_collector/resources/textures/icon_synchro.png differ diff --git a/games/wave_collector/resources/textures/icon_warp.png b/games/wave_collector/resources/textures/icon_warp.png index 2a5eb7bb7..e91f43d4a 100644 Binary files a/games/wave_collector/resources/textures/icon_warp.png and b/games/wave_collector/resources/textures/icon_warp.png differ diff --git a/games/wave_collector/resources/textures/lose.png b/games/wave_collector/resources/textures/lose.png index f01c02848..ba69a4c28 100644 Binary files a/games/wave_collector/resources/textures/lose.png and b/games/wave_collector/resources/textures/lose.png differ diff --git a/games/wave_collector/resources/textures/player.png b/games/wave_collector/resources/textures/player.png index c8913a8f1..86b843b29 100644 Binary files a/games/wave_collector/resources/textures/player.png and b/games/wave_collector/resources/textures/player.png differ diff --git a/games/wave_collector/resources/textures/sample_big.png b/games/wave_collector/resources/textures/sample_big.png index b4c4be975..6fb20b638 100644 Binary files a/games/wave_collector/resources/textures/sample_big.png and b/games/wave_collector/resources/textures/sample_big.png differ diff --git a/games/wave_collector/resources/textures/sample_small.png b/games/wave_collector/resources/textures/sample_small.png index 7b624f7f0..5fa7d01ba 100644 Binary files a/games/wave_collector/resources/textures/sample_small.png and b/games/wave_collector/resources/textures/sample_small.png differ diff --git a/games/wave_collector/resources/textures/win.png b/games/wave_collector/resources/textures/win.png index d7afdc25d..7d539f66b 100644 Binary files a/games/wave_collector/resources/textures/win.png and b/games/wave_collector/resources/textures/win.png differ diff --git a/logo/raylib.ico b/logo/raylib.ico index afeb12b91..0cedcc55c 100644 Binary files a/logo/raylib.ico and b/logo/raylib.ico differ diff --git a/logo/raylib_180x180.png b/logo/raylib_180x180.png new file mode 100644 index 000000000..1828fb332 Binary files /dev/null and b/logo/raylib_180x180.png differ diff --git a/logo/raylib_36x36.png b/logo/raylib_36x36.png index cc72255f4..1346789bf 100644 Binary files a/logo/raylib_36x36.png and b/logo/raylib_36x36.png differ diff --git a/logo/raylib_512x512.png b/logo/raylib_512x512.png index 0b8c48ec5..599549115 100644 Binary files a/logo/raylib_512x512.png and b/logo/raylib_512x512.png differ diff --git a/logo/raylib_72x72.png b/logo/raylib_72x72.png index 34a6155e6..b1c1f553d 100644 Binary files a/logo/raylib_72x72.png and b/logo/raylib_72x72.png differ diff --git a/logo/raylib_96x96.png b/logo/raylib_96x96.png index f2774d221..d211d8f46 100644 Binary files a/logo/raylib_96x96.png and b/logo/raylib_96x96.png differ diff --git a/projects/VS2017/examples/core_basic_window.vcxproj b/projects/VS2017/examples/core_basic_window.vcxproj index e7f28fb96..48e06e448 100644 --- a/projects/VS2017/examples/core_basic_window.vcxproj +++ b/projects/VS2017/examples/core_basic_window.vcxproj @@ -98,7 +98,7 @@ Disabled WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) Console @@ -115,7 +115,7 @@ Disabled WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) Console @@ -133,7 +133,7 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -155,7 +155,7 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2017/examples/core_basic_window_cpp.vcxproj b/projects/VS2017/examples/core_basic_window_cpp.vcxproj index 0467ee225..39a2aee77 100644 --- a/projects/VS2017/examples/core_basic_window_cpp.vcxproj +++ b/projects/VS2017/examples/core_basic_window_cpp.vcxproj @@ -97,7 +97,7 @@ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsCpp @@ -114,7 +114,7 @@ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsCpp @@ -134,7 +134,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) CompileAsCpp - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) Console @@ -155,7 +155,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) CompileAsCpp - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) Console diff --git a/raylib.rc.o b/raylib.rc.o index 28005c734..fa27177d2 100644 Binary files a/raylib.rc.o and b/raylib.rc.o differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c86cc0e79..1b1d0d06d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,13 +45,8 @@ endif() add_definitions("-DRAYLIB_CMAKE=1") if(USE_AUDIO) - if (NOT USE_OPENAL_BACKEND) - file(GLOB mini_al external/mini_al.c) - MESSAGE(STATUS "Audio Backend: mini_al") - else() - find_package(OpenAL REQUIRED) - MESSAGE(STATUS "Audio Backend: OpenAL") - endif() + file(GLOB mini_al external/mini_al.c) + MESSAGE(STATUS "Audio Backend: mini_al") file(GLOB stb_vorbis external/stb_vorbis.c) set(sources ${raylib_sources} ${mini_al} ${stb_vorbis}) else() @@ -73,6 +68,10 @@ if(${PLATFORM} MATCHES "Desktop") find_library(OPENGL_LIBRARY OpenGL) set(LIBS_PRIVATE ${OPENGL_LIBRARY}) link_libraries("${LIBS_PRIVATE}") + if (NOT CMAKE_SYSTEM STRLESS "Darwin-18.0.0") + add_definitions(-DGL_SILENCE_DEPRECATION) + MESSAGE(AUTHOR_WARNING "OpenGL is deprecated starting with macOS 10.14 (Mojave)!") + endif() elseif(WIN32) add_definitions(-D_CRT_SECURE_NO_WARNINGS) else() @@ -131,7 +130,7 @@ if (${OPENGL_VERSION}) elseif (${OPENGL_VERSION} MATCHES "ES 2.0") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") endif() - if (${SUGGESTED_GRAPHICS} AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}") + if ("${SUGGESTED_GRAPHICS}" AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}") message(WARNING "You are overriding the suggested GRAPHICS=${SUGGESTED_GRAPHICS} with ${GRAPHICS}! This may fail") endif() endif() @@ -146,6 +145,7 @@ include(LibraryPathToLinkerFlags) library_path_to_linker_flags(__PKG_CONFIG_LIBS_PRIVATE "${LIBS_PRIVATE}") if(STATIC) + MESSAGE(STATUS "Building raylib static library") if(${PLATFORM} MATCHES "Web") set(CMAKE_STATIC_LIBRARY_SUFFIX ".bc") endif() @@ -182,6 +182,7 @@ endif(STATIC) if(SHARED) + MESSAGE(STATUS "Building raylib shared library") add_library(raylib SHARED ${sources}) target_compile_definitions(raylib diff --git a/src/CMakeOptions.txt b/src/CMakeOptions.txt index 84643b284..eee3f1a97 100644 --- a/src/CMakeOptions.txt +++ b/src/CMakeOptions.txt @@ -12,11 +12,6 @@ option(SHARED "Build raylib as a dynamic library" OFF) option(STATIC "Build raylib as a static library" ON) option(MACOS_FATLIB "Build fat library for both i386 and x86_64 on macOS" OFF) option(USE_AUDIO "Build raylib with audio module" ON) -if(${PLATFORM} MATCHES "Web") - cmake_dependent_option(USE_OPENAL_BACKEND "Link raylib with openAL instead of mini-al" ON "USE_AUDIO" OFF) -else() - cmake_dependent_option(USE_OPENAL_BACKEND "Link raylib with openAL instead of mini-al" OFF "USE_AUDIO" OFF) -endif() enum_option(USE_EXTERNAL_GLFW "OFF;IF_POSSIBLE;ON" "Link raylib against system GLFW instead of embedded one") if(UNIX AND NOT APPLE) @@ -84,6 +79,14 @@ if(NOT (STATIC OR SHARED)) message(FATAL_ERROR "Nothing to do if both -DSHARED=OFF and -DSTATIC=OFF...") endif() +if (DEFINED BUILD_SHARED_LIBS) + set(SHARED ${BUILD_SHARED_LIBS}) + if (${BUILD_SHARED_LIBS}) + set(STATIC OFF) + else() + set(STATIC ON) + endif() +endif() if(DEFINED SHARED_RAYLIB) set(SHARED ${SHARED_RAYLIB}) message(DEPRECATION "-DSHARED_RAYLIB is deprecated. Please use -DSHARED instead.") diff --git a/src/Makefile b/src/Makefile index ac4b15b4f..0cfc82c16 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,14 +63,6 @@ RAYLIB_BUILD_MODE ?= RELEASE # NOTE: Some programs like tools could not require audio support INCLUDE_AUDIO_MODULE ?= TRUE -# Use OpenAL Soft backend for audio -USE_OPENAL_BACKEND ?= FALSE - -# OpenAL Soft audio backend forced on HTML5 and OSX (see below) -ifeq ($(PLATFORM),PLATFORM_WEB) - USE_OPENAL_BACKEND = TRUE -endif - # Use external GLFW library instead of rglfw module # TODO: Review usage of examples on Linux. USE_EXTERNAL_GLFW ?= FALSE @@ -154,13 +146,6 @@ endif # RAYLIB_PATH ?= /home/pi/raylib #endif -# Force OpenAL Soft audio backend for OSX platform -# NOTE 1: mini_al library does not support CoreAudio yet -# NOTE 2: Required OpenAL libraries should be available on OSX -ifeq ($(PLATFORM_OS),OSX) - USE_OPENAL_BACKEND = TRUE -endif - ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables EMSDK_PATH = C:/emsdk @@ -343,11 +328,6 @@ ifeq ($(RAYLIB_LIBTYPE),SHARED) CFLAGS += -fPIC -DBUILD_LIBTYPE_SHARED endif -# Use OpenAL Soft backend instead of mini_al -ifeq ($(USE_OPENAL_BACKEND),TRUE) - CFLAGS += -DUSE_OPENAL_BACKEND -endif - # Use Wayland display on Linux desktop ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS), LINUX) @@ -359,7 +339,7 @@ endif # Define include paths for required headers # NOTE: Several external required libraries (stb and others) -INCLUDE_PATHS = -I. -Iexternal -Iexternal/glfw/include +INCLUDE_PATHS = -I. -Iexternal/glfw/include ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),BSD) @@ -426,9 +406,7 @@ endif ifeq ($(INCLUDE_AUDIO_MODULE),TRUE) OBJS += audio.o OBJS += stb_vorbis.o - ifeq ($(USE_OPENAL_BACKEND),FALSE) - OBJS += mini_al.o - endif + OBJS += mini_al.o endif ifeq ($(PLATFORM),PLATFORM_ANDROID) diff --git a/src/audio.c b/src/audio.c index 9e44d7092..f0362b2d0 100644 --- a/src/audio.c +++ b/src/audio.c @@ -16,9 +16,6 @@ * Define to use the module as standalone library (independently of raylib). * Required types and functions are defined in the same module. * -* #define USE_OPENAL_BACKEND -* Use OpenAL Soft audio backend -* * #define SUPPORT_FILEFORMAT_WAV * #define SUPPORT_FILEFORMAT_OGG * #define SUPPORT_FILEFORMAT_XM @@ -82,25 +79,9 @@ #include "utils.h" // Required for: fopen() Android mapping #endif -#if !defined(USE_OPENAL_BACKEND) - #define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. -#endif - -#include "external/mini_al.h" // Implemented in mini_al.c. Cannot implement this here because it conflicts with Win32 APIs such as CloseWindow(), etc. - -#if !defined(USE_MINI_AL) || (USE_MINI_AL == 0) - #if defined(__APPLE__) - #include "OpenAL/al.h" // OpenAL basic header - #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) - #else - #include "AL/al.h" // OpenAL basic header - #include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) - //#include "AL/alext.h" // OpenAL extensions header, required for AL_EXT_FLOAT32 and AL_EXT_MCFORMATS - #endif - - // OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples - // OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1) -#endif +#include "external/mini_al.h" // mini_al audio library + // NOTE: Cannot be implement here because it conflicts with + // Win32 APIs: Rectangle, CloseWindow(), ShowCursor(), PlaySoundA() #include // Required for: malloc(), free() #include // Required for: strcmp(), strncmp() @@ -132,7 +113,7 @@ #include "external/dr_mp3.h" // MP3 loading functions #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) #undef bool #endif @@ -147,15 +128,6 @@ // In case of music-stalls, just increase this number #define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. 16bit, Mono: 8Kb) -// Support uncompressed PCM data in 32-bit float IEEE format -// NOTE: This definition is included in "AL/alext.h", but some OpenAL implementations -// could not provide the extensions header (Android), so its defined here -#if !defined(AL_EXT_float32) - #define AL_EXT_float32 1 - #define AL_FORMAT_MONO_FLOAT32 0x10010 - #define AL_FORMAT_STEREO_FLOAT32 0x10011 -#endif - //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -233,8 +205,6 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo //---------------------------------------------------------------------------------- // mini_al AudioBuffer Functionality //---------------------------------------------------------------------------------- -#if USE_MINI_AL - #define DEVICE_FORMAT mal_format_f32 #define DEVICE_CHANNELS 2 #define DEVICE_SAMPLE_RATE 44100 @@ -461,7 +431,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi mal_uint32 totalFramesRemaining = (frameCount - framesRead); if (totalFramesRemaining > 0) { - memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); + memset((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); // For static buffers we can fill the remaining frames with silence for safety, but we don't want // to report those frames as "read". The reason for this is that the caller uses the return value @@ -487,7 +457,6 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 f } } } -#endif //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Device initialization and Closing @@ -495,7 +464,6 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 f // Initialize audio device void InitAudioDevice(void) { -#if USE_MINI_AL // Context. mal_context_config contextConfig = mal_context_config_init(OnLog); mal_result result = mal_context_init(NULL, 0, &contextConfig, &context); @@ -545,45 +513,11 @@ void InitAudioDevice(void) TraceLog(LOG_INFO, "Audio buffer size: %d", device.bufferSizeInFrames); isAudioInitialized = MAL_TRUE; -#else - // Open and initialize a device with default settings - ALCdevice *device = alcOpenDevice(NULL); - - if (!device) TraceLog(LOG_ERROR, "Audio device could not be opened"); - else - { - ALCcontext *context = alcCreateContext(device, NULL); - - if ((context == NULL) || (alcMakeContextCurrent(context) == ALC_FALSE)) - { - if (context != NULL) alcDestroyContext(context); - - alcCloseDevice(device); - - TraceLog(LOG_ERROR, "Could not initialize audio context"); - } - else - { - TraceLog(LOG_INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER)); - - // Listener definition (just for 2D) - alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f); - alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f); - alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f); - - alListenerf(AL_GAIN, 1.0f); - - if (alIsExtensionPresent("AL_EXT_float32")) TraceLog(LOG_INFO, "[EXTENSION] AL_EXT_float32 supported"); - else TraceLog(LOG_INFO, "[EXTENSION] AL_EXT_float32 not supported"); - } - } -#endif } // Close the audio device for all contexts void CloseAudioDevice(void) { -#if USE_MINI_AL if (!isAudioInitialized) { TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized"); @@ -593,18 +527,6 @@ void CloseAudioDevice(void) mal_mutex_uninit(&audioLock); mal_device_uninit(&device); mal_context_uninit(&context); -#else - ALCdevice *device; - ALCcontext *context = alcGetCurrentContext(); - - if (context == NULL) TraceLog(LOG_WARNING, "Could not get current audio context for closing"); - - device = alcGetContextsDevice(context); - - alcMakeContextCurrent(NULL); - alcDestroyContext(context); - alcCloseDevice(device); -#endif TraceLog(LOG_INFO, "Audio device closed successfully"); } @@ -612,20 +534,7 @@ void CloseAudioDevice(void) // Check if device has been initialized successfully bool IsAudioDeviceReady(void) { -#if USE_MINI_AL return isAudioInitialized; -#else - ALCcontext *context = alcGetCurrentContext(); - - if (context == NULL) return false; - else - { - ALCdevice *device = alcGetContextsDevice(context); - - if (device == NULL) return false; - else return true; - } -#endif } // Set master volume (listener) @@ -634,17 +543,13 @@ void SetMasterVolume(float volume) if (volume < 0.0f) volume = 0.0f; else if (volume > 1.0f) volume = 1.0f; -#if USE_MINI_AL masterVolume = volume; -#else - alListenerf(AL_GAIN, volume); -#endif } //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Buffer management //---------------------------------------------------------------------------------- -#if USE_MINI_AL + // Create a new audio buffer. Initially filled with silence AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage) { @@ -843,7 +748,6 @@ void UntrackAudioBuffer(AudioBuffer *audioBuffer) mal_mutex_unlock(&audioLock); } -#endif //---------------------------------------------------------------------------------- // Module Functions Definition - Sounds loading and playing (.WAV) @@ -909,7 +813,6 @@ Sound LoadSoundFromWave(Wave wave) if (wave.data != NULL) { -#if USE_MINI_AL // When using mini_al 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: // @@ -931,61 +834,6 @@ Sound LoadSoundFromWave(Wave wave) if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed"); sound.audioBuffer = audioBuffer; -#else - ALenum format = 0; - - // The OpenAL format is worked out by looking at the number of channels and the sample size (bits per sample) - if (wave.channels == 1) - { - switch (wave.sampleSize) - { - case 8: format = AL_FORMAT_MONO8; break; - case 16: format = AL_FORMAT_MONO16; break; - case 32: format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(LOG_WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; - } - } - else if (wave.channels == 2) - { - switch (wave.sampleSize) - { - case 8: format = AL_FORMAT_STEREO8; break; - case 16: format = AL_FORMAT_STEREO16; break; - case 32: format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(LOG_WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; - } - } - else TraceLog(LOG_WARNING, "Wave number of channels not supported: %i", wave.channels); - - // Create an audio source - ALuint source; - alGenSources(1, &source); // Generate pointer to audio source - - alSourcef(source, AL_PITCH, 1.0f); - alSourcef(source, AL_GAIN, 1.0f); - alSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f); - alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - alSourcei(source, AL_LOOPING, AL_FALSE); - - // Convert loaded data to OpenAL buffer - //---------------------------------------- - ALuint buffer; - alGenBuffers(1, &buffer); // Generate pointer to buffer - - unsigned int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; // Size in bytes - - // Upload sound data to buffer - alBufferData(buffer, format, wave.data, dataSize, wave.sampleRate); - - // Attach sound buffer to source - alSourcei(source, AL_BUFFER, buffer); - - TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Sound data loaded successfully (%i Hz, %i bit, %s)", source, buffer, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); - - sound.source = source; - sound.buffer = buffer; - sound.format = format; -#endif } return sound; @@ -1002,14 +850,7 @@ void UnloadWave(Wave wave) // Unload sound void UnloadSound(Sound sound) { -#if USE_MINI_AL DeleteAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - alSourceStop(sound.source); - - alDeleteSources(1, &sound.source); - alDeleteBuffers(1, &sound.buffer); -#endif TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer); } @@ -1018,8 +859,8 @@ void UnloadSound(Sound sound) // NOTE: data must match sound.format void UpdateSound(Sound sound, const void *data, int samplesCount) { -#if USE_MINI_AL AudioBuffer *audioBuffer = (AudioBuffer *)sound.audioBuffer; + if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer"); @@ -1030,29 +871,6 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) // TODO: May want to lock/unlock this since this data buffer is read at mixing time. memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.formatConverterIn.config.channels*mal_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn)); -#else - ALint sampleRate, sampleSize, channels; - alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); - alGetBufferi(sound.buffer, AL_BITS, &sampleSize); // It could also be retrieved from sound.format - alGetBufferi(sound.buffer, AL_CHANNELS, &channels); // It could also be retrieved from sound.format - - TraceLog(LOG_DEBUG, "UpdateSound() : AL_FREQUENCY: %i", sampleRate); - TraceLog(LOG_DEBUG, "UpdateSound() : AL_BITS: %i", sampleSize); - TraceLog(LOG_DEBUG, "UpdateSound() : AL_CHANNELS: %i", channels); - - unsigned int dataSize = samplesCount*channels*sampleSize/8; // Size of data in bytes - - alSourceStop(sound.source); // Stop sound - alSourcei(sound.source, AL_BUFFER, 0); // Unbind buffer from sound to update - //alDeleteBuffers(1, &sound.buffer); // Delete current buffer data - //alGenBuffers(1, &sound.buffer); // Generate new buffer - - // Upload new data to sound buffer - alBufferData(sound.buffer, sound.format, data, dataSize, sampleRate); - - // Attach sound buffer to source again - alSourcei(sound.source, AL_BUFFER, sound.buffer); -#endif } // Export wave data to file @@ -1141,102 +959,48 @@ void ExportWave(Wave wave, const char *fileName) // Play a sound void PlaySound(Sound sound) { -#if USE_MINI_AL PlayAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - alSourcePlay(sound.source); // Play the sound -#endif - - //TraceLog(LOG_INFO, "Playing sound"); - - // Find the current position of the sound being played - // NOTE: Only work when the entire file is in a single buffer - //int byteOffset; - //alGetSourcei(sound.source, AL_BYTE_OFFSET, &byteOffset); - // - //int sampleRate; - //alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); // AL_CHANNELS, AL_BITS (bps) - - //float seconds = (float)byteOffset/sampleRate; // Number of seconds since the beginning of the sound - //or - //float result; - //alGetSourcef(sound.source, AL_SEC_OFFSET, &result); // AL_SAMPLE_OFFSET } // Pause a sound void PauseSound(Sound sound) { -#if USE_MINI_AL PauseAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - alSourcePause(sound.source); -#endif } // Resume a paused sound void ResumeSound(Sound sound) { -#if USE_MINI_AL ResumeAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - ALenum state; - - alGetSourcei(sound.source, AL_SOURCE_STATE, &state); - - if (state == AL_PAUSED) alSourcePlay(sound.source); -#endif } // Stop reproducing a sound void StopSound(Sound sound) { -#if USE_MINI_AL StopAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - alSourceStop(sound.source); -#endif } // Check if a sound is playing bool IsSoundPlaying(Sound sound) { -#if USE_MINI_AL return IsAudioBufferPlaying((AudioBuffer *)sound.audioBuffer); -#else - bool playing = false; - ALint state; - - alGetSourcei(sound.source, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) playing = true; - - return playing; -#endif } // Set volume for a sound void SetSoundVolume(Sound sound, float volume) { -#if USE_MINI_AL SetAudioBufferVolume((AudioBuffer *)sound.audioBuffer, volume); -#else - alSourcef(sound.source, AL_GAIN, volume); -#endif } // Set pitch for a sound void SetSoundPitch(Sound sound, float pitch) { -#if USE_MINI_AL SetAudioBufferPitch((AudioBuffer *)sound.audioBuffer, pitch); -#else - alSourcef(sound.source, AL_PITCH, pitch); -#endif } // Convert wave data to desired format void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) { -#if USE_MINI_AL mal_format formatIn = ((wave->sampleSize == 8) ? mal_format_u8 : ((wave->sampleSize == 16) ? mal_format_s16 : mal_format_f32)); mal_format formatOut = (( sampleSize == 8) ? mal_format_u8 : (( sampleSize == 16) ? mal_format_s16 : mal_format_f32)); @@ -1264,87 +1028,6 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) wave->channels = channels; free(wave->data); wave->data = data; - -#else - // Format sample rate - // NOTE: Only supported 22050 <--> 44100 - if (wave->sampleRate != sampleRate) - { - // TODO: Resample wave data (upsampling or downsampling) - // NOTE 1: To downsample, you have to drop samples or average them. - // NOTE 2: To upsample, you have to interpolate new samples. - - wave->sampleRate = sampleRate; - } - - // Format sample size - // NOTE: Only supported 8 bit <--> 16 bit <--> 32 bit - if (wave->sampleSize != sampleSize) - { - void *data = malloc(wave->sampleCount*wave->channels*sampleSize/8); - - for (int i = 0; i < wave->sampleCount; i++) - { - for (int j = 0; j < wave->channels; j++) - { - if (sampleSize == 8) - { - if (wave->sampleSize == 16) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float)(((short *)wave->data)[wave->channels*i + j])/32767.0f)*256); - else if (wave->sampleSize == 32) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float *)wave->data)[wave->channels*i + j]*127.0f + 127); - } - else if (sampleSize == 16) - { - if (wave->sampleSize == 8) ((short *)data)[wave->channels*i + j] = (short)(((float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f)*32767); - else if (wave->sampleSize == 32) ((short *)data)[wave->channels*i + j] = (short)((((float *)wave->data)[wave->channels*i + j])*32767); - } - else if (sampleSize == 32) - { - if (wave->sampleSize == 8) ((float *)data)[wave->channels*i + j] = (float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f; - else if (wave->sampleSize == 16) ((float *)data)[wave->channels*i + j] = (float)(((short *)wave->data)[wave->channels*i + j])/32767.0f; - } - } - } - - wave->sampleSize = sampleSize; - free(wave->data); - wave->data = data; - } - - // Format channels (interlaced mode) - // NOTE: Only supported mono <--> stereo - if (wave->channels != channels) - { - void *data = malloc(wave->sampleCount*wave->sampleSize/8*channels); - - if ((wave->channels == 1) && (channels == 2)) // mono ---> stereo (duplicate mono information) - { - for (int i = 0; i < wave->sampleCount; i++) - { - for (int j = 0; j < channels; j++) - { - if (wave->sampleSize == 8) ((unsigned char *)data)[channels*i + j] = ((unsigned char *)wave->data)[i]; - else if (wave->sampleSize == 16) ((short *)data)[channels*i + j] = ((short *)wave->data)[i]; - else if (wave->sampleSize == 32) ((float *)data)[channels*i + j] = ((float *)wave->data)[i]; - } - } - } - else if ((wave->channels == 2) && (channels == 1)) // stereo ---> mono (mix stereo channels) - { - for (int i = 0, j = 0; i < wave->sampleCount; i++, j += 2) - { - if (wave->sampleSize == 8) ((unsigned char *)data)[i] = (((unsigned char *)wave->data)[j] + ((unsigned char *)wave->data)[j + 1])/2; - else if (wave->sampleSize == 16) ((short *)data)[i] = (((short *)wave->data)[j] + ((short *)wave->data)[j + 1])/2; - else if (wave->sampleSize == 32) ((float *)data)[i] = (((float *)wave->data)[j] + ((float *)wave->data)[j + 1])/2.0f; - } - } - - // TODO: Add/remove additional interlaced channels - - wave->channels = channels; - free(wave->data); - wave->data = data; - } -#endif } // Copy a wave to a new wave @@ -1379,7 +1062,7 @@ void WaveCrop(Wave *wave, int initSample, int finalSample) void *data = malloc(sampleCount*wave->sampleSize/8*wave->channels); - memcpy(data, (unsigned char*)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8); + memcpy(data, (unsigned char *)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8); free(wave->data); wave->data = data; @@ -1463,21 +1146,26 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_MP3) else if (IsFileExtension(fileName, ".mp3")) { - drmp3_init_file(&music->ctxMp3, fileName, NULL); + int result = drmp3_init_file(&music->ctxMp3, fileName, NULL); - if (music->ctxMp3.framesRemaining <= 0) musicLoaded = false; + if (!result) musicLoaded = false; else { - music->stream = InitAudioStream(music->ctxMp3.sampleRate, 16, music->ctxMp3.channels); - music->totalSamples = (unsigned int)music->ctxMp3.framesRemaining*music->ctxMp3.channels; + TraceLog(LOG_INFO, "[%s] MP3 sample rate: %i", fileName, music->ctxMp3.sampleRate); + TraceLog(LOG_INFO, "[%s] MP3 bits per sample: %i", fileName, 32); + TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels); + TraceLog(LOG_INFO, "[%s] MP3 frames remaining: %i", fileName, (unsigned int)music->ctxMp3.framesRemaining); + + music->stream = InitAudioStream(music->ctxMp3.sampleRate, 32, music->ctxMp3.channels); + + // TODO: There is not an easy way to compute the total number of samples available + // in an MP3, frames size could be variable... we tried with a 60 seconds music... but crashes... + music->totalSamples = 60*music->ctxMp3.sampleRate*music->ctxMp3.channels; music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_AUDIO_MP3; music->loopCount = -1; // Infinite loop by default - - TraceLog(LOG_DEBUG, "[%s] MP3 total samples: %i", fileName, music->totalSamples); - TraceLog(LOG_DEBUG, "[%s] MP3 sample rate: %i", fileName, music->ctxMp3.sampleRate); - //TraceLog(LOG_DEBUG, "[%s] MP3 bits per sample: %i", fileName, music->ctxMp3.bitsPerSample); - TraceLog(LOG_DEBUG, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels); + + TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music->totalSamples); } } #endif @@ -1574,8 +1262,8 @@ void UnloadMusicStream(Music music) // Start music playing (open stream) void PlayMusicStream(Music music) { -#if USE_MINI_AL AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.audioBuffer; + if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); @@ -1591,61 +1279,25 @@ void PlayMusicStream(Music music) PlayAudioStream(music->stream); // <-- This resets the cursor position. audioBuffer->frameCursorPos = frameCursorPos; -#else - alSourcePlay(music->stream.source); -#endif } // Pause music playing void PauseMusicStream(Music music) { -#if USE_MINI_AL PauseAudioStream(music->stream); -#else - alSourcePause(music->stream.source); -#endif } // Resume music playing void ResumeMusicStream(Music music) { -#if USE_MINI_AL ResumeAudioStream(music->stream); -#else - ALenum state; - alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); - - if (state == AL_PAUSED) - { - TraceLog(LOG_INFO, "[AUD ID %i] Resume music stream playing", music->stream.source); - alSourcePlay(music->stream.source); - } -#endif } // Stop music playing (close stream) // TODO: To clear a buffer, make sure they have been already processed! void StopMusicStream(Music music) { -#if USE_MINI_AL StopAudioStream(music->stream); -#else - alSourceStop(music->stream.source); - - /* - // Clear stream buffers - // WARNING: Queued buffers must have been processed before unqueueing and reloaded with data!!! - void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1); - - for (int i = 0; i < MAX_STREAM_BUFFERS; i++) - { - //UpdateAudioStream(music->stream, pcm, AUDIO_BUFFER_SIZE); // Update one buffer at a time - alBufferData(music->stream.buffers[i], music->stream.format, pcm, AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, music->stream.sampleRate); - } - - free(pcm); - */ -#endif // Restart music context switch (music->ctxType) @@ -1673,7 +1325,6 @@ void StopMusicStream(Music music) // TODO: Make sure buffers are ready for update... check music state void UpdateMusicStream(Music music) { -#if USE_MINI_AL bool streamEnding = false; unsigned int subBufferSizeInFrames = ((AudioBuffer *)music->stream.audioBuffer)->bufferSizeInFrames/2; @@ -1757,139 +1408,24 @@ void UpdateMusicStream(Music music) // just make sure to play again on window restore if (IsMusicPlaying(music)) PlayMusicStream(music); } -#else - ALenum state; - ALint processed = 0; - - alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); // Get music stream state - alGetSourcei(music->stream.source, AL_BUFFERS_PROCESSED, &processed); // Get processed buffers - - if (processed > 0) - { - bool streamEnding = false; - - // NOTE: Using dynamic allocation because it could require more than 16KB - void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1); - - int numBuffersToProcess = processed; - int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats, - // individual L or R for ogg shorts - - for (int i = 0; i < numBuffersToProcess; i++) - { - if (music->samplesLeft >= AUDIO_BUFFER_SIZE) samplesCount = AUDIO_BUFFER_SIZE; - else samplesCount = music->samplesLeft; - - // TODO: Really don't like ctxType thingy... - switch (music->ctxType) - { - case MUSIC_AUDIO_OGG: - { - // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) - int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount*music->stream.channels); - - } break; - #if defined(SUPPORT_FILEFORMAT_FLAC) - case MUSIC_AUDIO_FLAC: - { - // NOTE: Returns the number of samples to process - unsigned int numSamplesFlac = (unsigned int)drflac_read_s16(music->ctxFlac, samplesCount*music->stream.channels, (short *)pcm); - - } break; - #endif - #if defined(SUPPORT_FILEFORMAT_MP3) - case MUSIC_AUDIO_MP3: - { - // NOTE: Returns the number of samples to process - unsigned int numSamplesMp3 = (unsigned int)drmp3_read_f32(&music->ctxMp3, samplesCount*music->stream.channels, (float *)pcm); - } break; - #endif - #if defined(SUPPORT_FILEFORMAT_XM) - case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break; - #endif - #if defined(SUPPORT_FILEFORMAT_MOD) - case MUSIC_MODULE_MOD: jar_mod_fillbuffer(&music->ctxMod, pcm, samplesCount, 0); break; - #endif - default: break; - } - - UpdateAudioStream(music->stream, pcm, samplesCount); - music->samplesLeft -= samplesCount; - - if (music->samplesLeft <= 0) - { - streamEnding = true; - break; - } - } - - // Free allocated pcm data - free(pcm); - - // Reset audio stream for looping - if (streamEnding) - { - StopMusicStream(music); // Stop music (and reset) - - // Decrease loopCount to stop when required - if (music->loopCount > 0) - { - music->loopCount--; // Decrease loop count - PlayMusicStream(music); // Play again - } - else - { - if (music->loopCount == -1) - { - PlayMusicStream(music); - } - } - } - else - { - // NOTE: In case window is minimized, music stream is stopped, - // just make sure to play again on window restore - if (state != AL_PLAYING) PlayMusicStream(music); - } - } -#endif } // Check if any music is playing bool IsMusicPlaying(Music music) { -#if USE_MINI_AL return IsAudioStreamPlaying(music->stream); -#else - bool playing = false; - ALint state; - - alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); - - if (state == AL_PLAYING) playing = true; - - return playing; -#endif } // Set volume for music void SetMusicVolume(Music music, float volume) { -#if USE_MINI_AL SetAudioStreamVolume(music->stream, volume); -#else - alSourcef(music->stream.source, AL_GAIN, volume); -#endif } // Set pitch for music void SetMusicPitch(Music music, float pitch) { -#if USE_MINI_AL SetAudioStreamPitch(music->stream, pitch); -#else - alSourcef(music->stream.source, AL_PITCH, pitch); -#endif } // Set music loop count (loop repeats) @@ -1935,12 +1471,10 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un stream.channels = 1; // Fallback to mono channel } - -#if USE_MINI_AL mal_format formatIn = ((stream.sampleSize == 8) ? mal_format_u8 : ((stream.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); // The size of a streaming buffer must be at least double the size of a period. - unsigned int periodSize = device.bufferSizeInFrames / device.periods; + unsigned int periodSize = device.bufferSizeInFrames/device.periods; unsigned int subBufferSize = AUDIO_BUFFER_SIZE; if (subBufferSize < periodSize) subBufferSize = periodSize; @@ -1951,54 +1485,8 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un return stream; } - audioBuffer->looping = true; // Always loop for streaming buffers. + audioBuffer->looping = true; // Always loop for streaming buffers. stream.audioBuffer = audioBuffer; -#else - // Setup OpenAL format - if (stream.channels == 1) - { - switch (sampleSize) - { - case 8: stream.format = AL_FORMAT_MONO8; break; - case 16: stream.format = AL_FORMAT_MONO16; break; - case 32: stream.format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(LOG_WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; - } - } - else if (stream.channels == 2) - { - switch (sampleSize) - { - case 8: stream.format = AL_FORMAT_STEREO8; break; - case 16: stream.format = AL_FORMAT_STEREO16; break; - case 32: stream.format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(LOG_WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; - } - } - - // Create an audio source - alGenSources(1, &stream.source); - alSourcef(stream.source, AL_PITCH, 1.0f); - alSourcef(stream.source, AL_GAIN, 1.0f); - alSource3f(stream.source, AL_POSITION, 0.0f, 0.0f, 0.0f); - alSource3f(stream.source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - - // Create Buffers (double buffering) - alGenBuffers(MAX_STREAM_BUFFERS, stream.buffers); - - // Initialize buffer with zeros by default - // NOTE: Using dynamic allocation because it requires more than 16KB - void *pcm = calloc(AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, 1); - - for (int i = 0; i < MAX_STREAM_BUFFERS; i++) - { - alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, stream.sampleRate); - } - - free(pcm); - - alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers); -#endif TraceLog(LOG_INFO, "[AUD ID %i] Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.source, stream.sampleRate, stream.sampleSize, (stream.channels == 1) ? "Mono" : "Stereo"); @@ -2008,28 +1496,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un // Close audio stream and free memory void CloseAudioStream(AudioStream stream) { -#if USE_MINI_AL DeleteAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - // Stop playing channel - alSourceStop(stream.source); - - // Flush out all queued buffers - int queued = 0; - alGetSourcei(stream.source, AL_BUFFERS_QUEUED, &queued); - - ALuint buffer = 0; - - while (queued > 0) - { - alSourceUnqueueBuffers(stream.source, 1, &buffer); - queued--; - } - - // Delete source and buffers - alDeleteSources(1, &stream.source); - alDeleteBuffers(MAX_STREAM_BUFFERS, stream.buffers); -#endif TraceLog(LOG_INFO, "[AUD ID %i] Unloaded audio stream data", stream.source); } @@ -2039,7 +1506,6 @@ void CloseAudioStream(AudioStream stream) // NOTE 2: To unqueue a buffer it needs to be processed: IsAudioBufferProcessed() void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { -#if USE_MINI_AL AudioBuffer *audioBuffer = (AudioBuffer *)stream.audioBuffer; if (audioBuffer == NULL) { @@ -2050,6 +1516,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]) { mal_uint32 subBufferToUpdate; + if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1]) { // Both buffers are available for updating. Update the first one and make sure the cursor is moved back to the front. @@ -2069,6 +1536,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) if (subBufferSizeInFrames >= (mal_uint32)samplesCount) { mal_uint32 framesToWrite = subBufferSizeInFrames; + if (framesToWrite > (mal_uint32)samplesCount) framesToWrite = (mal_uint32)samplesCount; mal_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8); @@ -2076,6 +1544,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) // Any leftover frames should be filled with zeros. mal_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; + if (leftoverFrameCount > 0) { memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8)); @@ -2094,24 +1563,11 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) TraceLog(LOG_ERROR, "Audio buffer not available for updating"); return; } -#else - ALuint buffer = 0; - alSourceUnqueueBuffers(stream.source, 1, &buffer); - - // Check if any buffer was available for unqueue - if (alGetError() != AL_INVALID_VALUE) - { - alBufferData(buffer, stream.format, data, samplesCount*stream.sampleSize/8*stream.channels, stream.sampleRate); - alSourceQueueBuffers(stream.source, 1, &buffer); - } - else TraceLog(LOG_WARNING, "[AUD ID %i] Audio buffer not available for unqueuing", stream.source); -#endif } // Check if any audio stream buffers requires refill bool IsAudioBufferProcessed(AudioStream stream) { -#if USE_MINI_AL AudioBuffer *audioBuffer = (AudioBuffer *)stream.audioBuffer; if (audioBuffer == NULL) { @@ -2120,92 +1576,46 @@ bool IsAudioBufferProcessed(AudioStream stream) } return audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]; -#else - ALint processed = 0; - - // Determine if music stream is ready to be written - alGetSourcei(stream.source, AL_BUFFERS_PROCESSED, &processed); - - return (processed > 0); -#endif } // Play audio stream void PlayAudioStream(AudioStream stream) { -#if USE_MINI_AL PlayAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - alSourcePlay(stream.source); -#endif } // Play audio stream void PauseAudioStream(AudioStream stream) { -#if USE_MINI_AL PauseAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - alSourcePause(stream.source); -#endif } // Resume audio stream playing void ResumeAudioStream(AudioStream stream) { -#if USE_MINI_AL ResumeAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - ALenum state; - alGetSourcei(stream.source, AL_SOURCE_STATE, &state); - - if (state == AL_PAUSED) alSourcePlay(stream.source); -#endif } // Check if audio stream is playing. bool IsAudioStreamPlaying(AudioStream stream) { -#if USE_MINI_AL return IsAudioBufferPlaying((AudioBuffer *)stream.audioBuffer); -#else - bool playing = false; - ALint state; - - alGetSourcei(stream.source, AL_SOURCE_STATE, &state); - - if (state == AL_PLAYING) playing = true; - - return playing; -#endif } // Stop audio stream void StopAudioStream(AudioStream stream) { -#if USE_MINI_AL StopAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - alSourceStop(stream.source); -#endif } void SetAudioStreamVolume(AudioStream stream, float volume) { -#if USE_MINI_AL SetAudioBufferVolume((AudioBuffer *)stream.audioBuffer, volume); -#else - alSourcef(stream.source, AL_GAIN, volume); -#endif } void SetAudioStreamPitch(AudioStream stream, float pitch) { -#if USE_MINI_AL SetAudioBufferPitch((AudioBuffer *)stream.audioBuffer, pitch); -#else - alSourcef(stream.source, AL_PITCH, pitch); -#endif } //---------------------------------------------------------------------------------- @@ -2398,17 +1808,17 @@ static Wave LoadFLAC(const char *fileName) // NOTE: Using dr_mp3 library static Wave LoadMP3(const char *fileName) { - Wave wave; + Wave wave = { 0 }; // Decode an entire MP3 file in one go - uint64_t totalSampleCount; - drmp3_config *config; - wave.data = drmp3_open_and_decode_file_f32(fileName, config, &totalSampleCount); + uint64_t totalSampleCount = 0; + drmp3_config config = { 0 }; + wave.data = drmp3_open_and_decode_file_f32(fileName, &config, &totalSampleCount); - wave.channels = config->outputChannels; - wave.sampleRate = config->outputSampleRate; - wave.sampleCount = (int)totalSampleCount/wave.channels; - wave.sampleSize = 16; + wave.channels = config.outputChannels; + wave.sampleRate = config.outputSampleRate; + wave.sampleCount = (int)totalSampleCount; + wave.sampleSize = 32; // NOTE: Only support up to 2 channels (mono, stereo) if (wave.channels > 2) TraceLog(LOG_WARNING, "[%s] MP3 channels number (%i) not supported", fileName, wave.channels); diff --git a/src/config.h b/src/config.h index c2238a796..47cc80688 100644 --- a/src/config.h +++ b/src/config.h @@ -25,7 +25,7 @@ * **********************************************************************************************/ -#define RAYLIB_VERSION "2.0" +#define RAYLIB_VERSION "2.1-dev" // Edit to control what features Makefile'd raylib is compiled with #if defined(RAYLIB_CMAKE) diff --git a/src/config.h.in b/src/config.h.in index 742067ce7..b0e624805 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -1,7 +1,5 @@ /* config.h.in */ -#cmakedefine USE_OPENAL_BACKEND 1 - // core.c /* Camera module is included (camera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital */ #cmakedefine SUPPORT_CAMERA_SYSTEM 1 diff --git a/src/core.c b/src/core.c index fc1a5a093..ffbf110e8 100644 --- a/src/core.c +++ b/src/core.c @@ -123,35 +123,55 @@ #include // Required for: strrchr(), strcmp() //#include // Macros for reporting and retrieving error conditions through error codes #include // Required for: tolower() [Used in IsFileExtension()] +#include // Required for stat() [Used in GetLastWriteTime()] + +#if defined(PLATFORM_DESKTOP) && defined(_WIN32) && defined(_MSC_VER) + #include "external/dirent.h" // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()] +#else + #include // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()] +#endif #if defined(_WIN32) #include // Required for: _getch(), _chdir() #define GETCWD _getcwd // NOTE: MSDN recommends not to use getcwd(), chdir() #define CHDIR _chdir + #include // Required for _access() [Used in FileExists()] #else - #include "unistd.h" // Required for: getch(), chdir() (POSIX) + #include "unistd.h" // Required for: getch(), chdir() (POSIX), access() #define GETCWD getcwd #define CHDIR chdir #endif -#if defined(__linux__) || defined(PLATFORM_WEB) - #include // Required for: timespec, nanosleep(), select() - POSIX -#elif defined(__APPLE__) - #include // Required for: usleep() -#endif - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - #if defined(PLATFORM_WEB) - #define GLFW_INCLUDE_ES2 - #endif - //#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 +#if defined(PLATFORM_DESKTOP) + #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) #include // GLFW3 library: Windows, OpenGL context and Input management // NOTE: GLFW3 already includes gl.h (OpenGL) headers - #if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32) - // NOTE: Those functions require linking with winmm library - unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); - unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); + // Support retrieving native window handlers + #if defined(_WIN32) + #define GLFW_EXPOSE_NATIVE_WIN32 + #include // WARNING: It requires customization to avoid windows.h inclusion! + + #if !defined(SUPPORT_BUSY_WAIT_LOOP) + // NOTE: Those functions require linking with winmm library + unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); + unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); + #endif + #elif defined(__linux__) + #include // Required for: timespec, nanosleep(), select() - POSIX + + //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type + //#define GLFW_EXPOSE_NATIVE_WAYLAND + //#define GLFW_EXPOSE_NATIVE_MIR + #include // Required for: glfwGetX11Window() + #elif defined(__APPLE__) + #include // Required for: usleep() + #include // Required for: objc_msgsend(), sel_registerName() + + //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition + #define GLFW_EXPOSE_NATIVE_NSGL + #include // Required for: glfwGetCocoaWindow(), glfwGetNSGLContext() #endif #endif @@ -169,6 +189,7 @@ #include // POSIX standard function definitions - read(), close(), STDIN_FILENO #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() #include // POSIX threads management (mouse input) + #include // POSIX directory browsing #include // UNIX System call for device-specific input/output operations - ioctl() #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition @@ -189,19 +210,24 @@ #endif #if defined(PLATFORM_WEB) - #include - #include + #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) + #include // GLFW3 library: Windows, OpenGL context and Input management + #include // Required for: timespec, nanosleep(), select() - POSIX + + #include // Emscripten library - LLVM to JavaScript compiler + #include // Emscripten HTML5 library #endif //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- #if defined(PLATFORM_RPI) + #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number + // Old device inputs system #define DEFAULT_KEYBOARD_DEV STDIN_FILENO // Standard input - #define DEFAULT_MOUSE_DEV "/dev/input/mouse0" // Mouse input - #define DEFAULT_TOUCH_DEV "/dev/input/event4" // Touch input virtual device (created by ts_uinput) #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) + #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events // New device input events (evdev) (must be detected) //#define DEFAULT_KEYBOARD_DEV "/dev/input/eventN" @@ -225,14 +251,46 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- + +// Window/Graphics related variables +//----------------------------------------------------------------------------------- #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static GLFWwindow *window; // Native window (graphic device) #endif - static bool windowReady = false; // Check if window has been initialized successfully static bool windowMinimized = false; // Check if window has been minimized static const char *windowTitle = NULL; // Window text title... +#if defined(__APPLE__) +static int windowNeedsUpdating = 2; // Times the Cocoa window needs to be updated initially +#endif + +static unsigned int displayWidth, displayHeight;// Display width and height (monitor, device-screen, LCD, ...) +static int screenWidth, screenHeight; // Screen width and height (used render area) +static int renderWidth, renderHeight; // Framebuffer width and height (render area, including black bars if required) +static int renderOffsetX = 0; // Offset X from render area (must be divided by 2) +static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2) +static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) +static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) + +#if defined(PLATFORM_RPI) +static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device) +#endif + +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) +static EGLDisplay display; // Native display device (physical screen connection) +static EGLSurface surface; // Surface to draw on, framebuffers (connected to context) +static EGLContext context; // Graphic context, mode in which drawing can be done +static EGLConfig config; // Graphic config +static uint64_t baseTime; // Base time measure for hi-res timer +static bool windowShouldClose = false; // Flag to set window for closing +#endif + +#if defined(PLATFORM_UWP) +extern EGLNativeWindowType uwpWindow; // Native EGL window handler for UWP (external, defined in UWP App) +#endif +//----------------------------------------------------------------------------------- + #if defined(PLATFORM_ANDROID) static struct android_app *androidApp; // Android activity static struct android_poll_source *source; // Android events polling source @@ -243,104 +301,97 @@ static bool appEnabled = true; // Used to detec if app is activ static bool contextRebindRequired = false; // Used to know context rebind required #endif -#if defined(PLATFORM_RPI) -static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device) +// Inputs related variables +//----------------------------------------------------------------------------------- +// Keyboard states +static char previousKeyState[512] = { 0 }; // Registers previous frame key state +static char currentKeyState[512] = { 0 }; // Registers current frame key state +static int lastKeyPressed = -1; // Register last key pressed +static int exitKey = KEY_ESCAPE; // Default exit key (ESC) -// Keyboard input variables +#if defined(PLATFORM_RPI) // NOTE: For keyboard we will use the standard input (but reconfigured...) static struct termios defaultKeyboardSettings; // Used to store default keyboard settings static int defaultKeyboardMode; // Used to store default keyboard mode - -// Mouse input variables -static int mouseStream = -1; // Mouse device file descriptor -static bool mouseReady = false; // Flag to know if mouse is ready -static pthread_t mouseThreadId; // Mouse reading thread id - -// Touch input variables -static int touchStream = -1; // Touch device file descriptor -static bool touchReady = false; // Flag to know if touch interface is ready -static pthread_t touchThreadId; // Touch reading thread id - -// Gamepad input variables -static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor -static pthread_t gamepadThreadId; // Gamepad reading thread id -static char gamepadName[64]; // Gamepad name holder #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) -static EGLDisplay display; // Native display device (physical screen connection) -static EGLSurface surface; // Surface to draw on, framebuffers (connected to context) -static EGLContext context; // Graphic context, mode in which drawing can be done -static EGLConfig config; // Graphic config -static uint64_t baseTime; // Base time measure for hi-res timer -static bool windowShouldClose = false; // Flag to set window for closing -#endif - -#if defined(PLATFORM_UWP) -extern EGLNativeWindowType uwpWindow; // Native EGL window handler for UWP (external, defined in UWP App) -#endif - -// Screen related variables -static unsigned int displayWidth, displayHeight; // Display width and height (monitor, device-screen, LCD, ...) -static int screenWidth, screenHeight; // Screen width and height (used render area) -static int renderWidth, renderHeight; // Framebuffer width and height (render area, including black bars if required) -static int renderOffsetX = 0; // Offset X from render area (must be divided by 2) -static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2) -static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) -static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) - -static bool cursorHidden = false; // Track if cursor is hidden -static bool cursorOnScreen = false; // Tracks if cursor is inside client area +// Mouse states +static Vector2 mousePosition; // Mouse position on screen +static float mouseScale = 1.0f; // Mouse default scale +static bool cursorHidden = false; // Track if cursor is hidden +static bool cursorOnScreen = false; // Tracks if cursor is inside client area +static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP) -// Register mouse states -static char previousMouseState[3] = { 0 }; // Registers previous mouse button state -static char currentMouseState[3] = { 0 }; // Registers current mouse button state -static int previousMouseWheelY = 0; // Registers previous mouse wheel variation -static int currentMouseWheelY = 0; // Registers current mouse wheel variation +static char previousMouseState[3] = { 0 }; // Registers previous mouse button state +static char currentMouseState[3] = { 0 }; // Registers current mouse button state +static int previousMouseWheelY = 0; // Registers previous mouse wheel variation +static int currentMouseWheelY = 0; // Registers current mouse wheel variation +#endif -// Register gamepads states +#if defined(PLATFORM_RPI) +static char currentMouseStateEvdev[3] = { 0 }; // Holds the new mouse state for the next polling event to grab (Can't be written directly due to multithreading, app could miss the update) + +typedef struct { + pthread_t threadId; // Event reading thread id + int fd; // File descriptor to the device it is assigned to + int eventNum; // Number of 'event' device + Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) + int touchSlot; // Hold the touch slot number of the currently being sent multitouch block + bool isMouse; // True if device supports relative X Y movements + bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH + bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH + bool isKeyboard; // True if device has letter keycodes + bool isGamepad; // True if device has gamepad buttons +} InputEventWorker; + +static InputEventWorker eventWorkers[10]; // List of worker threads for every monitored "/dev/input/event" + +#endif +#if defined(PLATFORM_WEB) +static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click +#endif + +// Gamepads states +static int lastGamepadButtonPressed = -1; // Register last gamepad button pressed +static int gamepadAxisCount = 0; // Register number of available gamepad axis + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP) static bool gamepadReady[MAX_GAMEPADS] = { false }; // Flag to know if gamepad is ready static float gamepadAxisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state static char previousGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state static char currentGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state - -// Keyboard configuration -static int exitKey = KEY_ESCAPE; // Default exit key (ESC) #endif -// Register keyboard states -static char previousKeyState[512] = { 0 }; // Registers previous frame key state -static char currentKeyState[512] = { 0 }; // Registers current frame key state - -static int lastKeyPressed = -1; // Register last key pressed -static int lastGamepadButtonPressed = -1; // Register last gamepad button pressed -static int gamepadAxisCount = 0; // Register number of available gamepad axis - -static Vector2 mousePosition; // Mouse position on screen -static float mouseScale = 1.0f; // Mouse default scale - -#if defined(PLATFORM_WEB) -static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click -#endif - -static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen - -#if defined(PLATFORM_DESKTOP) -static char **dropFilesPath; // Store dropped files paths as strings -static int dropFilesCount = 0; // Count stored strings +#if defined(PLATFORM_RPI) +static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor +static pthread_t gamepadThreadId; // Gamepad reading thread id +static char gamepadName[64]; // Gamepad name holder #endif +//----------------------------------------------------------------------------------- +// Timming system variables +//----------------------------------------------------------------------------------- static double currentTime = 0.0; // Current time measure static double previousTime = 0.0; // Previous time measure static double updateTime = 0.0; // Time measure for frame update static double drawTime = 0.0; // Time measure for frame draw static double frameTime = 0.0; // Time measure for one frame static double targetTime = 0.0; // Desired time for one frame, if 0 not applied +//----------------------------------------------------------------------------------- +// Config internal variables +//----------------------------------------------------------------------------------- static unsigned char configFlags = 0; // Configuration flags (bit based) static bool showLogo = false; // Track if showing logo at init is enabled +#if defined(PLATFORM_DESKTOP) +static char **dropFilesPath; // Store dropped files paths as strings +static int dropFilesCount = 0; // Count dropped files strings +#endif +static char **dirFilesPath; // Store directory files paths as strings +static int dirFilesCount = 0; // Count directory files strings + #if defined(SUPPORT_SCREEN_CAPTURE) static int screenshotCounter = 0; // Screenshots counter #endif @@ -349,6 +400,7 @@ static int screenshotCounter = 0; // Screenshots counter static int gifFramesCounter = 0; // GIF frames counter static bool gifRecording = false; // GIF recording state #endif +//----------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by core) @@ -362,15 +414,18 @@ extern void UnloadDefaultFont(void); // [Module: text] Unloads default fo // Module specific Functions Declaration //---------------------------------------------------------------------------------- static bool InitGraphicsDevice(int width, int height); // Initialize graphics device -static void SetupFramebufferSize(int displayWidth, int displayHeight); +static void SetupFramebuffer(int width, int height); // Setup main framebuffer +static void SetupViewport(void); // Set viewport parameters +static void SwapBuffers(void); // Copy back buffer to front buffers + static void InitTimer(void); // Initialize timer static void Wait(float ms); // Wait for some milliseconds (stop program execution) + static bool GetKeyStatus(int key); // Returns if a key has been pressed static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed static void PollInputEvents(void); // Register user events -static void SwapBuffers(void); // Copy back buffer to front buffers + static void LogoAnimation(void); // Plays raylib logo appearing animation -static void SetupViewport(void); // Set viewport parameters #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error @@ -383,7 +438,6 @@ static void CursorEnterCallback(GLFWwindow *window, int enter); static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored #endif - #if defined(PLATFORM_DESKTOP) static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window #endif @@ -406,9 +460,8 @@ static void InitKeyboard(void); // Init raw keyboard sys static void ProcessKeyboard(void); // Process keyboard events static void RestoreKeyboard(void); // Restore keyboard system static void InitMouse(void); // Mouse initialization (including mouse thread) -static void *MouseThread(void *arg); // Mouse reading thread -static void InitTouch(void); // Touch device initialization (including touch thread) -static void *TouchThread(void *arg); // Touch device reading thread +static void EventThreadSpawn(char *device); // Indetifies a input device and spawns a thread to handle it if needed +static void *EventThread(void *arg); // Input device event reading thread static void InitGamepad(void); // Init raw gamepad input static void *GamepadThread(void *arg); // Mouse reading thread #endif @@ -437,7 +490,7 @@ void android_main(struct android_app *app) androidApp = app; // TODO: Should we maybe report != 0 return codes somewhere? - (void)main(1, (char*[]) { arg0, NULL }); + (void)main(1, (char *[]) { arg0, NULL }); } // TODO: Add this to header (if apps really need it) @@ -525,7 +578,6 @@ void InitWindow(int width, int height, const char *title) #if defined(PLATFORM_RPI) // Init raw input system InitMouse(); // Mouse init - InitTouch(); // Touch init InitKeyboard(); // Keyboard init InitGamepad(); // Gamepad init #endif @@ -620,8 +672,13 @@ void CloseWindow(void) windowShouldClose = true; // Added to force threads to exit when the close window is called - pthread_join(mouseThreadId, NULL); - pthread_join(touchThreadId, NULL); + for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i) + { + if (eventWorkers[i].threadId == 0) + { + pthread_join(eventWorkers[i].threadId, NULL); + } + } pthread_join(gamepadThreadId, NULL); #endif @@ -730,7 +787,7 @@ void SetWindowMonitor(int monitor) { #if defined(PLATFORM_DESKTOP) int monitorCount; - GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); if ((monitor >= 0) && (monitor < monitorCount)) { @@ -770,6 +827,124 @@ int GetScreenHeight(void) return screenHeight; } +// Get native window handle +void *GetWindowHandle(void) +{ +#if defined(_WIN32) + // NOTE: Returned handle is: void *HWND (windows.h) + return glfwGetWin32Window(window); +#elif defined(__linux__) + // NOTE: Returned handle is: unsigned long Window (X.h) + // typedef unsigned long XID; + // typedef XID Window; + //unsigned long id = (unsigned long)glfwGetX11Window(window); + return NULL; // TODO: Find a way to return value... cast to void *? +#elif defined(__APPLE__) + // NOTE: Returned handle is: (objc_object *) + return NULL; // TODO: return (void *)glfwGetCocoaWindow(window); +#else + return NULL; +#endif +} + +// Get number of monitors +int GetMonitorCount(void) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + glfwGetMonitors(&monitorCount); + return monitorCount; +#else + return 1; +#endif +} + +// Get primary monitor width +int GetMonitorWidth(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + return mode->width; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get primary monitor width +int GetMonitorHeight(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + return mode->height; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get primary montior physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + int physicalWidth; + glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL); + return physicalWidth; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get primary monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + int physicalHeight; + glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight); + return physicalHeight; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the primary monitor +const char *GetMonitorName(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + return glfwGetMonitorName(monitors[monitor]); + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return ""; +} + // Show mouse cursor void ShowCursor() { @@ -1306,6 +1481,21 @@ void TakeScreenshot(const char *fileName) #endif } +// Check if the file exists +bool FileExists(const char *fileName) +{ + bool result = false; + +#if defined(_WIN32) + if (_access(fileName, 0) != -1) +#else + if (access(fileName, F_OK) != -1) +#endif + result = true; + + return result; +} + // Check file extension bool IsFileExtension(const char *fileName, const char *ext) { @@ -1366,6 +1556,41 @@ const char *GetFileName(const char *filePath) return fileName + 1; } +// Get filename string without extension (memory should be freed) +const char *GetFileNameWithoutExt(const char *filePath) +{ + char *result, *lastDot, *lastSep; + + char nameDot = '.'; // Default filename to extension separator character + char pathSep = '/'; // Default filepath separator character + + // Error checks and allocate string + if (filePath == NULL) return NULL; + + // Try to allocate new string, same size as original + // NOTE: By default strlen() does not count the '\0' character + if ((result = malloc(strlen(filePath) + 1)) == NULL) return NULL; + + strcpy(result, filePath); // Make a copy of the string + + // NOTE: strrchr() returns a pointer to the last occurrence of character + lastDot = strrchr(result, nameDot); + lastSep = (pathSep == 0) ? NULL : strrchr(result, pathSep); + + if (lastDot != NULL) // Check if it has an extension separator... + { + if (lastSep != NULL) // ...and it's before the extenstion separator... + { + if (lastSep < lastDot) + { + *lastDot = '\0'; // ...then remove it + } + } + else *lastDot = '\0'; // Has extension separator with no path separator + } + + return result; // Return the modified string +} // Get directory for a given fileName (with path) const char *GetDirectoryPath(const char *fileName) @@ -1375,11 +1600,11 @@ const char *GetDirectoryPath(const char *fileName) memset(filePath, 0, 256); lastSlash = strprbrk(fileName, "\\/"); - if (!lastSlash) - return NULL; + if (!lastSlash) return NULL; + // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' strncpy(filePath, fileName, strlen(fileName) - (strlen(lastSlash) - 1)); - filePath[strlen(fileName) - strlen(lastSlash)] = '\0'; + filePath[strlen(fileName) - strlen(lastSlash)] = '\0'; // Add '\0' manually return filePath; } @@ -1395,6 +1620,57 @@ const char *GetWorkingDirectory(void) return currentDir; } +// Get filenames in a directory path (max 256 files) +// NOTE: Files count is returned by parameters pointer +char **GetDirectoryFiles(const char *dirPath, int *fileCount) +{ + #define MAX_FILEPATH_LENGTH 256 + #define MAX_DIRECTORY_FILES 512 + + ClearDirectoryFiles(); + + // Memory allocation for MAX_DIRECTORY_FILES + dirFilesPath = (char **)malloc(sizeof(char *)*MAX_DIRECTORY_FILES); + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH); + + int counter = 0; + struct dirent *ent; + DIR *dir = opendir(dirPath); + + if (dir != NULL) // It's a directory + { + // TODO: Reading could be done in two passes, + // first one to count files and second one to read names + // That way we can allocate required memory, instead of a limited pool + + while ((ent = readdir(dir)) != NULL) + { + strcpy(dirFilesPath[counter], ent->d_name); + counter++; + } + + closedir(dir); + } + else TraceLog(LOG_WARNING, "Can not open directory...\n"); // Maybe it's a file... + + dirFilesCount = counter; + *fileCount = dirFilesCount; + + return dirFilesPath; +} + +// Clear directory files paths buffers +void ClearDirectoryFiles(void) +{ + if (dirFilesCount > 0) + { + for (int i = 0; i < dirFilesCount; i++) free(dirFilesPath[i]); + + free(dirFilesPath); + dirFilesCount = 0; + } +} + // Change working directory, returns true if success bool ChangeDirectory(const char *dir) { @@ -1438,6 +1714,21 @@ void ClearDroppedFiles(void) #endif } +// Get file modification time (last write time) +RLAPI long GetFileModTime(const char *fileName) +{ + struct stat result = { 0 }; + + if (stat(fileName, &result) == 0) + { + time_t mod = result.st_mtime; + + return (long)mod; + } + + return 0; +} + // Save integer value to storage file (to defined position) // NOTE: Storage positions is directly related to file memory layout (4 bytes each integer) void StorageSaveValue(int position, int value) @@ -1709,7 +2000,7 @@ bool IsMouseButtonPressed(int button) #else if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true; #endif - + return pressed; } @@ -1853,7 +2144,9 @@ Vector2 GetTouchPosition(int index) position.x = position.x*((float)renderWidth/(float)displayWidth) - renderOffsetX/2; position.y = position.y*((float)renderHeight/(float)displayHeight) - renderOffsetY/2; } -#else // PLATFORM_DESKTOP, PLATFORM_RPI +#elif defined(PLATFORM_RPI) + position = touchPosition[index]; +#else // PLATFORM_DESKTOP if (index == 0) position = GetMousePosition(); #endif @@ -1994,7 +2287,7 @@ static bool InitGraphicsDevice(int width, int height) // At this point we need to manage render size vs screen size // NOTE: This function uses and modifies global module variables: // screenWidth/screenHeight - renderWidth/renderHeight - downscaleView - SetupFramebufferSize(displayWidth, displayHeight); + SetupFramebuffer(displayWidth, displayHeight); window = glfwCreateWindow(displayWidth, displayHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); @@ -2228,7 +2521,7 @@ static bool InitGraphicsDevice(int width, int height) } } - //SetupFramebufferSize(displayWidth, displayHeight); + //SetupFramebuffer(displayWidth, displayHeight); EGLint numConfigs = 0; if ((eglChooseConfig(display, framebufferAttribs, &config, 1, &numConfigs) == EGL_FALSE) || (numConfigs == 0)) @@ -2334,7 +2627,7 @@ static bool InitGraphicsDevice(int width, int height) // At this point we need to manage render size vs screen size // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView - SetupFramebufferSize(displayWidth, displayHeight); + SetupFramebuffer(displayWidth, displayHeight); ANativeWindow_setBuffersGeometry(androidApp->window, renderWidth, renderHeight, displayFormat); //ANativeWindow_setBuffersGeometry(androidApp->window, 0, 0, displayFormat); // Force use of native display size @@ -2347,7 +2640,7 @@ static bool InitGraphicsDevice(int width, int height) // At this point we need to manage render size vs screen size // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView - SetupFramebufferSize(displayWidth, displayHeight); + SetupFramebuffer(displayWidth, displayHeight); dstRect.x = 0; dstRect.y = 0; @@ -2449,7 +2742,7 @@ static void SetupViewport(void) // Compute framebuffer size relative to screen size and display size // NOTE: Global variables renderWidth/renderHeight and renderOffsetX/renderOffsetY can be modified -static void SetupFramebufferSize(int displayWidth, int displayHeight) +static void SetupFramebuffer(int width, int height) { // Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var) if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) @@ -2620,6 +2913,21 @@ static void PollInputEvents(void) gamepadAxisCount = 0; #endif +#if defined(PLATFORM_RPI) + // Register previous keys states + for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i]; + + // Register previous mouse states + previousMouseWheelY = currentMouseWheelY; + currentMouseWheelY = 0; + for (int i = 0; i < 3; i++) + { + previousMouseState[i] = currentMouseState[i]; + currentMouseState[i] = currentMouseStateEvdev[i]; + } + +#endif + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // Mouse input polling double mouseX; @@ -2773,6 +3081,16 @@ static void SwapBuffers(void) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSwapBuffers(window); +#if __APPLE__ + // Workaround for missing/erroneous initial rendering on macOS + if (windowNeedsUpdating) + { + // Desugared version of Objective C: [glfwGetNSGLContext(window) update] + ((id (*)(id, SEL))objc_msgSend)(glfwGetNSGLContext(window), sel_registerName("update")); + + windowNeedsUpdating--; + } +#endif #endif #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) @@ -2952,7 +3270,7 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) } // GLFW3 WindowIconify Callback, runs when window is minimized/restored -static void WindowIconifyCallback(GLFWwindow* window, int iconified) +static void WindowIconifyCallback(GLFWwindow *window, int iconified) { if (iconified) windowMinimized = true; // The window was iconified else windowMinimized = false; // The window was restored @@ -3544,184 +3862,364 @@ static void RestoreKeyboard(void) // Mouse initialization (including mouse thread) static void InitMouse(void) { - // NOTE: We can use /dev/input/mice to read from all available mice - if ((mouseStream = open(DEFAULT_MOUSE_DEV, O_RDONLY|O_NONBLOCK)) < 0) + char Path[256]; + DIR *directory; + struct dirent *entity; + + // Reset variables + for (int i = 0; i < MAX_TOUCH_POINTS; ++i) { - TraceLog(LOG_WARNING, "Mouse device could not be opened, no mouse available"); + touchPosition[i].x = -1; + touchPosition[i].y = -1; + } + + // Open the linux directory of "/dev/input" + directory = opendir(DEFAULT_EVDEV_PATH); + if (directory) + { + while ((entity = readdir(directory)) != NULL) + { + if (strncmp("event", entity->d_name, strlen("event")) == 0) // Search for devices named "event*" + { + sprintf(Path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); + EventThreadSpawn(Path); // Identify the device and spawn a thread for it + } + } + + closedir(directory); } else { - mouseReady = true; - - int error = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL); - - if (error != 0) TraceLog(LOG_WARNING, "Error creating mouse input event thread"); - else TraceLog(LOG_INFO, "Mouse device initialized successfully"); + TraceLog(LOG_WARNING, "Unable to open linux event directory %s", DEFAULT_EVDEV_PATH); } } -// Mouse reading thread -// NOTE: We need a separate thread to avoid loosing mouse events, -// if too much time passes between reads, queue gets full and new events override older ones... -static void *MouseThread(void *arg) +static void EventThreadSpawn(char *device) { - const unsigned char XSIGN = (1 << 4); - const unsigned char YSIGN = (1 << 5); + #define BITS_PER_LONG (sizeof(long)*8) + #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1) + #define OFF(x) ((x)%BITS_PER_LONG) + #define BIT(x) (1UL<> OFF(bit)) & 1) + + struct input_absinfo absinfo; + unsigned long evBits[NBITS(EV_MAX)]; + unsigned long absBits[NBITS(ABS_MAX)]; + unsigned long relBits[NBITS(REL_MAX)]; + unsigned long keyBits[NBITS(KEY_MAX)]; + bool hasAbs = false; + bool hasRel = false; + bool hasAbsMulti = false; + int freeWorkerId = -1; + int fd = -1; + + InputEventWorker *worker; - typedef struct { - char buttons; - char dx, dy; - } MouseEvent; + /////////////////////////////////// Open the device and allocate worker ///////////////////////////////////////////// - MouseEvent mouse; - - int mouseRelX = 0; - int mouseRelY = 0; - - while (!windowShouldClose) + // Find a free spot in the workers array + for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i) { - if (read(mouseStream, &mouse, sizeof(MouseEvent)) == (int)sizeof(MouseEvent)) + if (eventWorkers[i].threadId == 0) { - if ((mouse.buttons & 0x08) == 0) break; // This bit should always be set - - // Check Left button pressed - if ((mouse.buttons & 0x01) > 0) currentMouseState[0] = 1; - else currentMouseState[0] = 0; - - // Check Right button pressed - if ((mouse.buttons & 0x02) > 0) currentMouseState[1] = 1; - else currentMouseState[1] = 0; - - // Check Middle button pressed - if ((mouse.buttons & 0x04) > 0) currentMouseState[2] = 1; - else currentMouseState[2] = 0; - - mouseRelX = (int)mouse.dx; - mouseRelY = (int)mouse.dy; - - if ((mouse.buttons & XSIGN) > 0) mouseRelX = -1*(255 - mouseRelX); - if ((mouse.buttons & YSIGN) > 0) mouseRelY = -1*(255 - mouseRelY); - - // NOTE: Mouse movement is normalized to not be screen resolution dependant - // We suppose 2*255 (max relative movement) is equivalent to screenWidth (max pixels width) - // Result after normalization is multiplied by MOUSE_SENSITIVITY factor - - mousePosition.x += (float)mouseRelX*((float)screenWidth/(2*255))*MOUSE_SENSITIVITY; - mousePosition.y -= (float)mouseRelY*((float)screenHeight/(2*255))*MOUSE_SENSITIVITY; - - if (mousePosition.x < 0) mousePosition.x = 0; - if (mousePosition.y < 0) mousePosition.y = 0; - - if (mousePosition.x > screenWidth) mousePosition.x = screenWidth; - if (mousePosition.y > screenHeight) mousePosition.y = screenHeight; - } - //else read(mouseStream, &mouse, 1); // Try to sync up again - } - - return NULL; -} - -// Touch initialization (including touch thread) -static void InitTouch(void) -{ - if ((touchStream = open(DEFAULT_TOUCH_DEV, O_RDONLY|O_NONBLOCK)) < 0) - { - TraceLog(LOG_WARNING, "Touch device could not be opened, no touchscreen available"); - } - else - { - touchReady = true; - - int error = pthread_create(&touchThreadId, NULL, &TouchThread, NULL); - - if (error != 0) TraceLog(LOG_WARNING, "Error creating touch input event thread"); - else TraceLog(LOG_INFO, "Touch device initialized successfully"); - } -} - -// Touch reading thread. -// This reads from a Virtual Input Event /dev/input/event4 which is -// created by the ts_uinput daemon. This takes, filters and scales -// raw input from the Touchscreen (which appears in /dev/input/event3) -// based on the Calibration data referenced by tslib. -static void *TouchThread(void *arg) -{ - struct input_event ev; - GestureEvent gestureEvent; - - while (!windowShouldClose) - { - if (read(touchStream, &ev, sizeof(ev)) == (int)sizeof(ev)) - { - // if pressure > 0 then simulate left mouse button click - if (ev.type == EV_ABS && ev.code == 24 && ev.value == 0 && currentMouseState[0] == 1) - { - currentMouseState[0] = 0; - gestureEvent.touchAction = TOUCH_UP; - gestureEvent.pointCount = 1; - gestureEvent.pointerId[0] = 0; - gestureEvent.pointerId[1] = 1; - gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - gestureEvent.position[1].x /= (float)GetScreenWidth(); - gestureEvent.position[1].y /= (float)GetScreenHeight(); - ProcessGestureEvent(gestureEvent); - } - if (ev.type == EV_ABS && ev.code == 24 && ev.value > 0 && currentMouseState[0] == 0) - { - currentMouseState[0] = 1; - gestureEvent.touchAction = TOUCH_DOWN; - gestureEvent.pointCount = 1; - gestureEvent.pointerId[0] = 0; - gestureEvent.pointerId[1] = 1; - gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - gestureEvent.position[1].x /= (float)GetScreenWidth(); - gestureEvent.position[1].y /= (float)GetScreenHeight(); - ProcessGestureEvent(gestureEvent); - } - // x & y values supplied by event4 have been scaled & de-jittered using tslib calibration data - if (ev.type == EV_ABS && ev.code == 0) - { - mousePosition.x = ev.value; - if (mousePosition.x < 0) mousePosition.x = 0; - if (mousePosition.x > screenWidth) mousePosition.x = screenWidth; - gestureEvent.touchAction = TOUCH_MOVE; - gestureEvent.pointCount = 1; - gestureEvent.pointerId[0] = 0; - gestureEvent.pointerId[1] = 1; - gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - gestureEvent.position[1].x /= (float)GetScreenWidth(); - gestureEvent.position[1].y /= (float)GetScreenHeight(); - ProcessGestureEvent(gestureEvent); - } - if (ev.type == EV_ABS && ev.code == 1) - { - mousePosition.y = ev.value; - if (mousePosition.y < 0) mousePosition.y = 0; - if (mousePosition.y > screenHeight) mousePosition.y = screenHeight; - gestureEvent.touchAction = TOUCH_MOVE; - gestureEvent.pointCount = 1; - gestureEvent.pointerId[0] = 0; - gestureEvent.pointerId[1] = 1; - gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - gestureEvent.position[1].x /= (float)GetScreenWidth(); - gestureEvent.position[1].y /= (float)GetScreenHeight(); - ProcessGestureEvent(gestureEvent); - } - + freeWorkerId = i; + break; } } + + // Select the free worker from array + if (freeWorkerId >= 0) + { + worker = &(eventWorkers[freeWorkerId]); // Grab a pointer to the worker + memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker + } + else + { + TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Out of worker slots", device); + return; + } + + // Open the device + fd = open(device, O_RDONLY | O_NONBLOCK); + if (fd < 0) + { + TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Can't open device (Err: %d)", device, worker->fd); + return; + } + worker->fd = fd; + + //Grab number on the end of the devices name "event" + int devNum = 0; + char *ptrDevName = strrchr(device, 't'); + worker->eventNum = -1; + + if (ptrDevName != NULL) + { + if (sscanf(ptrDevName, "t%d", &devNum) == 1) + worker->eventNum = devNum; + } + + // At this point we have a connection to the device, + // but we don't yet know what the device is (Could be + // many things, even as simple as a power button) + + /////////////////////////////////// Identify the device ///////////////////////////////////////////// + + ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the avalable device properties + + // Check for absolute input devices + if (TEST_BIT(evBits, EV_ABS)) + { + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); + + // Check for absolute movement support (usualy touchscreens, but also joysticks) + if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) + { + hasAbs = true; + + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + worker->absRange.x = absinfo.minimum; + worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + worker->absRange.y = absinfo.minimum; + worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + + // Check for multiple absolute movement support (usualy multitouch touchscreens) + if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) + { + hasAbsMulti = true; + + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + worker->absRange.x = absinfo.minimum; + worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + worker->absRange.y = absinfo.minimum; + worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + } + + // Check for relative movement support (usualy mouse) + if (TEST_BIT(evBits, EV_REL)) + { + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); + + if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; + } + + // Check for button support to determine the device type(usualy on all input devices) + if (TEST_BIT(evBits, EV_KEY)) + { + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); + + if (hasAbs || hasAbsMulti) + { + if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen + if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet + if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet + if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet + if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device + } + + if (hasRel) + { + if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse + if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse + } + + if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad + + if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard + } + + + /////////////////////////////////// Decide what to do with the device ///////////////////////////////////////////// + if (worker->isTouch || worker->isMouse) + { + // Looks like a interesting device + TraceLog(LOG_INFO, "Opening input device '%s' (%s%s%s%s%s)", device, + worker->isMouse ? "mouse " : "", + worker->isMultitouch ? "multitouch " : "", + worker->isTouch ? "touchscreen " : "", + worker->isGamepad ? "gamepad " : "", + worker->isKeyboard ? "keyboard " : ""); + + // Create a thread for this device + int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker); + if (error != 0) + { + TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Can't create thread (Err: %d)", device, error); + worker->threadId = 0; + close(fd); + } + +#if defined(USE_LAST_TOUCH_DEVICE) + // Find touchscreen with the highest index + int maxTouchNumber = -1; + + for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i) + { + if (eventWorkers[i].isTouch && (eventWorkers[i].eventNum > maxTouchNumber)) maxTouchNumber = eventWorkers[i].eventNum; + } + + // Find toucnscreens with lower indexes + for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i) + { + if (eventWorkers[i].isTouch && (eventWorkers[i].eventNum < maxTouchNumber)) + { + if (eventWorkers[i].threadId != 0) + { + TraceLog(LOG_WARNING, "Duplicate touchscreen found, killing toucnscreen on event%d", i); + pthread_cancel(eventWorkers[i].threadId); + close(eventWorkers[i].fd); + } + } + } +#endif + } + else close(fd); // We are not interested in this device +} + +static void *EventThread(void *arg) +{ + struct input_event event; + GestureEvent gestureEvent; + InputEventWorker *worker = (InputEventWorker *)arg; + bool GestureNeedsUpdate = false; + + while (!windowShouldClose) + { + if (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) + { + /////////////////////////////// Relative movement parsing //////////////////////////////////// + if (event.type == EV_REL) + { + if (event.code == REL_X) + { + mousePosition.x += event.value; + touchPosition[0].x = mousePosition.x; + gestureEvent.touchAction = TOUCH_MOVE; + GestureNeedsUpdate = true; + } + + if (event.code == REL_Y) + { + mousePosition.y += event.value; + touchPosition[0].y = mousePosition.y; + gestureEvent.touchAction = TOUCH_MOVE; + GestureNeedsUpdate = true; + } + + if (event.code == REL_WHEEL) + { + currentMouseWheelY += event.value; + } + } + + /////////////////////////////// Absolute movement parsing //////////////////////////////////// + if (event.type == EV_ABS) + { + // Basic movement + if (event.code == ABS_X) + { + mousePosition.x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange + gestureEvent.touchAction = TOUCH_MOVE; + GestureNeedsUpdate = true; + } + + if (event.code == ABS_Y) + { + mousePosition.y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange + gestureEvent.touchAction = TOUCH_MOVE; + GestureNeedsUpdate = true; + } + + // Multitouch movement + if (event.code == ABS_MT_SLOT) + { + worker->touchSlot = event.value; // Remeber the slot number for the folowing events + } + + if (event.code == ABS_MT_POSITION_X) + { + if (worker->touchSlot < MAX_TOUCH_POINTS) + touchPosition[worker->touchSlot].x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange + } + + if (event.code == ABS_MT_POSITION_Y) + { + if (worker->touchSlot < MAX_TOUCH_POINTS) + touchPosition[worker->touchSlot].y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange + } + + if (event.code == ABS_MT_TRACKING_ID) + { + if ( (event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS) ) + { + // Touch has ended for this point + touchPosition[worker->touchSlot].x = -1; + touchPosition[worker->touchSlot].y = -1; + } + } + } + + /////////////////////////////// Button parsing //////////////////////////////////// + if (event.type == EV_KEY) + { + if((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) + { + currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = event.value; + if (event.value > 0) gestureEvent.touchAction = TOUCH_DOWN; + else gestureEvent.touchAction = TOUCH_UP; + GestureNeedsUpdate = true; + } + + if (event.code == BTN_RIGHT) currentMouseStateEvdev[MOUSE_RIGHT_BUTTON] = event.value; + + if (event.code == BTN_MIDDLE) currentMouseStateEvdev[MOUSE_MIDDLE_BUTTON] = event.value; + } + + /////////////////////////////// Screen confinement //////////////////////////////////// + if (mousePosition.x < 0) mousePosition.x = 0; + if (mousePosition.x > screenWidth/mouseScale) mousePosition.x = screenWidth/mouseScale; + + if (mousePosition.y < 0) mousePosition.y = 0; + if (mousePosition.y > screenHeight/mouseScale) mousePosition.y = screenHeight/mouseScale; + + /////////////////////////////// Gesture update //////////////////////////////////// + if (GestureNeedsUpdate) + { + gestureEvent.pointCount = 0; + if (touchPosition[0].x >= 0) gestureEvent.pointCount++; + if (touchPosition[1].x >= 0) gestureEvent.pointCount++; + if (touchPosition[2].x >= 0) gestureEvent.pointCount++; + if (touchPosition[3].x >= 0) gestureEvent.pointCount++; + gestureEvent.pointerId[0] = 0; + gestureEvent.pointerId[1] = 1; + gestureEvent.pointerId[2] = 2; + gestureEvent.pointerId[3] = 3; + gestureEvent.position[0] = touchPosition[0]; + gestureEvent.position[1] = touchPosition[1]; + gestureEvent.position[2] = touchPosition[2]; + gestureEvent.position[3] = touchPosition[3]; + ProcessGestureEvent(gestureEvent); + } + } + else + { + usleep(5000); // Sleep for 5ms to avoid hogging CPU time + } + } + + close(worker->fd); + return NULL; } @@ -3805,6 +4303,10 @@ static void *GamepadThread(void *arg) } } } + else + { + usleep(1000); //Sleep for 1ms to avoid hogging CPU time + } } } diff --git a/src/external/dirent.h b/src/external/dirent.h new file mode 100644 index 000000000..a955253e0 --- /dev/null +++ b/src/external/dirent.h @@ -0,0 +1,198 @@ +#ifndef DIRENT_H +#define DIRENT_H + +/* + + Declaration of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003. + Rights: See end of file. + +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct DIR DIR; + +struct dirent +{ + char *d_name; +}; + +DIR *opendir(const char *); +int closedir(DIR *); +struct dirent *readdir(DIR *); +void rewinddir(DIR *); + +/* + + Copyright Kevlin Henney, 1997, 2003. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + +*/ + +#ifdef __cplusplus +} +#endif + +#endif // DIRENT_H + +/* + + Implementation of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003 and July 2012. + Rights: See end of file. + +*/ + +#include +#include /* _findfirst and _findnext set errno iff they return -1 */ +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */ + +struct DIR +{ + handle_type handle; /* -1 for failed rewind */ + struct _finddata_t info; + struct dirent result; /* d_name null iff first time */ + char *name; /* null-terminated char string */ +}; + +DIR *opendir(const char *name) +{ + DIR *dir = 0; + + if(name && name[0]) + { + size_t base_length = strlen(name); + const char *all = /* search pattern must end with suitable wildcard */ + strchr("/\\", name[base_length - 1]) ? "*" : "/*"; + + if((dir = (DIR *) malloc(sizeof *dir)) != 0 && + (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0) + { + strcat(strcpy(dir->name, name), all); + + if((dir->handle = + (handle_type) _findfirst(dir->name, &dir->info)) != -1) + { + dir->result.d_name = 0; + } + else /* rollback */ + { + free(dir->name); + free(dir); + dir = 0; + } + } + else /* rollback */ + { + free(dir); + dir = 0; + errno = ENOMEM; + } + } + else + { + errno = EINVAL; + } + + return dir; +} + +int closedir(DIR *dir) +{ + int result = -1; + + if(dir) + { + if(dir->handle != -1) + { + result = _findclose(dir->handle); + } + + free(dir->name); + free(dir); + } + + if(result == -1) /* map all errors to EBADF */ + { + errno = EBADF; + } + + return result; +} + +struct dirent *readdir(DIR *dir) +{ + struct dirent *result = 0; + + if(dir && dir->handle != -1) + { + if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) + { + result = &dir->result; + result->d_name = dir->info.name; + } + } + else + { + errno = EBADF; + } + + return result; +} + +void rewinddir(DIR *dir) +{ + if(dir && dir->handle != -1) + { + _findclose(dir->handle); + dir->handle = (handle_type) _findfirst(dir->name, &dir->info); + dir->result.d_name = 0; + } + else + { + errno = EBADF; + } +} + +#ifdef __cplusplus +} +#endif + +/* + + Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + +*/ diff --git a/src/external/dr_mp3.h b/src/external/dr_mp3.h index 467d319da..cd8920a14 100644 --- a/src/external/dr_mp3.h +++ b/src/external/dr_mp3.h @@ -1,5 +1,5 @@ // MP3 audio decoder. Public domain. See "unlicense" statement at the end of this file. -// dr_mp3 - v0.2.5 - 2018-06-22 +// dr_mp3 - v0.3.2 - 2018-09-11 // // David Reid - mackron@gmail.com // @@ -99,28 +99,26 @@ typedef drmp3_uint32 drmp3_bool32; // ================== typedef struct { - int frame_bytes; - int channels; - int hz; - int layer; - int bitrate_kbps; + int frame_bytes, channels, hz, layer, bitrate_kbps; } drmp3dec_frame_info; typedef struct { - float mdct_overlap[2][9*32]; - float qmf_state[15*2*32]; - int reserv; - int free_format_bytes; - unsigned char header[4]; - unsigned char reserv_buf[511]; + float mdct_overlap[2][9*32], qmf_state[15*2*32]; + int reserv, free_format_bytes; + unsigned char header[4], reserv_buf[511]; } drmp3dec; // Initializes a low level decoder. void drmp3dec_init(drmp3dec *dec); // Reads a frame from a low level decoder. -int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info); +int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); + +// Helper for converting between f32 and s16. +void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples); + + // Main API (Pull API) @@ -220,7 +218,7 @@ typedef struct drmp3_uint32 frameSampleRate; // The sample rate of the currently loaded MP3 frame. Internal use only. drmp3_uint32 framesConsumed; drmp3_uint32 framesRemaining; - drmp3_int16 frames[DRMP3_MAX_SAMPLES_PER_FRAME]; + drmp3_uint8 frames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; // <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. drmp3_src src; size_t dataSize; size_t dataCapacity; @@ -314,8 +312,12 @@ void drmp3_free(void* p); #define DR_MP3_NO_SIMD #endif +#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) + #define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES #define DRMP3_MAX_FRAME_SYNC_MATCHES 10 +#endif #define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ @@ -361,7 +363,7 @@ void drmp3_free(void* p); #if defined(_MSC_VER) #include #endif -#include +#include #define DRMP3_HAVE_SSE 1 #define DRMP3_HAVE_SIMD 1 #define DRMP3_VSTORE _mm_storeu_ps @@ -462,44 +464,27 @@ static int drmp3_have_simd() typedef struct { const drmp3_uint8 *buf; - int pos; - int limit; + int pos, limit; } drmp3_bs; typedef struct { - drmp3_uint8 total_bands; - drmp3_uint8 stereo_bands; - drmp3_uint8 bitalloc[64]; - drmp3_uint8 scfcod[64]; float scf[3*64]; + drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; } drmp3_L12_scale_info; typedef struct { - drmp3_uint8 tab_offset; - drmp3_uint8 code_tab_width; - drmp3_uint8 band_count; + drmp3_uint8 tab_offset, code_tab_width, band_count; } drmp3_L12_subband_alloc; typedef struct { const drmp3_uint8 *sfbtab; - drmp3_uint16 part_23_length; - drmp3_uint16 big_values; - drmp3_uint16 scalefac_compress; - drmp3_uint8 global_gain; - drmp3_uint8 block_type; - drmp3_uint8 mixed_block_flag; - drmp3_uint8 n_long_sfb; - drmp3_uint8 n_short_sfb; - drmp3_uint8 table_select[3]; - drmp3_uint8 region_count[3]; - drmp3_uint8 subblock_gain[3]; - drmp3_uint8 preflag; - drmp3_uint8 scalefac_scale; - drmp3_uint8 count1_table; - drmp3_uint8 scfsi; + drmp3_uint16 part_23_length, big_values, scalefac_compress; + drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; + drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; } drmp3_L3_gr_info; typedef struct @@ -507,10 +492,8 @@ typedef struct drmp3_bs bs; drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; drmp3_L3_gr_info gr_info[4]; - float grbuf[2][576]; - float scf[40]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; drmp3_uint8 ist_pos[2][39]; - float syn[18 + 15][2*32]; } drmp3dec_scratch; static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) @@ -988,17 +971,19 @@ static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *is } } +static const float g_drmp3_pow43[129 + 16] = { + 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, + 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f +}; + static float drmp3_L3_pow_43(int x) { - static const float g_pow43[129] = { - 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f - }; float frac; int sign, mult = 256; if (x < 129) { - return g_pow43[x]; + return g_drmp3_pow43[16 + x]; } if (x < 1024) @@ -1009,12 +994,11 @@ static float drmp3_L3_pow_43(int x) sign = 2*x & 64; frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_pow43[(x + sign) >> 6]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; + return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; } static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) { - static const float g_pow43_signed[32] = { 0,0,1,-1,2.519842f,-2.519842f,4.326749f,-4.326749f,6.349604f,-6.349604f,8.549880f,-8.549880f,10.902724f,-10.902724f,13.390518f,-13.390518f,16.000000f,-16.000000f,18.720754f,-18.720754f,21.544347f,-21.544347f,24.463781f,-24.463781f,27.473142f,-27.473142f,30.567351f,-30.567351f,33.741992f,-33.741992f,36.993181f,-36.993181f }; static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, @@ -1053,7 +1037,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g { int tab_num = gr_info->table_select[ireg]; int sfb_cnt = gr_info->region_count[ireg++]; - const short *codebook = tabs + tabindex[tab_num]; + const drmp3_int16 *codebook = tabs + tabindex[tab_num]; int linbits = g_linbits[tab_num]; do { @@ -1083,7 +1067,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g *dst = one*drmp3_L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1); } else { - *dst = g_pow43_signed[lsb*2 + (bs_cache >> 31)]*one; + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; } DRMP3_FLUSH_BITS(lsb ? 1 : 0); } @@ -1659,18 +1643,27 @@ static void drmp3d_DCT_II(float *grbuf, int n) #endif } -static short drmp3d_scale_pcm(float sample) -{ - if (sample > 32767.0) return (short) 32767; - if (sample < -32768.0) return (short)-32768; - int s = (int)(sample + .5f); - s -= (s < 0); /* away from zero, to be compliant */ - if (s > 32767) return (short) 32767; - if (s < -32768) return (short)-32768; - return (short)s; -} +#ifndef DR_MP3_FLOAT_OUTPUT +typedef drmp3_int16 drmp3d_sample_t; -static void drmp3d_synth_pair(short *pcm, int nch, const float *z) +static drmp3_int16 drmp3d_scale_pcm(float sample) +{ + if (sample >= 32766.5) return (drmp3_int16) 32767; + if (sample <= -32767.5) return (drmp3_int16)-32768; + drmp3_int16 s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + return (drmp3_int16)s; +} +#else +typedef float drmp3d_sample_t; + +static float drmp3d_scale_pcm(float sample) +{ + return sample*(1.f/32768.f); +} +#endif + +static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) { float a; a = (z[14*64] - z[ 0]) * 29; @@ -1695,11 +1688,11 @@ static void drmp3d_synth_pair(short *pcm, int nch, const float *z) pcm[16*nch] = drmp3d_scale_pcm(a); } -static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) +static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) { int i; float *xr = xl + 576*(nch - 1); - short *dstr = dstl + (nch - 1); + drmp3d_sample_t *dstr = dstl + (nch - 1); static const float g_win[] = { -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, @@ -1756,19 +1749,20 @@ static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) { +#ifndef DR_MP3_FLOAT_OUTPUT #if DRMP3_HAVE_SSE static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 6); + dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); #else int16x4_t pcma, pcmb; a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); @@ -1784,6 +1778,30 @@ static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); #endif +#else + static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + a = DRMP3_VMUL(a, g_scale); + b = DRMP3_VMUL(b, g_scale); +#if DRMP3_HAVE_SSE + _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); +#else + vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); + vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); + vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); + vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); + vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); + vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); + vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); + vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); +#endif +#endif /* DR_MP3_FLOAT_OUTPUT */ } } else #endif @@ -1821,7 +1839,7 @@ static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) #endif } -static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, short *pcm, float *lins) +static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) { int i; for (i = 0; i < nch; i++) @@ -1906,7 +1924,7 @@ void drmp3dec_init(drmp3dec *dec) dec->header[0] = 0; } -int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info) +int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) { int i = 0, igr, frame_size = 0, success = 1; const drmp3_uint8 *hdr; @@ -1940,6 +1958,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); + if (!pcm) + { + return drmp3_hdr_frame_samples(hdr); + } + drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); if (DRMP3_HDR_IS_CRC(hdr)) { @@ -1957,11 +1980,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); if (success) { - for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm += 576*info->channels) + for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) { memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, pcm, scratch.syn[0]); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); } } drmp3_L3_save_reservoir(dec, &scratch); @@ -1980,9 +2003,9 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes { i = 0; drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, pcm, scratch.syn[0]); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); - pcm += 384*info->channels; + pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); } if (bs_frame->pos > bs_frame->limit) { @@ -1995,6 +2018,64 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes return success*drmp3_hdr_frame_samples(dec->header); } +void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples) +{ + if(num_samples > 0) + { + int i = 0; +#if DRMP3_HAVE_SIMD + int aligned_count = num_samples & ~7; + for(; i < aligned_count; i+=8) + { + static const drmp3_f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f }; + drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), g_scale); + drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), g_scale); +#if DRMP3_HAVE_SSE + static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); + out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); +#else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(out+i , pcma, 0); + vst1_lane_s16(out+i+1, pcma, 1); + vst1_lane_s16(out+i+2, pcma, 2); + vst1_lane_s16(out+i+3, pcma, 3); + vst1_lane_s16(out+i+4, pcmb, 0); + vst1_lane_s16(out+i+5, pcmb, 1); + vst1_lane_s16(out+i+6, pcmb, 2); + vst1_lane_s16(out+i+7, pcmb, 3); +#endif + } +#endif + for(; i < num_samples; i++) + { + float sample = in[i] * 32768.0f; + if (sample >= 32766.5) + out[i] = (drmp3_int16) 32767; + else if (sample <= -32767.5) + out[i] = (drmp3_int16)-32768; + else + { + short s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + out[i] = s; + } + } + } +} @@ -2004,6 +2085,16 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes // /////////////////////////////////////////////////////////////////////////////// +#if defined(SIZE_MAX) + #define DRMP3_SIZE_MAX SIZE_MAX +#else + #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) + #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRMP3_SIZE_MAX 0xFFFFFFFF + #endif +#endif + // Options. #ifndef DR_MP3_DEFAULT_CHANNELS #define DR_MP3_DEFAULT_CHANNELS 2 @@ -2311,8 +2402,10 @@ static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3) size_t bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { - pMP3->atEnd = DRMP3_TRUE; - return DRMP3_FALSE; // No data. + if (pMP3->dataSize == 0) { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; // No data. + } } pMP3->dataSize += bytesRead; @@ -2324,7 +2417,7 @@ static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3) } drmp3dec_frame_info info; - drmp3_uint32 samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pMP3->frames, &info); // <-- Safe size_t -> int conversion thanks to the check above. + drmp3_uint32 samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, (drmp3d_sample_t*)pMP3->frames, &info); // <-- Safe size_t -> int conversion thanks to the check above. if (samplesRead != 0) { size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes); for (size_t i = 0; i < leftoverDataSize; ++i) { @@ -2377,28 +2470,54 @@ static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, voi while (frameCount > 0) { // Read from the in-memory buffer first. while (pMP3->framesRemaining > 0 && frameCount > 0) { + drmp3d_sample_t* frames = (drmp3d_sample_t*)pMP3->frames; +#ifndef DR_MP3_FLOAT_OUTPUT if (pMP3->frameChannels == 1) { if (pMP3->channels == 1) { // Mono -> Mono. - pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + pFramesOutF[0] = frames[pMP3->framesConsumed] / 32768.0f; } else { // Mono -> Stereo. - pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; - pFramesOutF[1] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + pFramesOutF[0] = frames[pMP3->framesConsumed] / 32768.0f; + pFramesOutF[1] = frames[pMP3->framesConsumed] / 32768.0f; } } else { if (pMP3->channels == 1) { // Stereo -> Mono float sample = 0; - sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; - sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; pFramesOutF[0] = sample * 0.5f; } else { // Stereo -> Stereo - pFramesOutF[0] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; - pFramesOutF[1] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + pFramesOutF[0] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + pFramesOutF[1] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; } } +#else + if (pMP3->frameChannels == 1) { + if (pMP3->channels == 1) { + // Mono -> Mono. + pFramesOutF[0] = frames[pMP3->framesConsumed]; + } else { + // Mono -> Stereo. + pFramesOutF[0] = frames[pMP3->framesConsumed]; + pFramesOutF[1] = frames[pMP3->framesConsumed]; + } + } else { + if (pMP3->channels == 1) { + // Stereo -> Mono + float sample = 0; + sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+0]; + sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+1]; + pFramesOutF[0] = sample * 0.5f; + } else { + // Stereo -> Stereo + pFramesOutF[0] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+0]; + pFramesOutF[1] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+1]; + } + } +#endif pMP3->framesConsumed += 1; pMP3->framesRemaining -= 1; @@ -2466,11 +2585,13 @@ drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek srcConfig.channels = pMP3->channels; srcConfig.algorithm = drmp3_src_algorithm_linear; if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) { + drmp3_uninit(pMP3); return DRMP3_FALSE; } // Decode the first frame to confirm that it is indeed a valid MP3 stream. if (!drmp3_decode_next_frame(pMP3)) { + drmp3_uninit(pMP3); return DRMP3_FALSE; // Not a valid MP3 stream. } @@ -2681,7 +2802,7 @@ float* drmp3__full_decode_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp } drmp3_uint64 newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float); - if (newFramesBufferSize > SIZE_MAX) { + if (newFramesBufferSize > DRMP3_SIZE_MAX) { break; } @@ -2771,6 +2892,37 @@ void drmp3_free(void* p) // REVISION HISTORY // =============== // +// v0.3.2 - 2018-09-11 +// - Fix a couple of memory leaks. +// - Bring up to date with minimp3. +// +// v0.3.1 - 2018-08-25 +// - Fix C++ build. +// +// v0.3.0 - 2018-08-25 +// - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has +// been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or +// not the DR_MP3_FLOAT_OUTPUT option is set. +// +// v0.2.11 - 2018-08-08 +// - Fix a bug where the last part of a file is not read. +// +// v0.2.10 - 2018-08-07 +// - Improve 64-bit detection. +// +// v0.2.9 - 2018-08-05 +// - Fix C++ build on older versions of GCC. +// - Bring up to date with minimp3. +// +// v0.2.8 - 2018-08-02 +// - Fix compilation errors with older versions of GCC. +// +// v0.2.7 - 2018-07-13 +// - Bring up to date with minimp3. +// +// v0.2.6 - 2018-07-12 +// - Bring up to date with minimp3. +// // v0.2.5 - 2018-06-22 // - Bring up to date with minimp3. // diff --git a/src/external/glfw/include/GLFW/glfw3native.h b/src/external/glfw/include/GLFW/glfw3native.h index 4372cb766..d585496c9 100644 --- a/src/external/glfw/include/GLFW/glfw3native.h +++ b/src/external/glfw/include/GLFW/glfw3native.h @@ -90,13 +90,21 @@ extern "C" { #undef APIENTRY #undef GLFW_APIENTRY_DEFINED #endif - #include +// RAY: Actually, only HWND handler needs to be defined +// Including windows.h could suppose symbols re-definition issues (i.e Rectangle type) +//#include + typedef void *PVOID; + typedef PVOID HANDLE; + typedef HANDLE HWND; #elif defined(GLFW_EXPOSE_NATIVE_COCOA) #include #if defined(__OBJC__) #import #else - typedef void* id; + // RAY: Added protection in case OBJC types defined + #if !OBJC_TYPES_DEFINED + typedef void* id; + #endif #endif #elif defined(GLFW_EXPOSE_NATIVE_X11) #include diff --git a/src/physac.h b/src/physac.h index 38789a6da..038361a45 100644 --- a/src/physac.h +++ b/src/physac.h @@ -96,8 +96,6 @@ #define PHYSAC_MAX_VERTICES 24 #define PHYSAC_CIRCLE_VERTICES 24 -#define PHYSAC_DESIRED_DELTATIME 1.0/60.0 -#define PHYSAC_MAX_TIMESTEP 0.02 #define PHYSAC_COLLISION_ITERATIONS 100 #define PHYSAC_PENETRATION_ALLOWANCE 0.05f #define PHYSAC_PENETRATION_CORRECTION 0.4f @@ -197,6 +195,7 @@ extern "C" { // Prevents name mangling of fun //---------------------------------------------------------------------------------- PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread PHYSACDEF void RunPhysicsStep(void); // Run physics step, to be used if PHYSICS_NO_THREADS is set in your main loop +PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters @@ -279,16 +278,15 @@ static pthread_t physicsThreadId; // Physics thread id #endif static unsigned int usedMemory = 0; // Total allocated dynamic memory static bool physicsThreadEnabled = false; // Physics thread enabled state - static double baseTime = 0.0; // Offset time for MONOTONIC clock static double startTime = 0.0; // Start time in milliseconds -static double deltaTime = 0.0; // Delta time used for physics steps +static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time used for physics steps, in milliseconds static double currentTime = 0.0; // Current time in milliseconds static uint64_t frequency = 0; // Hi-res clock frequency static double accumulator = 0.0; // Physics time step delta time accumulator static unsigned int stepsCount = 0; // Total physics steps processed -static Vector2 gravityForce = { 0.0f, 9.81f/1000 }; // Physics world gravity force +static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array @@ -322,13 +320,12 @@ static bool BiasGreaterThan(float valueA, float valueB); static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer -static uint64_t GetTimeCount(void); // Get hi-res MONOTONIC time measure in seconds -static double GetCurrentTime(void); // // Get hi-res MONOTONIC time measure in seconds +static uint64_t GetTimeCount(void); // Get hi-res MONOTONIC time measure in mseconds +static double GetCurrentTime(void); // Get current time measure in milliseconds static int GetRandomNumber(int min, int max); // Returns a random number between min and max (both included) // Math functions -static void MathClamp(double *value, double min, double max); // Clamp a value in a range static Vector2 MathCross(float value, Vector2 vector); // Returns the cross product of a vector and a value static float MathCrossVector2(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors static float MathLenSqr(Vector2 vector); // Returns the len square root of a vector @@ -363,6 +360,8 @@ PHYSACDEF void InitPhysics(void) #if defined(PHYSAC_DEBUG) printf("[PHYSAC] physics module initialized successfully\n"); #endif + + accumulator = 0.0; } // Returns true if physics thread is currently enabled @@ -607,7 +606,7 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) { int count = vertexData.vertexCount; Vector2 bodyPos = body->position; - Vector2 vertices[count]; + Vector2 *vertices = (Vector2*)malloc(sizeof(Vector2) * count); Mat2 trans = body->shape.transform; for (int i = 0; i < count; i++) vertices[i] = vertexData.positions[i]; @@ -699,6 +698,8 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) // Apply force to new physics body PhysicsAddForce(newBody, forceDirection); } + + free(vertices); } } } @@ -917,6 +918,18 @@ PHYSACDEF void ClosePhysics(void) #if !defined(PHYSAC_NO_THREADS) pthread_join(physicsThreadId, NULL); #endif + + // Unitialize physics manifolds dynamic memory allocations + for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]); + + // Unitialize physics bodies dynamic memory allocations + for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]); + + #if defined(PHYSAC_DEBUG) + if (physicsBodiesCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory); + else if (physicsManifoldsCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory); + else printf("[PHYSAC] physics module closed successfully\n"); + #endif } //---------------------------------------------------------------------------------- @@ -1011,7 +1024,6 @@ static void *PhysicsLoop(void *arg) // Initialize physics loop thread values physicsThreadEnabled = true; - accumulator = 0; // Physics update loop while (physicsThreadEnabled) @@ -1019,18 +1031,6 @@ static void *PhysicsLoop(void *arg) RunPhysicsStep(); } - // Unitialize physics manifolds dynamic memory allocations - for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]); - - // Unitialize physics bodies dynamic memory allocations - for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]); - - #if defined(PHYSAC_DEBUG) - if (physicsBodiesCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory); - else if (physicsManifoldsCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory); - else printf("[PHYSAC] physics module closed successfully\n"); - #endif - return NULL; } @@ -1147,17 +1147,18 @@ PHYSACDEF void RunPhysicsStep(void) currentTime = GetCurrentTime(); // Calculate current delta time - deltaTime = currentTime - startTime; + const double delta = currentTime - startTime; // Store the time elapsed since the last frame began - accumulator += deltaTime; - - // Clamp accumulator to max time step to avoid bad performance - MathClamp(&accumulator, 0.0, PHYSAC_MAX_TIMESTEP); + accumulator += delta; // Fixed time stepping loop - while (accumulator >= PHYSAC_DESIRED_DELTATIME) + while (accumulator >= deltaTime) { +#ifdef PHYSAC_DEBUG + //printf("currentTime %f, startTime %f, accumulator-pre %f, accumulator-post %f, delta %f, deltaTime %f\n", + // currentTime, startTime, accumulator, accumulator-deltaTime, delta, deltaTime); +#endif PhysicsStep(); accumulator -= deltaTime; } @@ -1166,6 +1167,11 @@ PHYSACDEF void RunPhysicsStep(void) startTime = currentTime; } +PHYSACDEF void SetPhysicsTimeStep(double delta) +{ + deltaTime = delta; +} + // Finds a valid index for a new manifold initialization static int FindAvailableManifoldIndex() { @@ -1557,8 +1563,8 @@ static void IntegratePhysicsForces(PhysicsBody body) if (body->useGravity) { - body->velocity.x += gravityForce.x*(deltaTime/2.0); - body->velocity.y += gravityForce.y*(deltaTime/2.0); + body->velocity.x += gravityForce.x*(deltaTime/1000/2.0); + body->velocity.y += gravityForce.y*(deltaTime/1000/2.0); } if (!body->freezeOrient) body->angularVelocity += body->torque*body->inverseInertia*(deltaTime/2.0); @@ -1592,7 +1598,7 @@ static void InitializePhysicsManifolds(PhysicsManifold manifold) // Determine if we should perform a resting collision or not; // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution - if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime, gravityForce.y*deltaTime }) + PHYSAC_EPSILON)) manifold->restitution = 0; + if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime/1000, gravityForce.y*deltaTime/1000 }) + PHYSAC_EPSILON)) manifold->restitution = 0; } } @@ -1953,13 +1959,6 @@ static int GetRandomNumber(int min, int max) return (rand()%(abs(max - min) + 1) + min); } -// Clamp a value in a range -static inline void MathClamp(double *value, double min, double max) -{ - if (*value < min) *value = min; - else if (*value > max) *value = max; -} - // Returns the cross product of a vector and a value static inline Vector2 MathCross(float value, Vector2 vector) { diff --git a/src/raylib.h b/src/raylib.h index a44b77eeb..be2383b13 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -223,7 +223,7 @@ #define MOUSE_MIDDLE_BUTTON 2 // Touch points registered -#define MAX_TOUCH_POINTS 2 +#define MAX_TOUCH_POINTS 10 // Gamepad Number #define GAMEPAD_PLAYER1 0 @@ -311,7 +311,7 @@ // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. -#ifdef __cplusplus +#if defined(__cplusplus) #define CLITERAL #else #define CLITERAL (Color) @@ -354,11 +354,11 @@ //---------------------------------------------------------------------------------- // Structures Definition //---------------------------------------------------------------------------------- -#ifndef __cplusplus // Boolean type - #ifndef bool - typedef enum { false, true } bool; - #endif +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; #endif // Vector2 type @@ -786,7 +786,7 @@ typedef enum { // Callbacks to be implemented by users typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args); -#ifdef __cplusplus +#if defined(__cplusplus) extern "C" { // Prevents name mangling of functions #endif @@ -814,6 +814,13 @@ RLAPI void SetWindowMinSize(int width, int height); // Set window RLAPI void SetWindowSize(int width, int height); // Set window dimensions RLAPI int GetScreenWidth(void); // Get current screen width RLAPI int GetScreenHeight(void); // Get current screen height +RLAPI void *GetWindowHandle(void); // Get native window handle +RLAPI int GetMonitorCount(void); // Get number of connected monitors +RLAPI int GetMonitorWidth(int monitor); // Get primary monitor width +RLAPI int GetMonitorHeight(int monitor); // Get primary monitor height +RLAPI int GetMonitorPhysicalWidth(int monitor); // Get primary monitor physical width in millimetres +RLAPI int GetMonitorPhysicalHeight(int monitor); // Get primary monitor physical height in millimetres +RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor // Cursor-related functions RLAPI void ShowCursor(void); // Shows cursor @@ -861,15 +868,20 @@ RLAPI void TakeScreenshot(const char *fileName); // Takes a scr RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) // Files management functions +RLAPI bool FileExists(const char *fileName); // Check if file exists RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension RLAPI const char *GetExtension(const char *fileName); // Get pointer to extension for a filename string RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string +RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (memory should be freed) RLAPI const char *GetDirectoryPath(const char *fileName); // Get full path for a given fileName (uses static string) RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) +RLAPI char **GetDirectoryFiles(const char *dirPath, int *count); // Get filenames in a directory path (memory should be freed) +RLAPI void ClearDirectoryFiles(void); // Clear directory files paths buffers (free memory) RLAPI bool ChangeDirectory(const char *dir); // Change working directory, returns true if success RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window -RLAPI char **GetDroppedFiles(int *count); // Get dropped files names -RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer +RLAPI char **GetDroppedFiles(int *count); // Get dropped files names (memory should be freed) +RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory) +RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) // Persistent storage management RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position) @@ -1010,11 +1022,12 @@ RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle -RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (bilinear filtering) +RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm) RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color color); // Resize canvas and fill with color RLAPI void ImageMipmaps(Image *image); // Generate all mipmap levels for a provided image RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) +RLAPI Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount); // Extract color palette from image to maximum size (memory should be freed) RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image @@ -1064,21 +1077,24 @@ RLAPI Font GetFontDefault(void); RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load font from file with extended parameters RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use -RLAPI Image GenImageFontAtlas(CharInfo *chars, int fontSize, int charsCount, int padding, int packMethod); // Generate image font atlas using chars info +RLAPI Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM) // Text drawing functions RLAPI void DrawFPS(int posX, int posY); // Shows current FPS RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) -RLAPI void DrawTextEx(Font font, const char* text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters +RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters // Text misc. functions 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 const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' -RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font +// Text string edition functions +RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' +RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string +RLAPI char **SplitText(char *text, char delimiter, int *strCount); // Split text string into multiple strings (memory should be freed manually!) + //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) //------------------------------------------------------------------------------------ @@ -1264,7 +1280,7 @@ RLAPI void StopAudioStream(AudioStream stream); // Stop au 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) -#ifdef __cplusplus +#if defined(__cplusplus) } #endif diff --git a/src/raylib.ico b/src/raylib.ico index afeb12b91..0cedcc55c 100644 Binary files a/src/raylib.ico and b/src/raylib.ico differ diff --git a/src/raylib.rc b/src/raylib.rc index ee2a5fabf..2aaa5e26e 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -9,7 +9,7 @@ BEGIN //BLOCK "080904E4" // English UK BLOCK "040904E4" // English US BEGIN - VALUE "CompanyName", "raylib technologies" + //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "Created using raylib (www.raylib.com)" VALUE "FileVersion", "2.0.0" VALUE "InternalName", "raylib app" diff --git a/src/raymath.h b/src/raymath.h index 331165325..1d5e26f9a 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -300,7 +300,7 @@ RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2) return result; } -// Substract two vectors +// Subtract two vectors RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) { Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; @@ -720,8 +720,8 @@ RMDEF Matrix MatrixAdd(Matrix left, Matrix right) return result; } -// Substract two matrices (left - right) -RMDEF Matrix MatrixSubstract(Matrix left, Matrix right) +// Subtract two matrices (left - right) +RMDEF Matrix MatrixSubtract(Matrix left, Matrix right) { Matrix result = MatrixIdentity(); diff --git a/src/rlgl.h b/src/rlgl.h index 75246cabf..d97d888e1 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -378,7 +378,7 @@ typedef unsigned char byte; } VrDevice; #endif -#ifdef __cplusplus +#if defined(__cplusplus) extern "C" { // Prevents name mangling of functions #endif @@ -446,9 +446,11 @@ void rlLoadExtensions(void *loader); // Load OpenGL extensions Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates // Textures data management -unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU -void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data -void rlUnloadTexture(unsigned int id); +unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU +void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats +void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory + void rlGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture void *rlReadTexturePixels(Texture2D texture); // Read texture pixel data unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) @@ -514,7 +516,7 @@ void TraceLog(int msgType, const char *text, ...); // Show trace log messag int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture) #endif -#ifdef __cplusplus +#if defined(__cplusplus) } #endif @@ -551,7 +553,7 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i #else // APIENTRY for OpenGL function pointer declarations is required #ifndef APIENTRY - #ifdef _WIN32 + #if defined(_WIN32) #define APIENTRY __stdcall #else #define APIENTRY @@ -913,9 +915,6 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); / #endif // defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -// Get OpenGL internal formats and data type from raylib PixelFormat -static void GetGlFormats(int format, int *glInternalFormat, int *glFormat, int *glType); - #if defined(GRAPHICS_API_OPENGL_11) static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight); @@ -1507,9 +1506,17 @@ void rlDeleteTextures(unsigned int id) void rlDeleteRenderTextures(RenderTexture2D target) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (target.id > 0) glDeleteFramebuffers(1, &target.id); if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id); - if (target.depth.id > 0) glDeleteTextures(1, &target.depth.id); + if (target.depth.id > 0) + { +#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + glDeleteRenderbuffers(1, &target.depth.id); +#elif defined(GRAPHICS_API_OPENGL_33) + glDeleteTextures(1, &target.depth.id); +#endif + } + + if (target.id > 0) glDeleteFramebuffers(1, &target.id); TraceLog(LOG_INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id); #endif @@ -1621,7 +1628,7 @@ void rlglInit(int width, int height) // NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that) glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); -#ifdef _MSC_VER +#if defined(_MSC_VER) const char **extList = malloc(sizeof(const char *)*numExt); #else const char *extList[numExt]; @@ -1632,17 +1639,13 @@ void rlglInit(int width, int height) #elif defined(GRAPHICS_API_OPENGL_ES2) char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string - // NOTE: We have to duplicate string because glGetString() returns a const value - // If not duplicated, it fails in some systems (Raspberry Pi) - // Equivalent to function: char *strdup(const char *str) - char *extensionsDup; - size_t len = strlen(extensions) + 1; - void *newstr = malloc(len); - if (newstr == NULL) extensionsDup = NULL; - extensionsDup = (char *)memcpy(newstr, extensions, len); + // NOTE: We have to duplicate string because glGetString() returns a const string + int len = strlen(extensions) + 1; + char *extensionsDup = (char *)malloc(len); + strcpy(extensionsDup, extensions); // NOTE: String could be splitted using strtok() function (string.h) - // NOTE: strtok() modifies the received string, it can not be const + // NOTE: strtok() modifies the passed string, it can not be const char *extList[512]; // Allocate 512 strings pointers (2 KB) @@ -2015,8 +2018,8 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi { unsigned int mipSize = GetPixelDataSize(mipWidth, mipHeight, format); - int glInternalFormat, glFormat, glType; - GetGlFormats(format, &glInternalFormat, &glFormat, &glType); + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); TraceLog(LOG_DEBUG, "Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); @@ -2105,8 +2108,8 @@ void rlUpdateTexture(unsigned int id, int width, int height, int format, const v { glBindTexture(GL_TEXTURE_2D, id); - int glInternalFormat, glFormat, glType; - GetGlFormats(format, &glInternalFormat, &glFormat, &glType); + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); if ((glInternalFormat != -1) && (format < COMPRESSED_DXT1_RGB)) { @@ -2115,17 +2118,68 @@ void rlUpdateTexture(unsigned int id, int width, int height, int format, const v else TraceLog(LOG_WARNING, "Texture format updating not supported"); } +// Get OpenGL internal formats and data type from raylib PixelFormat +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType) +{ + *glInternalFormat = -1; + *glFormat = -1; + *glType = -1; + + switch (format) + { + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + #if !defined(GRAPHICS_API_OPENGL_11) + case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + #endif + #elif defined(GRAPHICS_API_OPENGL_33) + case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; + case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; + case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + #endif + #if !defined(GRAPHICS_API_OPENGL_11) + case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; + case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + #endif + default: TraceLog(LOG_WARNING, "Texture format not supported"); break; + } +} + // Unload texture from GPU memory void rlUnloadTexture(unsigned int id) { if (id > 0) glDeleteTextures(1, &id); } - // Load a texture to be used for rendering (fbo with color and depth attachments) RenderTexture2D rlLoadRenderTexture(int width, int height) { - RenderTexture2D target; + RenderTexture2D target = { 0 }; target.id = 0; @@ -2205,8 +2259,16 @@ RenderTexture2D rlLoadRenderTexture(int width, int height) default: break; } - glDeleteTextures(1, &target.texture.id); - glDeleteTextures(1, &target.depth.id); + if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id); + if (target.depth.id > 0) + { +#if defined(USE_DEPTH_RENDERBUFFER) + glDeleteRenderbuffers(1, &target.depth.id); +#elif defined(USE_DEPTH_TEXTURE) + glDeleteTextures(1, &target.depth.id); +#endif + } + glDeleteFramebuffers(1, &target.id); } else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); @@ -2745,8 +2807,8 @@ void *rlReadTexturePixels(Texture2D texture) // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) glPixelStorei(GL_PACK_ALIGNMENT, 1); - int glInternalFormat, glFormat, glType; - GetGlFormats(texture.format, &glInternalFormat, &glFormat, &glType); + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(texture.format, &glInternalFormat, &glFormat, &glType); unsigned int size = GetPixelDataSize(texture.width, texture.height, texture.format); if ((glInternalFormat != -1) && (texture.format < COMPRESSED_DXT1_RGB)) @@ -3757,7 +3819,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); -#ifdef _MSC_VER +#if defined(_MSC_VER) char *log = malloc(maxLength); #else char log[maxLength]; @@ -3766,7 +3828,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad TraceLog(LOG_INFO, "%s", log); -#ifdef _MSC_VER +#if defined(_MSC_VER) free(log); #endif glDeleteProgram(program); @@ -4591,58 +4653,6 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) #endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -// Get OpenGL internal formats and data type from raylib PixelFormat -static void GetGlFormats(int format, int *glInternalFormat, int *glFormat, int *glType) -{ - *glInternalFormat = -1; - *glFormat = -1; - *glType = -1; - - switch (format) - { - #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA - case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; - case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; - case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; - case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; - #if !defined(GRAPHICS_API_OPENGL_11) - case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - #endif - #elif defined(GRAPHICS_API_OPENGL_33) - case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; - case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; - case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; - case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; - case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; - case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; - #endif - #if !defined(GRAPHICS_API_OPENGL_11) - case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; - case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; - case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; - case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; - case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU - case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU - case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - #endif - default: TraceLog(LOG_WARNING, "Texture format not supported"); break; - } -} - #if defined(GRAPHICS_API_OPENGL_11) // Mipmaps data is generated after image data // NOTE: Only works with RGBA (4 bytes) data! diff --git a/src/text.c b/src/text.c index 3dbb261be..a010666e8 100644 --- a/src/text.c +++ b/src/text.c @@ -276,15 +276,7 @@ Font LoadFont(const char *fileName) Font font = { 0 }; #if defined(SUPPORT_FILEFORMAT_TTF) - if (IsFileExtension(fileName, ".ttf")) - { - font.baseSize = DEFAULT_TTF_FONTSIZE; - font.charsCount = DEFAULT_TTF_NUMCHARS; - font.chars = LoadFontData(fileName, font.baseSize, NULL, font.charsCount, FONT_DEFAULT); - Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 4, 0); - font.texture = LoadTextureFromImage(atlas); - UnloadImage(atlas); - } + if (IsFileExtension(fileName, ".ttf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, DEFAULT_TTF_NUMCHARS, NULL); else #endif #if defined(SUPPORT_FILEFORMAT_FNT) @@ -317,9 +309,14 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha font.baseSize = fontSize; font.charsCount = (charsCount > 0) ? charsCount : 95; font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT); - Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0); - font.texture = LoadTextureFromImage(atlas); - UnloadImage(atlas); + + if (font.chars != NULL) + { + Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0); + font.texture = LoadTextureFromImage(atlas); + UnloadImage(atlas); + } + else font = GetFontDefault(); return font; } @@ -335,91 +332,97 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c #define SDF_PIXEL_DIST_SCALE 64.0f #define BITMAP_ALPHA_THRESHOLD 80 - - // In case no chars count provided, default to 95 - charsCount = (charsCount > 0) ? charsCount : 95; - - CharInfo *chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); - + + CharInfo *chars = NULL; + // Load font data (including pixel data) from TTF file // NOTE: Loaded information should be enough to generate font image atlas, // using any packaging method FILE *fontFile = fopen(fileName, "rb"); // Load font file - fseek(fontFile, 0, SEEK_END); - long size = ftell(fontFile); // Get file size - fseek(fontFile, 0, SEEK_SET); // Reset file pointer - - unsigned char *fontBuffer = (unsigned char *)malloc(size); - - fread(fontBuffer, size, 1, fontFile); - fclose(fontFile); - - // Init font for data reading - stbtt_fontinfo fontInfo; - if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!"); - - // Calculate font scale factor - float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize); - - // Calculate font basic metrics - // NOTE: ascent is equivalent to font baseline - int ascent, descent, lineGap; - stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap); - - // Fill fontChars in case not provided externally - // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space) - int genFontChars = false; - if (fontChars == NULL) genFontChars = true; - if (genFontChars) + if (fontFile != NULL) { - fontChars = (int *)malloc(charsCount*sizeof(int)); - for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32; - } - - // NOTE: Using simple packaging, one char after another - for (int i = 0; i < charsCount; i++) - { - int chw = 0, chh = 0; // Character width and height (on generation) - int ch = fontChars[i]; // Character value to get info for - chars[i].value = ch; + fseek(fontFile, 0, SEEK_END); + long size = ftell(fontFile); // Get file size + fseek(fontFile, 0, SEEK_SET); // Reset file pointer - // Render a unicode codepoint to a bitmap - // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap - // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be - // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide + unsigned char *fontBuffer = (unsigned char *)malloc(size); - if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); - else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); + fread(fontBuffer, size, 1, fontFile); + fclose(fontFile); + + // Init font for data reading + stbtt_fontinfo fontInfo; + if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!"); + + // Calculate font scale factor + float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize); + + // Calculate font basic metrics + // NOTE: ascent is equivalent to font baseline + int ascent, descent, lineGap; + stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap); - if (type == FONT_BITMAP) + // In case no chars count provided, default to 95 + charsCount = (charsCount > 0) ? charsCount : 95; + + // Fill fontChars in case not provided externally + // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space) + int genFontChars = false; + if (fontChars == NULL) { - // Aliased bitmap (black & white) font generation, avoiding anti-aliasing - // NOTE: For optimum results, bitmap font should be generated at base pixel size - for (int p = 0; p < chw*chh; p++) - { - if (chars[i].data[p] < BITMAP_ALPHA_THRESHOLD) chars[i].data[p] = 0; - else chars[i].data[p] = 255; - } + fontChars = (int *)malloc(charsCount*sizeof(int)); + for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32; + genFontChars = true; } - - chars[i].rec.width = (float)chw; - chars[i].rec.height = (float)chh; - chars[i].offsetY += (int)((float)ascent*scaleFactor); - - // Get bounding box for character (may be offset to account for chars that dip above or below the line) - int chX1, chY1, chX2, chY2; - stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2); - - TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1); - TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1); - stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); - chars[i].advanceX *= scaleFactor; + chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); + + // NOTE: Using simple packaging, one char after another + for (int i = 0; i < charsCount; i++) + { + int chw = 0, chh = 0; // Character width and height (on generation) + int ch = fontChars[i]; // Character value to get info for + chars[i].value = ch; + + // Render a unicode codepoint to a bitmap + // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap + // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be + // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide + + if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); + else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); + + if (type == FONT_BITMAP) + { + // Aliased bitmap (black & white) font generation, avoiding anti-aliasing + // NOTE: For optimum results, bitmap font should be generated at base pixel size + for (int p = 0; p < chw*chh; p++) + { + if (chars[i].data[p] < BITMAP_ALPHA_THRESHOLD) chars[i].data[p] = 0; + else chars[i].data[p] = 255; + } + } + + chars[i].rec.width = (float)chw; + chars[i].rec.height = (float)chh; + chars[i].offsetY += (int)((float)ascent*scaleFactor); + + // Get bounding box for character (may be offset to account for chars that dip above or below the line) + int chX1, chY1, chX2, chY2; + stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2); + + TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1); + TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1); + + stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); + chars[i].advanceX *= scaleFactor; + } + + free(fontBuffer); + if (genFontChars) free(fontChars); } - - free(fontBuffer); - if (genFontChars) free(fontChars); + else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName); return chars; } @@ -527,6 +530,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi else TraceLog(LOG_WARNING, "Character could not be packed: %i", i); } + free(rects); free(nodes); free(context); } @@ -563,6 +567,28 @@ void UnloadFont(Font font) } } +// Shows current FPS on top-left corner +// NOTE: Uses default font +void DrawFPS(int posX, int posY) +{ + // NOTE: We are rendering fps every second for better viewing on high framerates + + static int fps = 0; + static int counter = 0; + static int refreshRate = 20; + + if (counter < refreshRate) counter++; + else + { + fps = GetFPS(); + refreshRate = fps; + counter = 0; + } + + // NOTE: We have rounding errors every frame, so it oscillates a lot + DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME); +} + // 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 @@ -588,10 +614,10 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f int length = strlen(text); int textOffsetX = 0; // Offset between characters int textOffsetY = 0; // Required for line break! - float scaleFactor; + float scaleFactor = 0.0f; - unsigned char letter; // Current character - int index; // Index position in sprite font + unsigned char letter = 0; // Current character + int index = 0; // Index position in sprite font scaleFactor = fontSize/font.baseSize; @@ -639,44 +665,6 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f } } -// Formatting of text with variables to 'embed' -const char *FormatText(const char *text, ...) -{ - static char buffer[MAX_FORMATTEXT_LENGTH]; - - va_list args; - va_start(args, text); - vsprintf(buffer, text, args); - va_end(args); - - return buffer; -} - -// Get a piece of a text string -const char *SubText(const char *text, int position, int length) -{ - static char buffer[MAX_SUBTEXT_LENGTH]; - int textLength = strlen(text); - - if (position >= textLength) - { - position = textLength - 1; - length = 0; - } - - if (length >= textLength) length = textLength; - - for (int c = 0 ; c < length ; c++) - { - *(buffer + c) = *(text + position); - text++; - } - - *(buffer + length) = '\0'; - - return buffer; -} - // Measure string width for default font int MeasureText(const char *text, int fontSize) { @@ -702,8 +690,8 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing int tempLen = 0; // Used to count longer text line num chars int lenCounter = 0; - float textWidth = 0; - float tempTextWidth = 0; // Used to count longer text line width + float textWidth = 0.0f; + float tempTextWidth = 0.0f; // Used to count longer text line width float textHeight = (float)font.baseSize; float scaleFactor = fontSize/(float)font.baseSize; @@ -761,26 +749,78 @@ int GetGlyphIndex(Font font, int character) #endif } -// Shows current FPS on top-left corner -// NOTE: Uses default font -void DrawFPS(int posX, int posY) +// Formatting of text with variables to 'embed' +const char *FormatText(const char *text, ...) { - // NOTE: We are rendering fps every second for better viewing on high framerates + static char buffer[MAX_FORMATTEXT_LENGTH]; - static int fps = 0; - static int counter = 0; - static int refreshRate = 20; + va_list args; + va_start(args, text); + vsprintf(buffer, text, args); + va_end(args); - if (counter < refreshRate) counter++; - else + return buffer; +} + +// Get a piece of a text string +const char *SubText(const char *text, int position, int length) +{ + static char buffer[MAX_SUBTEXT_LENGTH] = { 0 }; + int textLength = strlen(text); + + if (position >= textLength) { - fps = GetFPS(); - refreshRate = fps; - counter = 0; + position = textLength - 1; + length = 0; + } + + if (length >= textLength) length = textLength; + + for (int c = 0 ; c < length ; c++) + { + *(buffer + c) = *(text + position); + text++; + } + + *(buffer + length) = '\0'; + + return buffer; +} + +// Split string into multiple strings +// NOTE: Files count is returned by parameters pointer +// NOTE: Allocated memory should be manually freed +char **SplitText(char *text, char delimiter, int *strCount) +{ + #define MAX_SUBSTRING_LENGTH 128 + + char **strings = NULL; + int len = strlen(text); + char *strDup = (char *)malloc(len + 1); + strcpy(strDup, text); + int counter = 1; + + // Count how many substrings we have on string + for (int i = 0; i < len; i++) if (text[i] == delimiter) counter++; + + // Memory allocation for substrings + strings = (char **)malloc(sizeof(char *)*counter); + for (int i = 0; i < counter; i++) strings[i] = (char *)malloc(sizeof(char)*MAX_SUBSTRING_LENGTH); + + char *substrPtr = NULL; + char delimiters[1] = { delimiter }; // Only caring for one delimiter + substrPtr = strtok(strDup, delimiters); + + for (int i = 0; (i < counter) && (substrPtr != NULL); i++) + { + strcpy(strings[i], substrPtr); + substrPtr = strtok(NULL, delimiters); } - // NOTE: We have rounding errors every frame, so it oscillates a lot - DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME); + *strCount = counter; + free(strDup); + + return strings; } //---------------------------------------------------------------------------------- @@ -911,17 +951,18 @@ static Font LoadBMFont(const char *fileName) Font font = { 0 }; font.texture.id = 0; - char buffer[MAX_BUFFER_SIZE]; + char buffer[MAX_BUFFER_SIZE] = { 0 }; char *searchPoint = NULL; int fontSize = 0; - int texWidth, texHeight; + int texWidth = 0; + int texHeight = 0; char texFileName[129]; int charsCount = 0; - int base; // Useless data + int base = 0; // Useless data - FILE *fntFile; + FILE *fntFile = NULL; fntFile = fopen(fileName, "rt"); diff --git a/src/textures.c b/src/textures.c index a7b9d3e2f..a0e74637b 100644 --- a/src/textures.c +++ b/src/textures.c @@ -148,6 +148,7 @@ static Image LoadPKM(const char *fileName); // Load PKM file #endif #if defined(SUPPORT_FILEFORMAT_KTX) static Image LoadKTX(const char *fileName); // Load KTX file +static void SaveKTX(Image image, const char *fileName); // Save image data as KTX file #endif #if defined(SUPPORT_FILEFORMAT_PVR) static Image LoadPVR(const char *fileName); // Load PVR file @@ -322,7 +323,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int // NOTE: fread() returns num read elements instead of bytes, // to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element) - size_t bytes = fread(image.data, 1, size, rawFile); + int bytes = fread(image.data, 1, size, rawFile); // Check if data has been read successfully if (bytes < size) @@ -726,10 +727,11 @@ void ExportImage(Image image, const char *fileName) if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4); else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData); else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData); - else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // Between 1 and 100 + else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100 + else if (IsFileExtension(fileName, ".ktx")) SaveKTX(image, fileName); else if (IsFileExtension(fileName, ".raw")) { - // Export raw pixel data + // Export raw pixel data (without header) // NOTE: It's up to the user to track image parameters FILE *rawFile = fopen(fileName, "wb"); fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile); @@ -1443,6 +1445,57 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) } } +// Extract color palette from image to maximum size +// NOTE: Memory allocated should be freed manually! +Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount) +{ + #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a)) + + Color *pixels = GetImageData(image); + Color *palette = (Color *)malloc(maxPaletteSize*sizeof(Color)); + + int palCount = 0; + for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK + + for (int i = 0; i < image.width*image.height; i++) + { + if (pixels[i].a > 0) + { + bool colorInPalette = false; + + // Check if the color is already on palette + for (int j = 0; j < maxPaletteSize; j++) + { + if (COLOR_EQUAL(pixels[i], palette[j])) + { + colorInPalette = true; + break; + } + } + + // Store color if not on the palette + if (!colorInPalette) + { + palette[palCount] = pixels[i]; // Add pixels[i] to palette + palCount++; + + // We reached the limit of colors supported by palette + if (palCount >= maxPaletteSize) + { + i = image.width*image.height; // Finish palette get + printf("WARNING: Image palette is greater than %i colors!\n", maxPaletteSize); + } + } + } + } + + free(pixels); + + *extractCount = palCount; + + return palette; +} + // Draw an image (source) within an image (destination) // TODO: Feel this function could be simplified... void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) @@ -2727,7 +2780,7 @@ static Image LoadDDS(const char *fileName) TraceLog(LOG_DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize); - image.data = (unsigned char*)malloc(size*sizeof(unsigned char)); + image.data = (unsigned char *)malloc(size*sizeof(unsigned char)); fread(image.data, size, 1, ddsFile); @@ -2824,7 +2877,7 @@ static Image LoadPKM(const char *fileName) int size = image.width*image.height*bpp/8; // Total data size in bytes - image.data = (unsigned char*)malloc(size*sizeof(unsigned char)); + image.data = (unsigned char *)malloc(size*sizeof(unsigned char)); fread(image.data, size, 1, pkmFile); @@ -2918,7 +2971,7 @@ static Image LoadKTX(const char *fileName) int dataSize; fread(&dataSize, sizeof(unsigned int), 1, ktxFile); - image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char)); + image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char)); fread(image.data, dataSize, 1, ktxFile); @@ -2932,6 +2985,93 @@ static Image LoadKTX(const char *fileName) return image; } + +// Save image data as KTX file +// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018) +static void SaveKTX(Image image, const char *fileName) +{ + // KTX file Header (64 bytes) + // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + // v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation + + typedef struct { + char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n" + unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 + unsigned int glType; // For compressed textures, glType must equal 0 + unsigned int glTypeSize; // For compressed texture data, usually 1 + unsigned int glFormat; // For compressed textures is 0 + unsigned int glInternalFormat; // Compressed internal format + unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) // KTX 2.0: UInt32 vkFormat + unsigned int width; // Texture image width in pixels + unsigned int height; // Texture image height in pixels + unsigned int depth; // For 2D textures is 0 + unsigned int elements; // Number of array elements, usually 0 + unsigned int faces; // Cubemap faces, for no-cubemap = 1 + unsigned int mipmapLevels; // Non-mipmapped textures = 1 + unsigned int keyValueDataSize; // Used to encode any arbitrary data... // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0 + // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)... + // KTX 2.0 defines additional header elements... + } KTXHeader; + + // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize + + FILE *ktxFile = fopen(fileName, "wb"); + + if (ktxFile == NULL) TraceLog(LOG_WARNING, "[%s] KTX image file could not be created", fileName); + else + { + KTXHeader ktxHeader; + + // KTX identifier (v2.2) + //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' }; + //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; + + // Get the image header + strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature + ktxHeader.endianness = 0; + ktxHeader.glType = 0; // Obtained from image.format + ktxHeader.glTypeSize = 1; + ktxHeader.glFormat = 0; // Obtained from image.format + ktxHeader.glInternalFormat = 0; // Obtained from image.format + ktxHeader.glBaseInternalFormat = 0; + ktxHeader.width = image.width; + ktxHeader.height = image.height; + ktxHeader.depth = 0; + ktxHeader.elements = 0; + ktxHeader.faces = 1; + ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats) + ktxHeader.keyValueDataSize = 0; // No extra data after the header + + rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function + ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only + + // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC + + if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export."); + else + { + fwrite(&ktxHeader, 1, sizeof(KTXHeader), ktxFile); + + int width = image.width; + int height = image.height; + int dataOffset = 0; + + // Save all mipmaps data + for (int i = 0; i < image.mipmaps; i++) + { + unsigned int dataSize = GetPixelDataSize(width, height, image.format); + fwrite(&dataSize, 1, sizeof(unsigned int), ktxFile); + fwrite((unsigned char *)image.data + dataOffset, 1, dataSize, ktxFile); + + width /= 2; + height /= 2; + dataOffset += dataSize; + } + } + + fclose(ktxFile); // Close file pointer + } +} #endif #if defined(SUPPORT_FILEFORMAT_PVR) @@ -3073,7 +3213,7 @@ static Image LoadPVR(const char *fileName) } int dataSize = image.width*image.height*bpp/8; // Total data size in bytes - image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char)); + image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char)); // Read data from file fread(image.data, dataSize, 1, pvrFile); diff --git a/templates/advance_game/resources/mecha.png b/templates/advance_game/resources/mecha.png index 8022d18c4..9213fa2d1 100644 Binary files a/templates/advance_game/resources/mecha.png and b/templates/advance_game/resources/mecha.png differ