diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 903ad9d69..b96670b17 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,8 +1,8 @@ # These are supported funding model platforms github: raysan5 -patreon: raylib +patreon: # raylib open_collective: # Replace with a single Open Collective username -ko_fi: raysan +ko_fi: # raysan tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel custom: # Replace with a single custom sponsorship URL diff --git a/BINDINGS.md b/BINDINGS.md index bdddf095f..f9c667748 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -12,6 +12,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [cray](https://github.com/tapgg/cray) : raylib **Crystal** binding - [Graphics::Raylib](https://metacpan.org/pod/Graphics::Raylib) : raylib **Perl** wrapper - [raylib-pascal](https://github.com/drezgames/raylib-pascal) - raylib **Pascal** binding + - [raylib-pas](https://github.com/tazdij/raylib-pas) - raylib **Pascal** binding (including rlgl & raymath) - [Raylib-cs](https://github.com/ChrisDill/Raylib-cs) : raylib **C#** binding - [RaylibSharp](https://github.com/TheLumaio/RaylibSharp) : raylib **C#** binding - [raylib-ruby-ffi](https://github.com/D3nX/raylib-ruby-ffi) : raylib **Ruby** binding diff --git a/examples/Makefile b/examples/Makefile index f57888ace..c71758497 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -385,9 +385,9 @@ EXAMPLES = \ shapes/shapes_draw_circle_sector \ shapes/shapes_draw_rectangle_rounded \ text/text_raylib_fonts \ - text/text_sprite_fonts \ - text/text_ttf_loading \ - text/text_bmfont_ttf \ + text/text_font_spritefont \ + text/text_font_loading \ + text/text_font_filters \ text/text_font_sdf \ text/text_format_text \ text/text_input_box \ @@ -420,8 +420,7 @@ EXAMPLES = \ models/models_material_pbr \ models/models_mesh_generation \ models/models_mesh_picking \ - models/models_obj_loading \ - models/models_obj_viewer \ + models/models_loading \ models/models_orthographic_projection \ models/models_rlgl_solar_system \ models/models_skybox \ @@ -438,6 +437,8 @@ EXAMPLES = \ shaders/shaders_julia_set \ shaders/shaders_eratosthenes \ shaders/shaders_basic_lighting \ + shaders/shaders_fog \ + shaders/shaders_simple_mask \ audio/audio_module_playing \ audio/audio_music_stream \ audio/audio_raw_stream \ @@ -471,7 +472,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) del *.o *.exe /s endif ifeq ($(PLATFORM_OS),LINUX) - find -type f -executable | xargs file -i | grep -E 'x-object|x-archive|x-sharedlib|x-executable' | rev | cut -d ':' -f 2- | rev | xargs rm -fv + find -type f -executable | xargs file -i | grep -E 'x-object|x-archive|x-sharedlib|x-executable|x-pie-executable' | rev | cut -d ':' -f 2- | rev | xargs rm -fv endif ifeq ($(PLATFORM_OS),OSX) find . -type f -perm +ugo+x -delete diff --git a/examples/audio/audio_module_playing.c b/examples/audio/audio_module_playing.c index 0dae8aa21..557fcb092 100644 --- a/examples/audio/audio_module_playing.c +++ b/examples/audio/audio_module_playing.c @@ -46,13 +46,13 @@ int main(void) circles[i].radius = GetRandomValue(10, 40); circles[i].position.x = GetRandomValue(circles[i].radius, screenWidth - circles[i].radius); circles[i].position.y = GetRandomValue(circles[i].radius, screenHeight - circles[i].radius); - circles[i].speed = (float)GetRandomValue(1, 100)/20000.0f; + circles[i].speed = (float)GetRandomValue(1, 100)/2000.0f; circles[i].color = colors[GetRandomValue(0, 13)]; } - Music xm = LoadMusicStream("resources/chiptun1.mod"); + Music music = LoadMusicStream("resources/mini1111.xm"); - PlayMusicStream(xm); + PlayMusicStream(music); float timePlayed = 0.0f; bool pause = false; @@ -65,13 +65,13 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateMusicStream(xm); // Update music buffer with new stream data + UpdateMusicStream(music); // Update music buffer with new stream data // Restart music playing (stop and play) if (IsKeyPressed(KEY_SPACE)) { - StopMusicStream(xm); - PlayMusicStream(xm); + StopMusicStream(music); + PlayMusicStream(music); } // Pause/Resume music playing @@ -79,12 +79,12 @@ int main(void) { pause = !pause; - if (pause) PauseMusicStream(xm); - else ResumeMusicStream(xm); + if (pause) PauseMusicStream(music); + else ResumeMusicStream(music); } // Get timePlayed scaled to bar dimensions - timePlayed = GetMusicTimePlayed(xm)/GetMusicTimeLength(xm)*(screenWidth - 40); + timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music)*(screenWidth - 40); // Color circles animation for (int i = MAX_CIRCLES - 1; (i >= 0) && !pause; i--) @@ -101,7 +101,7 @@ int main(void) circles[i].position.x = GetRandomValue(circles[i].radius, screenWidth - circles[i].radius); circles[i].position.y = GetRandomValue(circles[i].radius, screenHeight - circles[i].radius); circles[i].color = colors[GetRandomValue(0, 13)]; - circles[i].speed = (float)GetRandomValue(1, 100)/20000.0f; + circles[i].speed = (float)GetRandomValue(1, 100)/2000.0f; } } //---------------------------------------------------------------------------------- @@ -128,7 +128,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadMusicStream(xm); // Unload music stream buffers from RAM + UnloadMusicStream(music); // Unload music stream buffers from RAM CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index 136e02f30..85a77bc0a 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -99,7 +99,7 @@ int main(void) } // Refill audio stream if required - if (IsAudioBufferProcessed(stream)) + if (IsAudioStreamProcessed(stream)) { // Synthesize a buffer that is exactly the requested size int writeCursor = 0; diff --git a/examples/core/core_2d_camera.c b/examples/core/core_2d_camera.c index 81f580ad3..d6c85079e 100644 --- a/examples/core/core_2d_camera.c +++ b/examples/core/core_2d_camera.c @@ -42,7 +42,7 @@ int main(void) Camera2D camera = { 0 }; camera.target = (Vector2){ player.x + 20, player.y + 20 }; - camera.offset = (Vector2){ 0, 0 }; + camera.offset = (Vector2){ screenWidth/2, screenHeight/2 }; camera.rotation = 0.0f; camera.zoom = 1.0f; @@ -54,16 +54,10 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - if (IsKeyDown(KEY_RIGHT)) - { - player.x += 2; // Player movement - camera.offset.x -= 2; // Camera displacement with player movement - } - else if (IsKeyDown(KEY_LEFT)) - { - player.x -= 2; // Player movement - camera.offset.x += 2; // Camera displacement with player movement - } + + // Player movement + if (IsKeyDown(KEY_RIGHT)) player.x += 2; + else if (IsKeyDown(KEY_LEFT)) player.x -= 2; // Camera target follows player camera.target = (Vector2){ player.x + 20, player.y + 20 }; @@ -135,4 +129,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/core/core_2d_camera_ext.c b/examples/core/core_2d_camera_ext.c new file mode 100644 index 000000000..330e39ef9 --- /dev/null +++ b/examples/core/core_2d_camera_ext.c @@ -0,0 +1,284 @@ +/******************************************************************************************* +* +* raylib [core] example - 2d camera extended +* +* This example has been created using raylib 1.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2016 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +#define G 400 +#define PLAYER_JUMP_SPD 350.f +#define PLAYER_HOR_SPD 200.f + +typedef struct Player { + Vector2 pos; + float vel; + int canJump; +} Player; + +typedef struct EnvItem { + Rectangle rect; + int blocking; + Color color; +} EnvItem; + +void updateCameraCenter( + float delta, + Camera2D *camera, + Player *player, + EnvItem *envItems, + int envItemsLength, + int width, int height +) { + camera->offset = (Vector2){ width/2, height/2 }; + camera->target = player->pos; +} + +void updateCameraCenterInsideMap( + float delta, + Camera2D *camera, + Player *player, + EnvItem *envItems, + int envItemsLength, + int width, int height +) { + camera->target = player->pos; + camera->offset = (Vector2){ width/2, height/2 }; + float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000; + for (int i = 0; i < envItemsLength; i++) { + EnvItem *ei = envItems + i; + minX = fminf(ei->rect.x, minX); + maxX = fmaxf(ei->rect.x + ei->rect.width, maxX); + minY = fminf(ei->rect.y, minY); + maxY = fmaxf(ei->rect.y + ei->rect.height, maxY); + } + Vector2 max = GetWorldToScreen2D((Vector2){ maxX, maxY }, *camera); + Vector2 min = GetWorldToScreen2D((Vector2){ minX, minY }, *camera); + if (max.x < width) { + camera->offset.x = width - (max.x - width/2); + } + if (max.y < height) { + camera->offset.y = height - (max.y - height/2); + } + if (min.x > 0) { + camera->offset.x = width/2 - min.x; + } + if (min.y > 0) { + camera->offset.y = height/2- min.y; + } +} + +void updateCameraCenterSmoothFollow( + float delta, + Camera2D *camera, + Player *player, + EnvItem *envItems, + int envItemsLength, + int width, int height +) { + static float minSpeed = 30; + static float minEffectLength = 10; + static float fractionSpeed = 0.8f; + camera->offset = (Vector2){ width/2, height/2 }; + Vector2 diff = Vector2Subtract(player->pos, camera->target); + float length = Vector2Length(diff); + if (length > minEffectLength) { + float speed = fmaxf(fractionSpeed * length, minSpeed); + camera->target = Vector2Add(camera->target, Vector2Scale(diff, speed*delta/length)); + } +} + +void updateCameraEvenOutOnLanding( + float delta, + Camera2D *camera, + Player *player, + EnvItem *envItems, + int envItemsLength, + int width, int height +) { + static float evenOutSpeed = 700; + static int eveningOut = false; + static float evenOutTarget; + camera->offset = (Vector2){ width/2, height/2 }; + camera->target.x = player->pos.x; + if (eveningOut) { + if (evenOutTarget > camera->target.y) { + camera->target.y += evenOutSpeed * delta; + if (camera->target.y > evenOutTarget) { + camera->target.y = evenOutTarget; + eveningOut = 0; + } + } else { + camera->target.y -= evenOutSpeed * delta; + if (camera->target.y < evenOutTarget) { + camera->target.y = evenOutTarget; + eveningOut = 0; + } + } + } else { + if (player->canJump && + player->vel == 0 && + player->pos.y != camera->target.y + ) { + eveningOut = 1; + evenOutTarget = player->pos.y; + } + } +} + +void updateCameraPlayerBoundsPush( + float delta, + Camera2D *camera, + Player *player, + EnvItem *envItems, + int envItemsLength, + int width, int height +) { + static Vector2 bbox = { 0.2f, 0.2f }; + + Vector2 bboxWorldMin = GetScreenToWorld2D((Vector2){ (1 - bbox.x) * 0.5 * width, (1 - bbox.y) * 0.5 * height }, *camera); + Vector2 bboxWorldMax = GetScreenToWorld2D((Vector2){ (1 + bbox.x) * 0.5 * width, (1 + bbox.y) * 0.5 * height }, *camera); + camera->offset = (Vector2){ (1 - bbox.x) * 0.5 * width, (1 - bbox.y) * 0.5 * height }; + + if (player->pos.x < bboxWorldMin.x) { + camera->target.x = player->pos.x; + } + if (player->pos.y < bboxWorldMin.y) { + camera->target.y = player->pos.y; + } + if (player->pos.x > bboxWorldMax.x) { + camera->target.x = bboxWorldMin.x + (player->pos.x - bboxWorldMax.x); + } + if (player->pos.y > bboxWorldMax.y) { + camera->target.y = bboxWorldMin.y + (player->pos.y - bboxWorldMax.y); + } +} + + +void updatePlayer(float delta, Player *player, EnvItem *envItems, int envItemsLength) { + if (IsKeyDown(KEY_LEFT)) player->pos.x -= PLAYER_HOR_SPD*delta; + if (IsKeyDown(KEY_RIGHT)) player->pos.x += PLAYER_HOR_SPD*delta; + if (IsKeyDown(KEY_SPACE) && player->canJump) { + player->vel = -PLAYER_JUMP_SPD; + player->canJump = 0; + } + + int hitObstacle = 0; + for (int i = 0; i < envItemsLength; i++) { + EnvItem *ei = envItems + i; + Vector2 *p = &(player->pos); + if (ei->blocking && + ei->rect.x <= p->x && + ei->rect.x + ei->rect.width >= p->x && + ei->rect.y >= p->y && + ei->rect.y < p->y + player->vel * delta) + { + hitObstacle = 1; + player->vel = 0.0f; + p->y = ei->rect.y; + } + } + if (!hitObstacle) { + player->pos.y += player->vel * delta; + player->vel += G * delta; + player->canJump = 0; + } else { + player->canJump = 1; + } +} + +void renderWorld(Player *player, EnvItem *envItems, int envItemsLength) { + for (int i = 0; i < envItemsLength; i++) { + DrawRectangleRec(envItems[i].rect, envItems[i].color); + } + Rectangle playerRect = { player->pos.x - 20, player->pos.y - 40, 40, 40 }; + DrawRectangleRec(playerRect, RED); +} + +int main(void) +{ + const int screenWidth = 800; + const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera"); + SetTargetFPS(60); + + Player player; + player.pos = (Vector2){ 400, 280 }; + player.vel = 0; + player.canJump = 0; + EnvItem envItems[] = { + {{ 0, 0, 1000, 400 }, 0, LIGHTGRAY }, + {{ 0, 400, 1000, 200 }, 1, GRAY }, + {{ 300, 200, 400, 10 }, 1, GRAY }, + {{ 250, 300, 100, 10 }, 1, GRAY }, + {{ 650, 300, 100, 10 }, 1, GRAY } + }; + int envItemsLength = sizeof(envItems) / sizeof (envItems[0]); + + Camera2D camera = { 0 }; + camera.target = player.pos; + camera.offset = (Vector2){ screenWidth/2, screenHeight/2 }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; + + int cameraOption = 0; + void (*cameraUpdaters[])(float, Camera2D*, Player*, EnvItem*, int, int, int) = { + updateCameraCenter, + updateCameraCenterInsideMap, + updateCameraCenterSmoothFollow, + updateCameraEvenOutOnLanding, + updateCameraPlayerBoundsPush + }; + int cameraUpdatersLength = sizeof(cameraUpdaters) / sizeof(cameraUpdaters[0]); + char* cameraDescriptions[] = { + "Follow player center", + "Follow player center, but clamp to map edges", + "Follow player center; smoothed", + "Follow player center horizontally; updateplayer center vertically after landing", + "Player push camera on getting too close to screen edge" + }; + + while (!WindowShouldClose()) { + float delta = GetFrameTime(); + updatePlayer(delta, &player, envItems, envItemsLength); + + camera.zoom += ((float)GetMouseWheelMove()*0.05f); + if (camera.zoom > 3.0f) camera.zoom = 3.0f; + else if (camera.zoom < 0.25f) camera.zoom = 0.25f; + if (IsKeyPressed(KEY_R)) + { + camera.zoom = 1.0f; + } + + if (IsKeyPressed(KEY_C)) { + cameraOption = (cameraOption + 1) % cameraUpdatersLength; + } + cameraUpdaters[cameraOption](delta, &camera, &player, envItems, envItemsLength, screenWidth, screenHeight); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + BeginMode2D(camera); + renderWorld(&player, envItems, envItemsLength); + EndMode2D(); + + DrawText("Controls:", 20, 20, 10, BLACK); + DrawText("- Right/Left to move", 40, 40, 10, DARKGRAY); + DrawText("- Space to jump", 40, 60, 10, DARKGRAY); + DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 40, 80, 10, DARKGRAY); + DrawText("- C to change camera mode", 40, 100, 10, DARKGRAY); + DrawText("Current camera mode:", 20, 120, 10, BLACK); + DrawText(cameraDescriptions[cameraOption], 40, 140, 10, DARKGRAY); + EndDrawing(); + } + + CloseWindow(); // Close window and OpenGL context + + return 0; +} diff --git a/examples/core/core_basic_window.cpp b/examples/core/core_basic_window.cpp index fa12026a0..414f91852 100644 --- a/examples/core/core_basic_window.cpp +++ b/examples/core/core_basic_window.cpp @@ -23,40 +23,40 @@ int main(int argc, char* argv[]) { - // Initialization - //-------------------------------------------------------------------------------------- - int screenWidth = 800; - int screenHeight = 450; + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - CloseWindow(); // Close window and OpenGL context + SetTargetFPS(60); //-------------------------------------------------------------------------------------- - return 0; + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // TODO: Update your variables here + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; } \ No newline at end of file diff --git a/examples/core/resources/distortion100.fs b/examples/core/resources/distortion100.fs index 6bfe252b3..112cbcb5c 100644 --- a/examples/core/resources/distortion100.fs +++ b/examples/core/resources/distortion100.fs @@ -11,14 +11,14 @@ uniform sampler2D texture0; uniform vec4 colDiffuse; // NOTE: Add here your custom variables -uniform vec2 leftLensCenter = vec2(0.288, 0.5); -uniform vec2 rightLensCenter = vec2(0.712, 0.5); -uniform vec2 leftScreenCenter = vec2(0.25, 0.5); -uniform vec2 rightScreenCenter = vec2(0.75, 0.5); -uniform vec2 scale = vec2(0.25, 0.45); -uniform vec2 scaleIn = vec2(4, 2.2222); -uniform vec4 hmdWarpParam = vec4(1, 0.22, 0.24, 0); -uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); +uniform vec2 leftLensCenter; +uniform vec2 rightLensCenter; +uniform vec2 leftScreenCenter; +uniform vec2 rightScreenCenter; +uniform vec2 scale; +uniform vec2 scaleIn; +uniform vec4 hmdWarpParam; +uniform vec4 chromaAbParam; void main() { diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c index 7f38b7f55..2aa321cf7 100644 --- a/examples/models/models_animation.c +++ b/examples/models/models_animation.c @@ -9,8 +9,15 @@ * * Copyright (c) 2019 Culacant (@culacant) and Ramon Santamaria (@raysan5) * +******************************************************************************************** +* +* To export a model from blender, make sure it is not posed, the vertices need to be in the +* same position as they would be in edit mode. +* and that the scale of your models is set to 0. Scaling can be done from the export menu. +* ********************************************************************************************/ +#include #include "raylib.h" int main(void) @@ -91,8 +98,11 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- + UnloadTexture(texture); // Unload texture + // Unload model animations data for (int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); + RL_FREE(anims); UnloadModel(model); // Unload model diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c new file mode 100644 index 000000000..af9c5e390 --- /dev/null +++ b/examples/models/models_loading.c @@ -0,0 +1,142 @@ +/******************************************************************************************* +* +* raylib [models] example - Models loading +* +* raylib supports multiple models file formats: +* +* - OBJ > Text file, must include vertex position-texcoords-normals information, +* if files references some .mtl materials file, it will be loaded (or try to) +* - GLTF > Modern text/binary file format, includes lot of information and it could +* also reference external files, raylib will try loading mesh and materials data +* - IQM > Binary file format including mesh vertex data but also animation data, +* raylib can load .iqm animations. +* +* This example has been created using raylib 2.6 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - models loading"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 50.0f, 50.0f, 50.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 10.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + camera.type = CAMERA_PERSPECTIVE; // Camera mode type + + Model model = LoadModel("resources/models/castle.obj"); // Load model + Texture2D texture = LoadTexture("resources/models/castle_diffuse.png"); // Load model texture + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture + + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + + BoundingBox bounds = MeshBoundingBox(model.meshes[0]); // Set model bounds + + // NOTE: bounds are calculated from the original size of the model, + // if model is scaled on drawing, bounds must be also scaled + + SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode + + bool selected = false; // Selected object flag + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); + + // Load new models/textures on drag&drop + if (IsFileDropped()) + { + int count = 0; + char **droppedFiles = GetDroppedFiles(&count); + + if (count == 1) // Only support one file dropped + { + if (IsFileExtension(droppedFiles[0], ".obj") || + IsFileExtension(droppedFiles[0], ".gltf") || + IsFileExtension(droppedFiles[0], ".iqm")) // Model file formats supported + { + UnloadModel(model); // Unload previous model + model = LoadModel(droppedFiles[0]); // Load new model + model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set current map diffuse texture + + bounds = MeshBoundingBox(model.meshes[0]); + + // TODO: Move camera position from target enough distance to visualize model properly + } + else if (IsFileExtension(droppedFiles[0], ".png")) // Texture file formats supported + { + // Unload current model texture and load new one + UnloadTexture(texture); + texture = LoadTexture(droppedFiles[0]); + model.materials[0].maps[MAP_DIFFUSE].texture = texture; + } + } + + ClearDroppedFiles(); // Clear internal buffers + } + + // Select model on mouse click + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + // Check collision between ray and box + if (CheckCollisionRayBox(GetMouseRay(GetMousePosition(), camera), bounds)) selected = !selected; + else selected = false; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture + + DrawGrid(20, 10.0f); // Draw a grid + + if (selected) DrawBoundingBox(bounds, GREEN); // Draw selection box + + EndMode3D(); + + DrawText("Drag & drop model to load mesh/texture.", 10, GetScreenHeight() - 20, 10, DARKGRAY); + if (selected) DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, GREEN); + + DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(texture); // Unload texture + UnloadModel(model); // Unload model + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/models/models_loading.png b/examples/models/models_loading.png new file mode 100644 index 000000000..8ad8cb195 Binary files /dev/null and b/examples/models/models_loading.png differ diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index 8d51eefd2..0da741408 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -50,16 +50,15 @@ int main(void) // NOTE: New VBO for tangents is generated at default location and also binded to mesh VAO MeshTangents(&model.meshes[0]); + UnloadMaterial(model.materials[0]); // get rid of default material model.materials[0] = LoadMaterialPBR((Color){ 255, 255, 255, 255 }, 1.0f, 1.0f); - // Define lights attributes - // NOTE: Shader is passed to every light on creation to define shader bindings internally - Light lights[MAX_LIGHTS] = { - CreateLight(LIGHT_POINT, (Vector3){ LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 0, 255 }, model.materials[0].shader), - CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.materials[0].shader), - CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.materials[0].shader), - CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 0.0f, LIGHT_HEIGHT*2.0f, -LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 255, 255 }, model.materials[0].shader) - }; + // Create lights + // NOTE: Lights are added to an internal lights pool automatically + CreateLight(LIGHT_POINT, (Vector3){ LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 0, 255 }, model.materials[0].shader); + CreateLight(LIGHT_POINT, (Vector3){ 0.0f, LIGHT_HEIGHT, LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 255, 0, 255 }, model.materials[0].shader); + CreateLight(LIGHT_POINT, (Vector3){ -LIGHT_DISTANCE, LIGHT_HEIGHT, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 0, 0, 255, 255 }, model.materials[0].shader); + CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 0.0f, LIGHT_HEIGHT*2.0f, -LIGHT_DISTANCE }, (Vector3){ 0.0f, 0.0f, 0.0f }, (Color){ 255, 0, 255, 255 }, model.materials[0].shader); SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode @@ -100,7 +99,20 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModel(model); // Unload skybox model + + // Shaders and textures must be unloaded by user, + // they could be in use by other models + UnloadTexture(model.materials[0].maps[MAP_ALBEDO].texture); + UnloadTexture(model.materials[0].maps[MAP_NORMAL].texture); + UnloadTexture(model.materials[0].maps[MAP_METALNESS].texture); + UnloadTexture(model.materials[0].maps[MAP_ROUGHNESS].texture); + UnloadTexture(model.materials[0].maps[MAP_OCCLUSION].texture); + UnloadTexture(model.materials[0].maps[MAP_IRRADIANCE].texture); + UnloadTexture(model.materials[0].maps[MAP_PREFILTER].texture); + UnloadTexture(model.materials[0].maps[MAP_BRDF].texture); + UnloadShader(model.materials[0].shader); + + UnloadModel(model); // Unload model CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- @@ -112,8 +124,8 @@ int main(void) // NOTE: PBR shader is loaded inside this function static Material LoadMaterialPBR(Color albedo, float metalness, float roughness) { - Material mat = { 0 }; // NOTE: All maps textures are set to { 0 } - + Material mat = LoadMaterialDefault(); // Initialize material to default + #if defined(PLATFORM_DESKTOP) mat.shader = LoadShader("resources/shaders/glsl330/pbr.vs", "resources/shaders/glsl330/pbr.fs"); #else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB @@ -135,7 +147,7 @@ static Material LoadMaterialPBR(Color albedo, float metalness, float roughness) // Set view matrix location mat.shader.locs[LOC_MATRIX_MODEL] = GetShaderLocation(mat.shader, "matModel"); - mat.shader.locs[LOC_MATRIX_VIEW] = GetShaderLocation(mat.shader, "view"); + //mat.shader.locs[LOC_MATRIX_VIEW] = GetShaderLocation(mat.shader, "view"); mat.shader.locs[LOC_VECTOR_VIEW] = GetShaderLocation(mat.shader, "viewPos"); // Set PBR standard maps diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index 6c0ae6530..eaecb2712 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -115,11 +115,12 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- + UnloadTexture(texture); // Unload texture // Unload models data (GPU VRAM) for (int i = 0; i < NUM_MODELS; i++) UnloadModel(models[i]); - CloseWindow(); // Close window and OpenGL context + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index 0bf95dd10..f600aae84 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -105,7 +105,7 @@ int main(void) // Check ray collision against model // NOTE: It considers model.transform matrix! - meshHitInfo = GetCollisionRayModel(ray, &tower); + meshHitInfo = GetCollisionRayModel(ray, tower); if ((meshHitInfo.hit) && (meshHitInfo.distance < nearestHit.distance)) { diff --git a/examples/models/models_obj_loading.c b/examples/models/models_obj_loading.c deleted file mode 100644 index 51578bc1c..000000000 --- a/examples/models/models_obj_loading.c +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************************* -* -* raylib [models] example - Load and draw a 3d model (OBJ) -* -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) -* -* Copyright (c) 2014 Ramon Santamaria (@raysan5) -* -********************************************************************************************/ - -#include "raylib.h" - -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - obj model loading"); - - // Define the camera to look into our 3d world - Camera camera = { 0 }; - camera.position = (Vector3){ 8.0f, 8.0f, 8.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 2.5f, 0.0f }; // Camera looking at point - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) - camera.fovy = 45.0f; // Camera field-of-view Y - camera.type = CAMERA_PERSPECTIVE; // Camera mode type - - Model model = LoadModel("resources/models/castle.obj"); // Load OBJ model - Texture2D texture = LoadTexture("resources/models/castle_diffuse.png"); // Load model texture - model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Set map diffuse texture - Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - //... - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - BeginMode3D(camera); - - DrawModel(model, position, 0.2f, WHITE); // Draw 3d model with texture - - DrawGrid(10, 1.0f); // Draw a grid - - DrawGizmo(position); // Draw gizmo - - EndMode3D(); - - DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadTexture(texture); // Unload texture - UnloadModel(model); // Unload model - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} \ No newline at end of file diff --git a/examples/models/models_obj_loading.png b/examples/models/models_obj_loading.png deleted file mode 100644 index 098aa601f..000000000 Binary files a/examples/models/models_obj_loading.png and /dev/null differ diff --git a/examples/models/models_obj_viewer.c b/examples/models/models_obj_viewer.c deleted file mode 100644 index 83c8f2f1c..000000000 --- a/examples/models/models_obj_viewer.c +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************************* -* -* raylib [models] example - OBJ models viewer -* -* This example has been created using raylib 2.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) -* -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) -* -********************************************************************************************/ - -#include "raylib.h" - -#include // Required for: strcpy() - -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib example - obj viewer"); - - // Define the camera to look into our 3d world - Camera camera = { { 30.0f, 30.0f, 30.0f }, { 0.0f, 10.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; - - Model model = LoadModel("resources/models/turret.obj"); // Load default model obj - Texture2D texture = LoadTexture("resources/models/turret_diffuse.png"); // Load default model texture - model.materials[0].maps[MAP_DIFFUSE].texture = texture; // Bind texture to model - - Vector3 position = { 0.0, 0.0, 0.0 }; // Set model position - BoundingBox bounds = MeshBoundingBox(model.meshes[0]); // Set model bounds - bool selected = false; // Selected object flag - - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode - - char objFilename[64] = "turret.obj"; - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - if (IsFileDropped()) - { - int count = 0; - char **droppedFiles = GetDroppedFiles(&count); - - if (count == 1) - { - if (IsFileExtension(droppedFiles[0], ".obj")) - { - for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); - model.meshes = LoadMeshes(droppedFiles[0], &model.meshCount); - bounds = MeshBoundingBox(model.meshes[0]); - } - else if (IsFileExtension(droppedFiles[0], ".png")) - { - UnloadTexture(texture); - texture = LoadTexture(droppedFiles[0]); - model.materials[0].maps[MAP_DIFFUSE].texture = texture; - } - - strcpy(objFilename, GetFileName(droppedFiles[0])); - } - - ClearDroppedFiles(); // Clear internal buffers - } - - UpdateCamera(&camera); - - // Select model on mouse click - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) - { - // Check collision between ray and box - if (CheckCollisionRayBox(GetMouseRay(GetMousePosition(), camera), bounds)) selected = !selected; - else selected = false; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - BeginMode3D(camera); - - DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture - - DrawGrid(20.0, 10.0); // Draw a grid - - if (selected) DrawBoundingBox(bounds, GREEN); - - EndMode3D(); - - DrawText("Free camera default controls:", 10, 20, 10, DARKGRAY); - DrawText("- Mouse Wheel to Zoom in-out", 20, 40, 10, GRAY); - DrawText("- Mouse Wheel Pressed to Pan", 20, 60, 10, GRAY); - DrawText("- Alt + Mouse Wheel Pressed to Rotate", 20, 80, 10, GRAY); - DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 20, 100, 10, GRAY); - - DrawText("Drag & drop .obj/.png to load mesh/texture.", 10, GetScreenHeight() - 20, 10, DARKGRAY); - DrawText(FormatText("Current file: %s", objFilename), 250, GetScreenHeight() - 20, 10, GRAY); - if (selected) DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, GREEN); - - DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadModel(model); // Unload model - - ClearDroppedFiles(); // Clear internal buffers - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} \ No newline at end of file diff --git a/examples/models/models_obj_viewer.png b/examples/models/models_obj_viewer.png deleted file mode 100644 index 6ac707675..000000000 Binary files a/examples/models/models_obj_viewer.png and /dev/null differ diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index bad29b96a..c2849032d 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -89,7 +89,10 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModel(skybox); // Unload skybox model (and textures) + UnloadShader(skybox.materials[0].shader); + UnloadTexture(skybox.materials[0].maps[MAP_CUBEMAP].texture); + + UnloadModel(skybox); // Unload skybox model CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index 0931c00e9..652738113 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -92,6 +92,7 @@ int main(void) while (pitchOffset < -180) pitchOffset += 360; pitchOffset *= 10; + /* matrix transform done with multiplication to combine rotations Matrix transform = MatrixIdentity(); transform = MatrixMultiply(transform, MatrixRotateZ(DEG2RAD*roll)); @@ -99,8 +100,11 @@ int main(void) transform = MatrixMultiply(transform, MatrixRotateY(DEG2RAD*yaw)); model.transform = transform; - //---------------------------------------------------------------------------------- + */ + // matrix created from multiple axes at once + model.transform = MatrixRotateXYZ((Vector3){DEG2RAD*pitch,DEG2RAD*yaw,DEG2RAD*roll}); + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- BeginDrawing(); @@ -165,6 +169,7 @@ int main(void) //-------------------------------------------------------------------------------------- // Unload all loaded data + UnloadTexture(model.materials[0].maps[MAP_DIFFUSE].texture); UnloadModel(model); UnloadRenderTexture(framebuffer); diff --git a/examples/models/resources/models/Duck/Duck.glb b/examples/models/resources/models/Duck/Duck.glb new file mode 100644 index 000000000..217170d2b Binary files /dev/null and b/examples/models/resources/models/Duck/Duck.glb differ diff --git a/examples/models/resources/models/Duck/Duck.gltf b/examples/models/resources/models/Duck/Duck.gltf new file mode 100644 index 000000000..b80c842ce --- /dev/null +++ b/examples/models/resources/models/Duck/Duck.gltf @@ -0,0 +1,219 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 2, + 1 + ], + "matrix": [ + 0.009999999776482582, + 0.0, + 0.0, + 0.0, + 0.0, + 0.009999999776482582, + 0.0, + 0.0, + 0.0, + 0.0, + 0.009999999776482582, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "matrix": [ + -0.7289686799049377, + 0.0, + -0.6845470666885376, + 0.0, + -0.4252049028873444, + 0.7836934328079224, + 0.4527972936630249, + 0.0, + 0.5364750623703003, + 0.6211478114128113, + -0.571287989616394, + 0.0, + 400.1130065917969, + 463.2640075683594, + -431.0780334472656, + 1.0 + ], + "camera": 0 + }, + { + "mesh": 0 + } + ], + "cameras": [ + { + "perspective": { + "aspectRatio": 1.5, + "yfov": 0.6605925559997559, + "zfar": 10000.0, + "znear": 1.0 + }, + "type": "perspective" + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2, + "TEXCOORD_0": 3 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "LOD3spShape" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 12636, + "max": [ + 2398 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 2399, + "max": [ + 0.9995989799499512, + 0.999580979347229, + 0.9984359741210938 + ], + "min": [ + -0.9990839958190918, + -1.0, + -0.9998319745063782 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 28788, + "componentType": 5126, + "count": 2399, + "max": [ + 96.17990112304688, + 163.97000122070313, + 53.92519760131836 + ], + "min": [ + -69.29850006103516, + 9.929369926452637, + -61.32819747924805 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 2399, + "max": [ + 0.9833459854125976, + 0.9800369739532472 + ], + "min": [ + 0.026409000158309938, + 0.01996302604675293 + ], + "type": "VEC2" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0.0 + }, + "emissiveFactor": [ + 0.0, + 0.0, + 0.0 + ], + "name": "blinn3-fx" + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + } + ], + "images": [ + { + "uri": "DuckCM.png" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9986, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 76768, + "byteLength": 25272, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 57576, + "byteStride": 12, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 57576, + "byteLength": 19192, + "byteStride": 8, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 102040, + "uri": "Duck0.bin" + } + ] +} diff --git a/examples/models/resources/models/Duck/Duck0.bin b/examples/models/resources/models/Duck/Duck0.bin new file mode 100644 index 000000000..5f01f88ac Binary files /dev/null and b/examples/models/resources/models/Duck/Duck0.bin differ diff --git a/examples/models/resources/models/Duck/DuckCM.png b/examples/models/resources/models/Duck/DuckCM.png new file mode 100644 index 000000000..9fa2dd4cc Binary files /dev/null and b/examples/models/resources/models/Duck/DuckCM.png differ diff --git a/examples/models/resources/models/Duck/Duck_license.txt b/examples/models/resources/models/Duck/Duck_license.txt new file mode 100644 index 000000000..d1b4f3c34 --- /dev/null +++ b/examples/models/resources/models/Duck/Duck_license.txt @@ -0,0 +1,14 @@ +# Duck +## Screenshot + +![screenshot](screenshot/screenshot.png) + +## License Information + +Copyright 2006 Sony Computer Entertainment Inc. + +Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: + +http://research.scea.com/scea_shared_source_license.html + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/examples/models/resources/plane_diffuse.png b/examples/models/resources/plane_diffuse.png index fb16f24c1..8cf75c7d2 100644 Binary files a/examples/models/resources/plane_diffuse.png and b/examples/models/resources/plane_diffuse.png differ diff --git a/examples/models/resources/shaders/glsl330/pbr.fs b/examples/models/resources/shaders/glsl330/pbr.fs index 38d56c5d7..68bf203a2 100644 --- a/examples/models/resources/shaders/glsl330/pbr.fs +++ b/examples/models/resources/shaders/glsl330/pbr.fs @@ -73,6 +73,8 @@ vec3 fresnelSchlick(float cosTheta, vec3 F0); vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness); vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir); +// WARNING: There is some weird behaviour with this function, always returns black! +// Yes, I even tried: return texture(property.sampler, texCoord).rgb; vec3 ComputeMaterialProperty(MaterialProperty property) { vec3 result = vec3(0.0, 0.0, 0.0); @@ -187,17 +189,17 @@ void main() else texCoord = fragTexCoord; // Use default texture coordinates // Fetch material values from texture sampler or color attributes - vec3 color = ComputeMaterialProperty(albedo); - vec3 metal = ComputeMaterialProperty(metalness); - vec3 rough = ComputeMaterialProperty(roughness); - vec3 emiss = ComputeMaterialProperty(emission); - vec3 ao = ComputeMaterialProperty(occlusion); + vec3 color = texture(albedo.sampler, texCoord).rgb; //ComputeMaterialProperty(albedo); + vec3 metal = texture(metalness.sampler, texCoord).rgb; //ComputeMaterialProperty(metalness); + vec3 rough = texture(roughness.sampler, texCoord).rgb; //ComputeMaterialProperty(roughness); + vec3 emiss = texture(emission.sampler, texCoord).rgb; //ComputeMaterialProperty(emission); + vec3 ao = texture(occlusion.sampler, texCoord).rgb; //ComputeMaterialProperty(occlusion); // Check if normal mapping is enabled if (normals.useSampler == 1) { // Fetch normal map color and transform lighting values to tangent space - normal = ComputeMaterialProperty(normals); + normal = texture(normals.sampler, texCoord).rgb; //ComputeMaterialProperty(normals); normal = normalize(normal*2.0 - 1.0); normal = normalize(normal*TBN); diff --git a/examples/models/rlights.h b/examples/models/rlights.h index 19504473a..6593ab4a5 100644 --- a/examples/models/rlights.h +++ b/examples/models/rlights.h @@ -33,6 +33,8 @@ #ifndef RLIGHTS_H #define RLIGHTS_H +#include "raylib.h" + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -65,16 +67,11 @@ typedef struct { extern "C" { // Prevents name mangling of functions #endif -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -int lightsCount = 0; // Current amount of created lights - //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- -Light CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader); // Defines a light and get locations from PBR shader -void UpdateLightValues(Shader shader, Light light); // Send to PBR shader light values +void CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader); // Defines a light and get locations from PBR shader +void UpdateLightValues(Shader shader, Light light); // Send to PBR shader light values #ifdef __cplusplus } @@ -106,7 +103,8 @@ void UpdateLightValues(Shader shader, Light light); //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -// ... +static Light lights[MAX_LIGHTS] = { 0 }; +static int lightsCount = 0; // Current amount of created lights //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -118,7 +116,7 @@ void UpdateLightValues(Shader shader, Light light); //---------------------------------------------------------------------------------- // Defines a light and get locations from PBR shader -Light CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader) +void CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader) { Light light = { 0 }; @@ -148,10 +146,10 @@ Light CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shade light.colorLoc = GetShaderLocation(shader, colorName); UpdateLightValues(shader, light); + + lights[lightsCount] = light; lightsCount++; } - - return light; } // Send to PBR shader light values diff --git a/examples/network/network_ping_pong.c b/examples/network/network_ping_pong.c index 719f6739e..684678c51 100644 --- a/examples/network/network_ping_pong.c +++ b/examples/network/network_ping_pong.c @@ -48,178 +48,178 @@ char recvBuffer[512]; // Attempt to connect to the network (Either TCP, or UDP) void NetworkConnect() { - // If the server is configured as UDP, ignore connection requests - if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { - ping = true; - connected = true; - } else { - // If the client is connected, run the server code to check for a connection - if (client_connected) { - int active = CheckSockets(socket_set, 0); - if (active != 0) { - TraceLog(LOG_DEBUG, - "There are currently %d socket(s) with data to be processed.", active); - } - if (active > 0) { - if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) { - AddSocket(socket_set, connection); - ping = true; - connected = true; - } - } - } else { - // Check if we're connected every _delay_ seconds - elapsed += GetFrameTime(); - if (elapsed > delay) { - if (IsSocketConnected(client_res->socket)) { - client_connected = true; - } - elapsed = 0.0f; - } - } - } + // If the server is configured as UDP, ignore connection requests + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + ping = true; + connected = true; + } else { + // If the client is connected, run the server code to check for a connection + if (client_connected) { + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + if (active > 0) { + if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) { + AddSocket(socket_set, connection); + ping = true; + connected = true; + } + } + } else { + // Check if we're connected every _delay_ seconds + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (IsSocketConnected(client_res->socket)) { + client_connected = true; + } + elapsed = 0.0f; + } + } + } } // Once connected to the network, check the sockets for pending information // and when information is ready, send either a Ping or a Pong. void NetworkUpdate() { - // CheckSockets - // - // If any of the sockets in the socket_set are pending (received data, or requests) - // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) - int active = CheckSockets(socket_set, 0); - if (active != 0) { - TraceLog(LOG_DEBUG, - "There are currently %d socket(s) with data to be processed.", active); - } + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } - // IsSocketReady - // - // If the socket is ready, attempt to receive data from the socket - int bytesRecv = 0; - if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { - if (IsSocketReady(client_res->socket)) { - bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); - } - if (IsSocketReady(server_res->socket)) { - bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); - } - } else { - if (IsSocketReady(connection)) { - bytesRecv = SocketReceive(connection, recvBuffer, msglen); - } - } + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); + } + if (IsSocketReady(server_res->socket)) { + bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); + } + } else { + if (IsSocketReady(connection)) { + bytesRecv = SocketReceive(connection, recvBuffer, msglen); + } + } - // If we received data, was that data a "Ping!" or a "Pong!" - if (bytesRecv > 0) { - if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } - if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } - } + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } - // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa - elapsed += GetFrameTime(); - if (elapsed > delay) { - if (ping) { - ping = false; - if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { - SocketSend(client_res->socket, pingmsg, msglen); - } else { - SocketSend(client_res->socket, pingmsg, msglen); - } - } else if (pong) { - pong = false; - if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { - SocketSend(client_res->socket, pongmsg, msglen); - } else { - SocketSend(client_res->socket, pongmsg, msglen); - } - } - elapsed = 0.0f; - } + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + SocketSend(client_res->socket, pingmsg, msglen); + } else { + SocketSend(client_res->socket, pingmsg, msglen); + } + } else if (pong) { + pong = false; + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { + SocketSend(client_res->socket, pongmsg, msglen); + } else { + SocketSend(client_res->socket, pongmsg, msglen); + } + } + elapsed = 0.0f; + } } int main() { - // Setup - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [network] example - ping pong"); - SetTargetFPS(60); - SetTraceLogLevel(LOG_DEBUG); + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - ping pong"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); - // Networking - InitNetwork(); + // Networking + InitNetwork(); - // Create the server - // - // Performs - // getaddrinfo - // socket - // setsockopt - // bind - // listen - server_res = AllocSocketResult(); - if (!SocketCreate(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", - server_res->status, server_res->socket->status); - } else { - if (!SocketBind(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", - server_res->status, server_res->socket->status); - } else { - if (!(server_cfg.type == SOCKET_UDP)) { - if (!SocketListen(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, - "Failed to start listen server: status %d, errno %d", - server_res->status, server_res->socket->status); - } - } - } - } + // Create the server + // + // Performs + // getaddrinfo + // socket + // setsockopt + // bind + // listen + server_res = AllocSocketResult(); + if (!SocketCreate(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!SocketBind(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!(server_cfg.type == SOCKET_UDP)) { + if (!SocketListen(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, + "Failed to start listen server: status %d, errno %d", + server_res->status, server_res->socket->status); + } + } + } + } - // Create the client - // - // Performs - // getaddrinfo - // socket - // setsockopt - // connect (TCP only) - client_res = AllocSocketResult(); - if (!SocketCreate(&client_cfg, client_res)) { - TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", - client_res->status, client_res->socket->status); - } else { - if (!(client_cfg.type == SOCKET_UDP)) { - if (!SocketConnect(&client_cfg, client_res)) { - TraceLog(LOG_WARNING, - "Failed to connect to server: status %d, errno %d", - client_res->status, client_res->socket->status); - } - } - } + // Create the client + // + // Performs + // getaddrinfo + // socket + // setsockopt + // connect (TCP only) + client_res = AllocSocketResult(); + if (!SocketCreate(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", + client_res->status, client_res->socket->status); + } else { + if (!(client_cfg.type == SOCKET_UDP)) { + if (!SocketConnect(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, + "Failed to connect to server: status %d, errno %d", + client_res->status, client_res->socket->status); + } + } + } - // Create & Add sockets to the socket set - socket_set = AllocSocketSet(3); - msglen = strlen(pingmsg) + 1; - memset(recvBuffer, '\0', sizeof(recvBuffer)); - AddSocket(socket_set, server_res->socket); - AddSocket(socket_set, client_res->socket); + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(3); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, server_res->socket); + AddSocket(socket_set, client_res->socket); - // Main game loop - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(RAYWHITE); - if (connected) { - NetworkUpdate(); - } else { - NetworkConnect(); - } - EndDrawing(); - } + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + if (connected) { + NetworkUpdate(); + } else { + NetworkConnect(); + } + EndDrawing(); + } - // Cleanup - CloseWindow(); - return 0; + // Cleanup + CloseWindow(); + return 0; } \ No newline at end of file diff --git a/examples/network/network_resolve_host.c b/examples/network/network_resolve_host.c index 195e03b52..45d6e4e9a 100644 --- a/examples/network/network_resolve_host.c +++ b/examples/network/network_resolve_host.c @@ -28,30 +28,30 @@ uint16_t port = 0; int main() { - // Setup - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [network] example - ping pong"); - SetTargetFPS(60); + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - ping pong"); + SetTargetFPS(60); - SetTraceLogLevel(LOG_DEBUG); + SetTraceLogLevel(LOG_DEBUG); - // Networking - InitNetwork(); - + // Networking + InitNetwork(); + AddressInformation* addr = AllocAddressList(1); - int count = ResolveHost( - NULL, - "5210", - ADDRESS_TYPE_IPV4, + int count = ResolveHost( + NULL, + "5210", + ADDRESS_TYPE_IPV4, 0 // Uncomment any of these flags // ADDRESS_INFO_NUMERICHOST // or try them in conjunction to // ADDRESS_INFO_NUMERICSERV // specify custom behaviour from // ADDRESS_INFO_DNS_ONLY // the function getaddrinfo() // ADDRESS_INFO_ALL // // ADDRESS_INFO_FQDN // e.g. ADDRESS_INFO_CANONNAME | ADDRESS_INFO_NUMERICSERV - , + , addr ); @@ -61,20 +61,20 @@ int main() TraceLog(LOG_INFO, "Resolved to ip %s::%d\n", buffer, port); } - // Main game loop - while (!WindowShouldClose()) - { - // Draw - BeginDrawing(); + // Main game loop + while (!WindowShouldClose()) + { + // Draw + BeginDrawing(); - // Clear - ClearBackground(RAYWHITE); + // Clear + ClearBackground(RAYWHITE); - // End draw - EndDrawing(); - } + // End draw + EndDrawing(); + } - // Cleanup - CloseWindow(); - return 0; + // Cleanup + CloseWindow(); + return 0; } \ No newline at end of file diff --git a/examples/network/network_tcp_client.c b/examples/network/network_tcp_client.c index 6eed205ae..3f69dcd28 100644 --- a/examples/network/network_tcp_client.c +++ b/examples/network/network_tcp_client.c @@ -43,109 +43,109 @@ char recvBuffer[512]; // Attempt to connect to the network (Either TCP, or UDP) void NetworkConnect() { - // Check if we're connected every _delay_ seconds - elapsed += GetFrameTime(); - if (elapsed > delay) { - if (IsSocketConnected(client_res->socket)) { connected = true; } - elapsed = 0.0f; - } + // Check if we're connected every _delay_ seconds + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (IsSocketConnected(client_res->socket)) { connected = true; } + elapsed = 0.0f; + } } // Once connected to the network, check the sockets for pending information // and when information is ready, send either a Ping or a Pong. void NetworkUpdate() { - // CheckSockets - // - // If any of the sockets in the socket_set are pending (received data, or requests) - // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) - int active = CheckSockets(socket_set, 0); - if (active != 0) { - TraceLog(LOG_DEBUG, - "There are currently %d socket(s) with data to be processed.", active); - } + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } - // IsSocketReady - // - // If the socket is ready, attempt to receive data from the socket - int bytesRecv = 0; - if (IsSocketReady(client_res->socket)) { - bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); - } + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); + } - // If we received data, was that data a "Ping!" or a "Pong!" - if (bytesRecv > 0) { - if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } - if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } - } + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } - // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa - elapsed += GetFrameTime(); - if (elapsed > delay) { - if (ping) { - ping = false; - SocketSend(client_res->socket, pingmsg, msglen); - } else if (pong) { - pong = false; - SocketSend(client_res->socket, pongmsg, msglen); - } - elapsed = 0.0f; - } + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(client_res->socket, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(client_res->socket, pongmsg, msglen); + } + elapsed = 0.0f; + } } int main() { - // Setup - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [network] example - tcp client"); - SetTargetFPS(60); - SetTraceLogLevel(LOG_DEBUG); + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - tcp client"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); - // Networking - InitNetwork(); + // Networking + InitNetwork(); - // Create the client - // - // Performs - // getaddrinfo - // socket - // setsockopt - // connect (TCP only) - client_res = AllocSocketResult(); - if (!SocketCreate(&client_cfg, client_res)) { - TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", - client_res->status, client_res->socket->status); - } else { - if (!(client_cfg.type == SOCKET_UDP)) { - if (!SocketConnect(&client_cfg, client_res)) { - TraceLog(LOG_WARNING, - "Failed to connect to server: status %d, errno %d", - client_res->status, client_res->socket->status); - } - } - } + // Create the client + // + // Performs + // getaddrinfo + // socket + // setsockopt + // connect (TCP only) + client_res = AllocSocketResult(); + if (!SocketCreate(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", + client_res->status, client_res->socket->status); + } else { + if (!(client_cfg.type == SOCKET_UDP)) { + if (!SocketConnect(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, + "Failed to connect to server: status %d, errno %d", + client_res->status, client_res->socket->status); + } + } + } - // Create & Add sockets to the socket set - socket_set = AllocSocketSet(1); - msglen = strlen(pingmsg) + 1; - memset(recvBuffer, '\0', sizeof(recvBuffer)); - AddSocket(socket_set, client_res->socket); + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(1); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, client_res->socket); - // Main game loop - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(RAYWHITE); - if (connected) { - NetworkUpdate(); - } else { - NetworkConnect(); - } - EndDrawing(); - } + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + if (connected) { + NetworkUpdate(); + } else { + NetworkConnect(); + } + EndDrawing(); + } - // Cleanup - CloseWindow(); - return 0; + // Cleanup + CloseWindow(); + return 0; } \ No newline at end of file diff --git a/examples/network/network_tcp_server.c b/examples/network/network_tcp_server.c index 89e9c1810..e93687260 100644 --- a/examples/network/network_tcp_server.c +++ b/examples/network/network_tcp_server.c @@ -45,121 +45,121 @@ char recvBuffer[512]; // Attempt to connect to the network (Either TCP, or UDP) void NetworkConnect() { - int active = CheckSockets(socket_set, 0); - if (active != 0) { - TraceLog(LOG_DEBUG, - "There are currently %d socket(s) with data to be processed.", active); - } - if (active > 0) { - if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) { - AddSocket(socket_set, connection); - ping = true; - connected = true; - } - } + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } + if (active > 0) { + if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) { + AddSocket(socket_set, connection); + ping = true; + connected = true; + } + } } // Once connected to the network, check the sockets for pending information // and when information is ready, send either a Ping or a Pong. void NetworkUpdate() { - // CheckSockets - // - // If any of the sockets in the socket_set are pending (received data, or requests) - // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) - int active = CheckSockets(socket_set, 0); - if (active != 0) { - TraceLog(LOG_DEBUG, - "There are currently %d socket(s) with data to be processed.", active); - } + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } - // IsSocketReady - // - // If the socket is ready, attempt to receive data from the socket - int bytesRecv = 0; - if (IsSocketReady(connection)) { - bytesRecv = SocketReceive(connection, recvBuffer, msglen); - } + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (IsSocketReady(connection)) { + bytesRecv = SocketReceive(connection, recvBuffer, msglen); + } - // If we received data, was that data a "Ping!" or a "Pong!" - if (bytesRecv > 0) { - if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } - if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } - } + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } - // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa - elapsed += GetFrameTime(); - if (elapsed > delay) { - if (ping) { - ping = false; - SocketSend(connection, pingmsg, msglen); - } else if (pong) { - pong = false; - SocketSend(connection, pongmsg, msglen); - } - elapsed = 0.0f; - } + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(connection, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(connection, pongmsg, msglen); + } + elapsed = 0.0f; + } } int main() { - // Setup - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [network] example - tcp server"); - SetTargetFPS(60); - SetTraceLogLevel(LOG_DEBUG); + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - tcp server"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); - // Networking - InitNetwork(); + // Networking + InitNetwork(); - // Create the server - // - // Performs - // getaddrinfo - // socket - // setsockopt - // bind - // listen - server_res = AllocSocketResult(); - if (!SocketCreate(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", - server_res->status, server_res->socket->status); - } else { - if (!SocketBind(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", - server_res->status, server_res->socket->status); - } else { - if (!(server_cfg.type == SOCKET_UDP)) { - if (!SocketListen(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, - "Failed to start listen server: status %d, errno %d", - server_res->status, server_res->socket->status); - } - } - } - } + // Create the server + // + // Performs + // getaddrinfo + // socket + // setsockopt + // bind + // listen + server_res = AllocSocketResult(); + if (!SocketCreate(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!SocketBind(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!(server_cfg.type == SOCKET_UDP)) { + if (!SocketListen(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, + "Failed to start listen server: status %d, errno %d", + server_res->status, server_res->socket->status); + } + } + } + } - // Create & Add sockets to the socket set - socket_set = AllocSocketSet(2); - msglen = strlen(pingmsg) + 1; - memset(recvBuffer, '\0', sizeof(recvBuffer)); - AddSocket(socket_set, server_res->socket); + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(2); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, server_res->socket); - // Main game loop - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(RAYWHITE); - if (connected) { - NetworkUpdate(); - } else { - NetworkConnect(); - } - EndDrawing(); - } + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + if (connected) { + NetworkUpdate(); + } else { + NetworkConnect(); + } + EndDrawing(); + } - // Cleanup - CloseWindow(); - return 0; + // Cleanup + CloseWindow(); + return 0; } \ No newline at end of file diff --git a/examples/network/network_test.c b/examples/network/network_test.c index f18a8b13c..56d9095ed 100644 --- a/examples/network/network_test.c +++ b/examples/network/network_test.c @@ -27,80 +27,80 @@ void test_network_initialise() { - assert(InitNetwork() == true); + assert(InitNetwork() == true); } void test_socket_result() { - SocketResult *result = AllocSocketResult(); - assert(result != NULL); - FreeSocketResult(&result); - assert(result == NULL); + SocketResult *result = AllocSocketResult(); + assert(result != NULL); + FreeSocketResult(&result); + assert(result == NULL); } void test_socket() { - Socket *socket = AllocSocket(); - assert(socket != NULL); - FreeSocket(&socket); - assert(socket == NULL); + Socket *socket = AllocSocket(); + assert(socket != NULL); + FreeSocket(&socket); + assert(socket == NULL); } void test_resolve_ip() { - const char *host = "8.8.8.8"; - const char *port = "8080"; - char ip[ADDRESS_IPV6_ADDRSTRLEN]; + const char *host = "8.8.8.8"; + const char *port = "8080"; + char ip[ADDRESS_IPV6_ADDRSTRLEN]; char service[ADDRESS_MAXSERV]; - memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); - ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service); - TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); - assert(strcmp(ip, "8.8.8.8") == 0); + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "8.8.8.8") == 0); - memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); - ResolveIP(host, port, NAME_INFO_DEFAULT, ip, service); - TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); - assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_DEFAULT, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); - memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); - ResolveIP(host, port, NAME_INFO_NOFQDN, ip, service); - TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); - assert(strcmp(ip, "google-public-dns-a") == 0); + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NOFQDN, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a") == 0); - memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); - ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service); - TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); - assert(strcmp(ip, "8.8.8.8") == 0); + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "8.8.8.8") == 0); - memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); - ResolveIP(host, port, NAME_INFO_NAMEREQD, ip, service); - TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); - assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NAMEREQD, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); - memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); - ResolveIP(host, port, NAME_INFO_NUMERICSERV, ip, service); - TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); - assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_NUMERICSERV, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); - memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); - ResolveIP(host, port, NAME_INFO_DGRAM, ip, service); - TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); - assert(strcmp(ip, "google-public-dns-a.google.com") == 0); + memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN); + ResolveIP(host, port, NAME_INFO_DGRAM, ip, service); + TraceLog(LOG_INFO, "Resolved %s to %s", host, ip); + assert(strcmp(ip, "google-public-dns-a.google.com") == 0); } void test_resolve_host() { - const char * address = "localhost"; - const char * port = "80"; - AddressInformation *addr = AllocAddressList(3); - int count = ResolveHost(address, port, ADDRESS_TYPE_ANY, 0, addr); + const char * address = "localhost"; + const char * port = "80"; + AddressInformation *addr = AllocAddressList(3); + int count = ResolveHost(address, port, ADDRESS_TYPE_ANY, 0, addr); - assert(GetAddressFamily(addr[0]) == ADDRESS_TYPE_IPV6); - assert(GetAddressFamily(addr[1]) == ADDRESS_TYPE_IPV4); - assert(GetAddressSocketType(addr[0]) == 0); - assert(GetAddressProtocol(addr[0]) == 0); - // for (size_t i = 0; i < count; i++) { PrintAddressInfo(addr[i]); } + assert(GetAddressFamily(addr[0]) == ADDRESS_TYPE_IPV6); + assert(GetAddressFamily(addr[1]) == ADDRESS_TYPE_IPV4); + assert(GetAddressSocketType(addr[0]) == 0); + assert(GetAddressProtocol(addr[0]) == 0); + // for (size_t i = 0; i < count; i++) { PrintAddressInfo(addr[i]); } } void test_address() @@ -113,36 +113,36 @@ void test_address_list() void test_socket_create() { - SocketConfig server_cfg = {.host = "127.0.0.1", .port = "8080", .server = true, .nonblocking = true}; - Socket * socket = AllocSocket(); - SocketResult *server_res = AllocSocketResult(); - SocketSet * socket_set = AllocSocketSet(1); - assert(SocketCreate(&server_cfg, server_res)); - assert(AddSocket(socket_set, server_res->socket)); - assert(SocketListen(&server_cfg, server_res)); + SocketConfig server_cfg = {.host = "127.0.0.1", .port = "8080", .server = true, .nonblocking = true}; + Socket * socket = AllocSocket(); + SocketResult *server_res = AllocSocketResult(); + SocketSet * socket_set = AllocSocketSet(1); + assert(SocketCreate(&server_cfg, server_res)); + assert(AddSocket(socket_set, server_res->socket)); + assert(SocketListen(&server_cfg, server_res)); } int main() { - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [network] example - network test"); - SetTargetFPS(60); + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - network test"); + SetTargetFPS(60); - // Run the tests - test_network_initialise(); - test_resolve_host(); + // Run the tests + test_network_initialise(); + test_resolve_host(); // test_socket_create(); - // Main game loop - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(RAYWHITE); - DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); - EndDrawing(); - } - CloseWindow(); + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); + EndDrawing(); + } + CloseWindow(); - return 0; + return 0; } \ No newline at end of file diff --git a/examples/network/network_udp_client.c b/examples/network/network_udp_client.c index c1c89c8db..fbc0589fb 100644 --- a/examples/network/network_udp_client.c +++ b/examples/network/network_udp_client.c @@ -43,86 +43,86 @@ char recvBuffer[512]; // and when information is ready, send either a Ping or a Pong. void NetworkUpdate() { - // CheckSockets - // - // If any of the sockets in the socket_set are pending (received data, or requests) - // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) - int active = CheckSockets(socket_set, 0); - if (active != 0) { - TraceLog(LOG_DEBUG, - "There are currently %d socket(s) with data to be processed.", active); - } + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } - // IsSocketReady - // - // If the socket is ready, attempt to receive data from the socket - int bytesRecv = 0; - if (IsSocketReady(client_res->socket)) { - bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); - } + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + int bytesRecv = 0; + if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen); + } - // If we received data, was that data a "Ping!" or a "Pong!" - if (bytesRecv > 0) { - if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } - if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } - } + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } - // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa - elapsed += GetFrameTime(); - if (elapsed > delay) { - if (ping) { - ping = false; - SocketSend(client_res->socket, pingmsg, msglen); - } else if (pong) { - pong = false; - SocketSend(client_res->socket, pongmsg, msglen); - } - elapsed = 0.0f; - } + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(client_res->socket, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(client_res->socket, pongmsg, msglen); + } + elapsed = 0.0f; + } } int main() { - // Setup - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [network] example - udp client"); - SetTargetFPS(60); - SetTraceLogLevel(LOG_DEBUG); + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - udp client"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); - // Networking - InitNetwork(); + // Networking + InitNetwork(); - // Create the client - // - // Performs - // getaddrinfo - // socket - // setsockopt - // connect (TCP only) - client_res = AllocSocketResult(); - if (!SocketCreate(&client_cfg, client_res)) { - TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", - client_res->status, client_res->socket->status); - } + // Create the client + // + // Performs + // getaddrinfo + // socket + // setsockopt + // connect (TCP only) + client_res = AllocSocketResult(); + if (!SocketCreate(&client_cfg, client_res)) { + TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d", + client_res->status, client_res->socket->status); + } - // Create & Add sockets to the socket set - socket_set = AllocSocketSet(1); - msglen = strlen(pingmsg) + 1; - ping = true; - memset(recvBuffer, '\0', sizeof(recvBuffer)); - AddSocket(socket_set, client_res->socket); + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(1); + msglen = strlen(pingmsg) + 1; + ping = true; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, client_res->socket); - // Main game loop - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(RAYWHITE); - NetworkUpdate(); - EndDrawing(); - } + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + NetworkUpdate(); + EndDrawing(); + } - // Cleanup - CloseWindow(); - return 0; + // Cleanup + CloseWindow(); + return 0; } \ No newline at end of file diff --git a/examples/network/network_udp_server.c b/examples/network/network_udp_server.c index 982cdf633..5ab45bb5c 100644 --- a/examples/network/network_udp_server.c +++ b/examples/network/network_udp_server.c @@ -43,92 +43,92 @@ char recvBuffer[512]; // and when information is ready, send either a Ping or a Pong. void NetworkUpdate() { - // CheckSockets - // - // If any of the sockets in the socket_set are pending (received data, or requests) - // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) - int active = CheckSockets(socket_set, 0); - if (active != 0) { - TraceLog(LOG_DEBUG, - "There are currently %d socket(s) with data to be processed.", active); - } + // CheckSockets + // + // If any of the sockets in the socket_set are pending (received data, or requests) + // then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket) + int active = CheckSockets(socket_set, 0); + if (active != 0) { + TraceLog(LOG_DEBUG, + "There are currently %d socket(s) with data to be processed.", active); + } - // IsSocketReady - // - // If the socket is ready, attempt to receive data from the socket - // int bytesRecv = 0; - // if (IsSocketReady(server_res->socket)) { - // bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); - // } - int bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); + // IsSocketReady + // + // If the socket is ready, attempt to receive data from the socket + // int bytesRecv = 0; + // if (IsSocketReady(server_res->socket)) { + // bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); + // } + int bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen); - // If we received data, was that data a "Ping!" or a "Pong!" - if (bytesRecv > 0) { - if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } - if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } - } + // If we received data, was that data a "Ping!" or a "Pong!" + if (bytesRecv > 0) { + if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; } + if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; } + } - // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa - elapsed += GetFrameTime(); - if (elapsed > delay) { - if (ping) { - ping = false; - SocketSend(server_res->socket, pingmsg, msglen); - } else if (pong) { - pong = false; - SocketSend(server_res->socket, pongmsg, msglen); - } - elapsed = 0.0f; - } + // After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa + elapsed += GetFrameTime(); + if (elapsed > delay) { + if (ping) { + ping = false; + SocketSend(server_res->socket, pingmsg, msglen); + } else if (pong) { + pong = false; + SocketSend(server_res->socket, pongmsg, msglen); + } + elapsed = 0.0f; + } } int main() { - // Setup - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [network] example - udp server"); - SetTargetFPS(60); - SetTraceLogLevel(LOG_DEBUG); + // Setup + int screenWidth = 800; + int screenHeight = 450; + InitWindow( + screenWidth, screenHeight, "raylib [network] example - udp server"); + SetTargetFPS(60); + SetTraceLogLevel(LOG_DEBUG); - // Networking - InitNetwork(); + // Networking + InitNetwork(); - // Create the server - // - // Performs - // getaddrinfo - // socket - // setsockopt - // bind - // listen - server_res = AllocSocketResult(); - if (!SocketCreate(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", - server_res->status, server_res->socket->status); - } else { - if (!SocketBind(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", - server_res->status, server_res->socket->status); - } - } + // Create the server + // + // Performs + // getaddrinfo + // socket + // setsockopt + // bind + // listen + server_res = AllocSocketResult(); + if (!SocketCreate(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d", + server_res->status, server_res->socket->status); + } else { + if (!SocketBind(&server_cfg, server_res)) { + TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", + server_res->status, server_res->socket->status); + } + } - // Create & Add sockets to the socket set - socket_set = AllocSocketSet(1); - msglen = strlen(pingmsg) + 1; - memset(recvBuffer, '\0', sizeof(recvBuffer)); - AddSocket(socket_set, server_res->socket); + // Create & Add sockets to the socket set + socket_set = AllocSocketSet(1); + msglen = strlen(pingmsg) + 1; + memset(recvBuffer, '\0', sizeof(recvBuffer)); + AddSocket(socket_set, server_res->socket); - // Main game loop - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(RAYWHITE); - NetworkUpdate(); - EndDrawing(); - } + // Main game loop + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + NetworkUpdate(); + EndDrawing(); + } - // Cleanup - CloseWindow(); - return 0; + // Cleanup + CloseWindow(); + return 0; } \ No newline at end of file diff --git a/examples/others/raudio_standalone.c b/examples/others/raudio_standalone.c index 63d5b8d28..9122c3210 100644 --- a/examples/others/raudio_standalone.c +++ b/examples/others/raudio_standalone.c @@ -58,29 +58,29 @@ // Check if a key has been pressed static int kbhit(void) { - struct termios oldt, newt; - int ch; - int oldf; + struct termios oldt, newt; + int ch; + int oldf; - tcgetattr(STDIN_FILENO, &oldt); - newt = oldt; - newt.c_lflag &= ~(ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSANOW, &newt); - oldf = fcntl(STDIN_FILENO, F_GETFL, 0); - fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + oldf = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); - ch = getchar(); + ch = getchar(); - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); - fcntl(STDIN_FILENO, F_SETFL, oldf); + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + fcntl(STDIN_FILENO, F_SETFL, oldf); - if (ch != EOF) - { - ungetc(ch, stdin); - return 1; - } + if (ch != EOF) + { + ungetc(ch, stdin); + return 1; + } - return 0; + return 0; } // Get pressed character diff --git a/examples/physac/physics_restitution.c b/examples/physac/physics_restitution.c index a7012520d..12a2a9220 100644 --- a/examples/physac/physics_restitution.c +++ b/examples/physac/physics_restitution.c @@ -120,6 +120,10 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- + DestroyPhysicsBody(circleA); + DestroyPhysicsBody(circleB); + DestroyPhysicsBody(circleC); + DestroyPhysicsBody(floor); ClosePhysics(); // Unitialize physics CloseWindow(); // Close window and OpenGL context diff --git a/examples/shaders/resources/mask.png b/examples/shaders/resources/mask.png new file mode 100644 index 000000000..06a259787 Binary files /dev/null and b/examples/shaders/resources/mask.png differ diff --git a/examples/shaders/resources/plasma.png b/examples/shaders/resources/plasma.png new file mode 100644 index 000000000..01c2d8837 Binary files /dev/null and b/examples/shaders/resources/plasma.png differ diff --git a/examples/shaders/resources/shaders/glsl100/raymarching.fs b/examples/shaders/resources/shaders/glsl100/raymarching.fs index 4ae71297d..c823e01eb 100644 --- a/examples/shaders/resources/shaders/glsl100/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl100/raymarching.fs @@ -8,8 +8,6 @@ varying vec4 fragColor; uniform vec3 viewEye; uniform vec3 viewCenter; -uniform vec3 viewUp; -uniform float deltaTime; uniform float runTime; uniform vec2 resolution; @@ -428,4 +426,4 @@ void main() #endif gl_FragColor = vec4( tot, 1.0 ); -} \ No newline at end of file +} diff --git a/examples/shaders/resources/shaders/glsl330/fog.fs b/examples/shaders/resources/shaders/glsl330/fog.fs new file mode 100644 index 000000000..57ed14807 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/fog.fs @@ -0,0 +1,99 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec4 fragColor; +in vec3 fragPosition; +in vec3 fragNormal; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// Output fragment color +out vec4 finalColor; + +// NOTE: Add here your custom variables + +#define MAX_LIGHTS 4 +#define LIGHT_DIRECTIONAL 0 +#define LIGHT_POINT 1 + +struct MaterialProperty { + vec3 color; + int useSampler; + sampler2D sampler; +}; + +struct Light { + int enabled; + int type; + vec3 position; + vec3 target; + vec4 color; +}; + +// Input lighting values +uniform Light lights[MAX_LIGHTS]; +uniform vec4 ambient; +uniform vec3 viewPos; +uniform float fogDensity; + +void main() +{ + // Texel color fetching from texture sampler + vec4 texelColor = texture(texture0, fragTexCoord); + vec3 lightDot = vec3(0.0); + vec3 normal = normalize(fragNormal); + vec3 viewD = normalize(viewPos - fragPosition); + vec3 specular = vec3(0.0); + + // NOTE: Implement here your fragment shader code + + for (int i = 0; i < MAX_LIGHTS; i++) + { + if (lights[i].enabled == 1) + { + vec3 light = vec3(0.0); + if (lights[i].type == LIGHT_DIRECTIONAL) { + light = -normalize(lights[i].target - lights[i].position); + } + if (lights[i].type == LIGHT_POINT) { + light = normalize(lights[i].position - fragPosition); + } + float NdotL = max(dot(normal, light), 0.0); + lightDot += lights[i].color.rgb * NdotL; + + float specCo = 0.0; + if(NdotL > 0.0) + specCo = pow(max(0.0, dot(viewD, reflect(-(light), normal))), 16);//16 =shine + specular += specCo; + + } + } + + finalColor = (texelColor * ((colDiffuse+vec4(specular,1)) * vec4(lightDot, 1.0))); + finalColor += texelColor * (ambient/10.0); + + // Gamma correction + finalColor = pow(finalColor, vec4(1.0/2.2)); + + // Fog calculation + float dist = length(viewPos - fragPosition); + + // these could be parameters... + const vec4 fogColor = vec4(0.5, 0.5, 0.5, 1.0); + //const float fogDensity = 0.16; + + // Exponential fog + float fogFactor = 1.0/exp((dist*fogDensity)*(dist*fogDensity)); + + // Linear fog (less nice) + //const float fogStart = 2.0; + //const float fogEnd = 10.0; + //float fogFactor = (fogEnd - dist)/(fogEnd - fogStart); + + fogFactor = clamp(fogFactor, 0.0, 1.0); + + finalColor = mix(fogColor, finalColor, fogFactor); +} diff --git a/examples/shaders/resources/shaders/glsl330/fog.vs b/examples/shaders/resources/shaders/glsl330/fog.vs new file mode 100644 index 000000000..00779cfa3 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/fog.vs @@ -0,0 +1,32 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; + +// Output vertex attributes (to fragment shader) +out vec2 fragTexCoord; +out vec4 fragColor; +out vec3 fragPosition; +out vec3 fragNormal; + +// NOTE: Add here your custom variables + +void main() +{ + // Send vertex attributes to fragment shader + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + fragPosition = vec3(matModel*vec4(vertexPosition, 1.0f)); + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + fragNormal = normalize(normalMatrix*vertexNormal); + + // Calculate final vertex position + gl_Position = mvp*vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl330/mask.fs b/examples/shaders/resources/shaders/glsl330/mask.fs new file mode 100644 index 000000000..a06279096 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/mask.fs @@ -0,0 +1,21 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; + +// Input uniform values +uniform sampler2D texture0; +uniform sampler2D mask; +uniform int frame; + +// Output fragment color +out vec4 finalColor; + +void main() +{ + vec4 maskColour = texture(mask, fragTexCoord+vec2(sin(-frame/150.0)/10.0,cos(-frame/170.0)/10.0)); + if (maskColour.r < 0.25) discard; + vec4 texelColor = texture(texture0, fragTexCoord+vec2(sin(frame/90.0)/8.0,cos(frame/60.0)/8.0)); + + finalColor = texelColor * maskColour; +} diff --git a/examples/shaders/resources/shaders/glsl330/mask.vs b/examples/shaders/resources/shaders/glsl330/mask.vs new file mode 100644 index 000000000..66a151617 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/mask.vs @@ -0,0 +1,21 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; + +// Output vertex attributes (to fragment shader) +out vec2 fragTexCoord; + +void main() +{ + // Send vertex attributes to fragment shader + fragTexCoord = vertexTexCoord; + + // Calculate final vertex position + gl_Position = mvp*vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl330/raymarching.fs b/examples/shaders/resources/shaders/glsl330/raymarching.fs index 7c9fbcb10..3cec58a2b 100644 --- a/examples/shaders/resources/shaders/glsl330/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl330/raymarching.fs @@ -9,8 +9,6 @@ out vec4 finalColor; uniform vec3 viewEye; uniform vec3 viewCenter; -uniform vec3 viewUp; -uniform float deltaTime; uniform float runTime; uniform vec2 resolution; @@ -429,4 +427,4 @@ void main() #endif finalColor = vec4( tot, 1.0 ); -} \ No newline at end of file +} diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c new file mode 100644 index 000000000..e8f2691d2 --- /dev/null +++ b/examples/shaders/shaders_fog.c @@ -0,0 +1,153 @@ +/******************************************************************************************* +* +* raylib [shaders] example - fog +* +* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* +* This example has been created using raylib 2.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5) +* +* Chris Camacho (@codifies - http://bedroomcoders.co.uk/) notes: +* +* This is based on the PBR lighting example, but greatly simplified to aid learning... +* actually there is very little of the PBR example left! +* When I first looked at the bewildering complexity of the PBR example I feared +* I would never understand how I could do simple lighting with raylib however its +* a testement to the authors of raylib (including rlights.h) that the example +* came together fairly quickly. +* +* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#include "raymath.h" + +#define RLIGHTS_IMPLEMENTATION +#include "rlights.h" + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + SetConfigFlags(FLAG_MSAA_4X_HINT); // Enable Multi Sampling Anti Aliasing 4x (if available) + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - fog"); + + // Define the camera to look into our 3d world + Camera camera = { + (Vector3){ 2.0f, 2.0f, 6.0f }, // position + (Vector3){ 0.0f, 0.5f, 0.0f }, // target + (Vector3){ 0.0f, 1.0f, 0.0f }, // up + 45.0f, CAMERA_PERSPECTIVE }; // fov, type + + // Load models and texture + Model modelA = LoadModelFromMesh(GenMeshTorus(0.4f, 1.0f, 16, 32)); + Model modelB = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f)); + Model modelC = LoadModelFromMesh(GenMeshSphere(0.5f, 32, 32)); + Texture texture = LoadTexture("resources/texel_checker.png"); + + // Assign texture to default model material + modelA.materials[0].maps[MAP_DIFFUSE].texture = texture; + modelB.materials[0].maps[MAP_DIFFUSE].texture = texture; + modelC.materials[0].maps[MAP_DIFFUSE].texture = texture; + + // Load shader and set up some uniforms + Shader shader = LoadShader("resources/shaders/glsl330/fog.vs", "resources/shaders/glsl330/fog.fs"); + shader.locs[LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel"); + shader.locs[LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); + + // Ambient light level + int ambientLoc = GetShaderLocation(shader, "ambient"); + SetShaderValue(shader, ambientLoc, (float[4]){ 0.2f, 0.2f, 0.2f, 1.0f }, UNIFORM_VEC4); + + float fogDensity = 0.15f; + int fogDensityLoc = GetShaderLocation(shader, "fogDensity"); + SetShaderValue(shader, fogDensityLoc, &fogDensity, UNIFORM_FLOAT); + + // NOTE: All models share the same shader + modelA.materials[0].shader = shader; + modelB.materials[0].shader = shader; + modelC.materials[0].shader = shader; + + // Using just 1 point lights + CreateLight(LIGHT_POINT, (Vector3){ 0, 2, 6 }, Vector3Zero(), WHITE, shader); + + SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); // Update camera + + if (IsKeyDown(KEY_UP)) + { + fogDensity += 0.001; + if (fogDensity > 1.0) fogDensity = 1.0; + } + + if (IsKeyDown(KEY_DOWN)) + { + fogDensity -= 0.001; + if (fogDensity < 0.0) fogDensity = 0.0; + } + + SetShaderValue(shader, fogDensityLoc, &fogDensity, UNIFORM_FLOAT); + + // Rotate the torus + modelA.transform = MatrixMultiply(modelA.transform, MatrixRotateX(-0.025)); + modelA.transform = MatrixMultiply(modelA.transform, MatrixRotateZ(0.012)); + + // Update the light shader with the camera view position + SetShaderValue(shader, shader.locs[LOC_VECTOR_VIEW], &camera.position.x, UNIFORM_VEC3); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(GRAY); + + BeginMode3D(camera); + + // Draw the three models + DrawModel(modelA, Vector3Zero(), 1.0f, WHITE); + DrawModel(modelB, (Vector3){ -2.6, 0, 0 }, 1.0f, WHITE); + DrawModel(modelC, (Vector3){ 2.6, 0, 0 }, 1.0f, WHITE); + + for (int i = -20; i < 20; i += 2) DrawModel(modelA,(Vector3){ i, 0, 2 }, 1.0f, WHITE); + + EndMode3D(); + + DrawText(TextFormat("Use KEY_UP/KEY_DOWN to change fog density [%.2f]", fogDensity), 10, 10, 20, RAYWHITE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModel(modelA); // Unload the model A + UnloadModel(modelB); // Unload the model B + UnloadModel(modelC); // Unload the model C + UnloadTexture(texture); // Unload the texture + UnloadShader(shader); // Unload shader + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shaders/shaders_fog.png b/examples/shaders/shaders_fog.png new file mode 100644 index 000000000..52008e8a1 Binary files /dev/null and b/examples/shaders/shaders_fog.png differ diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index 34091792e..06ad0f92e 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -28,9 +28,10 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + int screenWidth = 800; + int screenHeight = 450; + SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(screenWidth, screenHeight, "raylib [shaders] example - raymarching shapes"); Camera camera = { 0 }; @@ -48,12 +49,10 @@ int main(void) // Get shader locations for required uniforms int viewEyeLoc = GetShaderLocation(shader, "viewEye"); int viewCenterLoc = GetShaderLocation(shader, "viewCenter"); - int viewUpLoc = GetShaderLocation(shader, "viewUp"); - int deltaTimeLoc = GetShaderLocation(shader, "deltaTime"); int runTimeLoc = GetShaderLocation(shader, "runTime"); int resolutionLoc = GetShaderLocation(shader, "resolution"); - float resolution[2] = { screenWidth, screenHeight }; + float resolution[2] = { (float)screenWidth, (float)screenHeight }; SetShaderValue(shader, resolutionLoc, resolution, UNIFORM_VEC2); float runTime = 0.0f; @@ -64,13 +63,22 @@ int main(void) // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { + // Check if screen is resized + //---------------------------------------------------------------------------------- + if(IsWindowResized()) + { + screenWidth = GetScreenWidth(); + screenHeight = GetScreenHeight(); + float resolution[2] = { (float)screenWidth, (float)screenHeight }; + SetShaderValue(shader, resolutionLoc, resolution, UNIFORM_VEC2); + } + // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera); // Update camera float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; float cameraTarget[3] = { camera.target.x, camera.target.y, camera.target.z }; - float cameraUp[3] = { camera.up.x, camera.up.y, camera.up.z }; float deltaTime = GetFrameTime(); runTime += deltaTime; @@ -78,8 +86,6 @@ int main(void) // Set shader required uniform values SetShaderValue(shader, viewEyeLoc, cameraPos, UNIFORM_VEC3); SetShaderValue(shader, viewCenterLoc, cameraTarget, UNIFORM_VEC3); - SetShaderValue(shader, viewUpLoc, cameraUp, UNIFORM_VEC3); - SetShaderValue(shader, deltaTimeLoc, &deltaTime, UNIFORM_FLOAT); SetShaderValue(shader, runTimeLoc, &runTime, UNIFORM_FLOAT); //---------------------------------------------------------------------------------- @@ -95,7 +101,7 @@ int main(void) DrawRectangle(0, 0, screenWidth, screenHeight, WHITE); EndShaderMode(); - DrawText("(c) Raymarching shader by Iñigo Quilez. MIT License.", screenWidth - 280, screenHeight - 20, 10, GRAY); + DrawText("(c) Raymarching shader by Iñigo Quilez. MIT License.", screenWidth - 280, screenHeight - 20, 10, BLACK); EndDrawing(); //---------------------------------------------------------------------------------- @@ -109,4 +115,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/shaders/shaders_simple_mask.c b/examples/shaders/shaders_simple_mask.c new file mode 100644 index 000000000..c8762be3a --- /dev/null +++ b/examples/shaders/shaders_simple_mask.c @@ -0,0 +1,139 @@ +/******************************************************************************************* +* +* raylib [shaders] example - Simple shader mask +* +* This example has been created using raylib 2.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5) +* +* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) +* +******************************************************************************************** +* +* After a model is loaded it has a default material, this material can be +* modified in place rather than creating one from scratch... +* While all of the maps have particular names, they can be used for any purpose +* except for three maps that are applied as cubic maps (see below) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib - simple shader mask"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 0.0f, 1.0f, 2.0f }; + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 45.0f; + camera.type = CAMERA_PERSPECTIVE; + + // Define our three models to show the shader on + Mesh torus = GenMeshTorus(.3, 1, 16, 32); + Model model1 = LoadModelFromMesh(torus); + + Mesh cube = GenMeshCube(.8,.8,.8); + Model model2 = LoadModelFromMesh(cube); + + // Generate model to be shaded just to see the gaps in the other two + Mesh sphere = GenMeshSphere(1, 16, 16); + Model model3 = LoadModelFromMesh(sphere); + + // Load the shader + Shader shader = LoadShader("resources/shaders/glsl330/mask.vs", "resources/shaders/glsl330/mask.fs"); + + // Load and apply the diffuse texture (colour map) + Texture texDiffuse = LoadTexture("resources/plasma.png"); + model1.materials[0].maps[MAP_DIFFUSE].texture = texDiffuse; + model2.materials[0].maps[MAP_DIFFUSE].texture = texDiffuse; + + // Using MAP_EMISSION as a spare slot to use for 2nd texture + // NOTE: Don't use MAP_IRRADIANCE, MAP_PREFILTER or MAP_CUBEMAP + // as they are bound as cube maps + Texture texMask = LoadTexture("resources/mask.png"); + model1.materials[0].maps[MAP_EMISSION].texture = texMask; + model2.materials[0].maps[MAP_EMISSION].texture = texMask; + shader.locs[LOC_MAP_EMISSION] = GetShaderLocation(shader, "mask"); + + // Frame is incremented each frame to animate the shader + int shaderFrame = GetShaderLocation(shader, "framesCounter"); + + // Apply the shader to the two models + model1.materials[0].shader = shader; + model2.materials[0].shader = shader; + + int framesCounter = 0; + Vector3 rotation = { 0 }; // Model rotation angles + + SetTargetFPS(60); // Set to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + framesCounter++; + rotation.x += 0.01f; + rotation.y += 0.005f; + rotation.z -= 0.0025f; + + // Send frames counter to shader for animation + SetShaderValue(shader, shaderFrame, &framesCounter, UNIFORM_INT); + + // Rotate one of the models + model1.transform = MatrixRotateXYZ(rotation); + + UpdateCamera(&camera); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(DARKBLUE); + + BeginMode3D(camera); + + DrawModel(model1, (Vector3){0.5,0,0}, 1, WHITE); + DrawModelEx(model2, (Vector3){-.5,0,0}, (Vector3){1,1,0}, 50, (Vector3){1,1,1}, WHITE); + DrawModel(model3,(Vector3){0,0,-1.5}, 1, WHITE); + DrawGrid(10, 1.0f); // Draw a grid + + EndMode3D(); + + DrawRectangle(16, 698, MeasureText(FormatText("Frame: %i", framesCounter), 20) + 8, 42, BLUE); + DrawText(FormatText("Frame: %i", framesCounter), 20, 700, 20, WHITE); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModel(model1); + UnloadModel(model2); + UnloadModel(model3); + + UnloadTexture(texDiffuse); // Unload default diffuse texture + UnloadTexture(texMask); // Unload texture mask + + UnloadShader(shader); // Unload shader + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shaders/shaders_texture_waves.c b/examples/shaders/shaders_texture_waves.c index 07186d379..c04503614 100644 --- a/examples/shaders/shaders_texture_waves.c +++ b/examples/shaders/shaders_texture_waves.c @@ -33,38 +33,38 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture waves"); - + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture waves"); + // Load texture texture to apply shaders - Texture2D texture = LoadTexture("resources/space.png"); - + Texture2D texture = LoadTexture("resources/space.png"); + // Load shader and setup location points and values Shader shader = LoadShader(0, FormatText("resources/shaders/glsl%i/wave.fs", GLSL_VERSION)); - int secondsLoc = GetShaderLocation(shader, "secondes"); - int freqXLoc = GetShaderLocation(shader, "freqX"); - int freqYLoc = GetShaderLocation(shader, "freqY"); - int ampXLoc = GetShaderLocation(shader, "ampX"); - int ampYLoc = GetShaderLocation(shader, "ampY"); - int speedXLoc = GetShaderLocation(shader, "speedX"); - int speedYLoc = GetShaderLocation(shader, "speedY"); + int secondsLoc = GetShaderLocation(shader, "secondes"); + int freqXLoc = GetShaderLocation(shader, "freqX"); + int freqYLoc = GetShaderLocation(shader, "freqY"); + int ampXLoc = GetShaderLocation(shader, "ampX"); + int ampYLoc = GetShaderLocation(shader, "ampY"); + int speedXLoc = GetShaderLocation(shader, "speedX"); + int speedYLoc = GetShaderLocation(shader, "speedY"); // Shader uniform values that can be updated at any time - float freqX = 25.0f; - float freqY = 25.0f; - float ampX = 5.0f; - float ampY = 5.0f; - float speedX = 8.0f; - float speedY = 8.0f; + float freqX = 25.0f; + float freqY = 25.0f; + float ampX = 5.0f; + float ampY = 5.0f; + float speedX = 8.0f; + float speedY = 8.0f; float screenSize[2] = { (float)GetScreenWidth(), (float)GetScreenHeight() }; - SetShaderValue(shader, GetShaderLocation(shader, "size"), &screenSize, UNIFORM_VEC2); - SetShaderValue(shader, freqXLoc, &freqX, UNIFORM_FLOAT); - SetShaderValue(shader, freqYLoc, &freqY, UNIFORM_FLOAT); - SetShaderValue(shader, ampXLoc, &X, UNIFORM_FLOAT); - SetShaderValue(shader, ampYLoc, &Y, UNIFORM_FLOAT); - SetShaderValue(shader, speedXLoc, &speedX, UNIFORM_FLOAT); - SetShaderValue(shader, speedYLoc, &speedY, UNIFORM_FLOAT); + SetShaderValue(shader, GetShaderLocation(shader, "size"), &screenSize, UNIFORM_VEC2); + SetShaderValue(shader, freqXLoc, &freqX, UNIFORM_FLOAT); + SetShaderValue(shader, freqYLoc, &freqY, UNIFORM_FLOAT); + SetShaderValue(shader, ampXLoc, &X, UNIFORM_FLOAT); + SetShaderValue(shader, ampYLoc, &Y, UNIFORM_FLOAT); + SetShaderValue(shader, speedXLoc, &speedX, UNIFORM_FLOAT); + SetShaderValue(shader, speedYLoc, &speedY, UNIFORM_FLOAT); float seconds = 0.0f; @@ -76,9 +76,9 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - seconds += GetFrameTime(); + seconds += GetFrameTime(); - SetShaderValue(shader, secondsLoc, &seconds, UNIFORM_FLOAT); + SetShaderValue(shader, secondsLoc, &seconds, UNIFORM_FLOAT); //---------------------------------------------------------------------------------- // Draw @@ -87,12 +87,12 @@ int main(void) ClearBackground(RAYWHITE); - BeginShaderMode(shader); + BeginShaderMode(shader); - DrawTexture(texture, 0, 0, WHITE); - DrawTexture(texture, texture.width, 0, WHITE); + DrawTexture(texture, 0, 0, WHITE); + DrawTexture(texture, texture.width, 0, WHITE); - EndShaderMode(); + EndShaderMode(); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/text/text_ttf_loading.c b/examples/text/text_font_filters.c similarity index 91% rename from examples/text/text_ttf_loading.c rename to examples/text/text_font_filters.c index b256bd1d3..60b16a045 100644 --- a/examples/text/text_ttf_loading.c +++ b/examples/text/text_font_filters.c @@ -1,6 +1,10 @@ /******************************************************************************************* * -* raylib [text] example - TTF loading and usage +* raylib [text] example - Font filters +* +* After font loading, font texture atlas filter could be configured for a softer +* display of the font when scaling it to different sizes, that way, it's not required +* to generate multiple fonts at multiple sizes (as long as the scaling is not very different) * * This example has been created using raylib 1.3.0 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) @@ -18,9 +22,9 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [text] example - ttf loading"); + InitWindow(screenWidth, screenHeight, "raylib [text] example - font filters"); - const char msg[50] = "TTF Font"; + const char msg[50] = "Loaded Font"; // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) @@ -78,7 +82,8 @@ int main(void) int count = 0; char **droppedFiles = GetDroppedFiles(&count); - if (count == 1) // Only support one ttf file dropped + // NOTE: We only support first ttf file dropped + if (IsFileExtension(droppedFiles[0], ".ttf")) { UnloadFont(font); font = LoadFontEx(droppedFiles[0], fontSize, 0, 0); diff --git a/examples/text/text_font_filters.png b/examples/text/text_font_filters.png new file mode 100644 index 000000000..7ad823fbd Binary files /dev/null and b/examples/text/text_font_filters.png differ diff --git a/examples/text/text_bmfont_ttf.c b/examples/text/text_font_loading.c similarity index 80% rename from examples/text/text_bmfont_ttf.c rename to examples/text/text_font_loading.c index 0fc82e97c..7eaaed8fe 100644 --- a/examples/text/text_bmfont_ttf.c +++ b/examples/text/text_font_loading.c @@ -1,11 +1,20 @@ /******************************************************************************************* * -* raylib [text] example - BMFont and TTF Fonts loading +* raylib [text] example - Font loading * -* This example has been created using raylib 1.4 (www.raylib.com) +* raylib can load fonts from multiple file formats: +* +* - TTF/OTF > Sprite font atlas is generated on loading, user can configure +* some of the generation parameters (size, characters to include) +* - BMFonts > Angel code font fileformat, sprite font image must be provided +* together with the .fnt file, font generation cna not be configured +* - XNA Spritefont > Sprite font image, following XNA Spritefont conventions, +* Characters in image must follow some spacing and order rules +* +* This example has been created using raylib 2.6 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2016 Ramon Santamaria (@raysan5) +* Copyright (c) 2016-2019 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -18,7 +27,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [text] example - bmfont and ttf sprite fonts loading"); + InitWindow(screenWidth, screenHeight, "raylib [text] example - font loading"); // Define characters to draw // NOTE: raylib supports UTF-8 encoding, following list is actually codified as UTF8 internally @@ -75,7 +84,7 @@ int main(void) UnloadFont(fontBm); // AngelCode Font unloading UnloadFont(fontTtf); // TTF Font unloading - CloseWindow(); // Close window and OpenGL context + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/examples/text/text_bmfont_ttf.png b/examples/text/text_font_loading.png similarity index 100% rename from examples/text/text_bmfont_ttf.png rename to examples/text/text_font_loading.png diff --git a/examples/text/text_sprite_fonts.c b/examples/text/text_font_spritefont.c similarity index 84% rename from examples/text/text_sprite_fonts.c rename to examples/text/text_font_spritefont.c index b7c9ab10f..dff2b47ae 100644 --- a/examples/text/text_sprite_fonts.c +++ b/examples/text/text_font_spritefont.c @@ -1,6 +1,15 @@ /******************************************************************************************* * -* raylib [text] example - Font loading and usage +* raylib [text] example - Sprite font loading +* +* Loaded sprite fonts have been generated following XNA SpriteFont conventions: +* - Characters must be ordered starting with character 32 (Space) +* - Every character must be contained within the same Rectangle height +* - Every character and every line must be separated the same distance +* - Rectangles must be defined by a MAGENTA color background +* +* If following this constraints, a font can be provided just by an image, +* this is quite handy to avoid additional information files (like BMFonts use). * * This example has been created using raylib 1.0 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) @@ -18,7 +27,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite fonts usage"); + InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite font loading"); const char msg1[50] = "THIS IS A custom SPRITE FONT..."; const char msg2[50] = "...and this is ANOTHER CUSTOM font..."; diff --git a/examples/text/text_sprite_fonts.png b/examples/text/text_font_spritefont.png similarity index 100% rename from examples/text/text_sprite_fonts.png rename to examples/text/text_font_spritefont.png diff --git a/examples/text/text_ttf_loading.png b/examples/text/text_ttf_loading.png deleted file mode 100644 index 5fdbda198..000000000 Binary files a/examples/text/text_ttf_loading.png and /dev/null differ diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index 3525f01a1..53803e050 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -271,7 +271,7 @@ int main(int argc, char **argv) // Draw the info text below the main message int size = strlen(messages[message].text); - unsigned int len = TextCountCodepoints(messages[message].text); + int len = GetCodepointsCount(messages[message].text); const char *info = TextFormat("%s %u characters %i bytes", messages[message].language, len, size); sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f); Vector2 pos = { textRect.x + textRect.width - sz.x, msgRect.y + msgRect.height - sz.y - 2 }; diff --git a/examples/textures/textures_bunnymark.c b/examples/textures/textures_bunnymark.c index 784417d21..86605b901 100644 --- a/examples/textures/textures_bunnymark.c +++ b/examples/textures/textures_bunnymark.c @@ -13,7 +13,7 @@ #include // Required for: malloc(), free() -#define MAX_BUNNIES 100000 // 100K bunnies limit +#define MAX_BUNNIES 50000 // 50K bunnies limit // This is the maximum amount of elements (quads) per batch // NOTE: This value is defined in [rlgl] module and can be changed there diff --git a/games/just_do/just_do.c b/games/just_do/just_do.c index e9a21526d..6a73a2b38 100644 --- a/games/just_do/just_do.c +++ b/games/just_do/just_do.c @@ -50,8 +50,8 @@ void UpdateDrawFrame(void); // Update and Draw one frame //---------------------------------------------------------------------------------- int main(void) { - // Initialization (Note windowTitle is unused on Android) - //--------------------------------------------------------- + // Initialization (Note windowTitle is unused on Android) + //--------------------------------------------------------- InitWindow(screenWidth, screenHeight, "JUST DO [GGJ15]"); // Load global data here (assets that must be available in all screens, i.e. fonts) diff --git a/games/koala_seasons/koala_seasons.c b/games/koala_seasons/koala_seasons.c index ad7892b88..aaad52769 100644 --- a/games/koala_seasons/koala_seasons.c +++ b/games/koala_seasons/koala_seasons.c @@ -48,8 +48,8 @@ void UpdateDrawFrame(void); // Update and Draw one frame //---------------------------------------------------------------------------------- int main(void) { - // Initialization (Note windowTitle is unused on Android) - //--------------------------------------------------------- + // Initialization (Note windowTitle is unused on Android) + //--------------------------------------------------------- InitWindow(screenWidth, screenHeight, "KOALA SEASONS"); // Load global data here (assets that must be available in all screens, i.e. fonts) diff --git a/games/light_my_ritual/light_my_ritual.c b/games/light_my_ritual/light_my_ritual.c index 87d12ad1a..73e40485e 100644 --- a/games/light_my_ritual/light_my_ritual.c +++ b/games/light_my_ritual/light_my_ritual.c @@ -53,8 +53,8 @@ void UpdateDrawFrame(void); // Update and Draw one frame //---------------------------------------------------------------------------------- int main(void) { - // Initialization (Note windowTitle is unused on Android) - //--------------------------------------------------------- + // Initialization (Note windowTitle is unused on Android) + //--------------------------------------------------------- InitWindow(screenWidth, screenHeight, "LIGHT MY RITUAL! [GGJ16]"); // Global data loading (assets that must be available in all screens, i.e. fonts) @@ -69,7 +69,7 @@ int main(void) UnloadImage(image); // Unload image from CPU memory (RAM) font = LoadFont("resources/font_arcadian.png"); - //doors = LoadTexture("resources/textures/doors.png"); + //doors = LoadTexture("resources/textures/doors.png"); //sndDoor = LoadSound("resources/audio/door.ogg"); music = LoadMusicStream("resources/audio/ambient.ogg"); @@ -270,7 +270,7 @@ void UpdateDrawFrame(void) case GAMEPLAY: DrawGameplayScreen(); break; default: break; } - + if (onTransition) DrawTransition(); //DrawFPS(10, 10); diff --git a/games/skully_escape/player.c b/games/skully_escape/player.c index 857ff5388..552326e18 100644 --- a/games/skully_escape/player.c +++ b/games/skully_escape/player.c @@ -271,11 +271,11 @@ static void DrawLifes(void) { if (player.numLifes != 0) { - Vector2 position = { 20, GetScreenHeight() - texLife.height - 20 }; - + Vector2 position = { 20, GetScreenHeight() - texLife.height - 20 }; + for(int i = 0; i < player.numLifes; i++) { - DrawTexture(texLife, position.x + i*texLife.width, position.y, Fade(RAYWHITE, 0.7f)); + DrawTexture(texLife, position.x + i*texLife.width, position.y, Fade(RAYWHITE, 0.7f)); } } } \ No newline at end of file diff --git a/games/skully_escape/skully_escape.c b/games/skully_escape/skully_escape.c index 712282cc7..1bb598ea3 100644 --- a/games/skully_escape/skully_escape.c +++ b/games/skully_escape/skully_escape.c @@ -52,8 +52,8 @@ void UpdateDrawFrame(void); // Update and Draw one frame //---------------------------------------------------------------------------------- int main(void) { - // Initialization (Note windowTitle is unused on Android) - //--------------------------------------------------------- + // Initialization (Note windowTitle is unused on Android) + //--------------------------------------------------------- InitWindow(screenWidth, screenHeight, "SKULLY ESCAPE [KING GAMEJAM 2015]"); // Global data loading (assets that must be available in all screens, i.e. fonts) @@ -63,10 +63,10 @@ int main(void) PlayMusicStream(music); font = LoadFont("resources/textures/alagard.png"); - doors = LoadTexture("resources/textures/doors.png"); + doors = LoadTexture("resources/textures/doors.png"); sndDoor = LoadSound("resources/audio/door.ogg"); sndScream = LoadSound("resources/audio/scream.ogg"); - + InitPlayer(); // Setup and Init first screen @@ -90,7 +90,7 @@ int main(void) //-------------------------------------------------------------------------------------- // Unload all global loaded data (i.e. fonts) here! - UnloadPlayer(); + UnloadPlayer(); UnloadFont(font); UnloadTexture(doors); UnloadSound(sndDoor); @@ -397,7 +397,7 @@ void UpdateDrawFrame(void) case ENDING: DrawEndingScreen(); break; default: break; } - + if (onTransition) DrawTransition(); //DrawFPS(10, 10); diff --git a/games/transmission/transmission.c b/games/transmission/transmission.c index a948365fc..79097b069 100644 --- a/games/transmission/transmission.c +++ b/games/transmission/transmission.c @@ -70,7 +70,7 @@ int main(void) fontMission = LoadFontEx("resources/fonts/traveling_typewriter.ttf", 64, 0, 250); texButton = LoadTexture("resources/textures/title_ribbon.png"); - // UI BUTTON + // UI BUTTON recButton.width = texButton.width; recButton.height = texButton.height; recButton.x = screenWidth - recButton.width; @@ -121,7 +121,7 @@ int main(void) UnloadFont(fontMission); UnloadTexture(texButton); - + CloseAudioDevice(); // Close audio context CloseWindow(); // Close window and OpenGL context @@ -438,7 +438,7 @@ bool IsButtonPressed() } } else fadeButton = 0.80f; - + return false; } diff --git a/games/wave_collector/wave_collector.c b/games/wave_collector/wave_collector.c index c52f88355..64881816d 100644 --- a/games/wave_collector/wave_collector.c +++ b/games/wave_collector/wave_collector.c @@ -58,8 +58,8 @@ static void UpdateDrawFrame(void); // Update and Draw one frame //---------------------------------------------------------------------------------- int main(int argc, char *argv[]) { - // Initialization - //--------------------------------------------------------- + // Initialization + //--------------------------------------------------------- #if defined(PLATFORM_DESKTOP) // TODO: Support for dropped files on the exe @@ -299,7 +299,7 @@ static void UpdateDrawFrame(void) case ENDING: DrawEndingScreen(); break; default: break; } - + // Draw full screen rectangle in front of everything if (onTransition) DrawTransition(); diff --git a/projects/4coder/main.c b/projects/4coder/main.c index e79184226..47e16b885 100644 --- a/projects/4coder/main.c +++ b/projects/4coder/main.c @@ -2,38 +2,38 @@ #include "raylib.h" int main() { - int screenWidth = 800; - int screenHeight = 450; + int screenWidth = 800; + int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib"); + InitWindow(screenWidth, screenHeight, "raylib"); - Camera cam; - cam.position = (Vector3){ 0.f, 10.f, 8.f }; - cam.target = (Vector3){ 0.f, 0.f, 0.f }; - cam.up = (Vector3){ 0.f, 1.f, 0.f }; - cam.fovy = 60.f; - cam.type = CAMERA_PERSPECTIVE; + Camera cam; + cam.position = (Vector3){ 0.f, 10.f, 8.f }; + cam.target = (Vector3){ 0.f, 0.f, 0.f }; + cam.up = (Vector3){ 0.f, 1.f, 0.f }; + cam.fovy = 60.f; + cam.type = CAMERA_PERSPECTIVE; - Vector3 cubePos = { 0.f, 0.f, 0.f }; + Vector3 cubePos = { 0.f, 0.f, 0.f }; - SetTargetFPS(60); + SetTargetFPS(60); - while (!WindowShouldClose()) { - cam.position.x = sin(GetTime()) * 10.f; - cam.position.z = cos(GetTime()) * 10.f; + while (!WindowShouldClose()) { + cam.position.x = sin(GetTime()) * 10.f; + cam.position.z = cos(GetTime()) * 10.f; - BeginDrawing(); - ClearBackground(RAYWHITE); - BeginMode3D(cam); - DrawCube(cubePos, 2.f, 2.f, 2.f, RED); - DrawCubeWires(cubePos, 2.f, 2.f, 2.f, MAROON); - DrawGrid(10, 1.f); - EndMode3D(); - DrawText("This is a raylib example", 10, 40, 20, DARKGRAY); - DrawFPS(10, 10); - EndDrawing(); - } - - CloseWindow(); - return 0; + BeginDrawing(); + ClearBackground(RAYWHITE); + BeginMode3D(cam); + DrawCube(cubePos, 2.f, 2.f, 2.f, RED); + DrawCubeWires(cubePos, 2.f, 2.f, 2.f, MAROON); + DrawGrid(10, 1.f); + EndMode3D(); + DrawText("This is a raylib example", 10, 40, 20, DARKGRAY); + DrawFPS(10, 10); + EndDrawing(); + } + + CloseWindow(); + return 0; } \ No newline at end of file diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index cf1d65a62..ef0d3c1b1 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.11) # FetchContent is available in 3.11+ project(example) -find_package(raylib 2.0 QUIET) # Let CMake search for a raylib-config.cmake +# Set this to the minimal version you want to support +find_package(raylib 2.5 QUIET) # Let CMake search for a raylib-config.cmake # You could change the QUIET above to REQUIRED and remove this if() clause # This part downloads raylib and builds it if it's not installed on your system diff --git a/projects/README.md b/projects/README.md index 4009a09b9..2f0e0f3d1 100644 --- a/projects/README.md +++ b/projects/README.md @@ -4,14 +4,17 @@ This folder contains raylib templates for some common IDEs. IDE | Platform | Template type | State ----| ---------| ------------- | ----- +[4coder](http://4coder.net/) | Windows | example compiling | DONE [Builder](https://wiki.gnome.org/Apps/Builder) | Linux | example compiling | DONE [CMake](https://cmake.org/) | n/a | example compiling and raylib source downloading/building if necessary | DONE [CodeBlocks](http://www.codeblocks.org/) | Linux, Windows | example compiling | DONE -[Geany](https://www.geany.org/) | Linux, Windows | - | INCOMPLETE +[Geany](https://www.geany.org/) | Linux, Windows | - | DONE [KDevelop](https://www.kdevelop.org/) | Linux, Windows, macOS | - | INCOMPLETE [Notepad++](https://notepad-plus-plus.org/) | Windows | source/example compiling | DONE +[Sublime Text](https://www.sublimetext.com/) | Windows, Linux, macOS | source and example | DONE [VS2015](https://www.visualstudio.com) | Windows | source/example compiling | DONE [VS2017](https://www.visualstudio.com) | Windows | source/example compiling | DONE [VSCode](https://code.visualstudio.com/) | Windows, macOS | example compiling | DONE +scripts | Windows, Linux, macOS | source and example | DONE *New IDEs config files are welcome!* diff --git a/projects/VSCode/main.c b/projects/VSCode/main.c index ecda25fae..97bccafbe 100644 --- a/projects/VSCode/main.c +++ b/projects/VSCode/main.c @@ -25,21 +25,21 @@ int main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + const int screenWidth = 800; + const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib"); + InitWindow(screenWidth, screenHeight, "raylib"); - Camera camera = { 0 }; - camera.position = (Vector3){ 10.0f, 10.0f, 8.0f }; - camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.fovy = 60.0f; - camera.type = CAMERA_PERSPECTIVE; - - SetCameraMode(camera, CAMERA_ORBITAL); + Camera camera = { 0 }; + camera.position = (Vector3){ 10.0f, 10.0f, 8.0f }; + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 60.0f; + camera.type = CAMERA_PERSPECTIVE; + + SetCameraMode(camera, CAMERA_ORBITAL); - Vector3 cubePosition = { 0.0f }; + Vector3 cubePosition = { 0.0f }; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -49,30 +49,30 @@ int main() { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera); //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- - BeginDrawing(); + BeginDrawing(); - ClearBackground(RAYWHITE); + ClearBackground(RAYWHITE); - BeginMode3D(camera); + BeginMode3D(camera); - DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, RED); - DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, MAROON); - DrawGrid(10, 1.0f); + DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, RED); + DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, MAROON); + DrawGrid(10, 1.0f); - EndMode3D(); + EndMode3D(); - DrawText("This is a raylib example", 10, 40, 20, DARKGRAY); + DrawText("This is a raylib example", 10, 40, 20, DARKGRAY); - DrawFPS(10, 10); + DrawFPS(10, 10); - EndDrawing(); - //---------------------------------------------------------------------------------- - } + EndDrawing(); + //---------------------------------------------------------------------------------- + } // De-Initialization //-------------------------------------------------------------------------------------- diff --git a/src/Makefile b/src/Makefile index 4af01d916..ac864680d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -147,28 +147,35 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables - EMSDK_PATH ?= C:/emsdk - EMSCRIPTEN_VERSION ?= 1.38.32 - CLANG_VERSION = e$(EMSCRIPTEN_VERSION)_64bit - PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64 - NODE_VERSION = 8.9.1_64bit - export PATH = $(EMSDK_PATH);$(EMSDK_PATH)\clang\$(CLANG_VERSION);$(EMSDK_PATH)\node\$(NODE_VERSION)\bin;$(EMSDK_PATH)\python\$(PYTHON_VERSION);$(EMSDK_PATH)\emscripten\$(EMSCRIPTEN_VERSION);C:\raylib\MinGW\bin:$$(PATH) - EMSCRIPTEN = $(EMSDK_PATH)\emscripten\$(EMSCRIPTEN_VERSION) + EMSDK_PATH ?= C:/emsdk + EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/fastcomp/emscripten + CLANG_PATH = $(EMSDK_PATH)/fastcomp/bin + PYTHON_PATH = $(EMSDK_PATH)/python/2.7.13.1_64bit/python-2.7.13.amd64 + NODE_PATH = $(EMSDK_PATH)/node/12.9.1_64bit/bin + export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH) endif ifeq ($(PLATFORM),PLATFORM_ANDROID) # Android architecture: ARM64 # Starting at 2019 using ARM64 is mandatory for published apps ANDROID_ARCH ?= ARM - ANDROID_API_VERSION = 21 + ANDROID_API_VERSION = 26 # Android required path variables # NOTE: Android NDK is just required to generate the standalone toolchain, # in case is not already provided - ANDROID_NDK = C:/android-ndk + ifeq ($(OS),Windows_NT) + ANDROID_NDK = C:/android-ndk + else + ANDROID_NDK = /usr/lib/android/ndk + endif # Android standalone toolchain path - ANDROID_TOOLCHAIN = C:/android_toolchain_$(ANDROID_ARCH)_API$(ANDROID_API_VERSION) + ifeq ($(OS),Windows_NT) + ANDROID_TOOLCHAIN = C:/android_toolchain_$(ANDROID_ARCH)_API$(ANDROID_API_VERSION) + else + ANDROID_TOOLCHAIN = /usr/lib/android/toolchain_$(ANDROID_ARCH)_API$(ANDROID_API_VERSION) + endif ifeq ($(ANDROID_ARCH),ARM) ANDROID_ARCH_NAME = armeabi-v7a @@ -176,6 +183,12 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) ifeq ($(ANDROID_ARCH),ARM64) ANDROID_ARCH_NAME = arm64-v8a endif + ifeq ($(ANDROID_ARCH),x86) + ANDROID_ARCH_NAME = i686 + endif + ifeq ($(ANDROID_ARCH),x86_64) + ANDROID_ARCH_NAME = x86_64 + endif endif # Define raylib source code path @@ -242,6 +255,14 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) CC = $(ANDROID_TOOLCHAIN)/bin/aarch64-linux-android-clang AR = $(ANDROID_TOOLCHAIN)/bin/aarch64-linux-android-ar endif + ifeq ($(ANDROID_ARCH),x86) + CC = $(ANDROID_TOOLCHAIN)/bin/i686-linux-android$(ANDROID_API_VERSION)-clang + AR = $(ANDROID_TOOLCHAIN)/bin/i686-linux-android-ar + endif + ifeq ($(ANDROID_ARCH),x86_64) + CC = $(ANDROID_TOOLCHAIN)/bin/x86_64-linux-android$(ANDROID_API_VERSION)-clang + AR = $(ANDROID_TOOLCHAIN)/bin/x86_64-linux-android-ar + endif endif @@ -256,7 +277,13 @@ endif # -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec # -Werror=pointer-arith catch unportable code that does direct arithmetic on void pointers # -fno-strict-aliasing jar_xm.h does shady stuff (breaks strict aliasing) -CFLAGS += -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing +CFLAGS += -Wall -D_DEFAULT_SOURCE -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing + +ifeq ($(PLATFORM), PLATFORM_WEB) + CFLAGS += -std=gnu99 +else + CFLAGS += -std=c99 +endif ifeq ($(PLATFORM_OS),LINUX) CFLAGS += -fPIC @@ -305,6 +332,12 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) ifeq ($(ANDROID_ARCH),ARM64) CFLAGS += -target aarch64 -mfix-cortex-a53-835769 endif + ifeq ($(ANDROID_ARCH), x86) + CFLAGS += -march=i686 + endif + ifeq ($(ANDROID_ARCH), x86_64) + CFLAGS += -march=x86-64 + endif # Compilation functions attributes options CFLAGS += -ffunction-sections -funwind-tables -fstack-protector-strong -fPIE -fPIC # Compiler options for the linker @@ -356,11 +389,12 @@ ifeq ($(PLATFORM),PLATFORM_RPI) INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vcos/pthreads endif ifeq ($(PLATFORM),PLATFORM_ANDROID) + NATIVE_APP_GLUE = $(RAYLIB_RELEASE_PATH)/external/android/native_app_glue + #NATIVE_APP_GLUE = $(ANDROID_NDK)/sources/android/native_app_glue # Android required libraries INCLUDE_PATHS += -I$(ANDROID_TOOLCHAIN)/sysroot/usr/include # Include android_native_app_glue.h - INCLUDE_PATHS += -Iexternal/android/native_app_glue - #INCLUDE_PATHS += -I$(ANDROID_NDK)/sources/android/native_app_glue + INCLUDE_PATHS += -I$(NATIVE_APP_GLUE) endif # Define linker options @@ -635,6 +669,6 @@ else rm -fv *.o $(RAYLIB_RELEASE_PATH)/libraylib.a $(RAYLIB_RELEASE_PATH)/libraylib.bc $(RAYLIB_RELEASE_PATH)/libraylib.so* endif ifeq ($(PLATFORM),PLATFORM_ANDROID) - rm -rf $(ANDROID_TOOLCHAIN) + rm -rf $(ANDROID_TOOLCHAIN) $(NATIVE_APP_GLUE)/android_native_app_glue.o endif @echo "removed all generated files!" diff --git a/src/camera.h b/src/camera.h index bc813b53b..51da4804f 100644 --- a/src/camera.h +++ b/src/camera.h @@ -8,7 +8,7 @@ * * #define CAMERA_IMPLEMENTATION * Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers +* If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation. * * #define CAMERA_STANDALONE @@ -77,7 +77,7 @@ } Camera3D; typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D - + // Camera system modes typedef enum { CAMERA_CUSTOM = 0, @@ -113,8 +113,8 @@ void UpdateCamera(Camera *camera); // Update camera pos void SetCameraPanControl(int panKey); // Set camera pan key to combine with mouse movement (free camera) void SetCameraAltControl(int altKey); // Set camera alt key to combine with mouse movement (free camera) void SetCameraSmoothZoomControl(int szoomKey); // Set camera smooth zoom key to combine with mouse (free camera) -void SetCameraMoveControls(int frontKey, int backKey, - int rightKey, int leftKey, +void SetCameraMoveControls(int frontKey, int backKey, + int rightKey, int leftKey, int upKey, int downKey); // Set camera move controls (1st person and 3rd person cameras) #endif @@ -188,21 +188,21 @@ void SetCameraMoveControls(int frontKey, int backKey, // Types and Structures Definition //---------------------------------------------------------------------------------- // Camera move modes (first person and third person cameras) -typedef enum { - MOVE_FRONT = 0, - MOVE_BACK, - MOVE_RIGHT, - MOVE_LEFT, - MOVE_UP, - MOVE_DOWN +typedef enum { + MOVE_FRONT = 0, + MOVE_BACK, + MOVE_RIGHT, + MOVE_LEFT, + MOVE_UP, + MOVE_DOWN } CameraMove; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- static Vector2 cameraAngle = { 0.0f, 0.0f }; // Camera angle in plane XZ -static float cameraTargetDistance = 0.0f; // Camera distance from position to target -static float playerEyesPosition = 1.85f; // Default player eyes position from ground (in meters) +static float cameraTargetDistance = 0.0f; // Camera distance from position to target +static float playerEyesPosition = 1.85f; // Default player eyes position from ground (in meters) static int cameraMoveControl[6] = { 'W', 'S', 'D', 'A', 'E', 'Q' }; static int cameraPanControlKey = 2; // raylib: MOUSE_MIDDLE_BUTTON @@ -236,21 +236,21 @@ void SetCameraMode(Camera camera, int mode) { Vector3 v1 = camera.position; Vector3 v2 = camera.target; - + float dx = v2.x - v1.x; float dy = v2.y - v1.y; float dz = v2.z - v1.z; - + cameraTargetDistance = sqrtf(dx*dx + dy*dy + dz*dz); - + Vector2 distance = { 0.0f, 0.0f }; distance.x = sqrtf(dx*dx + dz*dz); distance.y = sqrtf(dx*dx + dy*dy); - + // Camera angle calculation cameraAngle.x = asinf( (float)fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) cameraAngle.y = -asinf( (float)fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW) - + playerEyesPosition = camera.position.y; // Lock cursor for first person and third person cameras @@ -272,24 +272,24 @@ void UpdateCamera(Camera *camera) static Vector2 previousMousePosition = { 0.0f, 0.0f }; // TODO: Compute cameraTargetDistance and cameraAngle here - + // Mouse movement detection Vector2 mousePositionDelta = { 0.0f, 0.0f }; Vector2 mousePosition = GetMousePosition(); int mouseWheelMove = GetMouseWheelMove(); - + // Keys input detection bool panKey = IsMouseButtonDown(cameraPanControlKey); bool altKey = IsKeyDown(cameraAltControlKey); bool szoomKey = IsKeyDown(cameraSmoothZoomControlKey); - + bool direction[6] = { IsKeyDown(cameraMoveControl[MOVE_FRONT]), IsKeyDown(cameraMoveControl[MOVE_BACK]), IsKeyDown(cameraMoveControl[MOVE_RIGHT]), IsKeyDown(cameraMoveControl[MOVE_LEFT]), IsKeyDown(cameraMoveControl[MOVE_UP]), IsKeyDown(cameraMoveControl[MOVE_DOWN]) }; - + // TODO: Consider touch inputs for camera if (cameraMode != CAMERA_CUSTOM) @@ -384,7 +384,7 @@ void UpdateCamera(Camera *camera) camera->target.z += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.x)*sinf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER); } } - + // Update camera position with changes camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x; camera->position.y = ((cameraAngle.y <= 0.0f)? 1 : -1)*sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y; @@ -395,15 +395,15 @@ void UpdateCamera(Camera *camera) { cameraAngle.x += CAMERA_ORBITAL_SPEED; // Camera orbit angle cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom - + // Camera distance clamp if (cameraTargetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; - + // Update camera position with changes camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x; camera->position.y = ((cameraAngle.y <= 0.0f)? 1 : -1)*sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y; camera->position.z = cosf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.z; - + } break; case CAMERA_FIRST_PERSON: { @@ -411,11 +411,11 @@ void UpdateCamera(Camera *camera) sinf(cameraAngle.x)*direction[MOVE_FRONT] - cosf(cameraAngle.x)*direction[MOVE_LEFT] + cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; - + camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] - sinf(cameraAngle.y)*direction[MOVE_BACK] + 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; - + camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] - cosf(cameraAngle.x)*direction[MOVE_FRONT] + sinf(cameraAngle.x)*direction[MOVE_LEFT] - @@ -424,11 +424,11 @@ void UpdateCamera(Camera *camera) bool isMoving = false; // Required for swinging for (int i = 0; i < 6; i++) if (direction[i]) { isMoving = true; break; } - + // Camera orientation calculation cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); - + // Angle clamp if (cameraAngle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD; else if (cameraAngle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD; @@ -437,7 +437,7 @@ void UpdateCamera(Camera *camera) camera->target.x = camera->position.x - sinf(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; camera->target.y = camera->position.y + sinf(cameraAngle.y)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; camera->target.z = camera->position.z - cosf(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; - + if (isMoving) swingCounter++; // Camera position update @@ -446,8 +446,8 @@ void UpdateCamera(Camera *camera) camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; - - + + } break; case CAMERA_THIRD_PERSON: { @@ -455,11 +455,11 @@ void UpdateCamera(Camera *camera) sinf(cameraAngle.x)*direction[MOVE_FRONT] - cosf(cameraAngle.x)*direction[MOVE_LEFT] + cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; - + camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] - sinf(cameraAngle.y)*direction[MOVE_BACK] + 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; - + camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] - cosf(cameraAngle.x)*direction[MOVE_FRONT] + sinf(cameraAngle.x)*direction[MOVE_LEFT] - @@ -468,7 +468,7 @@ void UpdateCamera(Camera *camera) // Camera orientation calculation cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); - + // Angle clamp if (cameraAngle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD; else if (cameraAngle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD; @@ -487,7 +487,7 @@ void UpdateCamera(Camera *camera) } break; default: break; - } + } } // Set camera pan key to combine with mouse movement (free camera) diff --git a/src/config.h b/src/config.h index 07cf5fe74..169eefd24 100644 --- a/src/config.h +++ b/src/config.h @@ -25,7 +25,7 @@ * **********************************************************************************************/ -#define RAYLIB_VERSION "2.5" +#define RAYLIB_VERSION "2.6-dev" // Edit to control what features Makefile'd raylib is compiled with #if defined(RAYLIB_CMAKE) @@ -44,6 +44,8 @@ #define SUPPORT_MOUSE_GESTURES 1 // Reconfigure standard input to receive key inputs, works with SSH connection. #define SUPPORT_SSH_KEYBOARD_RPI 1 +// Draw a mouse reference on screen (square cursor box) +#define SUPPORT_MOUSE_CURSOR_RPI 1 // Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used //#define SUPPORT_BUSY_WAIT_LOOP 1 // Wait for events passively (sleeping while no events) instead of polling them actively every frame @@ -54,7 +56,8 @@ #define SUPPORT_GIF_RECORDING 1 // Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP) //#define SUPPORT_HIGH_DPI 1 - +// Support CompressData() and DecompressData() functions +#define SUPPORT_COMPRESSION_API 1 //------------------------------------------------------------------------------------ // Module: rlgl - Configuration Flags @@ -83,10 +86,10 @@ //#define SUPPORT_FILEFORMAT_JPG 1 //#define SUPPORT_FILEFORMAT_GIF 1 //#define SUPPORT_FILEFORMAT_PSD 1 -#define SUPPORT_FILEFORMAT_DDS 1 +//#define SUPPORT_FILEFORMAT_DDS 1 #define SUPPORT_FILEFORMAT_HDR 1 -#define SUPPORT_FILEFORMAT_KTX 1 -#define SUPPORT_FILEFORMAT_ASTC 1 +//#define SUPPORT_FILEFORMAT_KTX 1 +//#define SUPPORT_FILEFORMAT_ASTC 1 //#define SUPPORT_FILEFORMAT_PKM 1 //#define SUPPORT_FILEFORMAT_PVR 1 @@ -131,8 +134,8 @@ #define SUPPORT_FILEFORMAT_OGG 1 #define SUPPORT_FILEFORMAT_XM 1 #define SUPPORT_FILEFORMAT_MOD 1 -//#define SUPPORT_FILEFORMAT_FLAC 1 -#define SUPPORT_FILEFORMAT_MP3 1 +#define SUPPORT_FILEFORMAT_FLAC 1 +#define SUPPORT_FILEFORMAT_MP3 1 //------------------------------------------------------------------------------------ diff --git a/src/config.h.in b/src/config.h.in index 10e377269..6a71b77be 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -19,6 +19,8 @@ #cmakedefine SUPPORT_GIF_RECORDING 1 // Support high DPI displays #cmakedefine SUPPORT_HIGH_DPI 1 +// Support CompressData() and DecompressData() functions +#cmakedefine SUPPORT_COMPRESSION_API 1 // rlgl.h // Support VR simulation functionality (stereo rendering) diff --git a/src/core.c b/src/core.c index 6bb12619a..b247c1aa5 100644 --- a/src/core.c +++ b/src/core.c @@ -8,7 +8,7 @@ * - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) * - PLATFORM_DESKTOP: OSX/macOS * - PLATFORM_ANDROID: Android 4.0 (ARM, ARM64) -* - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian) +* - PLATFORM_RPI: Raspberry Pi 0,1,2,3,4 (Raspbian) * - PLATFORM_WEB: HTML5 with asm.js (Chrome, Firefox) * - PLATFORM_UWP: Windows 10 App, Windows Phone, Xbox One * @@ -55,6 +55,9 @@ * WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other running processes or * blocking the device is not restored properly. Use with care. * +* #define SUPPORT_MOUSE_CURSOR_RPI (Raspberry Pi only) +* Draw a mouse reference on screen (square cursor box) +* * #define SUPPORT_BUSY_WAIT_LOOP * Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used * @@ -71,6 +74,11 @@ * Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP) * NOTE: This flag is forced on macOS, since most displays are high-DPI * +* #define SUPPORT_COMPRESSION_API +* Support CompressData() and DecompressData() functions, those functions use zlib implementation +* provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module +* for linkage +* * DEPENDENCIES: * rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly) * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) @@ -105,7 +113,7 @@ #if !defined(EXTERNAL_CONFIG_FLAGS) #include "config.h" // Defines module configuration flags #else - #define RAYLIB_VERSION "2.5" + #define RAYLIB_VERSION "2.6-dev" #endif #if (defined(__linux__) || defined(PLATFORM_WEB)) && _POSIX_C_SOURCE < 199309L @@ -249,6 +257,12 @@ #include // Emscripten HTML5 library #endif +#if defined(SUPPORT_COMPRESSION_API) + // NOTE: Those declarations require stb_image and stb_image_write definitions, included in textures module + unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality); + char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen); +#endif + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -423,7 +437,6 @@ static double targetTime = 0.0; // Desired time for one frame, if 0 // Config internal variables //----------------------------------------------------------------------------------- static unsigned int configFlags = 0; // Configuration flags (bit based) -static bool showLogo = false; // Track if showing logo at init is enabled static char **dropFilesPath; // Store dropped files paths as strings static int dropFilesCount = 0; // Count dropped files strings @@ -466,8 +479,6 @@ static int GetGamepadButton(int button); // Get gamepad button ge static int GetGamepadAxis(int axis); // Get gamepad axis generic to all platforms static void PollInputEvents(void); // Register user events -static void LogoAnimation(void); // Plays raylib logo appearing animation - #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed @@ -568,7 +579,7 @@ static void InitTerminal(void) } else { - + ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); } @@ -579,7 +590,7 @@ static void InitTerminal(void) static void RestoreTerminal(void) { TraceLog(LOG_INFO, "Restore Terminal ..."); - + // Reset to default keyboard settings tcsetattr(STDIN_FILENO, TCSANOW, &defaultKeyboardSettings); @@ -697,13 +708,6 @@ void InitWindow(int width, int height, const char *title) mousePosition.x = (float)screenWidth/2.0f; mousePosition.y = (float)screenHeight/2.0f; - - // raylib logo appearing animation (if enabled) - if (showLogo) - { - SetTargetFPS(60); - LogoAnimation(); - } #endif // PLATFORM_ANDROID } @@ -770,7 +774,7 @@ void CloseWindow(void) pthread_join(eventWorkers[i].threadId, NULL); } } - + if (gamepadThreadId) pthread_join(gamepadThreadId, NULL); #endif @@ -1060,6 +1064,17 @@ int GetMonitorPhysicalHeight(int monitor) return 0; } +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + int x = 0; + int y = 0; +#if defined(PLATFORM_DESKTOP) + glfwGetWindowPos(window, &x, &y); +#endif + return (Vector2){ (float)x, (float)y }; +} + // Get the human-readable, UTF-8 encoded name of the primary monitor const char *GetMonitorName(int monitor) { @@ -1187,6 +1202,12 @@ void BeginDrawing(void) // End canvas drawing and swap buffers (double buffering) void EndDrawing(void) { +#if defined(PLATFORM_RPI) && defined(SUPPORT_MOUSE_CURSOR_RPI) + // On RPI native mode we have no system mouse cursor, so, + // we draw a small rectangle for user reference + DrawRectangle(mousePosition.x, mousePosition.y, 3, 3, MAROON); +#endif + rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) #if defined(SUPPORT_GIF_RECORDING) @@ -1249,17 +1270,12 @@ void BeginMode2D(Camera2D camera) rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlLoadIdentity(); // Reset current matrix (MODELVIEW) - rlMultMatrixf(MatrixToFloat(screenScaling)); // Apply screen scaling if required - // Camera rotation and scaling is always relative to target - Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f); - Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD); - Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f); - Matrix matTranslation = MatrixTranslate(camera.offset.x + camera.target.x, camera.offset.y + camera.target.y, 0.0f); + // Apply screen scaling if required + rlMultMatrixf(MatrixToFloat(screenScaling)); - Matrix matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation); - - rlMultMatrixf(MatrixToFloat(matTransform)); // Apply transformation to modelview + // Apply 2d camera transformation to modelview + rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera))); } // Ends 2D mode with custom camera @@ -1370,6 +1386,23 @@ void EndTextureMode(void) currentHeight = GetScreenHeight(); } +// Begin scissor mode (define screen area for following drawing) +// NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left +void BeginScissorMode(int x, int y, int width, int height) +{ + rlglDraw(); // Force drawing elements + + rlEnableScissorTest(); + rlScissor(x, GetScreenHeight() - (y + height), width, height); +} + +// End scissor mode +void EndScissorMode(void) +{ + rlglDraw(); // Force drawing elements + rlDisableScissorTest(); +} + // Returns a ray trace from mouse position Ray GetMouseRay(Vector2 mousePosition, Camera camera) { @@ -1425,6 +1458,40 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) return ray; } +// Get transform matrix for camera +Matrix GetCameraMatrix(Camera camera) +{ + return MatrixLookAt(camera.position, camera.target, camera.up); +} + +// Returns camera 2d transform matrix +Matrix GetCameraMatrix2D(Camera2D camera) +{ + Matrix matTransform = { 0 }; + // The camera in world-space is set by + // 1. Move it to target + // 2. Rotate by -rotation and scale by (1/zoom) + // When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller), + // not for the camera getting bigger, hence the invert. Same deal with rotation. + // 3. Move it by (-offset); + // Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera) + // we need to do it into opposite direction (inverse transform) + + // Having camera transform in world-space, inverse of it gives the modelview transform. + // Since (A*B*C)' = C'*B'*A', the modelview is + // 1. Move to offset + // 2. Rotate and Scale + // 3. Move by -target + Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f); + Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD); + Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f); + Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f); + + matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation); + + return matTransform; +} + // Returns the screen space position from a 3d world space position Vector2 GetWorldToScreen(Vector3 position, Camera camera) { @@ -1467,10 +1534,22 @@ Vector2 GetWorldToScreen(Vector3 position, Camera camera) return screenPosition; } -// Get transform matrix for camera -Matrix GetCameraMatrix(Camera camera) +// Returns the screen space position for a 2d camera world space position +Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera) { - return MatrixLookAt(camera.position, camera.target, camera.up); + Matrix matCamera = GetCameraMatrix2D(camera); + Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera); + + return (Vector2){ transform.x, transform.y }; +} + +// Returns the world space position for a 2d camera screen space position +Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera) +{ + Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera)); + Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera); + + return (Vector2){ transform.x, transform.y }; } // Set target FPS (maximum) @@ -1664,7 +1743,6 @@ void SetConfigFlags(unsigned int flags) { configFlags = flags; - if (configFlags & FLAG_SHOW_LOGO) showLogo = true; if (configFlags & FLAG_FULLSCREEN_MODE) fullscreen = true; if (configFlags & FLAG_WINDOW_ALWAYS_RUN) alwaysRun = true; } @@ -1718,29 +1796,36 @@ bool FileExists(const char *fileName) bool IsFileExtension(const char *fileName, const char *ext) { bool result = false; - const char *fileExt; + const char *fileExt = GetExtension(fileName); - if ((fileExt = strrchr(fileName, '.')) != NULL) + if (fileExt != NULL) { -#if defined(_WIN32) - result = true; - int extLen = strlen(ext); + int extCount = 0; + const char **checkExts = TextSplit(ext, ';', &extCount); - if (strlen(fileExt) == extLen) + for (int i = 0; i < extCount; i++) { - for (int i = 0; i < extLen; i++) + if (strcmp(fileExt, checkExts[i] + 1) == 0) { - if (tolower(fileExt[i]) != tolower(ext[i])) - { - result = false; - break; - } + result = true; + break; } } - else result = false; -#else - if (strcmp(fileExt, ext) == 0) result = true; -#endif + } + + return result; +} + +// Check if a directory path exists +bool DirectoryExists(const char *dirPath) +{ + bool result = false; + DIR *dir = opendir(dirPath); + + if (dir != NULL) + { + result = true; + closedir(dir); } return result; @@ -1767,22 +1852,23 @@ static const char *strprbrk(const char *s, const char *charset) // Get pointer to filename for a path string const char *GetFileName(const char *filePath) { - const char *fileName = strprbrk(filePath, "\\/"); + const char *fileName = NULL; + if (filePath != NULL) fileName = strprbrk(filePath, "\\/"); - if (!fileName || fileName == filePath) return filePath; + if (!fileName || (fileName == filePath)) return filePath; return fileName + 1; } -// Get filename string without extension (memory should be freed) +// Get filename string without extension (uses static string) const char *GetFileNameWithoutExt(const char *filePath) { - #define MAX_FILENAMEWITHOUTEXT_LENGTH 64 + #define MAX_FILENAMEWITHOUTEXT_LENGTH 128 static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH]; memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH); - strcpy(fileName, GetFileName(filePath)); // Get filename with extension + if (filePath != NULL) strcpy(fileName, GetFileName(filePath)); // Get filename with extension int len = strlen(fileName); @@ -1799,21 +1885,43 @@ const char *GetFileNameWithoutExt(const char *filePath) return fileName; } -// Get directory for a given fileName (with path) -const char *GetDirectoryPath(const char *fileName) +// Get directory for a given filePath +const char *GetDirectoryPath(const char *filePath) { const char *lastSlash = NULL; - static char filePath[MAX_FILEPATH_LENGTH]; - memset(filePath, 0, MAX_FILEPATH_LENGTH); + static char dirPath[MAX_FILEPATH_LENGTH]; + memset(dirPath, 0, MAX_FILEPATH_LENGTH); - lastSlash = strprbrk(fileName, "\\/"); + lastSlash = strprbrk(filePath, "\\/"); 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'; // Add '\0' manually + strncpy(dirPath, filePath, strlen(filePath) - (strlen(lastSlash) - 1)); + dirPath[strlen(filePath) - strlen(lastSlash)] = '\0'; // Add '\0' manually - return filePath; + return dirPath; +} + +// Get previous directory path for a given path +const char *GetPrevDirectoryPath(const char *dirPath) +{ + static char prevDirPath[MAX_FILEPATH_LENGTH]; + memset(prevDirPath, 0, MAX_FILEPATH_LENGTH); + int pathLen = strlen(dirPath); + + if (pathLen <= 3) strcpy(prevDirPath, dirPath); + + for (int i = (pathLen - 1); (i > 0) && (pathLen > 3); i--) + { + if ((dirPath[i] == '\\') || (dirPath[i] == '/')) + { + if (i == 2) i++; // Check for root: "C:\" + strncpy(prevDirPath, dirPath, i); + break; + } + } + + return prevDirPath; } // Get current working directory @@ -1870,11 +1978,12 @@ void ClearDirectoryFiles(void) { if (dirFilesCount > 0) { - for (int i = 0; i < dirFilesCount; i++) RL_FREE(dirFilesPath[i]); + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) RL_FREE(dirFilesPath[i]); RL_FREE(dirFilesPath); - dirFilesCount = 0; } + + dirFilesCount = 0; } // Change working directory, returns true if success @@ -1925,6 +2034,32 @@ long GetFileModTime(const char *fileName) return 0; } +// Compress data (DEFLATE algorythm) +unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLength) +{ + #define COMPRESSION_QUALITY_DEFLATE 8 + + unsigned char *compData = NULL; + +#if defined(SUPPORT_COMPRESSION_API) + compData = stbi_zlib_compress(data, dataLength, compDataLength, COMPRESSION_QUALITY_DEFLATE); +#endif + + return compData; +} + +// Decompress data (DEFLATE algorythm) +unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength) +{ + char *data = NULL; + +#if defined(SUPPORT_COMPRESSION_API) + data = stbi_zlib_decode_malloc((char *)compData, compDataLength, dataLength); +#endif + + return (unsigned char *)data; +} + // 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) @@ -2287,7 +2422,7 @@ int GetMouseX(void) int GetMouseY(void) { #if defined(PLATFORM_ANDROID) - return (int)touchPosition[0].x; + return (int)touchPosition[0].y; #else return (int)((mousePosition.y + mouseOffset.y)*mouseScale.y); #endif @@ -2457,9 +2592,6 @@ static bool InitGraphicsDevice(int width, int height) if (screenHeight <= 0) screenHeight = displayHeight; #endif // PLATFORM_DESKTOP - currentWidth = screenWidth; - currentHeight = screenHeight; - #if defined(PLATFORM_WEB) displayWidth = screenWidth; displayHeight = screenHeight; @@ -2525,9 +2657,9 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); #if defined(PLATFORM_DESKTOP) - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); #else - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); #endif } @@ -2990,6 +3122,9 @@ static bool InitGraphicsDevice(int width, int height) // Setup default viewport SetupViewport(fbWidth, fbHeight); + currentWidth = screenWidth; + currentHeight = screenHeight; + ClearBackground(RAYWHITE); // Default background color for raylib games :P #if defined(PLATFORM_ANDROID) @@ -3818,7 +3953,7 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified) } // GLFW3 Window Drop Callback, runs when drop files into window -// NOTE: Paths are stored in dinamic memory for further retrieval +// NOTE: Paths are stored in dynamic memory for further retrieval // Everytime new files are dropped, old ones are discarded static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) { @@ -3898,13 +4033,6 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } } */ - - // raylib logo appearing animation (if enabled) - if (showLogo) - { - SetTargetFPS(60); // Not required on Android - LogoAnimation(); - } } } } break; @@ -4235,8 +4363,8 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); - for(int i = 0; i < gamepadEvent->numAxes; ++i) TraceLog(LOG_DEBUG, "Axis %d: %g", i, gamepadEvent->axis[i]); - for(int i = 0; i < gamepadEvent->numButtons; ++i) TraceLog(LOG_DEBUG, "Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + for (int i = 0; i < gamepadEvent->numAxes; ++i) TraceLog(LOG_DEBUG, "Axis %d: %g", i, gamepadEvent->axis[i]); + for (int i = 0; i < gamepadEvent->numButtons; ++i) TraceLog(LOG_DEBUG, "Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); */ if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) gamepadReady[gamepadEvent->index] = true; @@ -4796,7 +4924,7 @@ static void *EventThread(void *arg) // TODO: This fifo is not fully threadsafe with multiple writers, so multiple keyboards hitting a key at the exact same time could miss a key (double write to head before it was incremented) } */ - + currentKeyState[keycode] = event.value; if (event.value == 1) lastKeyPressed = keycode; // Register last key pressed @@ -4810,7 +4938,7 @@ static void *EventThread(void *arg) #endif if (currentKeyState[exitKey] == 1) windowShouldClose = true; - + TraceLog(LOG_DEBUG, "KEY%s ScanCode: %4i KeyCode: %4i",event.value == 0 ? "UP":"DOWN", event.code, keycode); } } @@ -4952,117 +5080,3 @@ static void *GamepadThread(void *arg) return NULL; } #endif // PLATFORM_RPI - -// Plays raylib logo appearing animation -static void LogoAnimation(void) -{ -#if !defined(PLATFORM_WEB) && !defined(PLATFORM_UWP) - int logoPositionX = screenWidth/2 - 128; - int logoPositionY = screenHeight/2 - 128; - - int framesCounter = 0; - int lettersCount = 0; - - int topSideRecWidth = 16; - int leftSideRecHeight = 16; - - int bottomSideRecWidth = 16; - int rightSideRecHeight = 16; - - int state = 0; // Tracking animation states (State Machine) - float alpha = 1.0f; // Useful for fading - - while (!WindowShouldClose() && (state != 4)) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - if (state == 0) // State 0: Small box blinking - { - framesCounter++; - - if (framesCounter == 84) - { - state = 1; - framesCounter = 0; // Reset counter... will be used later... - } - } - else if (state == 1) // State 1: Top and left bars growing - { - topSideRecWidth += 4; - leftSideRecHeight += 4; - - if (topSideRecWidth == 256) state = 2; - } - else if (state == 2) // State 2: Bottom and right bars growing - { - bottomSideRecWidth += 4; - rightSideRecHeight += 4; - - if (bottomSideRecWidth == 256) state = 3; - } - else if (state == 3) // State 3: Letters appearing (one by one) - { - framesCounter++; - - if (framesCounter/12) // Every 12 frames, one more letter! - { - lettersCount++; - framesCounter = 0; - } - - if (lettersCount >= 10) // When all letters have appeared, just fade out everything - { - alpha -= 0.02f; - - if (alpha <= 0.0f) - { - alpha = 0.0f; - state = 4; - } - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - if (state == 0) - { - if ((framesCounter/12)%2) DrawRectangle(logoPositionX, logoPositionY, 16, 16, BLACK); - } - else if (state == 1) - { - DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, BLACK); - DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, BLACK); - } - else if (state == 2) - { - DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, BLACK); - DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, BLACK); - - DrawRectangle(logoPositionX + 240, logoPositionY, 16, rightSideRecHeight, BLACK); - DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, BLACK); - } - else if (state == 3) - { - DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, Fade(BLACK, alpha)); - DrawRectangle(logoPositionX, logoPositionY + 16, 16, leftSideRecHeight - 32, Fade(BLACK, alpha)); - - DrawRectangle(logoPositionX + 240, logoPositionY + 16, 16, rightSideRecHeight - 32, Fade(BLACK, alpha)); - DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, Fade(BLACK, alpha)); - - DrawRectangle(screenWidth/2 - 112, screenHeight/2 - 112, 224, 224, Fade(RAYWHITE, alpha)); - - DrawText(TextSubtext("raylib", 0, lettersCount), screenWidth/2 - 44, screenHeight/2 + 48, 50, Fade(BLACK, alpha)); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- - } -#endif - - showLogo = false; // Prevent for repeating when reloading window (Android) -} diff --git a/src/easings.h b/src/easings.h index 1b08af0a5..f6c384b00 100644 --- a/src/easings.h +++ b/src/easings.h @@ -135,7 +135,7 @@ EASEDEF float EaseQuadOut(float t, float b, float c, float d) { t /= d; return ( EASEDEF float EaseQuadInOut(float t, float b, float c, float d) { if ((t/=d/2) < 1) return (((c/2)*(t*t)) + b); - return (-c/2.0f*(((t - 1.0f)*(t - 3.0f)) - 1.0f) + b); + return (-c/2.0f*(((t - 1.0f)*(t - 3.0f)) - 1.0f) + b); } // Exponential Easing functions @@ -147,7 +147,7 @@ EASEDEF float EaseExpoInOut(float t, float b, float c, float d) if (t == d) return (b + c); if ((t/=d/2.0f) < 1.0f) return (c/2.0f*pow(2.0f, 10.0f*(t - 1.0f)) + b); - return (c/2.0f*(-pow(2.0f, -10.0f*(t - 1.0f)) + 2.0f) + b); + return (c/2.0f*(-pow(2.0f, -10.0f*(t - 1.0f)) + 2.0f) + b); } // Back Easing functions diff --git a/src/external/cgltf.h b/src/external/cgltf.h index 85d5c9850..7f3f77f7f 100644 --- a/src/external/cgltf.h +++ b/src/external/cgltf.h @@ -1,7 +1,7 @@ /** * cgltf - a single-file glTF 2.0 parser written in C99. * - * Version: 1.0 + * Version: 1.2 * * Website: https://github.com/jkuhlmann/cgltf * @@ -35,8 +35,15 @@ * variable. * * `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*, - * const char*)` can be optionally called to open and read buffer - * files using the `FILE*` APIs. + * const char* gltf_path)` can be optionally called to open and read buffer + * files using the `FILE*` APIs. The `gltf_path` argument is the path to + * the original glTF file, which allows the parser to resolve the path to + * buffer files. + * + * `cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, + * cgltf_size size, const char* base64, void** out_data)` decodes + * base64-encoded data content. Used internally by `cgltf_load_buffers()` + * and may be useful if you're not dealing with normal files. * * `cgltf_result cgltf_parse_file(const cgltf_options* options, const * char* path, cgltf_data** out_data)` can be used to open the given @@ -44,6 +51,29 @@ * * `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional * checks to make sure the parsed glTF data is valid. + * + * `cgltf_node_transform_local` converts the translation / rotation / scale properties of a node + * into a mat4. + * + * `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order + * to compute the root-to-node transformation. + * + * `cgltf_accessor_read_float` reads a certain element from an accessor and converts it to + * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element + * size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns + * false if the passed-in element_size is too small, or if the accessor is sparse. + * + * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t + * and only works with single-component data types. + * + * `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*, + * char* dest, cgltf_size* dest_size)` allows to retrieve the "extras" data that + * can be attached to many glTF objects (which can be arbitrary JSON data). The + * `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data + * as it appears in the complete glTF JSON data. This function copies the extras data + * into the provided buffer. If `dest` is NULL, the length of the data is written into + * `dest_size`. You can then parse this data using your own JSON parser + * or, if you've included the cgltf implementation using the integrated JSMN JSON parser. */ #ifndef CGLTF_H_INCLUDED__ #define CGLTF_H_INCLUDED__ @@ -175,11 +205,17 @@ typedef enum cgltf_light_type { cgltf_light_type_spot, } cgltf_light_type; +typedef struct cgltf_extras { + cgltf_size start_offset; + cgltf_size end_offset; +} cgltf_extras; + typedef struct cgltf_buffer { cgltf_size size; char* uri; void* data; /* loaded by cgltf_load_buffers */ + cgltf_extras extras; } cgltf_buffer; typedef struct cgltf_buffer_view @@ -189,6 +225,7 @@ typedef struct cgltf_buffer_view cgltf_size size; cgltf_size stride; /* 0 == automatically determined by accessor */ cgltf_buffer_view_type type; + cgltf_extras extras; } cgltf_buffer_view; typedef struct cgltf_accessor_sparse @@ -199,6 +236,9 @@ typedef struct cgltf_accessor_sparse cgltf_component_type indices_component_type; cgltf_buffer_view* values_buffer_view; cgltf_size values_byte_offset; + cgltf_extras extras; + cgltf_extras indices_extras; + cgltf_extras values_extras; } cgltf_accessor_sparse; typedef struct cgltf_accessor @@ -216,6 +256,7 @@ typedef struct cgltf_accessor cgltf_float max[16]; cgltf_bool is_sparse; cgltf_accessor_sparse sparse; + cgltf_extras extras; } cgltf_accessor; typedef struct cgltf_attribute @@ -232,6 +273,7 @@ typedef struct cgltf_image char* uri; cgltf_buffer_view* buffer_view; char* mime_type; + cgltf_extras extras; } cgltf_image; typedef struct cgltf_sampler @@ -240,6 +282,7 @@ typedef struct cgltf_sampler cgltf_int min_filter; cgltf_int wrap_s; cgltf_int wrap_t; + cgltf_extras extras; } cgltf_sampler; typedef struct cgltf_texture @@ -247,6 +290,7 @@ typedef struct cgltf_texture char* name; cgltf_image* image; cgltf_sampler* sampler; + cgltf_extras extras; } cgltf_texture; typedef struct cgltf_texture_transform @@ -264,6 +308,7 @@ typedef struct cgltf_texture_view cgltf_float scale; /* equivalent to strength for occlusion_texture */ cgltf_bool has_transform; cgltf_texture_transform transform; + cgltf_extras extras; } cgltf_texture_view; typedef struct cgltf_pbr_metallic_roughness @@ -274,6 +319,8 @@ typedef struct cgltf_pbr_metallic_roughness cgltf_float base_color_factor[4]; cgltf_float metallic_factor; cgltf_float roughness_factor; + + cgltf_extras extras; } cgltf_pbr_metallic_roughness; typedef struct cgltf_pbr_specular_glossiness @@ -301,6 +348,7 @@ typedef struct cgltf_material cgltf_float alpha_cutoff; cgltf_bool double_sided; cgltf_bool unlit; + cgltf_extras extras; } cgltf_material; typedef struct cgltf_morph_target { @@ -316,6 +364,7 @@ typedef struct cgltf_primitive { cgltf_size attributes_count; cgltf_morph_target* targets; cgltf_size targets_count; + cgltf_extras extras; } cgltf_primitive; typedef struct cgltf_mesh { @@ -324,6 +373,7 @@ typedef struct cgltf_mesh { cgltf_size primitives_count; cgltf_float* weights; cgltf_size weights_count; + cgltf_extras extras; } cgltf_mesh; typedef struct cgltf_node cgltf_node; @@ -334,6 +384,7 @@ typedef struct cgltf_skin { cgltf_size joints_count; cgltf_node* skeleton; cgltf_accessor* inverse_bind_matrices; + cgltf_extras extras; } cgltf_skin; typedef struct cgltf_camera_perspective { @@ -341,6 +392,7 @@ typedef struct cgltf_camera_perspective { cgltf_float yfov; cgltf_float zfar; cgltf_float znear; + cgltf_extras extras; } cgltf_camera_perspective; typedef struct cgltf_camera_orthographic { @@ -348,6 +400,7 @@ typedef struct cgltf_camera_orthographic { cgltf_float ymag; cgltf_float zfar; cgltf_float znear; + cgltf_extras extras; } cgltf_camera_orthographic; typedef struct cgltf_camera { @@ -357,6 +410,7 @@ typedef struct cgltf_camera { cgltf_camera_perspective perspective; cgltf_camera_orthographic orthographic; }; + cgltf_extras extras; } cgltf_camera; typedef struct cgltf_light { @@ -388,24 +442,28 @@ struct cgltf_node { cgltf_float rotation[4]; cgltf_float scale[3]; cgltf_float matrix[16]; + cgltf_extras extras; }; typedef struct cgltf_scene { char* name; cgltf_node** nodes; cgltf_size nodes_count; + cgltf_extras extras; } cgltf_scene; typedef struct cgltf_animation_sampler { cgltf_accessor* input; cgltf_accessor* output; cgltf_interpolation_type interpolation; + cgltf_extras extras; } cgltf_animation_sampler; typedef struct cgltf_animation_channel { cgltf_animation_sampler* sampler; cgltf_node* target_node; cgltf_animation_path_type target_path; + cgltf_extras extras; } cgltf_animation_channel; typedef struct cgltf_animation { @@ -414,6 +472,7 @@ typedef struct cgltf_animation { cgltf_size samplers_count; cgltf_animation_channel* channels; cgltf_size channels_count; + cgltf_extras extras; } cgltf_animation; typedef struct cgltf_asset { @@ -421,6 +480,7 @@ typedef struct cgltf_asset { char* generator; char* version; char* min_version; + cgltf_extras extras; } cgltf_asset; typedef struct cgltf_data @@ -474,6 +534,17 @@ typedef struct cgltf_data cgltf_animation* animations; cgltf_size animations_count; + cgltf_extras extras; + + char** extensions_used; + cgltf_size extensions_used_count; + + char** extensions_required; + cgltf_size extensions_required_count; + + const char* json; + cgltf_size json_size; + const void* bin; cgltf_size bin_size; @@ -495,7 +566,10 @@ cgltf_result cgltf_parse_file( cgltf_result cgltf_load_buffers( const cgltf_options* options, cgltf_data* data, - const char* base_path); + const char* gltf_path); + + +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data); cgltf_result cgltf_validate( cgltf_data* data); @@ -505,6 +579,11 @@ void cgltf_free(cgltf_data* data); void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix); void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix); +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); + +cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size); + #ifdef __cplusplus } #endif @@ -528,7 +607,14 @@ void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix) #include /* For uint8_t, uint32_t */ #include /* For strncpy */ #include /* For malloc, free */ -#include /* For fopen */ +#include /* For fopen */ +#include /* For UINT_MAX etc */ + +/* JSMN_PARENT_LINKS is necessary to make parsing large structures linear in input size */ +#define JSMN_PARENT_LINKS + +/* JSMN_STRICT is necessary to reject invalid JSON documents */ +#define JSMN_STRICT /* * -- jsmn.h start -- @@ -580,11 +666,13 @@ static const uint32_t GlbMagicBinChunk = 0x004E4942; static void* cgltf_default_alloc(void* user, cgltf_size size) { + (void)user; return malloc(size); } static void cgltf_default_free(void* user, void* ptr) { + (void)user; free(ptr); } @@ -810,22 +898,22 @@ static void cgltf_combine_paths(char* path, const char* base, const char* uri) } else { - strcpy(path, base); + strcpy(path, uri); } } -static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* base_path, void** out_data) +static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* gltf_path, void** out_data) { void* (*memory_alloc)(void*, cgltf_size) = options->memory_alloc ? options->memory_alloc : &cgltf_default_alloc; void (*memory_free)(void*, void*) = options->memory_free ? options->memory_free : &cgltf_default_free; - char* path = (char*)memory_alloc(options->memory_user_data, strlen(uri) + strlen(base_path) + 1); + char* path = (char*)memory_alloc(options->memory_user_data, strlen(uri) + strlen(gltf_path) + 1); if (!path) { return cgltf_result_out_of_memory; } - cgltf_combine_paths(path, base_path, uri); + cgltf_combine_paths(path, gltf_path, uri); FILE* file = fopen(path, "rb"); @@ -858,7 +946,7 @@ static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_s return cgltf_result_success; } -static cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data) +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data) { void* (*memory_alloc)(void*, cgltf_size) = options->memory_alloc ? options->memory_alloc : &cgltf_default_alloc; void (*memory_free)(void*, void*) = options->memory_free ? options->memory_free : &cgltf_default_free; @@ -905,7 +993,7 @@ static cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf return cgltf_result_success; } -cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* base_path) +cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path) { if (options == NULL) { @@ -954,9 +1042,9 @@ cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, return cgltf_result_unknown_format; } } - else if (strstr(uri, "://") == NULL) + else if (strstr(uri, "://") == NULL && gltf_path) { - cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, base_path, &data->buffers[i].data); + cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); if (res != cgltf_result_success) { @@ -1150,6 +1238,34 @@ cgltf_result cgltf_validate(cgltf_data* data) return cgltf_result_success; } +cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size) +{ + cgltf_size json_size = extras->end_offset - extras->start_offset; + + if (!dest) + { + if (dest_size) + { + *dest_size = json_size + 1; + return cgltf_result_success; + } + return cgltf_result_invalid_options; + } + + if (*dest_size + 1 < json_size) + { + strncpy(dest, data->json + extras->start_offset, *dest_size - 1); + dest[*dest_size - 1] = 0; + } + else + { + strncpy(dest, data->json + extras->start_offset, json_size); + dest[json_size] = 0; + } + + return cgltf_result_success; +} + void cgltf_free(cgltf_data* data) { if (!data) @@ -1282,6 +1398,20 @@ void cgltf_free(cgltf_data* data) data->memory_free(data->memory_user_data, data->animations); + for (cgltf_size i = 0; i < data->extensions_used_count; ++i) + { + data->memory_free(data->memory_user_data, data->extensions_used[i]); + } + + data->memory_free(data->memory_user_data, data->extensions_used); + + for (cgltf_size i = 0; i < data->extensions_required_count; ++i) + { + data->memory_free(data->memory_user_data, data->extensions_required[i]); + } + + data->memory_free(data->memory_user_data, data->extensions_required); + data->memory_free(data->memory_user_data, data->file_data); data->memory_free(data->memory_user_data, data); @@ -1367,6 +1497,142 @@ void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix) } } +static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_16: + return *((const int16_t*) in); + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + case cgltf_component_type_r_32f: + return (cgltf_size)*((const float*) in); + case cgltf_component_type_r_8: + return *((const int8_t*) in); + case cgltf_component_type_r_8u: + case cgltf_component_type_invalid: + default: + return *((const uint8_t*) in); + } +} + +static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type, cgltf_bool normalized) +{ + if (component_type == cgltf_component_type_r_32f) + { + return *((const float*) in); + } + + if (normalized) + { + switch (component_type) + { + case cgltf_component_type_r_32u: + return *((const uint32_t*) in) / (float) UINT_MAX; + case cgltf_component_type_r_16: + return *((const int16_t*) in) / (float) SHRT_MAX; + case cgltf_component_type_r_16u: + return *((const uint16_t*) in) / (float) USHRT_MAX; + case cgltf_component_type_r_8: + return *((const int8_t*) in) / (float) SCHAR_MAX; + case cgltf_component_type_r_8u: + case cgltf_component_type_invalid: + default: + return *((const uint8_t*) in) / (float) CHAR_MAX; + } + } + + return (cgltf_float)cgltf_component_read_index(in, component_type); +} + +static cgltf_size cgltf_num_components(cgltf_type type); +static cgltf_size cgltf_component_size(cgltf_component_type component_type); + +static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size) +{ + cgltf_size num_components = cgltf_num_components(type); + + if (element_size < num_components) { + return 0; + } + + // There are three special cases for component extraction, see #data-alignment in the 2.0 spec. + + cgltf_size component_size = cgltf_component_size(component_type); + + if (type == cgltf_type_mat2 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 5, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 2, component_type, normalized); + out[3] = cgltf_component_read_float(element + 4, component_type, normalized); + out[4] = cgltf_component_read_float(element + 5, component_type, normalized); + out[5] = cgltf_component_read_float(element + 6, component_type, normalized); + out[6] = cgltf_component_read_float(element + 8, component_type, normalized); + out[7] = cgltf_component_read_float(element + 9, component_type, normalized); + out[8] = cgltf_component_read_float(element + 10, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 2) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 2, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 8, component_type, normalized); + out[4] = cgltf_component_read_float(element + 10, component_type, normalized); + out[5] = cgltf_component_read_float(element + 12, component_type, normalized); + out[6] = cgltf_component_read_float(element + 16, component_type, normalized); + out[7] = cgltf_component_read_float(element + 18, component_type, normalized); + out[8] = cgltf_component_read_float(element + 20, component_type, normalized); + return 1; + } + + for (cgltf_size i = 0; i < num_components; ++i) + { + out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized); + } + return 1; +} + + +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) +{ + if (accessor->is_sparse || accessor->buffer_view == NULL) + { + return 0; + } + + cgltf_size offset = accessor->offset + accessor->buffer_view->offset; + const uint8_t* element = (const uint8_t*) accessor->buffer_view->buffer->data; + element += offset + accessor->stride * index; + return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size); +} + +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index) +{ + if (accessor->buffer_view) + { + cgltf_size offset = accessor->offset + accessor->buffer_view->offset; + const uint8_t* element = (const uint8_t*) accessor->buffer_view->buffer->data; + element += offset + accessor->stride * index; + return cgltf_component_read_index(element, accessor->component_type); + } + + return 0; +} + #define CGLTF_ERROR_JSON -1 #define CGLTF_ERROR_NOMEM -2 @@ -1413,39 +1679,31 @@ static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_c static int cgltf_skip_json(jsmntok_t const* tokens, int i) { - if (tokens[i].type == JSMN_ARRAY) + int end = i + 1; + + while (i < end) { - int size = tokens[i].size; - ++i; - for (int j = 0; j < size; ++j) + switch (tokens[i].type) { - i = cgltf_skip_json(tokens, i); - if (i < 0) - { - return i; - } + case JSMN_OBJECT: + end += tokens[i].size * 2; + break; + + case JSMN_ARRAY: + end += tokens[i].size; + break; + + case JSMN_PRIMITIVE: + case JSMN_STRING: + break; + + default: + return -1; } + + i++; } - else if (tokens[i].type == JSMN_OBJECT) - { - int size = tokens[i].size; - ++i; - for (int j = 0; j < size; ++j) - { - CGLTF_CHECK_KEY(tokens[i]); - ++i; - i = cgltf_skip_json(tokens, i); - if (i < 0) - { - return i; - } - } - } - else if (tokens[i].type == JSMN_PRIMITIVE - || tokens[i].type == JSMN_STRING) - { - return i + 1; - } + return i; } @@ -1495,6 +1753,7 @@ static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* toke static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, size_t element_size, void** out_array, cgltf_size* out_size) { + (void)json_chunk; CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); if (*out_array) { @@ -1511,6 +1770,26 @@ static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* token return i + 1; } +static int cgltf_parse_json_string_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char*** out_array, cgltf_size* out_size) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(char*), (void**)out_array, out_size); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < *out_size; ++j) + { + i = cgltf_parse_json_string(options, tokens, i, json_chunk, j + (*out_array)); + if (i < 0) + { + return i; + } + } + return i; +} + static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index) { const char* us = strchr(name, '_'); @@ -1592,6 +1871,15 @@ static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t con return i; } +static int cgltf_parse_json_extras(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) +{ + (void)json_chunk; + out_extras->start_offset = tokens[i].start; + out_extras->end_offset = tokens[i].end; + i = cgltf_skip_json(tokens, i); + return i; +} + static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); @@ -1637,15 +1925,19 @@ static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* t return i; } - for (cgltf_size j = 0; j < out_prim->targets_count; ++j) + for (cgltf_size k = 0; k < out_prim->targets_count; ++k) { - i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[j].attributes, &out_prim->targets[j].attributes_count); + i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[k].attributes, &out_prim->targets[k].attributes_count); if (i < 0) { return i; } } } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_prim->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -1702,6 +1994,10 @@ static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_mesh->weights, (int)out_mesh->weights_count); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_mesh->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -1805,6 +2101,10 @@ static int cgltf_parse_json_accessor_sparse(jsmntok_t const* tokens, int i, cons out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk); ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->indices_extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -1840,6 +2140,10 @@ static int cgltf_parse_json_accessor_sparse(jsmntok_t const* tokens, int i, cons out_sparse->values_byte_offset = cgltf_json_to_int(tokens + i, json_chunk); ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->values_extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -1851,6 +2155,10 @@ static int cgltf_parse_json_accessor_sparse(jsmntok_t const* tokens, int i, cons } } } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -1962,6 +2270,10 @@ static int cgltf_parse_json_accessor(jsmntok_t const* tokens, int i, const uint8 out_accessor->is_sparse = 1; i = cgltf_parse_json_accessor_sparse(tokens, i + 1, json_chunk, &out_accessor->sparse); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_accessor->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -2059,6 +2371,10 @@ static int cgltf_parse_json_texture_view(jsmntok_t const* tokens, int i, const u out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture_view->extras); + } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { ++i; @@ -2070,6 +2386,8 @@ static int cgltf_parse_json_texture_view(jsmntok_t const* tokens, int i, const u for (int k = 0; k < extensions_size; ++k) { + CGLTF_CHECK_KEY(tokens[i]); + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_texture_transform") == 0) { out_texture_view->has_transform = 1; @@ -2079,6 +2397,11 @@ static int cgltf_parse_json_texture_view(jsmntok_t const* tokens, int i, const u { i = cgltf_skip_json(tokens, i+1); } + + if (i < 0) + { + return i; + } } } else @@ -2134,6 +2457,10 @@ static int cgltf_parse_json_pbr_metallic_roughness(jsmntok_t const* tokens, int i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, &out_pbr->metallic_roughness_texture); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_pbr->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -2161,18 +2488,10 @@ static int cgltf_parse_json_pbr_specular_glossiness(jsmntok_t const* tokens, int if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseFactor") == 0) { i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->diffuse_factor, 4); - if (i < 0) - { - return i; - } } else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) { i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->specular_factor, 3); - if (i < 0) - { - return i; - } } else if (cgltf_json_strcmp(tokens+i, json_chunk, "glossinessFactor") == 0) { @@ -2183,18 +2502,10 @@ static int cgltf_parse_json_pbr_specular_glossiness(jsmntok_t const* tokens, int else if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseTexture") == 0) { i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, &out_pbr->diffuse_texture); - if (i < 0) - { - return i; - } } else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularGlossinessTexture") == 0) { i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, &out_pbr->specular_glossiness_texture); - if (i < 0) - { - return i; - } } else { @@ -2205,7 +2516,6 @@ static int cgltf_parse_json_pbr_specular_glossiness(jsmntok_t const* tokens, int { return i; } - } return i; @@ -2240,6 +2550,10 @@ static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* token { i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->name); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_image->extras); + } else { i = cgltf_skip_json(tokens, i + 1); @@ -2256,6 +2570,7 @@ static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* token static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sampler* out_sampler) { + (void)options; CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); out_sampler->wrap_s = 10497; @@ -2296,6 +2611,10 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok = cgltf_json_to_int(tokens + i, json_chunk); ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); + } else { i = cgltf_skip_json(tokens, i + 1); @@ -2338,6 +2657,10 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture->extras); + } else { i = cgltf_skip_json(tokens, i + 1); @@ -2431,6 +2754,10 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to cgltf_json_to_bool(tokens + i, json_chunk); ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_material->extras); + } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { ++i; @@ -2631,6 +2958,10 @@ static int cgltf_parse_json_buffer_view(jsmntok_t const* tokens, int i, const ui out_buffer_view->type = (cgltf_buffer_view_type)type; ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer_view->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -2686,6 +3017,10 @@ static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* toke { i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->uri); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -2762,6 +3097,10 @@ static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens out_skin->inverse_bind_matrices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_skin->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -2862,6 +3201,10 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke out_camera->perspective.znear = cgltf_json_to_float(tokens + i, json_chunk); ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->perspective.extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -2912,6 +3255,10 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke out_camera->orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk); ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->orthographic.extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -2923,6 +3270,10 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke } } } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -3162,6 +3513,10 @@ static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_node->weights, (int)out_node->weights_count); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_node->extras); + } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { ++i; @@ -3279,6 +3634,10 @@ static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* token ++i; } } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_scene->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -3314,6 +3673,7 @@ static int cgltf_parse_json_scenes(cgltf_options* options, jsmntok_t const* toke static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_sampler* out_sampler) { + (void)options; CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); int size = tokens[i].size; @@ -3352,6 +3712,10 @@ static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t } ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -3368,6 +3732,7 @@ static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_channel* out_channel) { + (void)options; CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); int size = tokens[i].size; @@ -3423,6 +3788,10 @@ static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t } ++i; } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_channel->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -3497,6 +3866,10 @@ static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* t } } } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_animation->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -3557,6 +3930,10 @@ static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* token { i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->min_version); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_asset->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -3571,58 +3948,58 @@ static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* token return i; } -static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) -{ - cgltf_size size = 0; +static cgltf_size cgltf_num_components(cgltf_type type) { + switch (type) + { + case cgltf_type_vec2: + return 2; + case cgltf_type_vec3: + return 3; + case cgltf_type_vec4: + return 4; + case cgltf_type_mat2: + return 4; + case cgltf_type_mat3: + return 9; + case cgltf_type_mat4: + return 16; + case cgltf_type_invalid: + case cgltf_type_scalar: + default: + return 1; + } +} +static cgltf_size cgltf_component_size(cgltf_component_type component_type) { switch (component_type) { case cgltf_component_type_r_8: case cgltf_component_type_r_8u: - size = 1; - break; + return 1; case cgltf_component_type_r_16: case cgltf_component_type_r_16u: - size = 2; - break; + return 2; case cgltf_component_type_r_32u: case cgltf_component_type_r_32f: - size = 4; - break; + return 4; case cgltf_component_type_invalid: default: - size = 0; - break; + return 0; } +} - switch (type) +static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) +{ + cgltf_size component_size = cgltf_component_size(component_type); + if (type == cgltf_type_mat2 && component_size == 1) { - case cgltf_type_vec2: - size *= 2; - break; - case cgltf_type_vec3: - size *= 3; - break; - case cgltf_type_vec4: - size *= 4; - break; - case cgltf_type_mat2: - size *= 4; - break; - case cgltf_type_mat3: - size *= 9; - break; - case cgltf_type_mat4: - size *= 16; - break; - case cgltf_type_invalid: - case cgltf_type_scalar: - default: - size *= 1; - break; + return 8 * component_size; } - - return size; + else if (type == cgltf_type_mat3 && (component_size == 1 || component_size == 2)) + { + return 12 * component_size; + } + return component_size * cgltf_num_components(type); } static int cgltf_fixup_pointers(cgltf_data* out_data); @@ -3700,6 +4077,10 @@ static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens { i = cgltf_parse_json_animations(options, tokens, i + 1, json_chunk, out_data); } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_data->extras); + } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { ++i; @@ -3743,7 +4124,7 @@ static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens } else { - i = cgltf_skip_json(tokens, i+1); + i = cgltf_skip_json(tokens, i + 1); } if (i < 0) @@ -3752,6 +4133,14 @@ static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens } } } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsUsed") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_used, &out_data->extensions_used_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsRequired") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_required, &out_data->extensions_required_count); + } else { i = cgltf_skip_json(tokens, i + 1); @@ -3768,7 +4157,7 @@ static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data) { - jsmn_parser parser = { 0 }; + jsmn_parser parser = { 0, 0, 0 }; if (options->json_token_count == 0) { @@ -3782,7 +4171,7 @@ cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, options->json_token_count = token_count; } - jsmntok_t* tokens = (jsmntok_t*)options->memory_alloc(options->memory_user_data, sizeof(jsmntok_t) * options->json_token_count); + jsmntok_t* tokens = (jsmntok_t*)options->memory_alloc(options->memory_user_data, sizeof(jsmntok_t) * (options->json_token_count + 1)); if (!tokens) { @@ -3799,6 +4188,10 @@ cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, return cgltf_result_invalid_json; } + // this makes sure that we always have an UNDEFINED token at the end of the stream + // for invalid JSON inputs this makes sure we don't perform out of bound reads of token data + tokens[token_count].type = JSMN_UNDEFINED; + cgltf_data* data = (cgltf_data*)options->memory_alloc(options->memory_user_data, sizeof(cgltf_data)); if (!data) @@ -3827,6 +4220,9 @@ cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, return cgltf_result_invalid_gltf; } + data->json = (const char*)json_chunk; + data->json_size = size; + *out_data = data; return cgltf_result_success; diff --git a/src/external/dr_flac.h b/src/external/dr_flac.h index 13f42b2a8..250d0bd2e 100644 --- a/src/external/dr_flac.h +++ b/src/external/dr_flac.h @@ -1,6 +1,6 @@ /* FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_flac - v0.11.7 - 2019-05-06 +dr_flac - v0.11.10 - 2019-06-26 David Reid - mackron@gmail.com */ @@ -149,7 +149,7 @@ typedef drflac_uint32 drflac_bool32; #elif (defined(__GNUC__) && __GNUC__ >= 4) /* GCC 4 */ #define DRFLAC_DEPRECATED __attribute__((deprecated)) #elif defined(__has_feature) /* Clang */ - #if defined(__has_feature(attribute_deprecated)) + #if __has_feature(attribute_deprecated) #define DRFLAC_DEPRECATED __attribute__((deprecated)) #else #define DRFLAC_DEPRECATED @@ -1008,7 +1008,7 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse2() return DRFLAC_FALSE; #else int info[4]; - drflac_cpuid(info, 1); + drflac__cpuid(info, 1); return (info[3] & (1 << 26)) != 0; #endif #endif @@ -1033,7 +1033,7 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41() return DRFLAC_FALSE; #else int info[4]; - drflac_cpuid(info, 1); + drflac__cpuid(info, 1); return (info[2] & (1 << 19)) != 0; #endif #endif @@ -1141,21 +1141,43 @@ reference excess prior samples. /* CPU caps. */ static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; #ifndef DRFLAC_NO_CPUID +/* +I've had a bug report that Clang's ThreadSanitizer presents a warning in this function. Having reviewed this, this does +actually make sense. However, since CPU caps should never differ for a running process, I don't think the trade off of +complicating internal API's by passing around CPU caps versus just disabling the warnings is worthwhile. I'm therefore +just going to disable these warnings. +*/ +#if defined(__has_feature) + #if __has_feature(thread_sanitizer) + #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) + #else + #define DRFLAC_NO_THREAD_SANITIZE + #endif +#else + #define DRFLAC_NO_THREAD_SANITIZE +#endif static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; -static void drflac__init_cpu_caps() +DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps() { - int info[4] = {0}; + static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; - /* LZCNT */ - drflac__cpuid(info, 0x80000001); - drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; + if (!isCPUCapsInitialized) { + int info[4] = {0}; - /* SSE2 */ - drflac__gIsSSE2Supported = drflac_has_sse2(); + /* LZCNT */ + drflac__cpuid(info, 0x80000001); + drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; - /* SSE4.1 */ - drflac__gIsSSE41Supported = drflac_has_sse41(); + /* SSE2 */ + drflac__gIsSSE2Supported = drflac_has_sse2(); + + /* SSE4.1 */ + drflac__gIsSSE41Supported = drflac_has_sse41(); + + /* Initialized. */ + isCPUCapsInitialized = DRFLAC_TRUE; + } } #endif @@ -4897,9 +4919,9 @@ typedef struct static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) { blockHeader = drflac__be2host_32(blockHeader); - *isLastBlock = (blockHeader & (0x01 << 31)) >> 31; - *blockType = (blockHeader & (0x7F << 24)) >> 24; - *blockSize = (blockHeader & 0xFFFFFF); + *isLastBlock = (blockHeader & 0x80000000UL) >> 31; + *blockType = (blockHeader & 0x7F000000UL) >> 24; + *blockSize = (blockHeader & 0x00FFFFFFUL); } static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) @@ -6759,10 +6781,10 @@ drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesT case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { if (channelIndex == 0) { - decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample; + decodedSample = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample); } else { - int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample; - int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample; + int side = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample); + int left = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample); decodedSample = left - side; } } break; @@ -6770,11 +6792,11 @@ drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesT case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { if (channelIndex == 0) { - int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample; - int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample; + int side = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample); + int right = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample); decodedSample = side + right; } else { - decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample; + decodedSample = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample); } } break; @@ -6783,14 +6805,14 @@ drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesT int mid; int side; if (channelIndex == 0) { - mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample; - side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample; + mid = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample); + side = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample); mid = (((unsigned int)mid) << 1) | (side & 0x01); decodedSample = (mid + side) >> 1; } else { - mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample; - side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample; + mid = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample); + side = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample); mid = (((unsigned int)mid) << 1) | (side & 0x01); decodedSample = (mid - side) >> 1; @@ -6800,11 +6822,11 @@ drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesT case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample; + decodedSample = (int)((drflac_uint32)pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample); } break; } - decodedSample <<= (32 - pFlac->bitsPerSample); + decodedSample = (int)((drflac_uint32)decodedSample << (32 - pFlac->bitsPerSample)); if (bufferOut) { *bufferOut++ = decodedSample; @@ -6876,8 +6898,8 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; for (i = 0; i < alignedSampleCountPerChannel; ++i) { - int left = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); - int side = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + int left = (int)((drflac_uint32)pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample)); + int side = (int)((drflac_uint32)pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample)); int right = left - side; bufferOut[i*2+0] = left; @@ -6892,8 +6914,8 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; for (i = 0; i < alignedSampleCountPerChannel; ++i) { - int side = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); - int right = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + int side = (int)((drflac_uint32)pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample)); + int right = (int)((drflac_uint32)pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample)); int left = right + side; bufferOut[i*2+0] = left; @@ -6908,13 +6930,13 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; for (i = 0; i < alignedSampleCountPerChannel; ++i) { - int mid = pDecodedSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample; - int side = pDecodedSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample; + int mid = (int)((drflac_uint32)pDecodedSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample); + int side = (int)((drflac_uint32)pDecodedSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample); mid = (((drflac_uint32)mid) << 1) | (side & 0x01); - bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample); - bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample); + bufferOut[i*2+0] = (drflac_int32)((drflac_uint32)((mid + side) >> 1) << (unusedBitsPerSample)); + bufferOut[i*2+1] = (drflac_int32)((drflac_uint32)((mid - side) >> 1) << (unusedBitsPerSample)); } } break; @@ -6929,8 +6951,8 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; for (i = 0; i < alignedSampleCountPerChannel; ++i) { - bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); - bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + bufferOut[i*2+0] = (drflac_int32)((drflac_uint32)pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample)); + bufferOut[i*2+1] = (drflac_int32)((drflac_uint32)pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample)); } } else @@ -6940,7 +6962,7 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac for (i = 0; i < alignedSampleCountPerChannel; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample); + bufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample)); } } } @@ -8649,6 +8671,15 @@ drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, /* REVISION HISTORY ================ +v0.11.10 - 2019-06-26 + - Fix a compiler error. + +v0.11.9 - 2019-06-16 + - Silence some ThreadSanitizer warnings. + +v0.11.8 - 2019-05-21 + - Fix warnings. + v0.11.7 - 2019-05-06 - C89 fixes. diff --git a/src/external/dr_mp3.h b/src/external/dr_mp3.h index 26aeec56f..0ecd0d3f6 100644 --- a/src/external/dr_mp3.h +++ b/src/external/dr_mp3.h @@ -1,6 +1,6 @@ /* MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_mp3 - v0.4.4 - 2019-05-06 +dr_mp3 - v0.4.7 - 2019-07-28 David Reid - mackron@gmail.com @@ -1143,41 +1143,72 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g int sfb_cnt = gr_info->region_count[ireg++]; const drmp3_int16 *codebook = tabs + tabindex[tab_num]; int linbits = g_linbits[tab_num]; - do + if (linbits) { - np = *sfb++ / 2; - pairs_to_decode = DRMP3_MIN(big_val_cnt, np); - one = *scf++; do { - int j, w = 5; - int leaf = codebook[DRMP3_PEEK_BITS(w)]; - while (leaf < 0) + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do { - DRMP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; - } - DRMP3_FLUSH_BITS(leaf >> 8); - - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - if (lsb == 15 && linbits) + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) { - lsb += DRMP3_PEEK_BITS(linbits); - DRMP3_FLUSH_BITS(linbits); - DRMP3_CHECK_BITS; - *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); - } else - { - *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; } - DRMP3_FLUSH_BITS(lsb ? 1 : 0); - } - DRMP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15) + { + lsb += DRMP3_PEEK_BITS(linbits); + DRMP3_FLUSH_BITS(linbits); + DRMP3_CHECK_BITS; + *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); + } else + { + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + } + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } else + { + do + { + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) + { + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + } + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } } for (np = 1 - big_val_cnt;; dst += 4) @@ -2133,14 +2164,14 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples) 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); + drmp3_f4 scale = DRMP3_VSET(32768.0f); + drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale); + drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), 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))); + drmp3_f4 s16max = DRMP3_VSET( 32767.0f); + drmp3_f4 s16min = DRMP3_VSET(-32768.0f); + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); 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); @@ -3779,6 +3810,15 @@ DIFFERENCES BETWEEN minimp3 AND dr_mp3 /* REVISION HISTORY ================ +v0.4.7 - 2019-07-28 + - Fix a compiler error. + +v0.4.6 - 2019-06-14 + - Fix a compiler error. + +v0.4.5 - 2019-06-06 + - Bring up to date with minimp3. + v0.4.4 - 2019-05-06 - Fixes to the VC6 build. diff --git a/src/external/dr_wav.h b/src/external/dr_wav.h index b2395bfb1..7c48ac400 100644 --- a/src/external/dr_wav.h +++ b/src/external/dr_wav.h @@ -1,6 +1,6 @@ /* WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_wav - v0.9.1 - 2019-05-05 +dr_wav - v0.9.2 - 2019-05-21 David Reid - mackron@gmail.com */ @@ -2040,7 +2040,7 @@ drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc drwav_uint32 drwav_riff_chunk_size_riff(drwav_uint64 dataChunkSize) { - if (dataChunkSize <= (0xFFFFFFFF - 36)) { + if (dataChunkSize <= (0xFFFFFFFFUL - 36)) { return 36 + (drwav_uint32)dataChunkSize; } else { return 0xFFFFFFFF; @@ -2049,10 +2049,10 @@ drwav_uint32 drwav_riff_chunk_size_riff(drwav_uint64 dataChunkSize) drwav_uint32 drwav_data_chunk_size_riff(drwav_uint64 dataChunkSize) { - if (dataChunkSize <= 0xFFFFFFFF) { + if (dataChunkSize <= 0xFFFFFFFFUL) { return (drwav_uint32)dataChunkSize; } else { - return 0xFFFFFFFF; + return 0xFFFFFFFFUL; } } @@ -2121,7 +2121,7 @@ drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pF so for the sake of simplicity I'm not doing any validation for that. */ if (pFormat->container == drwav_container_riff) { - if (initialDataChunkSize > (0xFFFFFFFF - 36)) { + if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { return DRWAV_FALSE; /* Not enough room to store every sample. */ } } @@ -3195,8 +3195,8 @@ void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCou size_t i; for (i = 0; i < sampleCount; ++i) { int x = pIn[i]; - r = x - 128; - r = r << 8; + r = x << 8; + r = r - 32768; pOut[i] = (short)r; } } @@ -4675,6 +4675,9 @@ void drwav_free(void* pDataReturnedByOpenAndRead) /* REVISION HISTORY ================ +v0.9.2 - 2019-05-21 + - Fix warnings. + v0.9.1 - 2019-05-05 - Add support for C89. - Change license to choice of public domain or MIT-0. diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index f51a1b681..7eb4beeeb 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,8 +1,10 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio (formerly mini_al) - v0.9.4 - 2019-05-06 +miniaudio (formerly mini_al) - v0.9.6 - 2019-08-04 David Reid - davidreidsoftware@gmail.com + +https://github.com/dr-soft/miniaudio */ /* @@ -304,7 +306,7 @@ UWP Web Audio / Emscripten ---------------------- -- The first time a context is initialized it will create a global object called "mal" whose primary purpose is to act +- The first time a context is initialized it will create a global object called "miniaudio" whose primary purpose is to act as a factory for device objects. - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated. - Google is implementing a policy in their browsers that prevent automatic media output without first receiving some kind @@ -395,7 +397,7 @@ OPTIONS MA_LOG_LEVEL_WARNING MA_LOG_LEVEL_ERROR -#define MA_DEBUT_OUTPUT +#define MA_DEBUG_OUTPUT Enable printf() debug output. #define MA_COINIT_VALUE @@ -542,10 +544,10 @@ extern "C" { #endif #endif -typedef ma_uint8 ma_bool8; -typedef ma_uint32 ma_bool32; -#define MA_TRUE 1 -#define MA_FALSE 0 +typedef ma_uint8 ma_bool8; +typedef ma_uint32 ma_bool32; +#define MA_TRUE 1 +#define MA_FALSE 0 typedef void* ma_handle; typedef void* ma_ptr; @@ -944,7 +946,7 @@ MA_ALIGNED_STRUCT(MA_SIMD_ALIGNMENT) ma_src }; typedef struct ma_pcm_converter ma_pcm_converter; -typedef ma_uint32 (* ma_pcm_converter_read_proc)(ma_pcm_converter* pDSP, void* pSamplesOut, ma_uint32 frameCount, void* pUserData); +typedef ma_uint32 (* ma_pcm_converter_read_proc)(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData); typedef struct { @@ -987,7 +989,7 @@ MA_ALIGNED_STRUCT(MA_SIMD_ALIGNMENT) ma_pcm_converter ma_bool32 isChannelRoutingRequired : 1; ma_bool32 isSRCRequired : 1; ma_bool32 isChannelRoutingAtStart : 1; - ma_bool32 isPassthrough : 1; /* <-- Will be set to true when the DSP pipeline is an optimized passthrough. */ + ma_bool32 isPassthrough : 1; /* <-- Will be set to true when the conversion pipeline is an optimized passthrough. */ }; @@ -1342,7 +1344,7 @@ determine the required size of the output buffer. A return value of 0 indicates an error. -This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead. +This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_pcm_converter APIs instead. */ ma_uint64 ma_convert_frames(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_uint64 frameCount); ma_uint64 ma_convert_frames_ex(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint64 frameCount); @@ -1770,7 +1772,9 @@ pInput is a pointer to a buffer containing input data from the device. This will null for a playback device. frameCount is the number of PCM frames to process. If an output buffer is provided (pOutput is not null), applications should write out -to the entire output buffer. +to the entire output buffer. Note that frameCount will not necessarily be exactly what you asked for when you initialized the deviced. +The bufferSizeInFrames and bufferSizeInMilliseconds members of the device config are just hints, and are not necessarily exactly what +you'll get. Do _not_ call any miniaudio APIs from the callback. Attempting the stop the device can result in a deadlock. The proper way to stop the device is to call ma_device_stop() from a different thread, normally the main application thread. @@ -2691,8 +2695,8 @@ Retrieves information about a device with the given ID. Do _not_ call this from within the ma_context_enumerate_devices() callback. -It's possible for a device to have different information and capabilities depending on wether or -not it's opened in shared or exclusive mode. For example, in shared mode, WASAPI always uses +It's possible for a device to have different information and capabilities depending on whether +or not it's opened in shared or exclusive mode. For example, in shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail @@ -2723,7 +2727,8 @@ initialization of other devices. The device's configuration is controlled with pConfig. This allows you to configure the sample format, channel count, sample rate, etc. Before calling ma_device_init(), you will need to initialize a ma_device_config object using ma_device_config_init(). You must set the callback in -the device config. +the device config. Once initialized, the device's config is immutable. If you need to change the +config you will need to initialize a new device. Passing in 0 to any property in pConfig will force the use of a default value. In the case of sample format, channel count, sample rate and channel map it will default to the values used by @@ -2787,6 +2792,8 @@ Uninitializes a device. This will explicitly stop the device. You do not need to call ma_device_stop() beforehand, but it's harmless if you do. +Do not call this in any callback. + Return Value: MA_SUCCESS if successful; any other error code otherwise. @@ -2815,6 +2822,8 @@ to be done _before_ the device begins playback. This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. +Do not call this in any callback. + Return Value: MA_SUCCESS if successful; any other error code otherwise. @@ -2836,6 +2845,8 @@ the resuming it with ma_device_start() (which you might do when your program los in a situation where those samples are never output to the speakers or received from the microphone which can in turn result in de-syncs. +Do not call this in any callback. + Return Value: MA_SUCCESS if successful; any other error code otherwise. @@ -2990,10 +3001,11 @@ typedef enum ma_seek_origin_current } ma_seek_origin; -typedef size_t (* ma_decoder_read_proc) (ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */ -typedef ma_bool32 (* ma_decoder_seek_proc) (ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin); -typedef ma_result (* ma_decoder_seek_to_pcm_frame_proc)(ma_decoder* pDecoder, ma_uint64 frameIndex); -typedef ma_result (* ma_decoder_uninit_proc) (ma_decoder* pDecoder); +typedef size_t (* ma_decoder_read_proc) (ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */ +typedef ma_bool32 (* ma_decoder_seek_proc) (ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin); +typedef ma_result (* ma_decoder_seek_to_pcm_frame_proc) (ma_decoder* pDecoder, ma_uint64 frameIndex); +typedef ma_result (* ma_decoder_uninit_proc) (ma_decoder* pDecoder); +typedef ma_uint64 (* ma_decoder_get_length_in_pcm_frames_proc)(ma_decoder* pDecoder); typedef struct { @@ -3027,6 +3039,7 @@ struct ma_decoder ma_pcm_converter dsp; /* <-- Format conversion is achieved by running frames through this. */ ma_decoder_seek_to_pcm_frame_proc onSeekToPCMFrame; ma_decoder_uninit_proc onUninit; + ma_decoder_get_length_in_pcm_frames_proc onGetLengthInPCMFrames; void* pInternalDecoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */ struct { @@ -3055,10 +3068,33 @@ ma_result ma_decoder_init_memory_raw(const void* pData, size_t dataSize, const m #ifndef MA_NO_STDIO ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); + +ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); #endif ma_result ma_decoder_uninit(ma_decoder* pDecoder); +/* +Retrieves the length of the decoder in PCM frames. + +Do not call this on streams of an undefined length, such as internet radio. + +If the length is unknown or an error occurs, 0 will be returned. + +This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio +uses internally. + +This will run in linear time for MP3 decoders. Do not call this in time critical scenarios. +*/ +ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder); + ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount); ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); @@ -3192,7 +3228,7 @@ typedef struct tagBITMAPINFOHEADER { #endif #else -#include /* For malloc()/free() */ +#include /* For malloc(), free(), wcstombs(). */ #include /* For memset() */ #endif @@ -3345,7 +3381,7 @@ typedef struct tagBITMAPINFOHEADER { #define MA_NO_CPUID #endif - #if _MSC_VER >= 1600 + #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) static MA_INLINE unsigned __int64 ma_xgetbv(int reg) { return _xgetbv(reg); @@ -3827,6 +3863,52 @@ int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) return 0; } +int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +{ + char* dstorig; + + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + return 22; + } + + dstorig = dst; + + while (dstSizeInBytes > 0 && dst[0] != '\0') { + dst += 1; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes == 0) { + return 22; /* Unterminated. */ + } + + + if (count == ((size_t)-1)) { /* _TRUNCATE */ + count = dstSizeInBytes - 1; + } + + while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { + *dst++ = *src++; + dstSizeInBytes -= 1; + count -= 1; + } + + if (dstSizeInBytes > 0) { + dst[0] = '\0'; + } else { + dstorig[0] = '\0'; + return 34; + } + + return 0; +} + int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) { int sign; @@ -3919,6 +4001,23 @@ int ma_strcmp(const char* str1, const char* str2) return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; } +int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) +{ + int result; + + result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); + if (result != 0) { + return result; + } + + result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); + if (result != 0) { + return result; + } + + return result; +} + char* ma_copy_string(const char* src) { size_t sz = strlen(src)+1; @@ -4383,6 +4482,63 @@ typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, L #define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" +const char* ma_log_level_to_string(ma_uint32 logLevel) +{ + switch (logLevel) + { + case MA_LOG_LEVEL_VERBOSE: return ""; + case MA_LOG_LEVEL_INFO: return "INFO"; + case MA_LOG_LEVEL_WARNING: return "WARNING"; + case MA_LOG_LEVEL_ERROR: return "ERROR"; + default: return "ERROR"; + } +} + +/* Posts a log message. */ +void ma_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message) +{ + if (pContext == NULL) { + return; + } + +#if defined(MA_LOG_LEVEL) + if (logLevel <= MA_LOG_LEVEL) { + ma_log_proc onLog; + + #if defined(MA_DEBUG_OUTPUT) + if (logLevel <= MA_LOG_LEVEL) { + printf("%s: %s\n", ma_log_level_to_string(logLevel), message); + } + #endif + + onLog = pContext->logCallback; + if (onLog) { + onLog(pContext, pDevice, logLevel, message); + } + } +#endif +} + +/* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */ +ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode) +{ + /* Derive the context from the device if necessary. */ + if (pContext == NULL) { + if (pDevice != NULL) { + pContext = pDevice->pContext; + } + } + + ma_log(pContext, pDevice, logLevel, message); + return resultCode; +} + +ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode) +{ + return ma_context_post_error(NULL, pDevice, logLevel, message, resultCode); +} + + /******************************************************************************* Timing @@ -4499,48 +4655,96 @@ double ma_timer_get_time_in_seconds(ma_timer* pTimer) Dynamic Linking *******************************************************************************/ -ma_handle ma_dlopen(const char* filename) +ma_handle ma_dlopen(ma_context* pContext, const char* filename) { + ma_handle handle; + +#if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE + if (pContext != NULL) { + char message[256]; + ma_strappend(message, sizeof(message), "Loading library: ", filename); + ma_log(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message); + } +#endif + #ifdef _WIN32 #ifdef MA_WIN32_DESKTOP - return (ma_handle)LoadLibraryA(filename); + handle = (ma_handle)LoadLibraryA(filename); #else /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ WCHAR filenameW[4096]; if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - return NULL; + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); } - - return (ma_handle)LoadPackagedLibrary(filenameW, 0); #endif #else - return (ma_handle)dlopen(filename, RTLD_NOW); + handle = (ma_handle)dlopen(filename, RTLD_NOW); #endif + + /* + I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority + backend is a deliberate design choice. Instead I'm logging it as an informational message. + */ +#if MA_LOG_LEVEL >= MA_LOG_LEVEL_INFO + if (handle == NULL) { + char message[256]; + ma_strappend(message, sizeof(message), "Failed to load library: ", filename); + ma_log(pContext, NULL, MA_LOG_LEVEL_INFO, message); + } +#endif + + (void)pContext; /* It's possible for pContext to be unused. */ + return handle; } -void ma_dlclose(ma_handle handle) +void ma_dlclose(ma_context* pContext, ma_handle handle) { #ifdef _WIN32 FreeLibrary((HMODULE)handle); #else dlclose((void*)handle); #endif + + (void)pContext; } -ma_proc ma_dlsym(ma_handle handle, const char* symbol) +ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol) { + ma_proc proc; + +#if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE + if (pContext != NULL) { + char message[256]; + ma_strappend(message, sizeof(message), "Loading symbol: ", symbol); + ma_log(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message); + } +#endif + #ifdef _WIN32 - return (ma_proc)GetProcAddress((HMODULE)handle, symbol); + proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); #else #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" #endif - return (ma_proc)dlsym((void*)handle, symbol); + proc = (ma_proc)dlsym((void*)handle, symbol); #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) #pragma GCC diagnostic pop #endif #endif + +#if MA_LOG_LEVEL >= MA_LOG_LEVEL_WARNING + if (handle == NULL) { + char message[256]; + ma_strappend(message, sizeof(message), "Failed to load symbol: ", symbol); + ma_log(pContext, NULL, MA_LOG_LEVEL_WARNING, message); + } +#endif + + (void)pContext; /* It's possible for pContext to be unused. */ + return proc; } @@ -4560,7 +4764,7 @@ int ma_thread_priority_to_win32(ma_thread_priority priority) case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; - default: return ma_thread_priority_normal; + default: return THREAD_PRIORITY_NORMAL; } } @@ -5102,63 +5306,6 @@ void ma_zero_pcm_frames(void* p, ma_uint32 frameCount, ma_format format, ma_uint } -const char* ma_log_level_to_string(ma_uint32 logLevel) -{ - switch (logLevel) - { - case MA_LOG_LEVEL_VERBOSE: return ""; - case MA_LOG_LEVEL_INFO: return "INFO"; - case MA_LOG_LEVEL_WARNING: return "WARNING"; - case MA_LOG_LEVEL_ERROR: return "ERROR"; - default: return "ERROR"; - } -} - -/* Posts a log message. */ -void ma_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message) -{ - if (pContext == NULL) { - return; - } - -#if defined(MA_LOG_LEVEL) - if (logLevel <= MA_LOG_LEVEL) { - ma_log_proc onLog; - - #if defined(MA_DEBUG_OUTPUT) - if (logLevel <= MA_LOG_LEVEL) { - printf("%s: %s\n", ma_log_level_to_string(logLevel), message); - } - #endif - - onLog = pContext->logCallback; - if (onLog) { - onLog(pContext, pDevice, logLevel, message); - } - } -#endif -} - -/* Posts an error. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */ -ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode) -{ - /* Derive the context from the device if necessary. */ - if (pContext == NULL) { - if (pDevice != NULL) { - pContext = pDevice->pContext; - } - } - - ma_log(pContext, pDevice, logLevel, message); - return resultCode; -} - -ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode) -{ - return ma_context_post_error(NULL, pDevice, logLevel, message, resultCode); -} - - /* The callback for reading from the client -> DSP -> device. */ ma_uint32 ma_device__on_read_from_client(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData) @@ -5421,13 +5568,10 @@ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_uint32 newStat /* A helper for getting the state of the device. */ static MA_INLINE ma_uint32 ma_device__get_state(ma_device* pDevice) { - return pDevice->state; -} + ma_uint32 state; + ma_atomic_exchange_32(&state, pDevice->state); -/* A helper for determining whether or not the device is running in async mode. */ -static MA_INLINE ma_bool32 ma_device__is_async(ma_device* pDevice) -{ - return pDevice->onData != NULL; + return state; } @@ -7116,6 +7260,8 @@ ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pCont ma_IPropertyStore_Release(pProperties); return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve device format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); } + + ma_IPropertyStore_Release(pProperties); } else { return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to open property store for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); } @@ -7578,6 +7724,8 @@ ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type d clientProperties.eCategory = MA_AudioCategory_Other; ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); } + + pAudioClient2->lpVtbl->Release(pAudioClient2); } @@ -7688,7 +7836,7 @@ ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type d } if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { - UINT bufferSizeInFrames; + ma_uint32 bufferSizeInFrames; hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); if (SUCCEEDED(hr)) { bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5); @@ -8744,7 +8892,6 @@ ma_result ma_context_init__wasapi(const ma_context_config* pConfig, ma_context* ma_assert(pContext != NULL); - (void)pContext; (void)pConfig; #ifdef MA_WIN32_DESKTOP @@ -8760,15 +8907,15 @@ ma_result ma_context_init__wasapi(const ma_context_config* pConfig, ma_context* ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; ma_PFNVerSetConditionMask _VerSetConditionMask; - kernel32DLL = ma_dlopen("kernel32.dll"); + kernel32DLL = ma_dlopen(pContext, "kernel32.dll"); if (kernel32DLL == NULL) { return MA_NO_BACKEND; } - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW)ma_dlsym(kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(kernel32DLL, "VerSetConditionMask"); + _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW)ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW"); + _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask"); if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(kernel32DLL); + ma_dlclose(pContext, kernel32DLL); return MA_NO_BACKEND; } @@ -8783,7 +8930,7 @@ ma_result ma_context_init__wasapi(const ma_context_config* pConfig, ma_context* result = MA_NO_BACKEND; } - ma_dlclose(kernel32DLL); + ma_dlclose(pContext, kernel32DLL); } #endif @@ -10436,7 +10583,7 @@ ma_result ma_context_uninit__dsound(ma_context* pContext) ma_assert(pContext != NULL); ma_assert(pContext->backend == ma_backend_dsound); - ma_dlclose(pContext->dsound.hDSoundDLL); + ma_dlclose(pContext, pContext->dsound.hDSoundDLL); return MA_SUCCESS; } @@ -10447,15 +10594,15 @@ ma_result ma_context_init__dsound(const ma_context_config* pConfig, ma_context* (void)pConfig; - pContext->dsound.hDSoundDLL = ma_dlopen("dsound.dll"); + pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll"); if (pContext->dsound.hDSoundDLL == NULL) { return MA_API_NOT_FOUND; } - pContext->dsound.DirectSoundCreate = ma_dlsym(pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); + pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate"); + pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); + pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); + pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); pContext->onUninit = ma_context_uninit__dsound; pContext->onDeviceIDEqual = ma_context_is_device_id_equal__dsound; @@ -11454,7 +11601,7 @@ ma_result ma_context_uninit__winmm(ma_context* pContext) ma_assert(pContext != NULL); ma_assert(pContext->backend == ma_backend_winmm); - ma_dlclose(pContext->winmm.hWinMM); + ma_dlclose(pContext, pContext->winmm.hWinMM); return MA_SUCCESS; } @@ -11464,28 +11611,28 @@ ma_result ma_context_init__winmm(const ma_context_config* pConfig, ma_context* p (void)pConfig; - pContext->winmm.hWinMM = ma_dlopen("winmm.dll"); + pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll"); if (pContext->winmm.hWinMM == NULL) { return MA_NO_BACKEND; } - pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(pContext->winmm.hWinMM, "waveInReset"); + pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs"); + pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA"); + pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen"); + pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose"); + pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader"); + pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader"); + pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite"); + pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset"); + pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs"); + pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA"); + pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen"); + pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose"); + pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader"); + pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader"); + pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer"); + pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart"); + pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset"); pContext->onUninit = ma_context_uninit__winmm; pContext->onDeviceIDEqual = ma_context_is_device_id_equal__winmm; @@ -13261,14 +13408,29 @@ ma_result ma_device_stop__alsa(ma_device* pDevice) if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + + /* We need to prepare the device again, otherwise we won't be able to restart the device. */ + if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { + #ifdef MA_DEBUG_OUTPUT + printf("[ALSA] Failed to prepare capture device after stopping.\n"); + #endif + } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { /* Using drain instead of drop because ma_device_stop() is defined such that pending frames are processed before returning. */ ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + + /* We need to prepare the device again, otherwise we won't be able to restart the device. */ + if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { + #ifdef MA_DEBUG_OUTPUT + printf("[ALSA] Failed to prepare playback device after stopping.\n"); + #endif + } } + return MA_SUCCESS; } @@ -13415,7 +13577,7 @@ ma_result ma_context_uninit__alsa(ma_context* pContext) ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext->alsa.asoundSO); + ma_dlclose(pContext, pContext->alsa.asoundSO); #endif ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); @@ -13433,7 +13595,7 @@ ma_result ma_context_init__alsa(const ma_context_config* pConfig, ma_context* pC size_t i; for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(libasoundNames[i]); + pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]); if (pContext->alsa.asoundSO != NULL) { break; } @@ -13446,61 +13608,61 @@ ma_result ma_context_init__alsa(const ma_context_config* pConfig, ma_context* pC return MA_NO_BACKEND; } - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext->alsa.asoundSO, "snd_config_update_free_global"); + pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open"); + pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close"); + pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); + pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); + pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); + pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); + pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); + pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); + pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); + pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); + pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); + pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); + pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); + pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); + pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); + pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); + pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); + pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); + pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); + pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); + pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params"); + pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); + pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); + pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); + pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); + pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); + pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params"); + pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); + pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); + pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap"); + pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state"); + pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare"); + pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start"); + pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop"); + pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain"); + pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint"); + pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint"); + pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index"); + pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint"); + pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); + pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); + pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover"); + pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi"); + pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei"); + pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail"); + pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update"); + pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait"); + pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info"); + pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); + pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name"); + pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global"); #else /* The system below is just for type safety. */ ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; @@ -14830,7 +14992,7 @@ ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 bufferSizeInFrames, ma attr.maxlength = bufferSizeInFrames * ma_get_bytes_per_sample(ma_format_from_pulse(ss->format)) * ss->channels; attr.tlength = attr.maxlength / periods; attr.prebuf = (ma_uint32)-1; - attr.minreq = attr.maxlength / periods; + attr.minreq = (ma_uint32)-1; attr.fragsize = attr.maxlength / periods; return attr; @@ -15481,7 +15643,7 @@ ma_result ma_context_uninit__pulse(ma_context* pContext) pContext->pulse.pApplicationName = NULL; #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext->pulse.pulseSO); + ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return MA_SUCCESS; @@ -15497,7 +15659,7 @@ ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* p size_t i; for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(libpulseNames[i]); + pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]); if (pContext->pulse.pulseSO != NULL) { break; } @@ -15507,50 +15669,50 @@ ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* p return MA_NO_BACKEND; } - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext->pulse.pulseSO, "pa_stream_readable_size"); + pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new"); + pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free"); + pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api"); + pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate"); + pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup"); + pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new"); + pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref"); + pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect"); + pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect"); + pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback"); + pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state"); + pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); + pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list"); + pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); + pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); + pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref"); + pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state"); + pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend"); + pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid"); + pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible"); + pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new"); + pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref"); + pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback"); + pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record"); + pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect"); + pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state"); + pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); + pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map"); + pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); + pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); + pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name"); + pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback"); + pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback"); + pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush"); + pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain"); + pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked"); + pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork"); + pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger"); + pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write"); + pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write"); + pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek"); + pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop"); + pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size"); + pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size"); #else /* This strange assignment system is just for type safety. */ ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; @@ -15678,7 +15840,7 @@ ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* p ma_free(pContext->pulse.pServerName); ma_free(pContext->pulse.pApplicationName); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext->pulse.pulseSO); + ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return MA_NO_BACKEND; } @@ -15689,7 +15851,7 @@ ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* p ma_free(pContext->pulse.pApplicationName); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext->pulse.pulseSO); + ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return MA_NO_BACKEND; } @@ -15700,7 +15862,7 @@ ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* p ma_free(pContext->pulse.pApplicationName); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext->pulse.pulseSO); + ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return MA_NO_BACKEND; } @@ -15712,7 +15874,7 @@ ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* p ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext->pulse.pulseSO); + ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return MA_NO_BACKEND; } @@ -16253,7 +16415,7 @@ ma_result ma_context_uninit__jack(ma_context* pContext) pContext->jack.pClientName = NULL; #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext->jack.jackSO); + ma_dlclose(pContext, pContext->jack.jackSO); #endif return MA_SUCCESS; @@ -16273,7 +16435,7 @@ ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_context* pC size_t i; for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(libjackNames[i]); + pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]); if (pContext->jack.jackSO != NULL) { break; } @@ -16283,22 +16445,22 @@ ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_context* pC return MA_NO_BACKEND; } - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext->jack.jackSO, "jack_free"); + pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open"); + pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close"); + pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size"); + pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback"); + pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback"); + pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown"); + pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate"); + pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size"); + pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports"); + pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate"); + pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate"); + pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect"); + pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register"); + pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name"); + pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer"); + pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free"); #else /* This strange assignment system is here just to ensure type safety of miniaudio's function pointer @@ -16365,7 +16527,7 @@ ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_context* pC if (result != MA_SUCCESS) { ma_free(pContext->jack.pClientName); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext->jack.jackSO); + ma_dlclose(pContext, pContext->jack.jackSO); #endif return MA_NO_BACKEND; } @@ -18793,9 +18955,9 @@ ma_result ma_context_uninit__coreaudio(ma_context* pContext) ma_assert(pContext->backend == ma_backend_coreaudio); #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); + ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); #endif (void)pContext; @@ -18824,24 +18986,24 @@ ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma_contex #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen("CoreFoundation.framework/CoreFoundation"); + pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MA_API_NOT_FOUND; } - pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); + pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.hCoreAudio = ma_dlopen("CoreAudio.framework/CoreAudio"); + pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio"); if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); + pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); + pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); + pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); + pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); /* It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still @@ -18849,35 +19011,35 @@ ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma_contex The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to AudioToolbox. */ - pContext->coreaudio.hAudioUnit = ma_dlopen("AudioUnit.framework/AudioUnit"); + pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit"); if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - if (ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { + if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen("AudioToolbox.framework/AudioToolbox"); + ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); + pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox"); if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } } - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitRender"); + pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); + pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); + pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); + pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); + pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); + pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); + pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); + pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); + pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); + pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); + pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender"); #else pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; @@ -18928,9 +19090,9 @@ ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma_contex pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); if (pContext->coreaudio.component == NULL) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); + ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); #endif return MA_FAILED_TO_INIT_BACKEND; } @@ -19734,7 +19896,7 @@ ma_result ma_context_init__sndio(const ma_context_config* pConfig, ma_context* p size_t i; for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(libsndioNames[i]); + pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]); if (pContext->sndio.sndioSO != NULL) { break; } @@ -19744,16 +19906,16 @@ ma_result ma_context_init__sndio(const ma_context_config* pConfig, ma_context* p return MA_NO_BACKEND; } - pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext->sndio.sndioSO, "sio_initpar"); + pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open"); + pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close"); + pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar"); + pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar"); + pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap"); + pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write"); + pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read"); + pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start"); + pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop"); + pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar"); #else pContext->sndio.sio_open = sio_open; pContext->sndio.sio_close = sio_close; @@ -21625,7 +21787,7 @@ ma_result ma_context_uninit__aaudio(ma_context* pContext) ma_assert(pContext != NULL); ma_assert(pContext->backend == ma_backend_aaudio); - ma_dlclose(pContext->aaudio.hAAudio); + ma_dlclose(pContext, pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; return MA_SUCCESS; @@ -21639,7 +21801,7 @@ ma_result ma_context_init__aaudio(const ma_context_config* pConfig, ma_context* size_t i; for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(libNames[i]); + pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]); if (pContext->aaudio.hAAudio != NULL) { break; } @@ -21649,30 +21811,30 @@ ma_result ma_context_init__aaudio(const ma_context_config* pConfig, ma_context* return MA_FAILED_TO_INIT_BACKEND; } - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext->aaudio.hAAudio, "AAudioStream_requestStop"); + pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); + pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); + pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); + pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); + pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); + pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); + pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); + pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); + pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); + pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); + pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); + pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close"); + pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState"); + pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); + pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat"); + pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); + pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); + pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); + pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); + pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); + pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart"); + pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop"); pContext->isBackendAsynchronous = MA_TRUE; @@ -22522,7 +22684,7 @@ ma_result ma_device_start__opensl(ma_device* pDevice) periodSizeInBytes = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods) * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.", MA_FAILED_TO_START_BACKEND_DEVICE); @@ -23594,9 +23756,9 @@ ma_bool32 ma_device__is_initialized(ma_device* pDevice) ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { ma_CoUninitialize(pContext); - ma_dlclose(pContext->win32.hUser32DLL); - ma_dlclose(pContext->win32.hOle32DLL); - ma_dlclose(pContext->win32.hAdvapi32DLL); + ma_dlclose(pContext, pContext->win32.hUser32DLL); + ma_dlclose(pContext, pContext->win32.hOle32DLL); + ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); return MA_SUCCESS; } @@ -23605,38 +23767,38 @@ ma_result ma_context_init_backend_apis__win32(ma_context* pContext) { #ifdef MA_WIN32_DESKTOP /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen("ole32.dll"); + pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll"); if (pContext->win32.hOle32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext->win32.hOle32DLL, "StringFromGUID2"); + pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx"); + pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize"); + pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance"); + pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree"); + pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear"); + pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2"); /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen("user32.dll"); + pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); if (pContext->win32.hUser32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext->win32.hUser32DLL, "GetDesktopWindow"); + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen("advapi32.dll"); + pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); if (pContext->win32.hAdvapi32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #endif ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); @@ -23646,7 +23808,7 @@ ma_result ma_context_init_backend_apis__win32(ma_context* pContext) ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) { #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING) - ma_dlclose(pContext->posix.pthreadSO); + ma_dlclose(pContext, pContext->posix.pthreadSO); #else (void)pContext; #endif @@ -23666,7 +23828,7 @@ ma_result ma_context_init_backend_apis__nix(ma_context* pContext) size_t i; for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) { - pContext->posix.pthreadSO = ma_dlopen(libpthreadFileNames[i]); + pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]); if (pContext->posix.pthreadSO != NULL) { break; } @@ -23676,21 +23838,21 @@ ma_result ma_context_init_backend_apis__nix(ma_context* pContext) return MA_FAILED_TO_INIT_BACKEND; } - pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_create"); - pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_join"); - pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_mutex_init"); - pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_mutex_destroy"); - pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_mutex_lock"); - pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_mutex_unlock"); - pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_cond_init"); - pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_cond_destroy"); - pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_cond_wait"); - pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_cond_signal"); - pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_attr_init"); - pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_attr_destroy"); - pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_attr_setschedpolicy"); - pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_attr_getschedparam"); - pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext->posix.pthreadSO, "pthread_attr_setschedparam"); + pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create"); + pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join"); + pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init"); + pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy"); + pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock"); + pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock"); + pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init"); + pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy"); + pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait"); + pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal"); + pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init"); + pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy"); + pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy"); + pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam"); + pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam"); #else pContext->posix.pthread_create = (ma_proc)pthread_create; pContext->posix.pthread_join = (ma_proc)pthread_join; @@ -24431,13 +24593,8 @@ ma_result ma_device_start(ma_device* pDevice) return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED); } - /* - Starting the device doesn't do anything in synchronous mode because in that case it's started automatically with - ma_device_write() and ma_device_read(). It's best to return an error so that the application can be aware that - it's not doing it right. - */ - if (!ma_device__is_async(pDevice)) { - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called in synchronous mode. This should only be used in asynchronous/callback mode.", MA_DEVICE_NOT_INITIALIZED); + if (ma_device__get_state(pDevice) == MA_STATE_STARTED) { + return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_start() called when the device is already started.", MA_INVALID_OPERATION); /* Already started. Returning an error to let the application know because it probably means they're doing something wrong. */ } result = MA_ERROR; @@ -24486,14 +24643,8 @@ ma_result ma_device_stop(ma_device* pDevice) return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED); } - /* - Stopping is slightly different for synchronous mode. In this case it just tells the driver to stop the internal processing of the device. Also, - stopping in synchronous mode does not require state checking. - */ - if (!ma_device__is_async(pDevice)) { - if (pDevice->pContext->onDeviceStop) { - return pDevice->pContext->onDeviceStop(pDevice); - } + if (ma_device__get_state(pDevice) == MA_STATE_STOPPED) { + return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_stop() called when the device is already stopped.", MA_INVALID_OPERATION); /* Already stopped. Returning an error to let the application know because it probably means they're doing something wrong. */ } result = MA_ERROR; @@ -30470,7 +30621,7 @@ ma_uint64 ma_convert_frames_ex(void* pOut, ma_format formatOut, ma_uint32 channe */ totalFramesRead = ma_pcm_converter_read(&converter, pOut, frameCountOut); if (totalFramesRead < frameCountOut) { - ma_uint32 bpf = ma_get_bytes_per_frame(formatIn, channelsIn); + ma_uint32 bpfOut = ma_get_bytes_per_frame(formatOut, channelsOut); data.isFeedingZeros = MA_TRUE; data.totalFrameCount = ((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF; /* C89 does not support 64-bit constants so need to instead construct it like this. Annoying... */ /*data.totalFrameCount = 0xFFFFFFFFFFFFFFFF;*/ @@ -30483,7 +30634,7 @@ ma_uint64 ma_convert_frames_ex(void* pOut, ma_format formatOut, ma_uint32 channe framesToRead = (frameCountOut - totalFramesRead); ma_assert(framesToRead > 0); - framesJustRead = ma_pcm_converter_read(&converter, ma_offset_ptr(pOut, totalFramesRead * bpf), framesToRead); + framesJustRead = ma_pcm_converter_read(&converter, ma_offset_ptr(pOut, totalFramesRead * bpfOut), framesToRead); totalFramesRead += framesJustRead; if (framesJustRead < framesToRead) { @@ -30493,7 +30644,7 @@ ma_uint64 ma_convert_frames_ex(void* pOut, ma_format formatOut, ma_uint32 channe /* At this point we should have output every sample, but just to be super duper sure, just fill the rest with zeros. */ if (totalFramesRead < frameCountOut) { - ma_zero_memory_64(ma_offset_ptr(pOut, totalFramesRead * bpf), ((frameCountOut - totalFramesRead) * bpf)); + ma_zero_memory_64(ma_offset_ptr(pOut, totalFramesRead * bpfOut), ((frameCountOut - totalFramesRead) * bpfOut)); totalFramesRead = frameCountOut; } } @@ -31395,6 +31546,11 @@ ma_result ma_decoder_internal_on_uninit__wav(ma_decoder* pDecoder) return MA_SUCCESS; } +ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__wav(ma_decoder* pDecoder) +{ + return ((drwav*)pDecoder->pInternalDecoder)->totalPCMFrameCount; +} + ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { drwav* pWav; @@ -31412,6 +31568,7 @@ ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_dec /* If we get here it means we successfully initialized the WAV decoder. We can now initialize the rest of the ma_decoder. */ pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__wav; pDecoder->onUninit = ma_decoder_internal_on_uninit__wav; + pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__wav; pDecoder->pInternalDecoder = pWav; /* Try to be as optimal as possible for the internal format. If miniaudio does not support a format we will fall back to f32. */ @@ -31529,6 +31686,11 @@ ma_result ma_decoder_internal_on_uninit__flac(ma_decoder* pDecoder) return MA_SUCCESS; } +ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__flac(ma_decoder* pDecoder) +{ + return ((drflac*)pDecoder->pInternalDecoder)->totalPCMFrameCount; +} + ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { drflac* pFlac; @@ -31546,6 +31708,7 @@ ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_de /* If we get here it means we successfully initialized the FLAC decoder. We can now initialize the rest of the ma_decoder. */ pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__flac; pDecoder->onUninit = ma_decoder_internal_on_uninit__flac; + pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__flac; pDecoder->pInternalDecoder = pFlac; /* @@ -31754,6 +31917,13 @@ ma_uint32 ma_decoder_internal_on_read_pcm_frames__vorbis(ma_pcm_converter* pDSP, return ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, pSamplesOut, frameCount); } +ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__vorbis(ma_decoder* pDecoder) +{ + /* No good way to do this with Vorbis. */ + (void)pDecoder; + return 0; +} + ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; @@ -31847,6 +32017,7 @@ ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_ pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__vorbis; pDecoder->onUninit = ma_decoder_internal_on_uninit__vorbis; + pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__vorbis; pDecoder->pInternalDecoder = pVorbis; /* The internal format is always f32. */ @@ -31927,6 +32098,11 @@ ma_result ma_decoder_internal_on_uninit__mp3(ma_decoder* pDecoder) return MA_SUCCESS; } +ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__mp3(ma_decoder* pDecoder) +{ + return drmp3_get_pcm_frame_count((drmp3*)pDecoder->pInternalDecoder); +} + ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { drmp3* pMP3; @@ -31962,6 +32138,7 @@ ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_dec /* If we get here it means we successfully initialized the MP3 decoder. We can now initialize the rest of the ma_decoder. */ pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__mp3; pDecoder->onUninit = ma_decoder_internal_on_uninit__mp3; + pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__mp3; pDecoder->pInternalDecoder = pMP3; /* Internal format. */ @@ -32047,6 +32224,12 @@ ma_result ma_decoder_internal_on_uninit__raw(ma_decoder* pDecoder) return MA_SUCCESS; } +ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__raw(ma_decoder* pDecoder) +{ + (void)pDecoder; + return 0; +} + ma_result ma_decoder_init_raw__internal(const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder) { ma_result result; @@ -32057,6 +32240,7 @@ ma_result ma_decoder_init_raw__internal(const ma_decoder_config* pConfigIn, cons pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__raw; pDecoder->onUninit = ma_decoder_internal_on_uninit__raw; + pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__raw; /* Internal format. */ pDecoder->internalFormat = pConfigIn->format; @@ -32431,6 +32615,7 @@ ma_result ma_decoder_init_memory_raw(const void* pData, size_t dataSize, const m #include #if !defined(_MSC_VER) && !defined(__DMC__) #include /* For strcasecmp(). */ +#include /* For wcsrtombs() */ #endif const char* ma_path_file_name(const char* path) @@ -32460,6 +32645,34 @@ const char* ma_path_file_name(const char* path) return fileName; } +const wchar_t* ma_path_file_name_w(const wchar_t* path) +{ + const wchar_t* fileName; + + if (path == NULL) { + return NULL; + } + + fileName = path; + + /* We just loop through the path until we find the last slash. */ + while (path[0] != '\0') { + if (path[0] == '/' || path[0] == '\\') { + fileName = path; + } + + path += 1; + } + + /* At this point the file name is sitting on a slash, so just move forward. */ + while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { + fileName += 1; + } + + return fileName; +} + + const char* ma_path_extension(const char* path) { const char* extension; @@ -32485,6 +32698,32 @@ const char* ma_path_extension(const char* path) return (lastOccurance != NULL) ? lastOccurance : extension; } +const wchar_t* ma_path_extension_w(const wchar_t* path) +{ + const wchar_t* extension; + const wchar_t* lastOccurance; + + if (path == NULL) { + path = L""; + } + + extension = ma_path_file_name_w(path); + lastOccurance = NULL; + + /* Just find the last '.' and return. */ + while (extension[0] != '\0') { + if (extension[0] == '.') { + extension += 1; + lastOccurance = extension; + } + + extension += 1; + } + + return (lastOccurance != NULL) ? lastOccurance : extension; +} + + ma_bool32 ma_path_extension_equal(const char* path, const char* extension) { const char* ext1; @@ -32504,6 +32743,49 @@ ma_bool32 ma_path_extension_equal(const char* path, const char* extension) #endif } +ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) +{ + const wchar_t* ext1; + const wchar_t* ext2; + + if (path == NULL || extension == NULL) { + return MA_FALSE; + } + + ext1 = extension; + ext2 = ma_path_extension_w(path); + +#if defined(_MSC_VER) || defined(__DMC__) + return _wcsicmp(ext1, ext2) == 0; +#else + /* + I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This + isn't the most efficient way to do it, but it should work OK. + */ + { + char ext1MB[4096]; + char ext2MB[4096]; + const wchar_t* pext1 = ext1; + const wchar_t* pext2 = ext2; + mbstate_t mbs1; + mbstate_t mbs2; + + ma_zero_object(&mbs1); + ma_zero_object(&mbs2); + + if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { + return MA_FALSE; + } + if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { + return MA_FALSE; + } + + return strcasecmp(ext1MB, ext2MB) == 0; + } +#endif +} + + size_t ma_decoder__on_read_stdio(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pDecoder->pUserData); @@ -32546,6 +32828,77 @@ ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_confi return MA_SUCCESS; } +ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + FILE* pFile; + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + ma_zero_object(pDecoder); + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + +#if defined(_WIN32) + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + if (_wfopen_s(&pFile, pFilePath, L"rb") != 0) { + return MA_ERROR; + } + #else + pFile = _wfopen(pFilePath, L"rb"); + if (pFile == NULL) { + return MA_ERROR; + } + #endif +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can + think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + */ + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + + /* Get the length first. */ + ma_zero_object(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return MA_ERROR; + } + + pFilePathMB = (char*)MA_MALLOC(lenMB + 1); + if (pFilePathMB == NULL) { + return MA_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + ma_zero_object(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + pFile = fopen(pFilePathMB, "rb"); + + MA_FREE(pFilePathMB); + } + + if (pFile == NULL) { + return MA_ERROR; + } +#endif + + /* We need to manually set the user data so the calls to ma_decoder__on_seek_stdio() succeed. */ + pDecoder->pUserData = pFile; + + (void)pConfig; + return MA_SUCCESS; +} + ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder); /* This sets pDecoder->pUserData to a FILE*. */ @@ -32626,6 +32979,88 @@ ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_confi return ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); } + + +ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder); /* This sets pDecoder->pUserData to a FILE*. */ + if (result != MA_SUCCESS) { + return result; + } + + /* WAV */ + if (ma_path_extension_equal_w(pFilePath, L"wav")) { + result = ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + + ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start); + } + + /* FLAC */ + if (ma_path_extension_equal_w(pFilePath, L"flac")) { + result = ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + + ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start); + } + + /* MP3 */ + if (ma_path_extension_equal_w(pFilePath, L"mp3")) { + result = ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + + ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start); + } + + /* Trial and error. */ + return ma_decoder_init(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} + +ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} + +ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} + +ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder_init_vorbis(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} + +ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} #endif ma_result ma_decoder_uninit(ma_decoder* pDecoder) @@ -32648,6 +33083,19 @@ ma_result ma_decoder_uninit(ma_decoder* pDecoder) return MA_SUCCESS; } +ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder) +{ + if (pDecoder == NULL) { + return 0; + } + + if (pDecoder->onGetLengthInPCMFrames) { + return pDecoder->onGetLengthInPCMFrames(pDecoder); + } + + return 0; +} + ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount) { if (pDecoder == NULL) { @@ -32913,6 +33361,21 @@ Device /* REVISION HISTORY ================ +v0.9.6 - 2019-08-04 + - Add support for loading decoders using a wchar_t string for file paths. + - Don't trigger an assert when ma_device_start() is called on a device that is already started. This will now log a warning + and return MA_INVALID_OPERATION. The same applies for ma_device_stop(). + - Try fixing an issue with PulseAudio taking a long time to start playback. + - Fix a bug in ma_convert_frames() and ma_convert_frames_ex(). + - Fix memory leaks in the WASAPI backend. + - Fix a compilation error with Visual Studio 2010. + +v0.9.5 - 2019-05-21 + - Add logging to ma_dlopen() and ma_dlsym(). + - Add ma_decoder_get_length_in_pcm_frames(). + - Fix a bug with capture on the OpenSL|ES backend. + - Fix a bug with the ALSA backend where a device would not restart after being stopped. + v0.9.4 - 2019-05-06 - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and Microsoft compilers back to VC6. Other compilers should also work, but have not been tested. diff --git a/src/external/stb_image.h b/src/external/stb_image.h index a6202a31f..196dfd5cc 100644 --- a/src/external/stb_image.h +++ b/src/external/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.22 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.23 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,7 @@ LICENSE RECENT REVISION HISTORY: + 2.23 (2019-08-11) fix clang static analysis warning 2.22 (2019-03-04) gif fixes, fix warnings 2.21 (2019-02-25) fix typo in comment 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs @@ -5079,7 +5080,7 @@ static int stbi__high_bit(unsigned int z) if (z >= 0x00100) { n += 8; z >>= 8; } if (z >= 0x00010) { n += 4; z >>= 4; } if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1; z >>= 1; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } @@ -5237,7 +5238,10 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req psize = (info.offset - 14 - info.hsz) >> 2; } - s->img_n = ma ? 4 : 3; + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else @@ -5547,6 +5551,8 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5710,6 +5716,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } @@ -6936,7 +6943,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) return 0; if (x) *x = s->img_x; if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } return 1; } #endif diff --git a/src/external/stb_image_write.h b/src/external/stb_image_write.h index a19b548ae..a9bf66c14 100644 --- a/src/external/stb_image_write.h +++ b/src/external/stb_image_write.h @@ -1,4 +1,4 @@ -/* stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h +/* stb_image_write - v1.13 - public domain - http://nothings.org/stb writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk @@ -10,11 +10,6 @@ Will probably not work correctly with strict-aliasing optimizations. - If using a modern Microsoft Compiler, non-safe versions of CRT calls may cause - compilation warnings or even errors. To avoid this, also before #including, - - #define STBI_MSC_SECURE_CRT - ABOUT: This header file is a library for writing images to C stdio or a callback. @@ -873,7 +868,7 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; - unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); if (hash_table == NULL) return NULL; if (quality < 5) quality = 5; @@ -1535,6 +1530,8 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.11 (2019-08-11) + 1.10 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 1.09 (2018-02-11) diff --git a/src/external/stb_perlin.h b/src/external/stb_perlin.h index d582d5a4a..941773f02 100644 --- a/src/external/stb_perlin.h +++ b/src/external/stb_perlin.h @@ -81,6 +81,7 @@ extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap extern float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves); extern float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); +extern float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed); #ifdef __cplusplus } #endif @@ -321,6 +322,66 @@ float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, return sum; } +float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + int px = stb__perlin_fastfloor(x); + int py = stb__perlin_fastfloor(y); + int pz = stb__perlin_fastfloor(z); + int x_wrap2 = (x_wrap ? x_wrap : 256); + int y_wrap2 = (y_wrap ? y_wrap : 256); + int z_wrap2 = (z_wrap ? z_wrap : 256); + int x0 = px % x_wrap2, x1; + int y0 = py % y_wrap2, y1; + int z0 = pz % z_wrap2, z1; + int r0,r1, r00,r01,r10,r11; + + if (x0 < 0) x0 += x_wrap2; + if (y0 < 0) y0 += y_wrap2; + if (z0 < 0) z0 += z_wrap2; + x1 = (x0+1) % x_wrap2; + y1 = (y0+1) % y_wrap2; + z1 = (z0+1) % z_wrap2; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0]; + r0 = stb__perlin_randtab[r0+seed]; + r1 = stb__perlin_randtab[x1]; + r1 = stb__perlin_randtab[r1+seed]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} #endif // STB_PERLIN_IMPLEMENTATION /* diff --git a/src/external/stb_truetype.h b/src/external/stb_truetype.h index 767f005ab..4a3ad33da 100644 --- a/src/external/stb_truetype.h +++ b/src/external/stb_truetype.h @@ -1,5 +1,5 @@ -// stb_truetype.h - v1.21 - public domain -// authored from 2009-2016 by Sean Barrett / RAD Game Tools +// stb_truetype.h - v1.22 - public domain +// authored from 2009-2019 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: // parse files @@ -46,9 +46,11 @@ // Rob Loach Cort Stratton // Kenney Phillis Jr. github:oyvindjam // Brian Costabile github:vassvik +// Ken Voskuil (kaesve) Ryan Griege // // VERSION HISTORY // +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined // 1.21 (2019-02-25) fix warning // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() // 1.19 (2018-02-11) GPOS kerning, STBTT_fmod @@ -2540,8 +2542,7 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int if (info->gpos) xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); - - if (info->kern) + else if (info->kern) xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); return xAdvance; @@ -3968,6 +3969,7 @@ static float stbtt__oversample_shift(int oversample) STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k; + int missing_glyph_added = 0; k=0; for (i=0; i < num_ranges; ++i) { @@ -3979,7 +3981,7 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb int x0,y0,x1,y1; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; int glyph = stbtt_FindGlyphIndex(info, codepoint); - if (glyph == 0 && spc->skip_missing) { + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { rects[k].w = rects[k].h = 0; } else { stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, @@ -3989,6 +3991,8 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb &x0,&y0,&x1,&y1); rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; } ++k; } @@ -4023,7 +4027,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info // rects array must be big enough to accommodate all characters in the given ranges STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { - int i,j,k, return_value = 1; + int i,j,k, missing_glyph = -1, return_value = 1; // save current values int old_h_over = spc->h_oversample; @@ -4088,6 +4092,13 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const bc->yoff = (float) y0 * recip_v + sub_y; bc->xoff2 = (x0 + r->w) * recip_h + sub_x; bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; } else { return_value = 0; // if any fail, report failure } @@ -4389,12 +4400,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc int w,h; unsigned char *data; - // if one scale is 0, use same scale for both - if (scale_x == 0) scale_x = scale_y; - if (scale_y == 0) { - if (scale_x == 0) return NULL; // if both scales are 0, return NULL - scale_y = scale_x; - } + if (scale == 0) return NULL; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h index 846784fa3..242b47d85 100644 --- a/src/external/tinyobj_loader_c.h +++ b/src/external/tinyobj_loader_c.h @@ -453,6 +453,11 @@ static void parseFloat3(float *x, float *y, float *z, const char **token) { (*z) = parseFloat(token); } +static unsigned int my_strnlen(const char *s, unsigned int n) { + const char *p = memchr(s, 0, n); + return p ? (unsigned int)(p - s) : n; +} + static char *my_strdup(const char *s, unsigned int max_length) { char *d; unsigned int len; @@ -478,15 +483,13 @@ static char *my_strndup(const char *s, unsigned int len) { if (s == NULL) return NULL; if (len == 0) return NULL; - d = (char *)TINYOBJ_MALLOC(len + 1); /* + '\0' */ - slen = strlen(s); - if (slen < len) { - memcpy(d, s, slen); - d[slen] = '\0'; - } else { - memcpy(d, s, len); - d[len] = '\0'; + slen = my_strnlen(s, len); + d = (char *)TINYOBJ_MALLOC(slen + 1); /* + '\0' */ + if (!d) { + return NULL; } + memcpy(d, s, slen); + d[slen] = '\0'; return d; } diff --git a/src/gestures.h b/src/gestures.h index 367753330..7b3d7f31b 100644 --- a/src/gestures.h +++ b/src/gestures.h @@ -8,7 +8,7 @@ * * #define GESTURES_IMPLEMENTATION * Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers +* If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation. * * #define GESTURES_STANDALONE @@ -216,8 +216,8 @@ static float pinchDistance = 0.0f; // PINCH displacement distance ( static int currentGesture = GESTURE_NONE; // Current detected gesture -// Enabled gestures flags, all gestures enabled by default -static unsigned int enabledGestures = 0b0000001111111111; +// Enabled gestures flags, all gestures enabled by default +static unsigned int enabledGestures = 0b0000001111111111; //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -251,13 +251,13 @@ void ProcessGestureEvent(GestureEvent event) { // Reset required variables pointCount = event.pointCount; // Required on UpdateGestures() - + if (pointCount < 2) { if (event.touchAction == TOUCH_DOWN) { tapCounter++; // Tap counter - + // Detect GESTURE_DOUBLE_TAP if ((currentGesture == GESTURE_NONE) && (tapCounter >= 2) && ((GetCurrentTime() - eventTime) < TAP_TIMEOUT) && (Vector2Distance(touchDownPosition, event.position[0]) < DOUBLETAP_RANGE)) { @@ -269,15 +269,15 @@ void ProcessGestureEvent(GestureEvent event) tapCounter = 1; currentGesture = GESTURE_TAP; } - + touchDownPosition = event.position[0]; touchDownDragPosition = event.position[0]; - + touchUpPosition = touchDownPosition; eventTime = GetCurrentTime(); - + firstTouchId = event.pointerId[0]; - + dragVector = (Vector2){ 0.0f, 0.0f }; } else if (event.touchAction == TOUCH_UP) @@ -287,15 +287,15 @@ void ProcessGestureEvent(GestureEvent event) // NOTE: dragIntensity dependend on the resolution of the screen dragDistance = Vector2Distance(touchDownPosition, touchUpPosition); dragIntensity = dragDistance/(float)((GetCurrentTime() - swipeTime)); - + startMoving = false; - + // Detect GESTURE_SWIPE if ((dragIntensity > FORCE_TO_SWIPE) && (firstTouchId == event.pointerId[0])) { // NOTE: Angle should be inverted in Y dragAngle = 360.0f - Vector2Angle(touchDownPosition, touchUpPosition); - + if ((dragAngle < 30) || (dragAngle > 330)) currentGesture = GESTURE_SWIPE_RIGHT; // Right else if ((dragAngle > 30) && (dragAngle < 120)) currentGesture = GESTURE_SWIPE_UP; // Up else if ((dragAngle > 120) && (dragAngle < 210)) currentGesture = GESTURE_SWIPE_LEFT; // Left @@ -307,31 +307,31 @@ void ProcessGestureEvent(GestureEvent event) dragDistance = 0.0f; dragIntensity = 0.0f; dragAngle = 0.0f; - + currentGesture = GESTURE_NONE; } - + touchDownDragPosition = (Vector2){ 0.0f, 0.0f }; pointCount = 0; } else if (event.touchAction == TOUCH_MOVE) { if (currentGesture == GESTURE_DRAG) eventTime = GetCurrentTime(); - + if (!startMoving) { swipeTime = GetCurrentTime(); startMoving = true; } - + moveDownPosition = event.position[0]; - + if (currentGesture == GESTURE_HOLD) { if (resetHold) touchDownPosition = event.position[0]; - + resetHold = false; - + // Detect GESTURE_DRAG if (Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_DRAG) { @@ -339,7 +339,7 @@ void ProcessGestureEvent(GestureEvent event) currentGesture = GESTURE_DRAG; } } - + dragVector.x = moveDownPosition.x - touchDownDragPosition.x; dragVector.y = moveDownPosition.y - touchDownDragPosition.y; } @@ -350,28 +350,28 @@ void ProcessGestureEvent(GestureEvent event) { touchDownPosition = event.position[0]; touchDownPosition2 = event.position[1]; - + //pinchDistance = Vector2Distance(touchDownPosition, touchDownPosition2); - + pinchVector.x = touchDownPosition2.x - touchDownPosition.x; pinchVector.y = touchDownPosition2.y - touchDownPosition.y; - + currentGesture = GESTURE_HOLD; timeHold = GetCurrentTime(); } else if (event.touchAction == TOUCH_MOVE) { pinchDistance = Vector2Distance(moveDownPosition, moveDownPosition2); - + touchDownPosition = moveDownPosition; touchDownPosition2 = moveDownPosition2; - + moveDownPosition = event.position[0]; moveDownPosition2 = event.position[1]; - + pinchVector.x = moveDownPosition2.x - moveDownPosition.x; pinchVector.y = moveDownPosition2.y - moveDownPosition.y; - + if ((Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_PINCH) || (Vector2Distance(touchDownPosition2, moveDownPosition2) >= MINIMUM_PINCH)) { if ((Vector2Distance(moveDownPosition, moveDownPosition2) - pinchDistance) < 0) currentGesture = GESTURE_PINCH_IN; @@ -382,7 +382,7 @@ void ProcessGestureEvent(GestureEvent event) currentGesture = GESTURE_HOLD; timeHold = GetCurrentTime(); } - + // NOTE: Angle should be inverted in Y pinchAngle = 360.0f - Vector2Angle(moveDownPosition, moveDownPosition2); } @@ -392,7 +392,7 @@ void ProcessGestureEvent(GestureEvent event) pinchAngle = 0.0f; pinchVector = (Vector2){ 0.0f, 0.0f }; pointCount = 0; - + currentGesture = GESTURE_NONE; } } @@ -409,14 +409,14 @@ void UpdateGestures(void) currentGesture = GESTURE_HOLD; timeHold = GetCurrentTime(); } - + if (((GetCurrentTime() - eventTime) > TAP_TIMEOUT) && (currentGesture == GESTURE_DRAG) && (pointCount < 2)) { currentGesture = GESTURE_HOLD; timeHold = GetCurrentTime(); resetHold = true; } - + // Detect GESTURE_NONE if ((currentGesture == GESTURE_SWIPE_RIGHT) || (currentGesture == GESTURE_SWIPE_UP) || (currentGesture == GESTURE_SWIPE_LEFT) || (currentGesture == GESTURE_SWIPE_DOWN)) { @@ -428,7 +428,7 @@ void UpdateGestures(void) int GetTouchPointsCount(void) { // NOTE: point count is calculated when ProcessGestureEvent(GestureEvent event) is called - + return pointCount; } @@ -443,11 +443,11 @@ int GetGestureDetected(void) float GetGestureHoldDuration(void) { // NOTE: time is calculated on current gesture HOLD - + double time = 0.0; - + if (currentGesture == GESTURE_HOLD) time = GetCurrentTime() - timeHold; - + return (float)time; } @@ -455,7 +455,7 @@ float GetGestureHoldDuration(void) Vector2 GetGestureDragVector(void) { // NOTE: drag vector is calculated on one touch points TOUCH_MOVE - + return dragVector; } @@ -464,7 +464,7 @@ Vector2 GetGestureDragVector(void) float GetGestureDragAngle(void) { // NOTE: drag angle is calculated on one touch points TOUCH_UP - + return dragAngle; } @@ -473,7 +473,7 @@ Vector2 GetGesturePinchVector(void) { // NOTE: The position values used for pinchDistance are not modified like the position values of [core.c]-->GetTouchPosition(int index) // NOTE: pinch distance is calculated on two touch points TOUCH_MOVE - + return pinchVector; } @@ -482,7 +482,7 @@ Vector2 GetGesturePinchVector(void) float GetGesturePinchAngle(void) { // NOTE: pinch angle is calculated on two touch points TOUCH_MOVE - + return pinchAngle; } @@ -494,7 +494,7 @@ float GetGesturePinchAngle(void) static float Vector2Angle(Vector2 v1, Vector2 v2) { float angle = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI); - + if (angle < 0) angle += 360.0f; return angle; @@ -518,13 +518,13 @@ static float Vector2Distance(Vector2 v1, Vector2 v2) static double GetCurrentTime(void) { double time = 0; - + #if defined(_WIN32) unsigned long long int clockFrequency, currentTime; - + QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation! QueryPerformanceCounter(¤tTime); - + time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds #endif @@ -533,24 +533,24 @@ static double GetCurrentTime(void) struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds - + time = ((double)nowTime/1000000.0); // Time in miliseconds #endif #if defined(__APPLE__) //#define CLOCK_REALTIME CALENDAR_CLOCK // returns UTC time since 1970-01-01 //#define CLOCK_MONOTONIC SYSTEM_CLOCK // returns the time since boot time - + clock_serv_t cclock; mach_timespec_t now; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); - + // NOTE: OS X does not have clock_gettime(), using clock_get_time() clock_get_time(cclock, &now); mach_port_deallocate(mach_task_self(), cclock); uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds - time = ((double)nowTime/1000000.0); // Time in miliseconds + time = ((double)nowTime/1000000.0); // Time in miliseconds #endif return time; diff --git a/src/models.c b/src/models.c index 6110051cd..501f1e706 100644 --- a/src/models.c +++ b/src/models.c @@ -71,7 +71,7 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -// ... +#define MAX_MESH_VBO 7 // Maximum number of vbo per mesh //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -296,7 +296,7 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei float x = position.x; float y = position.y; float z = position.z; - + if (rlCheckBufferLimit(36)) rlglDraw(); rlEnableTexture(texture.id); @@ -362,7 +362,7 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color { int numVertex = (rings + 2)*slices*6; if (rlCheckBufferLimit(numVertex)) rlglDraw(); - + rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -405,7 +405,7 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col { int numVertex = (rings + 2)*slices*6; if (rlCheckBufferLimit(numVertex)) rlglDraw(); - + rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -449,7 +449,7 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) { if (sides < 3) sides = 3; - + int numVertex = sides*6; if (rlCheckBufferLimit(numVertex)) rlglDraw(); @@ -508,7 +508,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) { if (sides < 3) sides = 3; - + int numVertex = sides*8; if (rlCheckBufferLimit(numVertex)) rlglDraw(); @@ -540,7 +540,7 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl void DrawPlane(Vector3 centerPos, Vector2 size, Color color) { if (rlCheckBufferLimit(4)) rlglDraw(); - + // NOTE: Plane is always created on XZ ground rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -669,7 +669,7 @@ Model LoadModel(const char *fileName) model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); - model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); + if (model.meshMaterial == NULL) model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); } return model; @@ -686,14 +686,14 @@ Model LoadModelFromMesh(Mesh mesh) model.transform = MatrixIdentity(); model.meshCount = 1; - model.meshes = (Mesh *)RL_MALLOC(model.meshCount*sizeof(Mesh)); + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshes[0] = mesh; model.materialCount = 1; - model.materials = (Material *)RL_MALLOC(model.materialCount*sizeof(Material)); + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); - model.meshMaterial = (int *)RL_MALLOC(model.meshCount*sizeof(int)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); model.meshMaterial[0] = 0; // First material index return model; @@ -702,8 +702,12 @@ Model LoadModelFromMesh(Mesh mesh) // Unload model from memory (RAM and/or VRAM) void UnloadModel(Model model) { - for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); - for (int i = 0; i < model.materialCount; i++) UnloadMaterial(model.materials[i]); + for (int i = 0; i < model.meshCount; i++) UnloadMesh(model.meshes[i]); + + // As the user could be sharing shaders and textures between models, + // we don't unload the material but just free it's maps, the user + // is responsible for freeing models shaders and textures + for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps); RL_FREE(model.meshes); RL_FREE(model.materials); @@ -721,17 +725,18 @@ Mesh *LoadMeshes(const char *fileName, int *meshCount) { Mesh *meshes = NULL; int count = 0; - + // TODO: Load meshes from file (OBJ, IQM, GLTF) - + *meshCount = count; return meshes; } // Unload mesh from memory (RAM and/or VRAM) -void UnloadMesh(Mesh *mesh) +void UnloadMesh(Mesh mesh) { rlUnloadMesh(mesh); + RL_FREE(mesh.vboId); } // Export mesh data to file @@ -795,7 +800,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount) { Material *materials = NULL; unsigned int count = 0; - + // TODO: Support IQM and GLTF for materials parsing #if defined(SUPPORT_FILEFORMAT_MTL) @@ -804,6 +809,9 @@ Material *LoadMaterials(const char *fileName, int *materialCount) tinyobj_material_t *mats; int result = tinyobj_parse_mtl_file(&mats, &count, fileName); + if (result != TINYOBJ_SUCCESS) { + TraceLog(LOG_WARNING, "[%s] Could not parse Materials file", fileName); + } // TODO: Process materials to return @@ -824,6 +832,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount) Material LoadMaterialDefault(void) { Material material = { 0 }; + material.maps = (MaterialMap *)RL_CALLOC(MAX_MATERIAL_MAPS, sizeof(MaterialMap)); material.shader = GetShaderDefault(); material.maps[MAP_DIFFUSE].texture = GetTextureDefault(); // White texture (1x1 pixel) @@ -847,6 +856,8 @@ void UnloadMaterial(Material material) { if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id); } + + RL_FREE(material.maps); } // Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...) @@ -867,9 +878,6 @@ void SetModelMeshMaterial(Model *model, int meshId, int materialId) // Load model animations from file ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) { - ModelAnimation *animations = (ModelAnimation *)RL_MALLOC(1*sizeof(ModelAnimation)); - int count = 1; - #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number #define IQM_VERSION 2 // only IQM version 2 supported @@ -903,8 +911,6 @@ ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) float framerate; unsigned int flags; } IQMAnim; - - ModelAnimation animation = { 0 }; FILE *iqmFile; IQMHeader iqm; @@ -916,7 +922,7 @@ ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) TraceLog(LOG_ERROR, "[%s] Unable to open file", filename); } - // header + // Read IQM header fread(&iqm, sizeof(IQMHeader), 1, iqmFile); if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) @@ -931,153 +937,151 @@ ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) fclose(iqmFile); } - // header - if (iqm.num_anims > 1) TraceLog(LOG_WARNING, "More than 1 animation in file, only the first one will be loaded"); - - // bones - IQMPose *poses; - poses = RL_MALLOC(sizeof(IQMPose)*iqm.num_poses); + // Get bones data + IQMPose *poses = RL_MALLOC(iqm.num_poses*sizeof(IQMPose)); fseek(iqmFile, iqm.ofs_poses, SEEK_SET); - fread(poses, sizeof(IQMPose)*iqm.num_poses, 1, iqmFile); + fread(poses, iqm.num_poses*sizeof(IQMPose), 1, iqmFile); - animation.boneCount = iqm.num_poses; - animation.bones = RL_MALLOC(sizeof(BoneInfo)*iqm.num_poses); - - for (int j = 0; j < iqm.num_poses; j++) - { - strcpy(animation.bones[j].name, "ANIMJOINTNAME"); - animation.bones[j].parent = poses[j].parent; - } - - // animations - IQMAnim anim = {0}; + // Get animations data + *animCount = iqm.num_anims; + IQMAnim *anim = RL_MALLOC(iqm.num_anims*sizeof(IQMAnim)); fseek(iqmFile, iqm.ofs_anims, SEEK_SET); - fread(&anim, sizeof(IQMAnim), 1, iqmFile); - - animation.frameCount = anim.num_frames; - //animation.framerate = anim.framerate; + fread(anim, iqm.num_anims*sizeof(IQMAnim), 1, iqmFile); + ModelAnimation *animations = RL_MALLOC(iqm.num_anims*sizeof(ModelAnimation)); // frameposes - unsigned short *framedata = RL_MALLOC(sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels); + unsigned short *framedata = RL_MALLOC(iqm.num_frames*iqm.num_framechannels*sizeof(unsigned short)); fseek(iqmFile, iqm.ofs_frames, SEEK_SET); - fread(framedata, sizeof(unsigned short)*iqm.num_frames*iqm.num_framechannels, 1, iqmFile); + fread(framedata, iqm.num_frames*iqm.num_framechannels*sizeof(unsigned short), 1, iqmFile); - animation.framePoses = RL_MALLOC(sizeof(Transform*)*anim.num_frames); - for (int j = 0; j < anim.num_frames; j++) animation.framePoses[j] = RL_MALLOC(sizeof(Transform)*iqm.num_poses); - - int dcounter = anim.first_frame*iqm.num_framechannels; - - for (int frame = 0; frame < anim.num_frames; frame++) + for (int a = 0; a < iqm.num_anims; a++) { - for (int i = 0; i < iqm.num_poses; i++) + animations[a].frameCount = anim[a].num_frames; + animations[a].boneCount = iqm.num_poses; + animations[a].bones = RL_MALLOC(iqm.num_poses*sizeof(BoneInfo)); + animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); + //animations[a].framerate = anim.framerate; // TODO: Use framerate? + + for (int j = 0; j < iqm.num_poses; j++) { - animation.framePoses[frame][i].translation.x = poses[i].channeloffset[0]; - - if (poses[i].mask & 0x01) - { - animation.framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; - dcounter++; - } - - animation.framePoses[frame][i].translation.y = poses[i].channeloffset[1]; - - if (poses[i].mask & 0x02) - { - animation.framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; - dcounter++; - } - - animation.framePoses[frame][i].translation.z = poses[i].channeloffset[2]; - - if (poses[i].mask & 0x04) - { - animation.framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; - dcounter++; - } - - animation.framePoses[frame][i].rotation.x = poses[i].channeloffset[3]; - - if (poses[i].mask & 0x08) - { - animation.framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; - dcounter++; - } - - animation.framePoses[frame][i].rotation.y = poses[i].channeloffset[4]; - - if (poses[i].mask & 0x10) - { - animation.framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; - dcounter++; - } - - animation.framePoses[frame][i].rotation.z = poses[i].channeloffset[5]; - - if (poses[i].mask & 0x20) - { - animation.framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; - dcounter++; - } - - animation.framePoses[frame][i].rotation.w = poses[i].channeloffset[6]; - - if (poses[i].mask & 0x40) - { - animation.framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; - dcounter++; - } - - animation.framePoses[frame][i].scale.x = poses[i].channeloffset[7]; - - if (poses[i].mask & 0x80) - { - animation.framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; - dcounter++; - } - - animation.framePoses[frame][i].scale.y = poses[i].channeloffset[8]; - - if (poses[i].mask & 0x100) - { - animation.framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; - dcounter++; - } - - animation.framePoses[frame][i].scale.z = poses[i].channeloffset[9]; - - if (poses[i].mask & 0x200) - { - animation.framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; - dcounter++; - } - - animation.framePoses[frame][i].rotation = QuaternionNormalize(animation.framePoses[frame][i].rotation); + strcpy(animations[a].bones[j].name, "ANIMJOINTNAME"); + animations[a].bones[j].parent = poses[j].parent; } - } - // Build frameposes - for (int frame = 0; frame < anim.num_frames; frame++) - { - for (int i = 0; i < animation.boneCount; i++) + for (int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = RL_MALLOC(iqm.num_poses*sizeof(Transform)); + + int dcounter = anim[a].first_frame*iqm.num_framechannels; + + for (int frame = 0; frame < anim[a].num_frames; frame++) { - if (animation.bones[i].parent >= 0) + for (int i = 0; i < iqm.num_poses; i++) { - animation.framePoses[frame][i].rotation = QuaternionMultiply(animation.framePoses[frame][animation.bones[i].parent].rotation, animation.framePoses[frame][i].rotation); - animation.framePoses[frame][i].translation = Vector3RotateByQuaternion(animation.framePoses[frame][i].translation, animation.framePoses[frame][animation.bones[i].parent].rotation); - animation.framePoses[frame][i].translation = Vector3Add(animation.framePoses[frame][i].translation, animation.framePoses[frame][animation.bones[i].parent].translation); - animation.framePoses[frame][i].scale = Vector3MultiplyV(animation.framePoses[frame][i].scale, animation.framePoses[frame][animation.bones[i].parent].scale); + animations[a].framePoses[frame][i].translation.x = poses[i].channeloffset[0]; + + if (poses[i].mask & 0x01) + { + animations[a].framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; + dcounter++; + } + + animations[a].framePoses[frame][i].translation.y = poses[i].channeloffset[1]; + + if (poses[i].mask & 0x02) + { + animations[a].framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; + dcounter++; + } + + animations[a].framePoses[frame][i].translation.z = poses[i].channeloffset[2]; + + if (poses[i].mask & 0x04) + { + animations[a].framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; + dcounter++; + } + + animations[a].framePoses[frame][i].rotation.x = poses[i].channeloffset[3]; + + if (poses[i].mask & 0x08) + { + animations[a].framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; + dcounter++; + } + + animations[a].framePoses[frame][i].rotation.y = poses[i].channeloffset[4]; + + if (poses[i].mask & 0x10) + { + animations[a].framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; + dcounter++; + } + + animations[a].framePoses[frame][i].rotation.z = poses[i].channeloffset[5]; + + if (poses[i].mask & 0x20) + { + animations[a].framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; + dcounter++; + } + + animations[a].framePoses[frame][i].rotation.w = poses[i].channeloffset[6]; + + if (poses[i].mask & 0x40) + { + animations[a].framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; + dcounter++; + } + + animations[a].framePoses[frame][i].scale.x = poses[i].channeloffset[7]; + + if (poses[i].mask & 0x80) + { + animations[a].framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; + dcounter++; + } + + animations[a].framePoses[frame][i].scale.y = poses[i].channeloffset[8]; + + if (poses[i].mask & 0x100) + { + animations[a].framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; + dcounter++; + } + + animations[a].framePoses[frame][i].scale.z = poses[i].channeloffset[9]; + + if (poses[i].mask & 0x200) + { + animations[a].framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; + dcounter++; + } + + animations[a].framePoses[frame][i].rotation = QuaternionNormalize(animations[a].framePoses[frame][i].rotation); + } + } + + // Build frameposes + for (int frame = 0; frame < anim[a].num_frames; frame++) + { + for (int i = 0; i < animations[a].boneCount; i++) + { + if (animations[a].bones[i].parent >= 0) + { + animations[a].framePoses[frame][i].rotation = QuaternionMultiply(animations[a].framePoses[frame][animations[a].bones[i].parent].rotation, animations[a].framePoses[frame][i].rotation); + animations[a].framePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].rotation); + animations[a].framePoses[frame][i].translation = Vector3Add(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].translation); + animations[a].framePoses[frame][i].scale = Vector3MultiplyV(animations[a].framePoses[frame][i].scale, animations[a].framePoses[frame][animations[a].bones[i].parent].scale); + } } } } RL_FREE(framedata); RL_FREE(poses); - + RL_FREE(anim); + fclose(iqmFile); - animations[0] = animation; - - *animCount = count; return animations; } @@ -1085,61 +1089,64 @@ ModelAnimation *LoadModelAnimations(const char *filename, int *animCount) // NOTE: Updated data is uploaded to GPU void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) { - if (frame >= anim.frameCount) frame = frame%anim.frameCount; - - for (int m = 0; m < model.meshCount; m++) + if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL)) { - Vector3 animVertex = { 0 }; - Vector3 animNormal = { 0 }; + if (frame >= anim.frameCount) frame = frame%anim.frameCount; - Vector3 inTranslation = { 0 }; - Quaternion inRotation = { 0 }; - Vector3 inScale = { 0 }; - - Vector3 outTranslation = { 0 }; - Quaternion outRotation = { 0 }; - Vector3 outScale = { 0 }; - - int vCounter = 0; - int boneCounter = 0; - int boneId = 0; - - for (int i = 0; i < model.meshes[m].vertexCount; i++) + for (int m = 0; m < model.meshCount; m++) { - boneId = model.meshes[m].boneIds[boneCounter]; - inTranslation = model.bindPose[boneId].translation; - inRotation = model.bindPose[boneId].rotation; - inScale = model.bindPose[boneId].scale; - outTranslation = anim.framePoses[frame][boneId].translation; - outRotation = anim.framePoses[frame][boneId].rotation; - outScale = anim.framePoses[frame][boneId].scale; + Vector3 animVertex = { 0 }; + Vector3 animNormal = { 0 }; - // Vertices processing - // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position) - animVertex = (Vector3){ model.meshes[m].vertices[vCounter], model.meshes[m].vertices[vCounter + 1], model.meshes[m].vertices[vCounter + 2] }; - animVertex = Vector3MultiplyV(animVertex, outScale); - animVertex = Vector3Subtract(animVertex, inTranslation); - animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); - animVertex = Vector3Add(animVertex, outTranslation); - model.meshes[m].animVertices[vCounter] = animVertex.x; - model.meshes[m].animVertices[vCounter + 1] = animVertex.y; - model.meshes[m].animVertices[vCounter + 2] = animVertex.z; + Vector3 inTranslation = { 0 }; + Quaternion inRotation = { 0 }; + Vector3 inScale = { 0 }; - // Normals processing - // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) - animNormal = (Vector3){ model.meshes[m].normals[vCounter], model.meshes[m].normals[vCounter + 1], model.meshes[m].normals[vCounter + 2] }; - animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); - model.meshes[m].animNormals[vCounter] = animNormal.x; - model.meshes[m].animNormals[vCounter + 1] = animNormal.y; - model.meshes[m].animNormals[vCounter + 2] = animNormal.z; - vCounter += 3; + Vector3 outTranslation = { 0 }; + Quaternion outRotation = { 0 }; + Vector3 outScale = { 0 }; - boneCounter += 4; + int vCounter = 0; + int boneCounter = 0; + int boneId = 0; + + for (int i = 0; i < model.meshes[m].vertexCount; i++) + { + boneId = model.meshes[m].boneIds[boneCounter]; + inTranslation = model.bindPose[boneId].translation; + inRotation = model.bindPose[boneId].rotation; + inScale = model.bindPose[boneId].scale; + outTranslation = anim.framePoses[frame][boneId].translation; + outRotation = anim.framePoses[frame][boneId].rotation; + outScale = anim.framePoses[frame][boneId].scale; + + // Vertices processing + // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position) + animVertex = (Vector3){ model.meshes[m].vertices[vCounter], model.meshes[m].vertices[vCounter + 1], model.meshes[m].vertices[vCounter + 2] }; + animVertex = Vector3MultiplyV(animVertex, outScale); + animVertex = Vector3Subtract(animVertex, inTranslation); + animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); + animVertex = Vector3Add(animVertex, outTranslation); + model.meshes[m].animVertices[vCounter] = animVertex.x; + model.meshes[m].animVertices[vCounter + 1] = animVertex.y; + model.meshes[m].animVertices[vCounter + 2] = animVertex.z; + + // Normals processing + // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) + animNormal = (Vector3){ model.meshes[m].normals[vCounter], model.meshes[m].normals[vCounter + 1], model.meshes[m].normals[vCounter + 2] }; + animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); + model.meshes[m].animNormals[vCounter] = animNormal.x; + model.meshes[m].animNormals[vCounter + 1] = animNormal.y; + model.meshes[m].animNormals[vCounter + 2] = animNormal.z; + vCounter += 3; + + boneCounter += 4; + } + + // Upload new vertex data to GPU for model drawing + rlUpdateBuffer(model.meshes[m].vboId[0], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex position + rlUpdateBuffer(model.meshes[m].vboId[2], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex normals } - - // Upload new vertex data to GPU for model drawing - rlUpdateBuffer(model.meshes[m].vboId[0], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex position - rlUpdateBuffer(model.meshes[m].vboId[2], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex normals } } @@ -1147,7 +1154,7 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) void UnloadModelAnimation(ModelAnimation anim) { for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]); - + RL_FREE(anim.bones); RL_FREE(anim.framePoses); } @@ -1157,7 +1164,7 @@ void UnloadModelAnimation(ModelAnimation anim) bool IsModelAnimationValid(Model model, ModelAnimation anim) { int result = true; - + if (model.boneCount != anim.boneCount) result = false; else { @@ -1175,6 +1182,7 @@ bool IsModelAnimationValid(Model model, ModelAnimation anim) Mesh GenMeshPoly(int sides, float radius) { Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); int vertexCount = sides*3; // Vertices definition @@ -1237,6 +1245,7 @@ Mesh GenMeshPoly(int sides, float radius) Mesh GenMeshPlane(float width, float length, int resX, int resZ) { Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); #define CUSTOM_MESH_GEN_PLANE #if defined(CUSTOM_MESH_GEN_PLANE) @@ -1339,6 +1348,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) mesh.vertices = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float)); mesh.texcoords = (float *)RL_MALLOC(plane->ntriangles*3*2*sizeof(float)); mesh.normals = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float)); + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); mesh.vertexCount = plane->ntriangles*3; mesh.triangleCount = plane->ntriangles; @@ -1370,6 +1380,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) Mesh GenMeshCube(float width, float height, float length) { Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); #define CUSTOM_MESH_GEN_CUBE #if defined(CUSTOM_MESH_GEN_CUBE) @@ -1535,6 +1546,7 @@ par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron RLAPI Mesh GenMeshSphere(float radius, int rings, int slices) { Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); par_shapes_mesh *sphere = par_shapes_create_parametric_sphere(slices, rings); par_shapes_scale(sphere, radius, radius, radius); @@ -1573,6 +1585,7 @@ RLAPI Mesh GenMeshSphere(float radius, int rings, int slices) RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices) { Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); par_shapes_mesh *sphere = par_shapes_create_hemisphere(slices, rings); par_shapes_scale(sphere, radius, radius, radius); @@ -1611,6 +1624,7 @@ RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices) Mesh GenMeshCylinder(float radius, float height, int slices) { Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); // Instance a cylinder that sits on the Z=0 plane using the given tessellation // levels across the UV domain. Think of "slices" like a number of pizza @@ -1669,6 +1683,7 @@ Mesh GenMeshCylinder(float radius, float height, int slices) Mesh GenMeshTorus(float radius, float size, int radSeg, int sides) { Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); if (radius > 1.0f) radius = 1.0f; else if (radius < 0.1f) radius = 0.1f; @@ -1711,6 +1726,7 @@ Mesh GenMeshTorus(float radius, float size, int radSeg, int sides) Mesh GenMeshKnot(float radius, float size, int radSeg, int sides) { Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); if (radius > 3.0f) radius = 3.0f; else if (radius < 0.5f) radius = 0.5f; @@ -1754,6 +1770,7 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size) #define GRAY_VALUE(c) ((c.r+c.g+c.b)/3) Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); int mapX = heightmap.width; int mapZ = heightmap.height; @@ -1862,13 +1879,14 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size) Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) { Mesh mesh = { 0 }; + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); Color *cubicmapPixels = GetImageData(cubicmap); int mapWidth = cubicmap.width; int mapHeight = cubicmap.height; - // NOTE: Max possible number of triangles numCubes * (12 triangles by cube) + // NOTE: Max possible number of triangles numCubes*(12 triangles by cube) int maxTriangles = cubicmap.width*cubicmap.height*12; int vCounter = 0; // Used to count vertices @@ -2321,7 +2339,7 @@ void MeshTangents(Mesh *mesh) RL_FREE(tan1); RL_FREE(tan2); - + // Load a new tangent attributes buffer mesh->vboId[LOC_VERTEX_TANGENT] = rlLoadAttribBuffer(mesh->vaoId, LOC_VERTEX_TANGENT, mesh->tangents, mesh->vertexCount*4*sizeof(float), false); @@ -2367,8 +2385,18 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota for (int i = 0; i < model.meshCount; i++) { - model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = tint; + // TODO: Review color + tint premultiplication mechanism + Color color = model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color; + + Color colorTint = WHITE; + colorTint.r = (((float)color.r/255.0)*((float)tint.r/255.0))*255; + colorTint.g = (((float)color.g/255.0)*((float)tint.g/255.0))*255; + colorTint.b = (((float)color.b/255.0)*((float)tint.b/255.0))*255; + colorTint.a = (((float)color.a/255.0)*((float)tint.a/255.0))*255; + + model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = colorTint; rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); + model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = color; } } @@ -2476,22 +2504,22 @@ void DrawBoundingBox(BoundingBox box, Color color) bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB) { bool collision = false; - + // Simple way to check for collision, just checking distance between two points // Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution /* - float dx = centerA.x - centerB.x; // X distance between centers - float dy = centerA.y - centerB.y; // Y distance between centers - float dz = centerA.z - centerB.z; // Y distance between centers + float dx = centerA.x - centerB.x; // X distance between centers + float dy = centerA.y - centerB.y; // Y distance between centers + float dz = centerA.z - centerB.z; // Z distance between centers - float distance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance between centers + float distance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance between centers if (distance <= (radiusA + radiusB)) collision = true; */ - + // Check for distances squared to avoid sqrtf() if (Vector3DotProduct(Vector3Subtract(centerB, centerA), Vector3Subtract(centerB, centerA)) <= (radiusA + radiusB)*(radiusA + radiusB)) collision = true; - + return collision; } @@ -2512,35 +2540,35 @@ bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2) } // Detect collision between box and sphere -bool CheckCollisionBoxSphere(BoundingBox box, Vector3 centerSphere, float radiusSphere) +bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius) { bool collision = false; float dmin = 0; - if (centerSphere.x < box.min.x) dmin += powf(centerSphere.x - box.min.x, 2); - else if (centerSphere.x > box.max.x) dmin += powf(centerSphere.x - box.max.x, 2); + if (center.x < box.min.x) dmin += powf(center.x - box.min.x, 2); + else if (center.x > box.max.x) dmin += powf(center.x - box.max.x, 2); - if (centerSphere.y < box.min.y) dmin += powf(centerSphere.y - box.min.y, 2); - else if (centerSphere.y > box.max.y) dmin += powf(centerSphere.y - box.max.y, 2); + if (center.y < box.min.y) dmin += powf(center.y - box.min.y, 2); + else if (center.y > box.max.y) dmin += powf(center.y - box.max.y, 2); - if (centerSphere.z < box.min.z) dmin += powf(centerSphere.z - box.min.z, 2); - else if (centerSphere.z > box.max.z) dmin += powf(centerSphere.z - box.max.z, 2); + if (center.z < box.min.z) dmin += powf(center.z - box.min.z, 2); + else if (center.z > box.max.z) dmin += powf(center.z - box.max.z, 2); - if (dmin <= (radiusSphere*radiusSphere)) collision = true; + if (dmin <= (radius*radius)) collision = true; return collision; } // Detect collision between ray and sphere -bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius) +bool CheckCollisionRaySphere(Ray ray, Vector3 center, float radius) { bool collision = false; - Vector3 raySpherePos = Vector3Subtract(spherePosition, ray.position); + Vector3 raySpherePos = Vector3Subtract(center, ray.position); float distance = Vector3Length(raySpherePos); float vector = Vector3DotProduct(raySpherePos, ray.direction); - float d = sphereRadius*sphereRadius - (distance*distance - vector*vector); + float d = radius*radius - (distance*distance - vector*vector); if (d >= 0.0f) collision = true; @@ -2548,21 +2576,21 @@ bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius } // Detect collision between ray and sphere with extended parameters and collision point detection -bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadius, Vector3 *collisionPoint) +bool CheckCollisionRaySphereEx(Ray ray, Vector3 center, float radius, Vector3 *collisionPoint) { bool collision = false; - Vector3 raySpherePos = Vector3Subtract(spherePosition, ray.position); + Vector3 raySpherePos = Vector3Subtract(center, ray.position); float distance = Vector3Length(raySpherePos); float vector = Vector3DotProduct(raySpherePos, ray.direction); - float d = sphereRadius*sphereRadius - (distance*distance - vector*vector); + float d = radius*radius - (distance*distance - vector*vector); if (d >= 0.0f) collision = true; // Check if ray origin is inside the sphere to calculate the correct collision point float collisionDistance = 0; - if (distance < sphereRadius) collisionDistance = vector + sqrtf(d); + if (distance < radius) collisionDistance = vector + sqrtf(d); else collisionDistance = vector - sqrtf(d); // Calculate collision point @@ -2596,29 +2624,29 @@ bool CheckCollisionRayBox(Ray ray, BoundingBox box) } // Get collision info between ray and model -RayHitInfo GetCollisionRayModel(Ray ray, Model *model) +RayHitInfo GetCollisionRayModel(Ray ray, Model model) { RayHitInfo result = { 0 }; - for (int m = 0; m < model->meshCount; m++) + for (int m = 0; m < model.meshCount; m++) { // Check if meshhas vertex data on CPU for testing - if (model->meshes[m].vertices != NULL) + if (model.meshes[m].vertices != NULL) { // model->mesh.triangleCount may not be set, vertexCount is more reliable - int triangleCount = model->meshes[m].vertexCount/3; + int triangleCount = model.meshes[m].vertexCount/3; // Test against all triangles in mesh for (int i = 0; i < triangleCount; i++) { Vector3 a, b, c; - Vector3 *vertdata = (Vector3 *)model->meshes[m].vertices; + Vector3 *vertdata = (Vector3 *)model.meshes[m].vertices; - if (model->meshes[m].indices) + if (model.meshes[m].indices) { - a = vertdata[model->meshes[m].indices[i*3 + 0]]; - b = vertdata[model->meshes[m].indices[i*3 + 1]]; - c = vertdata[model->meshes[m].indices[i*3 + 2]]; + a = vertdata[model.meshes[m].indices[i*3 + 0]]; + b = vertdata[model.meshes[m].indices[i*3 + 1]]; + c = vertdata[model.meshes[m].indices[i*3 + 2]]; } else { @@ -2627,9 +2655,9 @@ RayHitInfo GetCollisionRayModel(Ray ray, Model *model) c = vertdata[i*3 + 2]; } - a = Vector3Transform(a, model->transform); - b = Vector3Transform(b, model->transform); - c = Vector3Transform(c, model->transform); + a = Vector3Transform(a, model.transform); + b = Vector3Transform(b, model.transform); + c = Vector3Transform(c, model.transform); RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c); @@ -2775,11 +2803,15 @@ static Model LoadOBJ(const char *fileName) // TODO: Support multiple meshes... in the meantime, only one mesh is returned //model.meshCount = meshCount; model.meshCount = 1; - model.meshes = (Mesh *)RL_MALLOC(model.meshCount*sizeof(Mesh)); + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); // Init model materials array - model.materialCount = materialCount; - model.materials = (Material *)RL_MALLOC(model.materialCount*sizeof(Material)); + if (materialCount > 0) + { + model.materialCount = materialCount; + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); + } + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); /* @@ -2799,9 +2831,10 @@ static Model LoadOBJ(const char *fileName) memset(&mesh, 0, sizeof(Mesh)); mesh.vertexCount = attrib.num_faces*3; mesh.triangleCount = attrib.num_faces; - mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); - mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); - mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); + mesh.vertices = (float *)RL_CALLOC(mesh.vertexCount*3, sizeof(float)); + mesh.texcoords = (float *)RL_CALLOC(mesh.vertexCount*2, sizeof(float)); + mesh.normals = (float *)RL_CALLOC(mesh.vertexCount*3, sizeof(float)); + mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); int vCount = 0; int vtCount = 0; @@ -2840,6 +2873,9 @@ static Model LoadOBJ(const char *fileName) // Assign mesh material for current mesh model.meshMaterial[m] = attrib.material_ids[m]; + + // Set unfound materials to default + if (model.meshMaterial[m] == -1) model.meshMaterial[m] = 0; } // Init model materials @@ -2877,7 +2913,7 @@ static Model LoadOBJ(const char *fileName) */ model.materials[m].maps[MAP_DIFFUSE].texture = GetTextureDefault(); // Get default texture, in case no texture is defined - + if (materials[m].diffuse_texname != NULL) model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; model.materials[m].maps[MAP_DIFFUSE].value = 0.0f; @@ -2898,6 +2934,8 @@ static Model LoadOBJ(const char *fileName) tinyobj_attrib_free(&attrib); tinyobj_shapes_free(meshes, meshCount); tinyobj_materials_free(materials, materialCount); + + RL_FREE(data); } // NOTE: At this point we have all model data loaded @@ -2946,13 +2984,13 @@ static Model LoadIQM(const char *fileName) typedef struct IQMTriangle { unsigned int vertex[3]; } IQMTriangle; - + typedef struct IQMJoint { unsigned int name; int parent; float translate[3], rotate[4], scale[3]; } IQMJoint; - + typedef struct IQMVertexArray { unsigned int type; unsigned int flags; @@ -3048,34 +3086,36 @@ static Model LoadIQM(const char *fileName) model.meshCount = iqm.num_meshes; model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); - char name[MESH_NAME_LENGTH]; + char name[MESH_NAME_LENGTH] = { 0 }; for (int i = 0; i < model.meshCount; i++) { - fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET); + fseek(iqmFile, iqm.ofs_text + imesh[i].name, SEEK_SET); fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used... model.meshes[i].vertexCount = imesh[i].num_vertexes; - model.meshes[i].vertices = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex positions - model.meshes[i].normals = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); // Default vertex normals - model.meshes[i].texcoords = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*2); // Default vertex texcoords + model.meshes[i].vertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex positions + model.meshes[i].normals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex normals + model.meshes[i].texcoords = RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); // Default vertex texcoords - model.meshes[i].boneIds = RL_MALLOC(sizeof(int)*model.meshes[i].vertexCount*4); // Up-to 4 bones supported! - model.meshes[i].boneWeights = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*4); // Up-to 4 bones supported! + model.meshes[i].boneIds = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported! + model.meshes[i].boneWeights = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported! model.meshes[i].triangleCount = imesh[i].num_triangles; - model.meshes[i].indices = RL_MALLOC(sizeof(unsigned short)*model.meshes[i].triangleCount*3); + model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); // Animated verted data, what we actually process for rendering // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) - model.meshes[i].animVertices = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); - model.meshes[i].animNormals = RL_MALLOC(sizeof(float)*model.meshes[i].vertexCount*3); + model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + + model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); } // Triangles data processing - tri = RL_MALLOC(sizeof(IQMTriangle)*iqm.num_triangles); + tri = RL_MALLOC(iqm.num_triangles*sizeof(IQMTriangle)); fseek(iqmFile, iqm.ofs_triangles, SEEK_SET); - fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile); + fread(tri, iqm.num_triangles*sizeof(IQMTriangle), 1, iqmFile); for (int m = 0; m < model.meshCount; m++) { @@ -3092,9 +3132,9 @@ static Model LoadIQM(const char *fileName) } // Vertex arrays data processing - va = RL_MALLOC(sizeof(IQMVertexArray)*iqm.num_vertexarrays); + va = RL_MALLOC(iqm.num_vertexarrays*sizeof(IQMVertexArray)); fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET); - fread(va, sizeof(IQMVertexArray)*iqm.num_vertexarrays, 1, iqmFile); + fread(va, iqm.num_vertexarrays*sizeof(IQMVertexArray), 1, iqmFile); for (int i = 0; i < iqm.num_vertexarrays; i++) { @@ -3102,9 +3142,9 @@ static Model LoadIQM(const char *fileName) { case IQM_POSITION: { - vertex = RL_MALLOC(sizeof(float)*iqm.num_vertexes*3); + vertex = RL_MALLOC(iqm.num_vertexes*3*sizeof(float)); fseek(iqmFile, va[i].offset, SEEK_SET); - fread(vertex, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); + fread(vertex, iqm.num_vertexes*3*sizeof(float), 1, iqmFile); for (int m = 0; m < iqm.num_meshes; m++) { @@ -3119,9 +3159,9 @@ static Model LoadIQM(const char *fileName) } break; case IQM_NORMAL: { - normal = RL_MALLOC(sizeof(float)*iqm.num_vertexes*3); + normal = RL_MALLOC(iqm.num_vertexes*3*sizeof(float)); fseek(iqmFile, va[i].offset, SEEK_SET); - fread(normal, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); + fread(normal, iqm.num_vertexes*3*sizeof(float), 1, iqmFile); for (int m = 0; m < iqm.num_meshes; m++) { @@ -3136,9 +3176,9 @@ static Model LoadIQM(const char *fileName) } break; case IQM_TEXCOORD: { - text = RL_MALLOC(sizeof(float)*iqm.num_vertexes*2); + text = RL_MALLOC(iqm.num_vertexes*2*sizeof(float)); fseek(iqmFile, va[i].offset, SEEK_SET); - fread(text, sizeof(float)*iqm.num_vertexes*2, 1, iqmFile); + fread(text, iqm.num_vertexes*2*sizeof(float), 1, iqmFile); for (int m = 0; m < iqm.num_meshes; m++) { @@ -3152,9 +3192,9 @@ static Model LoadIQM(const char *fileName) } break; case IQM_BLENDINDEXES: { - blendi = RL_MALLOC(sizeof(char)*iqm.num_vertexes*4); + blendi = RL_MALLOC(iqm.num_vertexes*4*sizeof(char)); fseek(iqmFile, va[i].offset, SEEK_SET); - fread(blendi, sizeof(char)*iqm.num_vertexes*4, 1, iqmFile); + fread(blendi, iqm.num_vertexes*4*sizeof(char), 1, iqmFile); for (int m = 0; m < iqm.num_meshes; m++) { @@ -3168,9 +3208,9 @@ static Model LoadIQM(const char *fileName) } break; case IQM_BLENDWEIGHTS: { - blendw = RL_MALLOC(sizeof(unsigned char)*iqm.num_vertexes*4); - fseek(iqmFile,va[i].offset,SEEK_SET); - fread(blendw,sizeof(unsigned char)*iqm.num_vertexes*4,1,iqmFile); + blendw = RL_MALLOC(iqm.num_vertexes*4*sizeof(unsigned char)); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(blendw, iqm.num_vertexes*4*sizeof(unsigned char), 1, iqmFile); for (int m = 0; m < iqm.num_meshes; m++) { @@ -3186,20 +3226,20 @@ static Model LoadIQM(const char *fileName) } // Bones (joints) data processing - ijoint = RL_MALLOC(sizeof(IQMJoint)*iqm.num_joints); + ijoint = RL_MALLOC(iqm.num_joints*sizeof(IQMJoint)); fseek(iqmFile, iqm.ofs_joints, SEEK_SET); - fread(ijoint, sizeof(IQMJoint)*iqm.num_joints, 1, iqmFile); + fread(ijoint, iqm.num_joints*sizeof(IQMJoint), 1, iqmFile); model.boneCount = iqm.num_joints; - model.bones = RL_MALLOC(sizeof(BoneInfo)*iqm.num_joints); - model.bindPose = RL_MALLOC(sizeof(Transform)*iqm.num_joints); + model.bones = RL_MALLOC(iqm.num_joints*sizeof(BoneInfo)); + model.bindPose = RL_MALLOC(iqm.num_joints*sizeof(Transform)); for (int i = 0; i < iqm.num_joints; i++) { // Bones model.bones[i].parent = ijoint[i].parent; fseek(iqmFile, iqm.ofs_text + ijoint[i].name, SEEK_SET); - fread(model.bones[i].name,sizeof(char)*BONE_NAME_LENGTH, 1, iqmFile); + fread(model.bones[i].name, BONE_NAME_LENGTH*sizeof(char), 1, iqmFile); // Bind pose (base pose) model.bindPose[i].translation.x = ijoint[i].translate[0]; @@ -3264,7 +3304,7 @@ static const unsigned char base64Table[] = { static int GetSizeBase64(char *input) { int size = 0; - + for (int i = 0; input[4*i] != 0; i++) { if (input[4*i + 3] == '=') @@ -3274,7 +3314,7 @@ static int GetSizeBase64(char *input) } else size += 3; } - + return size; } @@ -3314,28 +3354,110 @@ static unsigned char *DecodeBase64(char *input, int *size) return buf; } +// Load texture from cgltf_image +static Texture LoadTextureFromCgltfImage(cgltf_image *image, const char *texPath, Color tint) +{ + Texture texture = { 0 }; + + if (image->uri) + { + if ((strlen(image->uri) > 5) && + (image->uri[0] == 'd') && + (image->uri[1] == 'a') && + (image->uri[2] == 't') && + (image->uri[3] == 'a') && + (image->uri[4] == ':')) + { + // Data URI + // Format: data:;base64, + + // Find the comma + int i = 0; + while ((image->uri[i] != ',') && (image->uri[i] != 0)) i++; + + if (image->uri[i] == 0) TraceLog(LOG_WARNING, "CGLTF Image: Invalid data URI"); + else + { + int size; + unsigned char *data = DecodeBase64(image->uri + i + 1, &size); + + int w, h; + unsigned char *raw = stbi_load_from_memory(data, size, &w, &h, NULL, 4); + + Image rimage = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8); + + // TODO: Tint shouldn't be applied here! + ImageColorTint(&rimage, tint); + texture = LoadTextureFromImage(rimage); + UnloadImage(rimage); + } + } + else + { + Image rimage = LoadImage(TextFormat("%s/%s", texPath, image->uri)); + + // TODO: Tint shouldn't be applied here! + ImageColorTint(&rimage, tint); + texture = LoadTextureFromImage(rimage); + UnloadImage(rimage); + } + } + else if (image->buffer_view) + { + unsigned char *data = RL_MALLOC(image->buffer_view->size); + int n = image->buffer_view->offset; + int stride = image->buffer_view->stride ? image->buffer_view->stride : 1; + + for (int i = 0; i < image->buffer_view->size; i++) + { + data[i] = ((unsigned char *)image->buffer_view->buffer->data)[n]; + n += stride; + } + + int w, h; + unsigned char *raw = stbi_load_from_memory(data, image->buffer_view->size, &w, &h, NULL, 4); + free(data); + + Image rimage = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8); + free(raw); + + // TODO: Tint shouldn't be applied here! + ImageColorTint(&rimage, tint); + texture = LoadTextureFromImage(rimage); + UnloadImage(rimage); + } + else + { + Image rimage = LoadImageEx(&tint, 1, 1); + texture = LoadTextureFromImage(rimage); + UnloadImage(rimage); + } + + return texture; +} + // Load glTF mesh data static Model LoadGLTF(const char *fileName) { /*********************************************************************************** - + Function implemented by Wilhem Barbier (@wbrbr) - + Features: - Supports .gltf and .glb files - Supports embedded (base64) or external textures - Loads the albedo/diffuse texture (other maps could be added) - Supports multiple mesh per model and multiple primitives per model - + Some restrictions (not exhaustive): - Triangle-only meshes - Not supported node hierarchies or transforms - Only loads the diffuse texture... but not too hard to support other maps (normal, roughness/metalness...) - - Only supports unsigned short indices (no byte/unsigned int) + - Only supports unsigned short indices (no byte/unsigned int) - Only supports float for texture coordinates (no byte/unsigned short) - + *************************************************************************************/ - + #define LOAD_ACCESSOR(type, nbcomp, acc, dst) \ { \ int n = 0; \ @@ -3347,7 +3469,7 @@ static Model LoadGLTF(const char *fileName) n += acc->stride/sizeof(type);\ }\ } - + Model model = { 0 }; // glTF file loading @@ -3379,124 +3501,77 @@ static Model LoadGLTF(const char *fileName) // Read data buffers result = cgltf_load_buffers(&options, data, fileName); + if (result != cgltf_result_success) TraceLog(LOG_INFO, "[%s][%s] Error loading mesh/material buffers", fileName, (data->file_type == 2)? "glb" : "gltf"); int primitivesCount = 0; - + for (int i = 0; i < data->meshes_count; i++) primitivesCount += (int)data->meshes[i].primitives_count; // Process glTF data and map to model model.meshCount = primitivesCount; model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); model.materialCount = data->materials_count + 1; - model.materials = RL_MALLOC(model.materialCount * sizeof(Material)); - model.meshMaterial = RL_MALLOC(model.meshCount * sizeof(int)); + model.materials = RL_MALLOC(model.materialCount*sizeof(Material)); + model.meshMaterial = RL_MALLOC(model.meshCount*sizeof(int)); + for (int i = 0; i < model.meshCount; i++) model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int)); + + //For each material for (int i = 0; i < model.materialCount - 1; i++) { - Color tint = WHITE; - Texture2D texture = { 0 }; + model.materials[i] = LoadMaterialDefault(); + Color tint = (Color){ 255, 255, 255, 255 }; const char *texPath = GetDirectoryPath(fileName); - - if (data->materials[i].pbr_metallic_roughness.base_color_factor) + + //Ensure material follows raylib support for PBR (metallic/roughness flow) + if (data->materials[i].has_pbr_metallic_roughness) { - tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0] * 255.99f); - tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1] * 255.99f); - tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2] * 255.99f); - tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3] * 255.99f); - } - else - { - tint.r = 1.f; - tint.g = 1.f; - tint.b = 1.f; - tint.a = 1.f; - } - - if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture) - { - cgltf_image *img = data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image; - - if (img->uri) + float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor; + float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor; + + // NOTE: Material name not used for the moment + //if (model.materials[i].name && data->materials[i].name) strcpy(model.materials[i].name, data->materials[i].name); + + // TODO: REview: shouldn't these be *255 ??? + tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255); + tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255); + tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255); + tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255); + + model.materials[i].maps[MAP_ROUGHNESS].color = tint; + + if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture) { - if ((strlen(img->uri) > 5) && - (img->uri[0] == 'd') && - (img->uri[1] == 'a') && - (img->uri[2] == 't') && - (img->uri[3] == 'a') && - (img->uri[4] == ':')) - { - // Data URI - // Format: data:;base64, - - // Find the comma - int i = 0; - while ((img->uri[i] != ',') && (img->uri[i] != 0)) i++; - - if (img->uri[i] == 0) TraceLog(LOG_WARNING, "[%s] Invalid data URI", fileName); - else - { - int size; - unsigned char *data = DecodeBase64(img->uri + i + 1, &size); - - int w, h; - unsigned char *raw = stbi_load_from_memory(data, size, &w, &h, NULL, 4); - - Image image = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8); - ImageColorTint(&image, tint); - texture = LoadTextureFromImage(image); - UnloadImage(image); - } - } - else - { - char *textureName = img->uri; - char *texturePath = RL_MALLOC(strlen(texPath) + strlen(textureName) + 2); - strcpy(texturePath, texPath); - strcat(texturePath, "/"); - strcat(texturePath, textureName); - - Image image = LoadImage(texturePath); - ImageColorTint(&image, tint); - texture = LoadTextureFromImage(image); - UnloadImage(image); - } + model.materials[i].maps[MAP_ALBEDO].texture = LoadTextureFromCgltfImage(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, texPath, tint); } - else if (img->buffer_view) - { - unsigned char *data = RL_MALLOC(img->buffer_view->size); - int n = img->buffer_view->offset; - int stride = img->buffer_view->stride ? img->buffer_view->stride : 1; - - for (int i = 0; i < img->buffer_view->size; i++) - { - data[i] = ((unsigned char *)img->buffer_view->buffer->data)[n]; - n += stride; - } - int w, h; - unsigned char *raw = stbi_load_from_memory(data, img->buffer_view->size, &w, &h, NULL, 4); - - Image image = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8); - ImageColorTint(&image, tint); - texture = LoadTextureFromImage(image); - UnloadImage(image); - } - else + // NOTE: Tint isn't need for other textures.. pass null or clear? + // Just set as white, multiplying by white has no effect + tint = WHITE; + + if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture) { - Image image = LoadImageEx(&tint, 1, 1); - texture = LoadTextureFromImage(image); - UnloadImage(image); + model.materials[i].maps[MAP_ROUGHNESS].texture = LoadTextureFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath, tint); + } + model.materials[i].maps[MAP_ROUGHNESS].value = roughness; + model.materials[i].maps[MAP_METALNESS].value = metallic; + + if (data->materials[i].normal_texture.texture) + { + model.materials[i].maps[MAP_NORMAL].texture = LoadTextureFromCgltfImage(data->materials[i].normal_texture.texture->image, texPath, tint); } - model.materials[i] = LoadMaterialDefault(); - model.materials[i].maps[MAP_DIFFUSE].texture = texture; + if (data->materials[i].occlusion_texture.texture) + { + model.materials[i].maps[MAP_OCCLUSION].texture = LoadTextureFromCgltfImage(data->materials[i].occlusion_texture.texture->image, texPath, tint); + } } } - + model.materials[model.materialCount - 1] = LoadMaterialDefault(); int primitiveIndex = 0; - + for (int i = 0; i < data->meshes_count; i++) { for (int p = 0; p < data->meshes[i].primitives_count; p++) @@ -3521,7 +3596,7 @@ static Model LoadGLTF(const char *fileName) else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_texcoord) { cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data; - + if (acc->component_type == cgltf_component_type_r_32f) { model.meshes[primitiveIndex].texcoords = RL_MALLOC(sizeof(float)*acc->count*2); @@ -3529,14 +3604,14 @@ static Model LoadGLTF(const char *fileName) } else { - // TODO: support normalized unsigned byte/unsigned short texture coordinates + // TODO: Support normalized unsigned byte/unsigned short texture coordinates TraceLog(LOG_WARNING, "[%s] Texture coordinates must be float", fileName); } } } cgltf_accessor *acc = data->meshes[i].primitives[p].indices; - + if (acc) { if (acc->component_type == cgltf_component_type_r_16u) @@ -3547,7 +3622,7 @@ static Model LoadGLTF(const char *fileName) } else { - // TODO: support unsigned byte/unsigned int + // TODO: Support unsigned byte/unsigned int TraceLog(LOG_WARNING, "[%s] Indices must be unsigned short", fileName); } } @@ -3566,7 +3641,7 @@ static Model LoadGLTF(const char *fileName) { model.meshMaterial[primitiveIndex] = model.materialCount - 1;; } - + primitiveIndex++; } } diff --git a/src/physac.h b/src/physac.h index 42cb01984..4119feaed 100644 --- a/src/physac.h +++ b/src/physac.h @@ -1896,7 +1896,7 @@ static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) static void InitTimer(void) { srand(time(NULL)); // Initialize random seed - + #if defined(_WIN32) QueryPerformanceFrequency((unsigned long long int *) &frequency); #endif @@ -1911,7 +1911,7 @@ static void InitTimer(void) mach_timebase_info(&timebase); frequency = (timebase.denom*1e9)/timebase.numer; #endif - + baseTime = GetTimeCount(); // Get MONOTONIC clock time offset startTime = GetCurrentTime(); // Get current time } @@ -1920,7 +1920,7 @@ static void InitTimer(void) static uint64_t GetTimeCount(void) { uint64_t value = 0; - + #if defined(_WIN32) QueryPerformanceCounter((unsigned long long int *) &value); #endif diff --git a/src/raudio.c b/src/raudio.c index bfd7ef220..188c05329 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -124,7 +124,7 @@ // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough // In case of music-stalls, just increase this number -#define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. 16bit, Mono: 8Kb) +#define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. 16bit, Mono: 8Kb) //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -189,6 +189,8 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo #define DEVICE_CHANNELS 2 #define DEVICE_SAMPLE_RATE 44100 +#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 + typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioBufferUsage; // Audio buffer structure @@ -196,25 +198,31 @@ typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioB // playback device depending on whether or not data is streamed struct rAudioBuffer { ma_pcm_converter dsp; // PCM data converter - + float volume; // Audio buffer volume float pitch; // Audio buffer pitch - + bool playing; // Audio buffer state: AUDIO_PLAYING bool paused; // Audio buffer state: AUDIO_PAUSED bool looping; // Audio buffer looping, always true for AudioStreams int usage; // Audio buffer usage mode: STATIC or STREAM - - bool isSubBufferProcessed[2]; - unsigned int frameCursorPos; - unsigned int bufferSizeInFrames; - - rAudioBuffer *next; - rAudioBuffer *prev; - unsigned char *buffer; + + bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer) + unsigned int frameCursorPos; // Frame cursor position + unsigned int bufferSizeInFrames; // Total buffer size in frames + unsigned int totalFramesProcessed; // Total frames processed in this buffer (required for play timming) + + unsigned char *buffer; // Data buffer, on music stream keeps filling + + rAudioBuffer *next; // Next audio buffer on the list + rAudioBuffer *prev; // Previous audio buffer on the list }; -#define AudioBuffer rAudioBuffer // HACK: To avoid CoreAudio (macOS) symbol collision +#define AudioBuffer rAudioBuffer // HACK: To avoid CoreAudio (macOS) symbol collision + +// Audio buffers are tracked in a linked list +static AudioBuffer *firstAudioBuffer = NULL; +static AudioBuffer *lastAudioBuffer = NULL; // miniaudio global variables static ma_context context; @@ -223,9 +231,10 @@ static ma_mutex audioLock; static bool isAudioInitialized = false; static float masterVolume = 1.0f; -// Audio buffers are tracked in a linked list -static AudioBuffer *firstAudioBuffer = NULL; -static AudioBuffer *lastAudioBuffer = NULL; +// Multi channel playback global variables +AudioBuffer *audioBufferPool[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 }; +unsigned int audioBufferPoolCounter = 0; +unsigned int audioBufferPoolChannels[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 }; // miniaudio functions declaration static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message); @@ -247,19 +256,9 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch); void TrackAudioBuffer(AudioBuffer *buffer); void UntrackAudioBuffer(AudioBuffer *buffer); + //---------------------------------------------------------------------------------- -// Multi channel playback globals -//---------------------------------------------------------------------------------- - -// Number of channels in the audio pool -#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 - -// Audio buffer pool -AudioBuffer *audioBufferPool[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 }; - -// These are used to determine the oldest playing channel -unsigned long audioBufferPoolCounter = 0; -unsigned long audioBufferPoolChannels[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 }; +// miniaudio functions definitions //---------------------------------------------------------------------------------- // Log callback function @@ -290,7 +289,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const if (!audioBuffer->playing || audioBuffer->paused) continue; ma_uint32 framesRead = 0; - + while (1) { if (framesRead > frameCount) @@ -303,7 +302,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const // Just read as much data as we can from the stream ma_uint32 framesToRead = (frameCount - framesRead); - + while (framesToRead > 0) { float tempBuffer[1024]; // 512 frames for stereo @@ -319,6 +318,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const { float *framesOut = (float *)pFramesOut + (framesRead*device.playback.channels); float *framesIn = tempBuffer; + MixAudioFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume); framesToRead -= framesJustRead; @@ -387,7 +387,7 @@ static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, { if (framesRead >= frameCount) break; } - else + else { if (isSubBufferProcessed[currentSubBufferIndex]) break; } @@ -402,7 +402,7 @@ static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, } else { - ma_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex; + ma_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames*currentSubBufferIndex; framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer); } @@ -410,7 +410,7 @@ static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer; memcpy((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->buffer + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); - audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead) % audioBuffer->bufferSizeInFrames; + audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead)%audioBuffer->bufferSizeInFrames; framesRead += framesToRead; // If we've read to the end of the buffer, mark it as processed @@ -465,7 +465,7 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr static void InitAudioBufferPool() { // Dummy buffers - for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) + for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) { audioBufferPool[i] = InitAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, 0, AUDIO_BUFFER_USAGE_STATIC); } @@ -474,7 +474,11 @@ static void InitAudioBufferPool() // Close the audio buffers pool static void CloseAudioBufferPool() { - for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) RL_FREE(audioBufferPool[i]); + for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) + { + RL_FREE(audioBufferPool[i]->buffer); + RL_FREE(audioBufferPool[i]); + } } //---------------------------------------------------------------------------------- @@ -485,9 +489,8 @@ void InitAudioDevice(void) { // Init audio context ma_context_config contextConfig = ma_context_config_init(); - contextConfig.logCallback = OnLog; - + ma_result result = ma_context_init(NULL, 0, &contextConfig, &context); if (result != MA_SUCCESS) { @@ -553,11 +556,7 @@ void InitAudioDevice(void) // Close the audio device for all contexts void CloseAudioDevice(void) { - if (!isAudioInitialized) - { - TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized"); - } - else + if (isAudioInitialized) { ma_mutex_uninit(&audioLock); ma_device_uninit(&device); @@ -567,6 +566,7 @@ void CloseAudioDevice(void) TraceLog(LOG_INFO, "Audio device closed successfully"); } + else TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized"); } // Check if device has been initialized successfully @@ -588,12 +588,12 @@ void SetMasterVolume(float volume) // Module Functions Definition - Audio Buffer management //---------------------------------------------------------------------------------- -// Create a new audio buffer. Initially filled with silence +// Initialize a new audio buffer (filled with silence) AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 bufferSizeInFrames, int usage) { - AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(sizeof(*audioBuffer), 1); - audioBuffer->buffer = RL_CALLOC((bufferSizeInFrames*channels*ma_get_bytes_per_sample(format)), 1); - + AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(1, sizeof(AudioBuffer)); + audioBuffer->buffer = RL_CALLOC(bufferSizeInFrames*channels*ma_get_bytes_per_sample(format), 1); + if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "InitAudioBuffer() : Failed to allocate memory for audio buffer"); @@ -612,7 +612,7 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam dspConfig.onRead = OnAudioBufferDSPRead; // Callback on data reading dspConfig.pUserData = audioBuffer; // Audio data pointer dspConfig.allowDynamicSampleRate = true; // Required for pitch shifting - + ma_result result = ma_pcm_converter_init(&dspConfig, &audioBuffer->dsp); if (result != MA_SUCCESS) @@ -659,7 +659,7 @@ void CloseAudioBuffer(AudioBuffer *buffer) bool IsAudioBufferPlaying(AudioBuffer *buffer) { bool result = false; - + if (buffer != NULL) result = (buffer->playing && !buffer->paused); else TraceLog(LOG_ERROR, "IsAudioBufferPlaying() : No audio buffer"); @@ -690,6 +690,7 @@ void StopAudioBuffer(AudioBuffer *buffer) buffer->playing = false; buffer->paused = false; buffer->frameCursorPos = 0; + buffer->totalFramesProcessed = 0; buffer->isSubBufferProcessed[0] = true; buffer->isSubBufferProcessed[1] = true; } @@ -701,7 +702,7 @@ void StopAudioBuffer(AudioBuffer *buffer) void PauseAudioBuffer(AudioBuffer *buffer) { if (buffer != NULL) buffer->paused = true; - else TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer"); + else TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer"); } // Resume an audio buffer @@ -725,8 +726,10 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch) { float pitchMul = pitch/buffer->pitch; - // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches - // will make the sound faster; lower pitches make it slower. + // Pitching is just an adjustment of the sample rate. + // Note that this changes the duration of the sound: + // - higher pitches will make the sound faster + // - lower pitches make it slower ma_uint32 newOutputSampleRate = (ma_uint32)((float)buffer->dsp.src.config.sampleRateOut/pitchMul); buffer->pitch *= (float)buffer->dsp.src.config.sampleRateOut/newOutputSampleRate; @@ -817,7 +820,7 @@ Sound LoadSoundFromWave(Wave wave) if (wave.data != NULL) { - // When using miniaudio we need to do our own mixing. + // When using miniaudio we need to do our own mixing. // To simplify this we need convert the format of each sound to be consistent with // the format used to open the playback device. We can do this two ways: // @@ -869,16 +872,14 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) { AudioBuffer *audioBuffer = sound.stream.buffer; - if (audioBuffer == NULL) + if (audioBuffer != NULL) { - TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer"); - return; + StopAudioBuffer(audioBuffer); + + // 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*ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn)); } - - StopAudioBuffer(audioBuffer); - - // 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*ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn)); + else TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer"); } // Export wave data to file @@ -913,37 +914,40 @@ void ExportWaveAsCode(Wave wave, const char *fileName) FILE *txtFile = fopen(fileName, "wt"); - fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n"); - fprintf(txtFile, "// //\n"); - fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n"); - fprintf(txtFile, "// //\n"); - fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n"); - fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n"); - fprintf(txtFile, "// //\n"); - fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n"); - fprintf(txtFile, "// //\n"); - fprintf(txtFile, "//////////////////////////////////////////////////////////////////////////////////\n\n"); + if (txtFile != NULL) + { + fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n"); + fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "//////////////////////////////////////////////////////////////////////////////////\n\n"); #if !defined(RAUDIO_STANDALONE) - // Get file name from path and convert variable name to uppercase - strcpy(varFileName, GetFileNameWithoutExt(fileName)); - for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } + // Get file name from path and convert variable name to uppercase + strcpy(varFileName, GetFileNameWithoutExt(fileName)); + for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } #else - strcpy(varFileName, fileName); + strcpy(varFileName, fileName); #endif - fprintf(txtFile, "// Wave data information\n"); - fprintf(txtFile, "#define %s_SAMPLE_COUNT %i\n", varFileName, wave.sampleCount); - fprintf(txtFile, "#define %s_SAMPLE_RATE %i\n", varFileName, wave.sampleRate); - fprintf(txtFile, "#define %s_SAMPLE_SIZE %i\n", varFileName, wave.sampleSize); - fprintf(txtFile, "#define %s_CHANNELS %i\n\n", varFileName, wave.channels); + fprintf(txtFile, "// Wave data information\n"); + fprintf(txtFile, "#define %s_SAMPLE_COUNT %i\n", varFileName, wave.sampleCount); + fprintf(txtFile, "#define %s_SAMPLE_RATE %i\n", varFileName, wave.sampleRate); + fprintf(txtFile, "#define %s_SAMPLE_SIZE %i\n", varFileName, wave.sampleSize); + fprintf(txtFile, "#define %s_CHANNELS %i\n\n", varFileName, wave.channels); - // Write byte data as hexadecimal text - fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize); - for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)wave.data)[i]); - fprintf(txtFile, "0x%x };\n", ((unsigned char *)wave.data)[dataSize - 1]); + // Write byte data as hexadecimal text + fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize); + for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)wave.data)[i]); + fprintf(txtFile, "0x%x };\n", ((unsigned char *)wave.data)[dataSize - 1]); - fclose(txtFile); + fclose(txtFile); + } } // Play a sound @@ -956,7 +960,7 @@ void PlaySound(Sound sound) void PlaySoundMulti(Sound sound) { int index = -1; - unsigned long oldAge = 0; + unsigned int oldAge = 0; int oldIndex = -1; // find the first non playing pool entry @@ -967,7 +971,7 @@ void PlaySoundMulti(Sound sound) oldAge = audioBufferPoolChannels[i]; oldIndex = i; } - + if (!IsAudioBufferPlaying(audioBufferPool[i])) { index = i; @@ -979,17 +983,17 @@ void PlaySoundMulti(Sound sound) if (index == -1) { TraceLog(LOG_WARNING,"pool age %i ended a sound early no room in buffer pool", audioBufferPoolCounter); - + if (oldIndex == -1) { // Shouldn't be able to get here... but just in case something odd happens! TraceLog(LOG_ERROR,"sound buffer pool couldn't determine oldest buffer not playing sound"); - + return; } - + index = oldIndex; - + // Just in case... StopAudioBuffer(audioBufferPool[index]); } @@ -1000,7 +1004,7 @@ void PlaySoundMulti(Sound sound) audioBufferPoolChannels[index] = audioBufferPoolCounter; audioBufferPoolCounter++; - + audioBufferPool[index]->volume = sound.stream.buffer->volume; audioBufferPool[index]->pitch = sound.stream.buffer->pitch; audioBufferPool[index]->looping = sound.stream.buffer->looping; @@ -1023,12 +1027,12 @@ void StopSoundMulti(void) int GetSoundsPlaying(void) { int counter = 0; - + for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) { if (IsAudioBufferPlaying(audioBufferPool[i])) counter++; } - + return counter; } @@ -1184,14 +1188,8 @@ Music LoadMusicStream(const char *fileName) // OGG bit rate defaults to 16 bit, it's enough for compressed format music.stream = InitAudioStream(info.sample_rate, 16, info.channels); music.sampleCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData)*info.channels; - music.sampleLeft = music.sampleCount; music.loopCount = 0; // Infinite loop by default musicLoaded = true; - - TraceLog(LOG_DEBUG, "[%s] OGG total samples: %i", fileName, music.sampleCount); - TraceLog(LOG_DEBUG, "[%s] OGG sample rate: %i", fileName, info.sample_rate); - TraceLog(LOG_DEBUG, "[%s] OGG channels: %i", fileName, info.channels); - TraceLog(LOG_DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required); } } #endif @@ -1207,14 +1205,8 @@ Music LoadMusicStream(const char *fileName) music.stream = InitAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); music.sampleCount = (unsigned int)ctxFlac->totalSampleCount; - music.sampleLeft = music.sampleCount; music.loopCount = 0; // Infinite loop by default musicLoaded = true; - - TraceLog(LOG_DEBUG, "[%s] FLAC total samples: %i", fileName, music.sampleCount); - TraceLog(LOG_DEBUG, "[%s] FLAC sample rate: %i", fileName, ctxFlac->sampleRate); - TraceLog(LOG_DEBUG, "[%s] FLAC bits per sample: %i", fileName, ctxFlac->bitsPerSample); - TraceLog(LOG_DEBUG, "[%s] FLAC channels: %i", fileName, ctxFlac->channels); } } #endif @@ -1223,7 +1215,7 @@ Music LoadMusicStream(const char *fileName) { drmp3 *ctxMp3 = RL_MALLOC(sizeof(drmp3)); music.ctxData = ctxMp3; - + int result = drmp3_init_file(ctxMp3, fileName, NULL); if (result > 0) @@ -1232,14 +1224,8 @@ Music LoadMusicStream(const char *fileName) music.stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); music.sampleCount = drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels; - music.sampleLeft = music.sampleCount; music.loopCount = 0; // Infinite loop by default musicLoaded = true; - - TraceLog(LOG_INFO, "[%s] MP3 sample rate: %i", fileName, ctxMp3->sampleRate); - TraceLog(LOG_INFO, "[%s] MP3 bits per sample: %i", fileName, 32); - TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, ctxMp3->channels); - TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music.sampleCount); } } #endif @@ -1250,7 +1236,7 @@ Music LoadMusicStream(const char *fileName) int result = jar_xm_create_context_from_file(&ctxXm, 48000, fileName); - if (result > 0) // XM context created successfully + if (result == 0) // XM context created successfully { music.ctxType = MUSIC_MODULE_XM; jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops @@ -1258,14 +1244,10 @@ Music LoadMusicStream(const char *fileName) // NOTE: Only stereo is supported for XM music.stream = InitAudioStream(48000, 16, 2); music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); - music.sampleLeft = music.sampleCount; music.loopCount = 0; // Infinite loop by default musicLoaded = true; - - music.ctxData = ctxXm; - TraceLog(LOG_INFO, "[%s] XM number of samples: %i", fileName, music.sampleCount); - TraceLog(LOG_INFO, "[%s] XM track length: %11.6f sec", fileName, (float)music.sampleCount/48000.0f); + music.ctxData = ctxXm; } } #endif @@ -1274,7 +1256,7 @@ Music LoadMusicStream(const char *fileName) { jar_mod_context_t *ctxMod = RL_MALLOC(sizeof(jar_mod_context_t)); music.ctxData = ctxMod; - + jar_mod_init(ctxMod); int result = jar_mod_load_file(ctxMod, fileName); @@ -1285,12 +1267,8 @@ Music LoadMusicStream(const char *fileName) // NOTE: Only stereo is supported for MOD music.stream = InitAudioStream(48000, 16, 2); music.sampleCount = (unsigned int)jar_mod_max_samples(ctxMod); - music.sampleLeft = music.sampleCount; music.loopCount = 0; // Infinite loop by default musicLoaded = true; - - TraceLog(LOG_INFO, "[%s] MOD number of samples: %i", fileName, music.sampleLeft); - TraceLog(LOG_INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music.sampleCount/48000.0f); } } #endif @@ -1316,6 +1294,15 @@ Music LoadMusicStream(const char *fileName) TraceLog(LOG_WARNING, "[%s] Music file could not be opened", fileName); } + else + { + // Show some music stream info + TraceLog(LOG_INFO, "[%s] Music file successfully loaded:", fileName); + TraceLog(LOG_INFO, " Total samples: %i", music.sampleCount); + TraceLog(LOG_INFO, " Sample rate: %i Hz", music.stream.sampleRate); + TraceLog(LOG_INFO, " Sample size: %i bits", music.stream.sampleSize); + TraceLog(LOG_INFO, " Channels: %i (%s)", music.stream.channels, (music.stream.channels == 1)? "Mono" : (music.stream.channels == 2)? "Stereo" : "Multi"); + } return music; } @@ -1348,21 +1335,18 @@ void PlayMusicStream(Music music) { AudioBuffer *audioBuffer = music.stream.buffer; - if (audioBuffer == NULL) + if (audioBuffer != NULL) { - TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); - return; + // For music streams, we need to make sure we maintain the frame cursor position + // This is a hack for this section of code in UpdateMusicStream() + // NOTE: In case window is minimized, music stream is stopped, just make sure to + // play again on window restore: if (IsMusicPlaying(music)) PlayMusicStream(music); + ma_uint32 frameCursorPos = audioBuffer->frameCursorPos; + PlayAudioStream(music.stream); // WARNING: This resets the cursor position. + audioBuffer->frameCursorPos = frameCursorPos; } + else TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); - // For music streams, we need to make sure we maintain the frame cursor position. This is hack for this section of code in UpdateMusicStream() - // // NOTE: In case window is minimized, music stream is stopped, - // // just make sure to play again on window restore - // if (IsMusicPlaying(music)) PlayMusicStream(music); - ma_uint32 frameCursorPos = audioBuffer->frameCursorPos; - - PlayAudioStream(music.stream); // <-- This resets the cursor position. - - audioBuffer->frameCursorPos = frameCursorPos; } // Pause music playing @@ -1389,7 +1373,7 @@ void StopMusicStream(Music music) case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break; #endif #if defined(SUPPORT_FILEFORMAT_FLAC) - case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break; + case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, 0); break; #endif #if defined(SUPPORT_FILEFORMAT_MP3) case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, 0); break; @@ -1402,8 +1386,6 @@ void StopMusicStream(Music music) #endif default: break; } - - music.sampleLeft = music.sampleCount; } // Update (re-fill) music buffers if data already processed @@ -1416,12 +1398,16 @@ void UpdateMusicStream(Music music) // NOTE: Using dynamic allocation because it could require more than 16KB void *pcm = RL_CALLOC(subBufferSizeInFrames*music.stream.channels*music.stream.sampleSize/8, 1); - int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts + int samplesCount = 0; // Total size of data streamed in L+R samples for xm floats, individual L or R for ogg shorts - while (IsAudioBufferProcessed(music.stream)) + // TODO: Get the sampleLeft using totalFramesProcessed... but first, get total frames processed correctly... + //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels; + int sampleLeft = music.sampleCount - (music.stream.buffer->totalFramesProcessed*music.stream.channels); + + while (IsAudioStreamProcessed(music.stream)) { - if ((music.sampleLeft/music.stream.channels) >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames*music.stream.channels; - else samplesCount = music.sampleLeft; + if ((sampleLeft/music.stream.channels) >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames*music.stream.channels; + else samplesCount = sampleLeft; switch (music.ctxType) { @@ -1437,7 +1423,7 @@ void UpdateMusicStream(Music music) case MUSIC_AUDIO_FLAC: { // NOTE: Returns the number of samples to process (not required) - drflac_read_s16((drflac *)music.ctxData, samplesCount, (short *)pcm); + drflac_read_pcm_frames_s16((drflac *)music.ctxData, samplesCount, (short *)pcm); } break; #endif @@ -1467,15 +1453,15 @@ void UpdateMusicStream(Music music) } UpdateAudioStream(music.stream, pcm, samplesCount); - + if ((music.ctxType == MUSIC_MODULE_XM) || (music.ctxType == MUSIC_MODULE_MOD)) { - if (samplesCount > 1) music.sampleLeft -= samplesCount/2; - else music.sampleLeft -= samplesCount; + if (samplesCount > 1) sampleLeft -= samplesCount/2; + else sampleLeft -= samplesCount; } - else music.sampleLeft -= samplesCount; + else sampleLeft -= samplesCount; - if (music.sampleLeft <= 0) + if (sampleLeft <= 0) { streamEnding = true; break; @@ -1493,13 +1479,10 @@ void UpdateMusicStream(Music music) // Decrease loopCount to stop when required if (music.loopCount > 1) { - music.loopCount--; // Decrease loop count + music.loopCount--; // Decrease loop count PlayMusicStream(music); // Play again } - else - { - if (music.loopCount == 0) PlayMusicStream(music); - } + else if (music.loopCount == 0) PlayMusicStream(music); } else { @@ -1528,7 +1511,7 @@ void SetMusicPitch(Music music, float pitch) } // Set music loop count (loop repeats) -// NOTE: If set to -1, means infinite loop +// NOTE: If set to 0, means infinite loop void SetMusicLoopCount(Music music, int count) { music.loopCount = count; @@ -1549,7 +1532,8 @@ float GetMusicTimePlayed(Music music) { float secondsPlayed = 0.0f; - unsigned int samplesPlayed = music.sampleCount - music.sampleLeft; + //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels; + unsigned int samplesPlayed = music.stream.buffer->totalFramesProcessed*music.stream.channels; secondsPlayed = (float)samplesPlayed/(music.stream.sampleRate*music.stream.channels); return secondsPlayed; @@ -1562,35 +1546,24 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un stream.sampleRate = sampleRate; stream.sampleSize = sampleSize; - - // Only mono and stereo channels are supported - if ((channels > 0) && (channels < 3)) stream.channels = channels; - else - { - TraceLog(LOG_WARNING, "Init audio stream: Number of channels not supported: %i", channels); - stream.channels = 1; // Fallback to mono channel - } + stream.channels = channels; ma_format formatIn = ((stream.sampleSize == 8)? ma_format_u8 : ((stream.sampleSize == 16)? ma_format_s16 : ma_format_f32)); // The size of a streaming buffer must be at least double the size of a period unsigned int periodSize = device.playback.internalBufferSizeInFrames/device.playback.internalPeriods; unsigned int subBufferSize = AUDIO_BUFFER_SIZE; - + if (subBufferSize < periodSize) subBufferSize = periodSize; - AudioBuffer *audioBuffer = InitAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); - - if (audioBuffer == NULL) + stream.buffer = InitAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); + + if (stream.buffer != NULL) { - TraceLog(LOG_ERROR, "InitAudioStream() : Failed to create audio buffer"); - return stream; + stream.buffer->looping = true; // Always loop for streaming buffers + TraceLog(LOG_INFO, "Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.sampleRate, stream.sampleSize, (stream.channels == 1)? "Mono" : "Stereo"); } - - audioBuffer->looping = true; // Always loop for streaming buffers - stream.buffer = audioBuffer; - - TraceLog(LOG_INFO, "Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.sampleRate, stream.sampleSize, (stream.channels == 1)? "Mono" : "Stereo"); + else TraceLog(LOG_ERROR, "InitAudioStream() : Failed to create audio buffer"); return stream; } @@ -1605,67 +1578,67 @@ void CloseAudioStream(AudioStream stream) // Update audio stream buffers with data // NOTE 1: Only updates one buffer of the stream source: unqueue -> update -> queue -// NOTE 2: To unqueue a buffer it needs to be processed: IsAudioBufferProcessed() +// NOTE 2: To unqueue a buffer it needs to be processed: IsAudioStreamProcessed() void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { AudioBuffer *audioBuffer = stream.buffer; - - if (audioBuffer == NULL) + + if (audioBuffer != NULL) { - TraceLog(LOG_ERROR, "UpdateAudioStream() : No audio buffer"); - return; - } - - if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]) - { - ma_uint32 subBufferToUpdate = 0; - - if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1]) + 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. - subBufferToUpdate = 0; - audioBuffer->frameCursorPos = 0; - } - else - { - // Just update whichever sub-buffer is processed. - subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0])? 0 : 1; - } + ma_uint32 subBufferToUpdate = 0; - ma_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; - unsigned char *subBuffer = audioBuffer->buffer + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate); - - // Does this API expect a whole buffer to be updated in one go? Assuming so, but if not will need to change this logic. - if (subBufferSizeInFrames >= (ma_uint32)samplesCount/stream.channels) - { - ma_uint32 framesToWrite = subBufferSizeInFrames; - - if (framesToWrite > ((ma_uint32)samplesCount/stream.channels)) framesToWrite = (ma_uint32)samplesCount/stream.channels; - - ma_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8); - memcpy(subBuffer, data, bytesToWrite); - - // Any leftover frames should be filled with zeros. - ma_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; - - if (leftoverFrameCount > 0) + if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1]) { - memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8)); + // Both buffers are available for updating. + // Update the first one and make sure the cursor is moved back to the front. + subBufferToUpdate = 0; + audioBuffer->frameCursorPos = 0; + } + else + { + // Just update whichever sub-buffer is processed. + subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0])? 0 : 1; } - audioBuffer->isSubBufferProcessed[subBufferToUpdate] = false; + ma_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; + unsigned char *subBuffer = audioBuffer->buffer + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate); + + // TODO: Get total frames processed on this buffer... DOES NOT WORK. + audioBuffer->totalFramesProcessed += subBufferSizeInFrames; + + // Does this API expect a whole buffer to be updated in one go? + // Assuming so, but if not will need to change this logic. + if (subBufferSizeInFrames >= (ma_uint32)samplesCount/stream.channels) + { + ma_uint32 framesToWrite = subBufferSizeInFrames; + + if (framesToWrite > ((ma_uint32)samplesCount/stream.channels)) framesToWrite = (ma_uint32)samplesCount/stream.channels; + + ma_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8); + memcpy(subBuffer, data, bytesToWrite); + + // Any leftover frames should be filled with zeros. + ma_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; + + if (leftoverFrameCount > 0) memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8)); + + audioBuffer->isSubBufferProcessed[subBufferToUpdate] = false; + } + else TraceLog(LOG_ERROR, "UpdateAudioStream() : Attempting to write too many frames to buffer"); } - else TraceLog(LOG_ERROR, "UpdateAudioStream() : Attempting to write too many frames to buffer"); + else TraceLog(LOG_ERROR, "UpdateAudioStream() : Audio buffer not available for updating"); } - else TraceLog(LOG_ERROR, "Audio buffer not available for updating"); + else TraceLog(LOG_ERROR, "UpdateAudioStream() : No audio buffer"); } // Check if any audio stream buffers requires refill -bool IsAudioBufferProcessed(AudioStream stream) +bool IsAudioStreamProcessed(AudioStream stream) { if (stream.buffer == NULL) { - TraceLog(LOG_ERROR, "IsAudioBufferProcessed() : No audio buffer"); + TraceLog(LOG_ERROR, "IsAudioStreamProcessed() : No audio buffer"); return false; } @@ -1901,9 +1874,9 @@ static int SaveWAV(Wave wave, const char *fileName) waveData.subChunkID[3] = 'a'; waveData.subChunkSize = dataSize; - success = fwrite(&riffHeader, sizeof(RiffHeader), 1, wavFile); - success = fwrite(&waveFormat, sizeof(WaveFormat), 1, wavFile); - success = fwrite(&waveData, sizeof(WaveData), 1, wavFile); + fwrite(&riffHeader, sizeof(RiffHeader), 1, wavFile); + fwrite(&waveFormat, sizeof(WaveFormat), 1, wavFile); + fwrite(&waveData, sizeof(WaveData), 1, wavFile); success = fwrite(wave.data, dataSize, 1, wavFile); @@ -1962,7 +1935,7 @@ static Wave LoadFLAC(const char *fileName) // Decode an entire FLAC file in one go uint64_t totalSampleCount; - wave.data = drflac_open_and_decode_file_s16(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount); + wave.data = drflac_open_file_and_read_pcm_frames_s16(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount); wave.sampleCount = (unsigned int)totalSampleCount; wave.sampleSize = 16; diff --git a/src/raudio.h b/src/raudio.h index 8bbbe8613..f53150052 100644 --- a/src/raudio.h +++ b/src/raudio.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raudio - A simple and easy-to-use audio library based on mini_al +* raudio - A simple and easy-to-use audio library based on miniaudio * * FEATURES: * - Manage audio device (init/close) @@ -20,7 +20,7 @@ * * CONTRIBUTORS: * David Reid (github: @mackron) (Nov. 2017): -* - Complete port to mini_al library +* - Complete port to miniaudio library * * Joshua Reisenauer (github: @kd7tck) (2015) * - XM audio module support (jar_xm) @@ -110,9 +110,8 @@ typedef struct Sound { typedef struct Music { int ctxType; // Type of music context (audio filetype) void *ctxData; // Audio context data, depends on type - + unsigned int sampleCount; // Total number of samples - unsigned int sampleLeft; // Number of samples left to end unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop AudioStream stream; // Audio stream @@ -182,7 +181,7 @@ float GetMusicTimePlayed(Music music); // Get current m AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Init audio stream (to stream raw audio pcm data) void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount); // Update audio stream buffers with data void CloseAudioStream(AudioStream stream); // Close audio stream and free memory -bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill +bool IsAudioStreamProcessed(AudioStream stream); // Check if any audio stream buffers requires refill void PlayAudioStream(AudioStream stream); // Play audio stream void PauseAudioStream(AudioStream stream); // Pause audio stream void ResumeAudioStream(AudioStream stream); // Resume audio stream diff --git a/src/raylib.h b/src/raylib.h index 6180eb346..7b6bb328c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -33,7 +33,7 @@ * [core] rgif (Charlie Tangora, Ramon Santamaria) for GIF recording * [textures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) * [textures] stb_image_write (Sean Barret) for image writting (BMP, TGA, PNG, JPG) -* [textures] stb_image_resize (Sean Barret) for image resizing algorythms +* [textures] stb_image_resize (Sean Barret) for image resizing algorithms * [textures] stb_perlin (Sean Barret) for Perlin noise image generation * [text] stb_truetype (Sean Barret) for ttf fonts loading * [text] stb_rect_pack (Sean Barret) for rectangles packing @@ -96,10 +96,6 @@ #define MAX_TOUCH_POINTS 10 // Maximum number of touch points supported -// Shader and material limits -#define MAX_SHADER_LOCATIONS 32 // Maximum number of predefined locations stored in shader struct -#define MAX_MATERIAL_MAPS 12 // Maximum number of texture maps stored in shader struct - // Allow custom memory allocators #ifndef RL_MALLOC #define RL_MALLOC(sz) malloc(sz) @@ -110,7 +106,7 @@ #ifndef RL_FREE #define RL_FREE(p) free(p) #endif - + // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. #if defined(__cplusplus) @@ -322,13 +318,13 @@ typedef struct Mesh { // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id - unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (default vertex data) + unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data) } Mesh; // Shader type (generic) typedef struct Shader { - unsigned int id; // Shader program id - int locs[MAX_SHADER_LOCATIONS]; // Shader locations array + unsigned int id; // Shader program id + int *locs; // Shader locations array (MAX_SHADER_LOCATIONS) } Shader; // Material texture map @@ -341,7 +337,7 @@ typedef struct MaterialMap { // Material type (generic) typedef struct Material { Shader shader; // Material shader - MaterialMap maps[MAX_MATERIAL_MAPS]; // Material maps + MaterialMap *maps; // Material maps array (MAX_MATERIAL_MAPS) float *params; // Material generic parameters (if required) } Material; @@ -436,9 +432,8 @@ typedef struct Sound { typedef struct Music { int ctxType; // Type of music context (audio filetype) void *ctxData; // Audio context data, depends on type - + unsigned int sampleCount; // Total number of samples - unsigned int sampleLeft; // Number of samples left to end unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop AudioStream stream; // Audio stream @@ -464,7 +459,7 @@ typedef struct VrDeviceInfo { // System config flags // NOTE: Used for bit masks typedef enum { - FLAG_SHOW_LOGO = 1, // Set to show raylib logo at startup + FLAG_RESERVED = 1, // Reserved FLAG_FULLSCREEN_MODE = 2, // Set to run program in fullscreen FLAG_WINDOW_RESIZABLE = 4, // Set to allow resizable window FLAG_WINDOW_UNDECORATED = 8, // Set to disable window decoration (frame and buttons) @@ -887,6 +882,7 @@ RLAPI int GetMonitorWidth(int monitor); // Get primary 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 Vector2 GetWindowPosition(void); // Get window position XY on monitor RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor RLAPI const char *GetClipboardText(void); // Get clipboard text content RLAPI void SetClipboardText(const char *text); // Set clipboard text content @@ -908,11 +904,16 @@ RLAPI void BeginMode3D(Camera3D camera); // Initializes RLAPI void EndMode3D(void); // Ends 3D mode and returns to default 2D orthographic mode RLAPI void BeginTextureMode(RenderTexture2D target); // Initializes render texture for drawing RLAPI void EndTextureMode(void); // Ends drawing to render texture +RLAPI void BeginScissorMode(int x, int y, int width, int height); // Begin scissor mode (define screen area for following drawing) +RLAPI void EndScissorMode(void); // End scissor mode // Screen-space-related functions RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Returns a ray trace from mouse position -RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position for a 3d world space position RLAPI Matrix GetCameraMatrix(Camera camera); // Returns camera transform matrix (view matrix) +RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Returns camera 2d transform matrix +RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position for a 3d world space position +RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Returns the screen space position for a 2d camera world space position +RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Returns the world space position for a 2d camera screen space position // Timing-related functions RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) @@ -940,10 +941,12 @@ RLAPI int GetRandomValue(int min, int max); // Returns a r // 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 bool DirectoryExists(const char *dirPath); // Check if a directory path exists 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 *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) +RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string) +RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (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) @@ -953,6 +956,9 @@ RLAPI char **GetDroppedFiles(int *count); // Get dropped RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory) RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) +RLAPI unsigned char *CompressData(unsigned char *data, int dataLength, int *compDataLength); // Compress data (DEFLATE algorythm) +RLAPI unsigned char *DecompressData(unsigned char *compData, int compDataLength, int *dataLength); // Decompress data (DEFLATE algorythm) + // Persistent storage management RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position) RLAPI int StorageLoadValue(int position); // Load integer value from storage file (from defined position) @@ -1056,9 +1062,9 @@ RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color c RLAPI void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color); // Draw rectangle outline with extended parameters RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int lineThick, Color color); // Draw rectangle with rounded edges outline -RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle -RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline -RLAPI void DrawTriangleFan(Vector2 *points, int numPoints, Color color); // Draw a triangle fan defined by points +RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!) +RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline (vertex in counter-clockwise order!) +RLAPI void DrawTriangleFan(Vector2 *points, int numPoints, Color color); // Draw a triangle fan defined by points (first vertex is the center) RLAPI void DrawTriangleStrip(Vector2 *points, int pointsCount, Color color); // Draw a triangle strip defined by points RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) @@ -1093,6 +1099,7 @@ RLAPI void UnloadTexture(Texture2D texture); RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) RLAPI Color *GetImageData(Image image); // Get pixel data from image as a Color struct array RLAPI Vector4 *GetImageDataNormalized(Image image); // Get pixel data from image as Vector4 array (float normalized) +RLAPI Rectangle GetImageAlphaBorder(Image image, float threshold); // Get image alpha border rectangle RLAPI int GetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) RLAPI Image GetTextureData(Texture2D texture); // Get pixel data from GPU texture and return an Image RLAPI Image GetScreenData(void); // Get pixel data from screen buffer and return an Image (screenshot) @@ -1181,18 +1188,15 @@ RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontS RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font -RLAPI int GetNextCodepoint(const char *text, int *bytesProcessed); // Returns next codepoint in a UTF8 encoded string - // NOTE: 0x3f('?') is returned on failure -// Text strings management functions +// Text strings management functions (no utf8 strings, only byte chars) // NOTE: Some strings allocate memory internally for returned strings, just be careful! RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending -RLAPI unsigned int TextCountCodepoints(const char *text); // Get total number of characters (codepoints) in a UTF8 encoded string RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf style) RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string -RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory should be freed!) -RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (memory should be freed!) +RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory must be freed!) +RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (memory must be freed!) RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! @@ -1201,6 +1205,13 @@ RLAPI const char *TextToUpper(const char *text); // Get upp RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) +RLAPI char *TextToUtf8(int *codepoints, int length); // Encode text codepoint into utf8 text (memory must be freed!) + +// UTF8 text strings management functions +RLAPI int *GetCodepoints(const char *text, int *count); // Get all codepoints in a string, codepoints count returned by parameters +RLAPI int GetCodepointsCount(const char *text); // Get total number of characters (codepoints) in a UTF8 encoded string +RLAPI int GetNextCodepoint(const char *text, int *bytesProcessed); // Returns next codepoint in a UTF8 encoded string; 0x3f('?') is returned on failure +RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength); // Encode codepoint into utf8 text (char array length returned as parameter) //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) @@ -1237,7 +1248,7 @@ RLAPI void UnloadModel(Model model); // Mesh loading/unloading functions RLAPI Mesh *LoadMeshes(const char *fileName, int *meshCount); // Load meshes from model file RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file -RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) +RLAPI void UnloadMesh(Mesh mesh); // Unload mesh from memory (RAM and/or VRAM) // Material loading/unloading functions RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file @@ -1281,11 +1292,11 @@ RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRe // Collision detection functions RLAPI bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); // Detect collision between two spheres RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Detect collision between two bounding boxes -RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 centerSphere, float radiusSphere); // Detect collision between box and sphere -RLAPI bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius); // Detect collision between ray and sphere -RLAPI bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadius, Vector3 *collisionPoint); // Detect collision between ray and sphere, returns collision point +RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Detect collision between box and sphere +RLAPI bool CheckCollisionRaySphere(Ray ray, Vector3 center, float radius); // Detect collision between ray and sphere +RLAPI bool CheckCollisionRaySphereEx(Ray ray, Vector3 center, float radius, Vector3 *collisionPoint); // Detect collision between ray and sphere, returns collision point RLAPI bool CheckCollisionRayBox(Ray ray, BoundingBox box); // Detect collision between ray and box -RLAPI RayHitInfo GetCollisionRayModel(Ray ray, Model *model); // Get collision info between ray and model +RLAPI RayHitInfo GetCollisionRayModel(Ray ray, Model model); // Get collision info between ray and model RLAPI RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle RLAPI RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight); // Get collision info between ray and ground plane (Y-normal plane) @@ -1297,7 +1308,7 @@ RLAPI RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight); // Shader loading/unloading functions RLAPI char *LoadText(const char *fileName); // Load chars array from text file RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations -RLAPI Shader LoadShaderCode(char *vsCode, char *fsCode); // Load shader from code strings and bind default locations +RLAPI Shader LoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) RLAPI Shader GetShaderDefault(void); // Get default shader @@ -1312,6 +1323,7 @@ RLAPI void SetShaderValueTexture(Shader shader, int uniformLoc, Texture2D textur RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) RLAPI Matrix GetMatrixModelview(void); // Get internal modelview matrix +RLAPI Matrix GetMatrixProjection(void); // Get internal projection matrix // Texture maps generation (PBR) // NOTE: Required shaders should be provided @@ -1325,8 +1337,6 @@ RLAPI void BeginShaderMode(Shader shader); // Beg RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader) RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied) RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending) -RLAPI void BeginScissorMode(int x, int y, int width, int height); // Begin scissor mode (define screen area for following drawing) -RLAPI void EndScissorMode(void); // End scissor mode // VR control functions RLAPI void InitVrSimulator(void); // Init VR simulator for selected device parameters @@ -1393,7 +1403,7 @@ RLAPI float GetMusicTimePlayed(Music music); // Get cur RLAPI AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Init audio stream (to stream raw audio pcm data) RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount); // Update audio stream buffers with data RLAPI void CloseAudioStream(AudioStream stream); // Close audio stream and free memory -RLAPI bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill +RLAPI bool IsAudioStreamProcessed(AudioStream stream); // Check if any audio stream buffers requires refill RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream @@ -1401,7 +1411,7 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check i RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) - + //------------------------------------------------------------------------------------ // Network (Module: network) //------------------------------------------------------------------------------------ diff --git a/src/raymath.h b/src/raymath.h index b9dae5546..12ea76b4e 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -20,7 +20,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2015-2017 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-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. @@ -56,7 +56,7 @@ #if defined(RAYMATH_IMPLEMENTATION) #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) #define RMDEF __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll). - #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) + #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) #define RMDEF __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) #else #define RMDEF extern inline // Provide external definition @@ -113,7 +113,7 @@ float y; float z; } Vector3; - + // Quaternion type typedef struct Quaternion { float x; @@ -148,7 +148,7 @@ RMDEF float Clamp(float value, float min, float max) return res > max ? max : res; } -// Calculate linear interpolation between two vectors +// Calculate linear interpolation between two floats RMDEF float Lerp(float start, float end, float amount) { return start + amount*(end - start); @@ -225,8 +225,8 @@ RMDEF Vector2 Vector2Scale(Vector2 v, float scale) // Multiply vector by vector RMDEF Vector2 Vector2MultiplyV(Vector2 v1, Vector2 v2) { - Vector2 result = { v1.x*v2.x, v1.y*v2.y }; - return result; + Vector2 result = { v1.x*v2.x, v1.y*v2.y }; + return result; } // Negate vector @@ -246,8 +246,8 @@ RMDEF Vector2 Vector2Divide(Vector2 v, float div) // Divide vector by vector RMDEF Vector2 Vector2DivideV(Vector2 v1, Vector2 v2) { - Vector2 result = { v1.x/v2.x, v1.y/v2.y }; - return result; + Vector2 result = { v1.x/v2.x, v1.y/v2.y }; + return result; } // Normalize provided vector @@ -388,15 +388,15 @@ RMDEF Vector3 Vector3Negate(Vector3 v) // Divide vector by a float value RMDEF Vector3 Vector3Divide(Vector3 v, float div) { - Vector3 result = { v.x / div, v.y / div, v.z / div }; - return result; + Vector3 result = { v.x / div, v.y / div, v.z / div }; + return result; } // Divide vector by vector RMDEF Vector3 Vector3DivideV(Vector3 v1, Vector3 v2) { - Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; - return result; + Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; + return result; } // Normalize provided vector @@ -794,6 +794,33 @@ RMDEF Matrix MatrixRotate(Vector3 axis, float angle) return result; } +// Returns xyz-rotation matrix (angles in radians) +RMDEF Matrix MatrixRotateXYZ(Vector3 ang) +{ + Matrix result = MatrixIdentity(); + + float cosz = cosf(-ang.z); + float sinz = sinf(-ang.z); + float cosy = cosf(-ang.y); + float siny = sinf(-ang.y); + float cosx = cosf(-ang.x); + float sinx = sinf(-ang.x); + + result.m0 = cosz * cosy; + result.m4 = (cosz * siny * sinx) - (sinz * cosx); + result.m8 = (cosz * siny * cosx) + (sinz * sinx); + + result.m1 = sinz * cosy; + result.m5 = (sinz * siny * sinx) + (cosz * cosx); + result.m9 = (sinz * siny * cosx) - (cosz * sinx); + + result.m2 = -siny; + result.m6 = cosy * sinx; + result.m10= cosy * cosx; + + return result; +} + // Returns x-rotation matrix (angle in radians) RMDEF Matrix MatrixRotateX(float angle) { @@ -1159,7 +1186,7 @@ RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) // Above lines are equivalent to: //Quaternion result = QuaternionNlerp(q, QuaternionIdentity(), 0.5f); - return result; + return result; } // Returns a quaternion for a given rotation matrix @@ -1320,21 +1347,21 @@ RMDEF void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle // Returns he quaternion equivalent to Euler angles RMDEF Quaternion QuaternionFromEuler(float roll, float pitch, float yaw) { - Quaternion q = { 0 }; + Quaternion q = { 0 }; - float x0 = cosf(roll*0.5f); - float x1 = sinf(roll*0.5f); - float y0 = cosf(pitch*0.5f); - float y1 = sinf(pitch*0.5f); - float z0 = cosf(yaw*0.5f); - float z1 = sinf(yaw*0.5f); + float x0 = cosf(roll*0.5f); + float x1 = sinf(roll*0.5f); + float y0 = cosf(pitch*0.5f); + float y1 = sinf(pitch*0.5f); + float z0 = cosf(yaw*0.5f); + float z1 = sinf(yaw*0.5f); - q.x = x1*y0*z0 - x0*y1*z1; - q.y = x0*y1*z0 + x1*y0*z1; - q.z = x0*y0*z1 - x1*y1*z0; - q.w = x0*y0*z0 + x1*y1*z1; + q.x = x1*y0*z0 - x0*y1*z1; + q.y = x0*y1*z0 + x1*y0*z1; + q.z = x0*y0*z1 - x1*y1*z0; + q.w = x0*y0*z0 + x1*y1*z1; - return q; + return q; } // Return the Euler angles equivalent to quaternion (roll, pitch, yaw) @@ -1343,21 +1370,21 @@ RMDEF Vector3 QuaternionToEuler(Quaternion q) { Vector3 result = { 0 }; - // roll (x-axis rotation) - float x0 = 2.0f*(q.w*q.x + q.y*q.z); - float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); - result.x = atan2f(x0, x1)*RAD2DEG; + // roll (x-axis rotation) + float x0 = 2.0f*(q.w*q.x + q.y*q.z); + float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); + result.x = atan2f(x0, x1)*RAD2DEG; - // pitch (y-axis rotation) - float y0 = 2.0f*(q.w*q.y - q.z*q.x); - y0 = y0 > 1.0f ? 1.0f : y0; - y0 = y0 < -1.0f ? -1.0f : y0; - result.y = asinf(y0)*RAD2DEG; + // pitch (y-axis rotation) + float y0 = 2.0f*(q.w*q.y - q.z*q.x); + y0 = y0 > 1.0f ? 1.0f : y0; + y0 = y0 < -1.0f ? -1.0f : y0; + result.y = asinf(y0)*RAD2DEG; - // yaw (z-axis rotation) - float z0 = 2.0f*(q.w*q.z + q.x*q.y); - float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); - result.z = atan2f(z0, z1)*RAD2DEG; + // yaw (z-axis rotation) + float z0 = 2.0f*(q.w*q.z + q.x*q.y); + float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); + result.z = atan2f(z0, z1)*RAD2DEG; return result; } diff --git a/src/rglfw.c b/src/rglfw.c index 3463bb96d..b05ff3c74 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -2,7 +2,7 @@ * * rglfw - raylib GLFW single file compilation * -* This file includes latest GLFW sources (https://github.com/glfw/glfw) to be compiled together +* This file includes latest GLFW sources (https://github.com/glfw/glfw) to be compiled together * with raylib for all supported platforms, this way, no external dependencies are required. * * LICENSE: zlib/libpng @@ -46,7 +46,7 @@ #define _GLFW_USE_RETINA // To have windows use the full resolution of Retina displays #endif #if defined(__TINYC__) - #define _WIN32_WINNT_WINXP 0x0501 + #define _WIN32_WINNT_WINXP 0x0501 #endif // NOTE: _GLFW_MIR experimental platform not supported at this moment diff --git a/src/rlgl.h b/src/rlgl.h index 797ea9c06..b83da6e8c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -131,6 +131,10 @@ #define MAX_MATRIX_STACK_SIZE 32 // Max size of Matrix stack #define MAX_DRAWCALL_REGISTERED 256 // Max draws by state changes (mode, texture) +// Shader and material limits +#define MAX_SHADER_LOCATIONS 32 // Maximum number of predefined locations stored in shader struct +#define MAX_MATERIAL_MAPS 12 // Maximum number of texture maps stored in shader struct + // Texture parameters (equivalent to OpenGL defines) #define RL_TEXTURE_WRAP_S 0x2802 // GL_TEXTURE_WRAP_S #define RL_TEXTURE_WRAP_T 0x2803 // GL_TEXTURE_WRAP_T @@ -228,7 +232,7 @@ typedef unsigned char byte; // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id - unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (7 types of vertex data) + unsigned int *vboId; // OpenGL Vertex Buffer Objects id (7 types of vertex data) } Mesh; // Shader and material limits @@ -237,8 +241,8 @@ typedef unsigned char byte; // Shader type (generic) typedef struct Shader { - unsigned int id; // Shader program id - int locs[MAX_SHADER_LOCATIONS]; // Shader locations array + unsigned int id; // Shader program id + int *locs; // Shader locations array (MAX_SHADER_LOCATIONS) } Shader; // Material texture map @@ -251,7 +255,7 @@ typedef unsigned char byte; // Material type (generic) typedef struct Material { Shader shader; // Material shader - MaterialMap maps[MAX_MATERIAL_MAPS]; // Material maps + MaterialMap *maps; // Material maps (MAX_MATERIAL_MAPS) float *params; // Material generic parameters (if required) } Material; @@ -453,6 +457,9 @@ RLAPI void rlEnableDepthTest(void); // Enable depth te RLAPI void rlDisableDepthTest(void); // Disable depth test RLAPI void rlEnableBackfaceCulling(void); // Enable backface culling RLAPI void rlDisableBackfaceCulling(void); // Disable backface culling +RLAPI void rlEnableScissorTest(void); // Enable scissor test +RLAPI void rlDisableScissorTest(void); // Disable scissor test +RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test RLAPI void rlEnableWireMode(void); // Enable wire mode RLAPI void rlDisableWireMode(void); // Disable wire mode RLAPI void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU @@ -499,7 +506,7 @@ RLAPI bool rlRenderTextureComplete(RenderTexture target); // Ver RLAPI void rlLoadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids RLAPI void rlUpdateMesh(Mesh mesh, int buffer, int numVertex); // Update vertex data on GPU (upload new data to one buffer) RLAPI void rlDrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform -RLAPI void rlUnloadMesh(Mesh *mesh); // Unload mesh data from CPU and GPU +RLAPI void rlUnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU // NOTE: There is a set of shader related functions that are available to end user, // to avoid creating function wrappers through core module, they have been directly declared in raylib.h @@ -512,7 +519,7 @@ RLAPI void rlUnloadMesh(Mesh *mesh); // Unl // Shader loading/unloading functions RLAPI char *LoadText(const char *fileName); // Load chars array from text file RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations -RLAPI Shader LoadShaderCode(char *vsCode, char *fsCode); // Load shader from code strings and bind default locations +RLAPI Shader LoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) RLAPI Shader GetShaderDefault(void); // Get default shader @@ -1340,28 +1347,25 @@ void rlDisableRenderTexture(void) } // Enable depth test -void rlEnableDepthTest(void) -{ - glEnable(GL_DEPTH_TEST); -} +void rlEnableDepthTest(void) { glEnable(GL_DEPTH_TEST); } // Disable depth test -void rlDisableDepthTest(void) -{ - glDisable(GL_DEPTH_TEST); -} +void rlDisableDepthTest(void) { glDisable(GL_DEPTH_TEST); } // Enable backface culling -void rlEnableBackfaceCulling(void) -{ - glEnable(GL_CULL_FACE); -} +void rlEnableBackfaceCulling(void) { glEnable(GL_CULL_FACE); } // Disable backface culling -void rlDisableBackfaceCulling(void) -{ - glDisable(GL_CULL_FACE); -} +void rlDisableBackfaceCulling(void) { glDisable(GL_CULL_FACE); } + +// Enable scissor test +RLAPI void rlEnableScissorTest(void) { glEnable(GL_SCISSOR_TEST); } + +// Disable scissor test +RLAPI void rlDisableScissorTest(void) { glDisable(GL_SCISSOR_TEST); } + +// Scissor test +RLAPI void rlScissor(int x, int y, int width, int height) { glScissor(x, y, width, height); } // Enable wire mode void rlEnableWireMode(void) @@ -1523,26 +1527,35 @@ void rlglInit(int width, int height) // Allocate numExt strings pointers const char **extList = RL_MALLOC(sizeof(const char *)*numExt); - + // Get extensions strings - for (int i = 0; i < numExt; i++) extList[i] = (char *)glGetStringi(GL_EXTENSIONS, i); + for (int i = 0; i < numExt; i++) extList[i] = (const char *)glGetStringi(GL_EXTENSIONS, i); #elif defined(GRAPHICS_API_OPENGL_ES2) // Allocate 512 strings pointers (2 KB) const char **extList = RL_MALLOC(sizeof(const char *)*512); - - // Get extensions strings - char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big static const string returned - int len = strlen(extensions); + + const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string + + // NOTE: We have to duplicate string because glGetString() returns a const string + int len = strlen(extensions) + 1; + char *extensionsDup = (char *)RL_CALLOC(len, sizeof(char)); + strcpy(extensionsDup, extensions); + + extList[numExt] = extensionsDup; for (int i = 0; i < len; i++) { - if (i == ' ') + if (extensionsDup[i] == ' ') { - extList[numExt] = &extensions[i + 1]; + extensionsDup[i] = '\0'; + numExt++; + extList[numExt] = &extensionsDup[i + 1]; } } + + // NOTE: Duplicated string (extensionsDup) must be deallocated #endif TraceLog(LOG_INFO, "Number of supported extensions: %i", numExt); @@ -1618,6 +1631,8 @@ void rlglInit(int width, int height) RL_FREE(extList); #if defined(GRAPHICS_API_OPENGL_ES2) + RL_FREE(extensionsDup); // Duplicated string must be deallocated + if (vaoSupported) TraceLog(LOG_INFO, "[EXTENSION] VAO extension detected, VAO functions initialized successfully"); else TraceLog(LOG_WARNING, "[EXTENSION] VAO extension not found, VAO usage not supported"); @@ -1683,7 +1698,6 @@ void rlglInit(int width, int height) // Initialize OpenGL default states //---------------------------------------------------------- - // Init state: Depth test glDepthFunc(GL_LEQUAL); // Type of depth testing to apply glDisable(GL_DEPTH_TEST); // Disable depth testing for 2D (only used for 3D) @@ -2622,11 +2636,11 @@ void rlDrawMesh(Mesh mesh, Material material, Matrix transform) // That's because BeginMode3D() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() Matrix matView = modelview; // View matrix (camera) Matrix matProjection = projection; // Projection matrix (perspective) - + // TODO: Matrix nightmare! Trying to combine stack matrices with view matrix and local model transform matrix.. // There is some problem in the order matrices are multiplied... it requires some time to figure out... Matrix matStackTransform = MatrixIdentity(); - + // TODO: Consider possible transform matrices in the stack // Is this the right order? or should we start with the first stored matrix instead of the last one? //for (int i = stackCounter; i > 0; i--) matStackTransform = MatrixMultiply(stack[i], matStackTransform); @@ -2757,30 +2771,30 @@ void rlDrawMesh(Mesh mesh, Material material, Matrix transform) } // Unload mesh data from CPU and GPU -void rlUnloadMesh(Mesh *mesh) +void rlUnloadMesh(Mesh mesh) { - RL_FREE(mesh->vertices); - RL_FREE(mesh->texcoords); - RL_FREE(mesh->normals); - RL_FREE(mesh->colors); - RL_FREE(mesh->tangents); - RL_FREE(mesh->texcoords2); - RL_FREE(mesh->indices); + RL_FREE(mesh.vertices); + RL_FREE(mesh.texcoords); + RL_FREE(mesh.normals); + RL_FREE(mesh.colors); + RL_FREE(mesh.tangents); + RL_FREE(mesh.texcoords2); + RL_FREE(mesh.indices); - RL_FREE(mesh->animVertices); - RL_FREE(mesh->animNormals); - RL_FREE(mesh->boneWeights); - RL_FREE(mesh->boneIds); + RL_FREE(mesh.animVertices); + RL_FREE(mesh.animNormals); + RL_FREE(mesh.boneWeights); + RL_FREE(mesh.boneIds); - rlDeleteBuffers(mesh->vboId[0]); // vertex - rlDeleteBuffers(mesh->vboId[1]); // texcoords - rlDeleteBuffers(mesh->vboId[2]); // normals - rlDeleteBuffers(mesh->vboId[3]); // colors - rlDeleteBuffers(mesh->vboId[4]); // tangents - rlDeleteBuffers(mesh->vboId[5]); // texcoords2 - rlDeleteBuffers(mesh->vboId[6]); // indices + rlDeleteBuffers(mesh.vboId[0]); // vertex + rlDeleteBuffers(mesh.vboId[1]); // texcoords + rlDeleteBuffers(mesh.vboId[2]); // normals + rlDeleteBuffers(mesh.vboId[3]); // colors + rlDeleteBuffers(mesh.vboId[4]); // tangents + rlDeleteBuffers(mesh.vboId[5]); // texcoords2 + rlDeleteBuffers(mesh.vboId[6]); // indices - rlDeleteVertexArrays(mesh->vaoId); + rlDeleteVertexArrays(mesh.vaoId); } // Read screen pixel data (color buffer) @@ -2954,6 +2968,8 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName) { Shader shader = { 0 }; + // NOTE: Shader.locs is allocated by LoadShaderCode() + char *vShaderStr = NULL; char *fShaderStr = NULL; @@ -2970,9 +2986,10 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName) // Load shader from code strings // NOTE: If shader string is NULL, using default vertex/fragment shaders -Shader LoadShaderCode(char *vsCode, char *fsCode) +Shader LoadShaderCode(const char *vsCode, const char *fsCode) { Shader shader = { 0 }; + shader.locs = (int *)RL_CALLOC(MAX_SHADER_LOCATIONS, sizeof(int)); // NOTE: All locations must be reseted to -1 (no location) for (int i = 0; i < MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; @@ -3008,7 +3025,7 @@ Shader LoadShaderCode(char *vsCode, char *fsCode) glGetProgramiv(shader.id, GL_ACTIVE_UNIFORMS, &uniformCount); - for(int i = 0; i < uniformCount; i++) + for (int i = 0; i < uniformCount; i++) { int namelen = -1; int num = -1; @@ -3038,6 +3055,8 @@ void UnloadShader(Shader shader) rlDeleteShader(shader.id); TraceLog(LOG_INFO, "[SHDR ID %i] Unloaded shader program data", shader.id); } + + RL_FREE(shader.locs); } // Begin custom shader mode @@ -3136,6 +3155,23 @@ void SetMatrixProjection(Matrix proj) #endif } +// Return internal projection matrix +Matrix GetMatrixProjection(void) { +#if defined(GRAPHICS_API_OPENGL_11) + float mat[16]; + glGetFloatv(GL_PROJECTION_MATRIX,mat); + Matrix m; + m.m0 = mat[0]; m.m1 = mat[1]; m.m2 = mat[2]; m.m3 = mat[3]; + m.m4 = mat[4]; m.m5 = mat[5]; m.m6 = mat[6]; m.m7 = mat[7]; + m.m8 = mat[8]; m.m9 = mat[9]; m.m10 = mat[10]; m.m11 = mat[11]; + m.m12 = mat[12]; m.m13 = mat[13]; m.m14 = mat[14]; m.m15 = mat[15]; + return m; +#else + return projection; +#endif +# +} + // Set a custom modelview matrix (replaces internal modelview matrix) void SetMatrixModelview(Matrix view) { @@ -3151,6 +3187,10 @@ Matrix GetMatrixModelview(void) #if defined(GRAPHICS_API_OPENGL_11) float mat[16]; glGetFloatv(GL_MODELVIEW_MATRIX, mat); + matrix.m0 = mat[0]; matrix.m1 = mat[1]; matrix.m2 = mat[2]; matrix.m3 = mat[3]; + matrix.m4 = mat[4]; matrix.m5 = mat[5]; matrix.m6 = mat[6]; matrix.m7 = mat[7]; + matrix.m8 = mat[8]; matrix.m9 = mat[9]; matrix.m10 = mat[10]; matrix.m11 = mat[11]; + matrix.m12 = mat[12]; matrix.m13 = mat[13]; matrix.m14 = mat[14]; matrix.m15 = mat[15]; #else matrix = modelview; #endif @@ -3505,24 +3545,6 @@ void EndBlendMode(void) BeginBlendMode(BLEND_ALPHA); } -// Begin scissor mode (define screen area for following drawing) -// NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left -void BeginScissorMode(int x, int y, int width, int height) -{ - rlglDraw(); // Force drawing elements - - glEnable(GL_SCISSOR_TEST); - glScissor(x, framebufferHeight - (y + height), width, height); -} - -// End scissor mode -void EndScissorMode(void) -{ - rlglDraw(); // Force drawing elements - - glDisable(GL_SCISSOR_TEST); -} - #if defined(SUPPORT_VR_SIMULATOR) // Init VR simulator for selected device parameters // NOTE: It modifies the global variable: stereoFbo @@ -3861,12 +3883,13 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad static Shader LoadShaderDefault(void) { Shader shader = { 0 }; + shader.locs = (int *)RL_CALLOC(MAX_SHADER_LOCATIONS, sizeof(int)); // NOTE: All locations must be reseted to -1 (no location) for (int i = 0; i < MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; // Vertex shader directly defined, no external file required - char defaultVShaderStr[] = + const char *defaultVShaderStr = #if defined(GRAPHICS_API_OPENGL_21) "#version 120 \n" #elif defined(GRAPHICS_API_OPENGL_ES2) @@ -3895,7 +3918,7 @@ static Shader LoadShaderDefault(void) "} \n"; // Fragment shader directly defined, no external file required - char defaultFShaderStr[] = + const char *defaultFShaderStr = #if defined(GRAPHICS_API_OPENGL_21) "#version 120 \n" #elif defined(GRAPHICS_API_OPENGL_ES2) @@ -4615,6 +4638,14 @@ int GetPixelDataSize(int width, int height, int format) dataSize = width*height*bpp/8; // Total data size in bytes + // Most compressed formats works on 4x4 blocks, + // if texture is smaller, minimum dataSize is 8 or 16 + if ((width < 4) && (height < 4)) + { + if ((format >= COMPRESSED_DXT1_RGB) && (format < COMPRESSED_DXT3_RGBA)) dataSize = 8; + else if ((format >= COMPRESSED_DXT3_RGBA) && (format < COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16; + } + return dataSize; } #endif // RLGL_STANDALONE diff --git a/src/rmem.h b/src/rmem.h index 87ceacc23..46e3b6532 100644 --- a/src/rmem.h +++ b/src/rmem.h @@ -5,7 +5,7 @@ * A quick, efficient, and minimal free list and stack-based allocator * * PURPOSE: -* - Aquicker, efficient memory allocator alternative to 'malloc' and friends. +* - A quicker, efficient memory allocator alternative to 'malloc' and friends. * - Reduce the possibilities of memory leaks for beginner developers using Raylib. * - Being able to flexibly range check memory if necessary. * @@ -54,7 +54,7 @@ #else #define RMEMAPI // We are building or using library as a static library (or Linux shared library) #endif - + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -139,9 +139,9 @@ RMEMAPI void ObjPoolCleanUp(ObjPool *objpool, void **ptrref); #if defined(RMEM_IMPLEMENTATION) -#include // Required for: -#include // Required for: -#include // Required for: +#include // Required for: +#include // Required for: +#include // Required for: //---------------------------------------------------------------------------------- // Defines and Macros @@ -163,24 +163,9 @@ RMEMAPI void ObjPoolCleanUp(ObjPool *objpool, void **ptrref); //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static inline size_t __AlignSize(const size_t size, const size_t align) -{ - return (size + (align - 1)) & -align; -} - -static void __RemoveNode(MemPool *const mempool, MemNode **const node) +static inline size_t __AlignSize(const size_t size, const size_t align) { - if ((*node)->next != NULL) (*node)->next->prev = (*node)->prev; - else { - mempool->freeList.tail = (*node)->prev; - if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL; - } - - if ((*node)->prev != NULL) (*node)->prev->next = (*node)->next; - else { - mempool->freeList.head = (*node)->next; - if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL; - } + return (size + (align - 1)) & -align; } //---------------------------------------------------------------------------------- @@ -190,9 +175,9 @@ static void __RemoveNode(MemPool *const mempool, MemNode **const node) MemPool CreateMemPool(const size_t size) { MemPool mempool = { 0 }; - + if (size == 0UL) return mempool; - else + else { // Align the mempool size to at least the size of an alloc node. mempool.stack.size = size; @@ -203,7 +188,7 @@ MemPool CreateMemPool(const size_t size) mempool.stack.size = 0UL; return mempool; } - else + else { mempool.stack.base = mempool.stack.mem + mempool.stack.size; return mempool; @@ -214,9 +199,9 @@ MemPool CreateMemPool(const size_t size) MemPool CreateMemPoolFromBuffer(void *buf, const size_t size) { MemPool mempool = { 0 }; - + if ((size == 0UL) || (buf == NULL) || (size <= sizeof(MemNode))) return mempool; - else + else { mempool.stack.size = size; mempool.stack.mem = buf; @@ -228,7 +213,7 @@ MemPool CreateMemPoolFromBuffer(void *buf, const size_t size) void DestroyMemPool(MemPool *const mempool) { if ((mempool == NULL) || (mempool->stack.mem == NULL)) return; - else + else { free(mempool->stack.mem); *mempool = (MemPool){ 0 }; @@ -243,7 +228,8 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) MemNode *new_mem = NULL; const size_t ALLOC_SIZE = __AlignSize(size + sizeof *new_mem, sizeof(intptr_t)); const size_t BUCKET_INDEX = (ALLOC_SIZE >> MEMPOOL_BUCKET_BITS) - 1; - + + // If the size is small enough, let's check if our buckets has a fitting memory block. if (BUCKET_INDEX < MEMPOOL_BUCKET_SIZE && mempool->buckets[BUCKET_INDEX] != NULL && mempool->buckets[BUCKET_INDEX]->size >= ALLOC_SIZE) { new_mem = mempool->buckets[BUCKET_INDEX]; @@ -254,30 +240,36 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) else if (mempool->freeList.head != NULL) { const size_t MEM_SPLIT_THRESHOLD = 16; - + // If the freelist is valid, let's allocate FROM the freelist then! - for (MemNode **inode = &mempool->freeList.head; *inode != NULL; inode = &(*inode)->next) + for (MemNode *inode = mempool->freeList.head; inode != NULL; inode = inode->next) { - if ((*inode)->size < ALLOC_SIZE) continue; - else if ((*inode)->size <= (ALLOC_SIZE + MEM_SPLIT_THRESHOLD)) + if (inode->size < ALLOC_SIZE) continue; + else if (inode->size <= (ALLOC_SIZE + MEM_SPLIT_THRESHOLD)) { // Close in size - reduce fragmentation by not splitting. - new_mem = *inode; - __RemoveNode(mempool, inode); + new_mem = inode; + (inode->prev != NULL)? (inode->prev->next = inode->next) : (mempool->freeList.head = inode->next); + (inode->next != NULL)? (inode->next->prev = inode->prev) : (mempool->freeList.tail = inode->prev); + + if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL; + else mempool->freeList.tail = NULL; + + if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL; mempool->freeList.len--; break; } else { // Split the memory chunk. - new_mem = (MemNode *)((uint8_t *)*inode + ((*inode)->size - ALLOC_SIZE)); - (*inode)->size -= ALLOC_SIZE; + new_mem = (MemNode *)((uint8_t *)inode + (inode->size - ALLOC_SIZE)); + inode->size -= ALLOC_SIZE; new_mem->size = ALLOC_SIZE; break; } } } - + if (new_mem == NULL) { // not enough memory to support the size! @@ -287,13 +279,13 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) // Couldn't allocate from a freelist, allocate from available mempool. // Subtract allocation size from the mempool. mempool->stack.base -= ALLOC_SIZE; - + // Use the available mempool space as the new node. new_mem = (MemNode *)mempool->stack.base; new_mem->size = ALLOC_SIZE; } } - + // Visual of the allocation block. // -------------- // | mem size | lowest addr of block @@ -322,7 +314,7 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si MemNode *const node = (MemNode *)((uint8_t *)ptr - sizeof *node); const size_t NODE_SIZE = sizeof *node; uint8_t *const resized_block = MemPoolAlloc(mempool, size); - + if (resized_block == NULL) return NULL; else { @@ -337,16 +329,16 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si void MemPoolFree(MemPool *const restrict mempool, void *ptr) { if ((mempool == NULL) || (ptr == NULL) || ((uintptr_t)ptr - sizeof(MemNode) < (uintptr_t)mempool->stack.mem)) return; - else + else { // Behind the actual pointer data is the allocation info. MemNode *const mem_node = (MemNode *)((uint8_t *)ptr - sizeof *mem_node); const size_t BUCKET_INDEX = (mem_node->size >> MEMPOOL_BUCKET_BITS) - 1; - + // Make sure the pointer data is valid. - if (((uintptr_t)mem_node < (uintptr_t)mempool->stack.base) || - (((uintptr_t)mem_node - (uintptr_t)mempool->stack.mem) > mempool->stack.size) || - (mem_node->size == 0UL) || + if (((uintptr_t)mem_node < (uintptr_t)mempool->stack.base) || + (((uintptr_t)mem_node - (uintptr_t)mempool->stack.mem) > mempool->stack.size) || + (mem_node->size == 0UL) || (mem_node->size > mempool->stack.size)) return; // If the mem_node is right at the stack base ptr, then add it to the stack. else if ((uintptr_t)mem_node == (uintptr_t)mempool->stack.base) @@ -356,13 +348,13 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr) // attempted stack merge failed, try to place it into the memnode buckets else if (BUCKET_INDEX < MEMPOOL_BUCKET_SIZE) { - if (mempool->buckets[index] == NULL) mempool->buckets[index] = node; + if (mempool->buckets[BUCKET_INDEX] == NULL) mempool->buckets[BUCKET_INDEX] = mem_node; else { - for (MemNode *n = mempool->buckets[index]; n != NULL; n = n->next) if( n==node ) return; - mempool->buckets[index]->prev = node; - node->next = mempool->buckets[index]; - mempool->buckets[index] = node; + for (MemNode *n = mempool->buckets[BUCKET_INDEX]; n != NULL; n = n->next) if( n==mem_node ) return; + mempool->buckets[BUCKET_INDEX]->prev = mem_node; + mem_node->next = mempool->buckets[BUCKET_INDEX]; + mempool->buckets[BUCKET_INDEX] = mem_node; } } // Otherwise, we add it to the free list. @@ -370,13 +362,13 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr) else /*if ((mempool->freeList.len == 0UL) || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size))*/ { for (MemNode *n = mempool->freeList.head; n != NULL; n = n->next) if (n == mem_node) return; - + // This code insertion sorts where largest size is last. if (mempool->freeList.head == NULL) { mempool->freeList.head = mempool->freeList.tail = mem_node; mempool->freeList.len++; - } + } else if (mempool->freeList.head->size >= mem_node->size) { mem_node->next = mempool->freeList.head; @@ -391,7 +383,7 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr) mempool->freeList.tail = mem_node; mempool->freeList.len++; } - + if (mempool->freeList.autoDefrag && (mempool->freeList.maxNodes != 0UL) && (mempool->freeList.len > mempool->freeList.maxNodes)) MemPoolDefrag(mempool); } } @@ -400,7 +392,7 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr) void MemPoolCleanUp(MemPool *const restrict mempool, void **ptrref) { if ((mempool == NULL) || (ptrref == NULL) || (*ptrref == NULL)) return; - else + else { MemPoolFree(mempool, *ptrref); *ptrref = NULL; @@ -410,11 +402,11 @@ void MemPoolCleanUp(MemPool *const restrict mempool, void **ptrref) size_t GetMemPoolFreeMemory(const MemPool mempool) { size_t total_remaining = (uintptr_t)mempool.stack.base - (uintptr_t)mempool.stack.mem; - + for (MemNode *n=mempool.freeList.head; n != NULL; n = n->next) total_remaining += n->size; - + for (size_t i=0; inext) total_remaining += n->size; - + return total_remaining; } @@ -431,12 +423,12 @@ bool MemPoolDefrag(MemPool *const mempool) for (size_t i = 0; i < MEMPOOL_BUCKET_SIZE; i++) mempool->buckets[i] = NULL; mempool->stack.base = mempool->stack.mem + mempool->stack.size; return true; - } + } else { for (size_t i=0; ibuckets[i] != NULL) + while (mempool->buckets[i] != NULL) { if ((uintptr_t)mempool->buckets[i] == (uintptr_t)mempool->stack.base) { @@ -448,36 +440,42 @@ bool MemPoolDefrag(MemPool *const mempool) else break; } } - + const size_t PRE_DEFRAG_LEN = mempool->freeList.len; MemNode **node = &mempool->freeList.head; - + while (*node != NULL) { - if ((uintptr_t)*node == (uintptr_t)mempool->stack.base) + if ((uintptr_t)*node == (uintptr_t)mempool->stack.base) { // If node is right at the stack, merge it back into the stack. mempool->stack.base += (*node)->size; (*node)->size = 0UL; - __RemoveNode(mempool, node); + ((*node)->prev != NULL)? ((*node)->prev->next = (*node)->next) : (mempool->freeList.head = (*node)->next); + ((*node)->next != NULL)? ((*node)->next->prev = (*node)->prev) : (mempool->freeList.tail = (*node)->prev); + + if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL; + else mempool->freeList.tail = NULL; + + if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL; mempool->freeList.len--; node = &mempool->freeList.head; - } + } else if (((uintptr_t)*node + (*node)->size) == (uintptr_t)(*node)->next) { // Next node is at a higher address. (*node)->size += (*node)->next->size; (*node)->next->size = 0UL; - + // <-[P Curr N]-> <-[P Next N]-> <-[P NextNext N]-> - // + // // |--------------------| // <-[P Curr N]-> <-[P Next N]-> [P NextNext N]-> if ((*node)->next->next != NULL) (*node)->next->next->prev = *node; - + // <-[P Curr N]-> <-[P NextNext N]-> (*node)->next = (*node)->next->next; - + mempool->freeList.len--; node = &mempool->freeList.head; } @@ -486,16 +484,16 @@ bool MemPoolDefrag(MemPool *const mempool) // Prev node is at a higher address. (*node)->size += (*node)->prev->size; (*node)->prev->size = 0UL; - + // <-[P PrevPrev N]-> <-[P Prev N]-> <-[P Curr N]-> // // |--------------------| // <-[P PrevPrev N] <-[P Prev N]-> <-[P Curr N]-> (*node)->prev->prev->next = *node; - + // <-[P PrevPrev N]-> <-[P Curr N]-> (*node)->prev = (*node)->prev->prev; - + mempool->freeList.len--; node = &mempool->freeList.head; } @@ -503,12 +501,12 @@ bool MemPoolDefrag(MemPool *const mempool) { // Next node is at a lower address. (*node)->next->size += (*node)->size; - + (*node)->size = 0UL; (*node)->next->prev = (*node)->prev; (*node)->prev->next = (*node)->next; *node = (*node)->next; - + mempool->freeList.len--; node = &mempool->freeList.head; } @@ -516,21 +514,21 @@ bool MemPoolDefrag(MemPool *const mempool) { // Prev node is at a lower address. (*node)->prev->size += (*node)->size; - + (*node)->size = 0UL; (*node)->next->prev = (*node)->prev; (*node)->prev->next = (*node)->next; *node = (*node)->prev; - + mempool->freeList.len--; node = &mempool->freeList.head; - } + } else { node = &(*node)->next; } } - + return PRE_DEFRAG_LEN > mempool->freeList.len; } } @@ -553,19 +551,19 @@ union ObjInfo { ObjPool CreateObjPool(const size_t objsize, const size_t len) { ObjPool objpool = { 0 }; - + if ((len == 0UL) || (objsize == 0UL)) return objpool; else { objpool.objSize = __AlignSize(objsize, sizeof(size_t)); objpool.stack.size = objpool.freeBlocks = len; objpool.stack.mem = calloc(objpool.stack.size, objpool.objSize); - + if (objpool.stack.mem == NULL) { objpool.stack.size = 0UL; return objpool; - } + } else { for (size_t i=0; istack.base }; objpool->freeBlocks--; - + // after allocating, we set head to the address of the index that *Head holds. // Head = &pool[*Head * pool.objsize]; objpool->stack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + (*ret.index*objpool->objSize) : NULL; diff --git a/src/rnet.h b/src/rnet.h index 6dbcb9253..6110bf849 100644 --- a/src/rnet.h +++ b/src/rnet.h @@ -95,10 +95,10 @@ // Platform type definitions // From: https://github.com/DFHack/clsocket/blob/master/src/Host.h //---------------------------------------------------------------------------------- - + #ifdef WIN32 typedef int socklen_t; -#endif +#endif #ifndef RESULT_SUCCESS # define RESULT_SUCCESS 0 @@ -171,7 +171,7 @@ typedef int socklen_t; #define SOCKET_MAX_QUEUE_SIZE (16) // Maximum socket queue size #define SOCKET_MAX_SOCK_OPTS (4) // Maximum socket options #define SOCKET_MAX_UDPCHANNELS (32) // Maximum UDP channels -#define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses +#define SOCKET_MAX_UDPADDRESSES (4) // Maximum bound UDP addresses // Network address related defines @@ -386,7 +386,7 @@ int AddSocket(SocketSet *set, Socket *sock); int RemoveSocket(SocketSet *set, Socket *sock); int CheckSockets(SocketSet *set, unsigned int timeout); -// Packet API +// Packet API void PacketSend(Packet *packet); void PacketReceive(Packet *packet); void PacketWrite8(Packet *packet, uint16_t value); diff --git a/src/shapes.c b/src/shapes.c index d1956b26a..4fd4eff57 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -1179,10 +1179,11 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int } // Draw a triangle +// NOTE: Vertex must be provided in counter-clockwise order void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { if (rlCheckBufferLimit(4)) rlglDraw(); - + #if defined(SUPPORT_QUADS_DRAW_MODE) rlEnableTexture(GetShapesTexture().id); @@ -1214,10 +1215,11 @@ void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) } // Draw a triangle using lines +// NOTE: Vertex must be provided in counter-clockwise order void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { if (rlCheckBufferLimit(6)) rlglDraw(); - + rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(v1.x, v1.y); @@ -1232,7 +1234,7 @@ void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) } // Draw a triangle fan defined by points -// NOTE: First point provided is shared by all triangles +// NOTE: First vertex provided is the center, shared by all triangles void DrawTriangleFan(Vector2 *points, int pointsCount, Color color) { if (pointsCount >= 3) @@ -1263,7 +1265,7 @@ void DrawTriangleFan(Vector2 *points, int pointsCount, Color color) } // Draw a triangle strip defined by points -// NOTE: Every new point connects with previous two +// NOTE: Every new vertex connects with previous two void DrawTriangleStrip(Vector2 *points, int pointsCount, Color color) { if (pointsCount >= 3) @@ -1296,6 +1298,7 @@ void DrawTriangleStrip(Vector2 *points, int pointsCount, Color color) void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) { if (sides < 3) sides = 3; + float centralAngle = 0.0f; if (rlCheckBufferLimit(4*(360/sides))) rlglDraw(); @@ -1307,7 +1310,7 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col rlEnableTexture(GetShapesTexture().id); rlBegin(RL_QUADS); - for (int i = 0; i < 360; i += 360/sides) + for (int i = 0; i < sides; i++) { rlColor4ub(color.r, color.g, color.b, color.a); @@ -1315,25 +1318,28 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col rlVertex2f(0, 0); rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); + rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); + rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); + centralAngle += 360.0f/(float)sides; rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); - rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius); + rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); } rlEnd(); rlDisableTexture(); #else rlBegin(RL_TRIANGLES); - for (int i = 0; i < 360; i += 360/sides) + for (int i = 0; i < sides; i++) { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(0, 0); - rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); - rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius); + rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); + + centralAngle += 360.0f/(float)sides; + rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); } rlEnd(); #endif @@ -1390,8 +1396,8 @@ bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2) { bool collision = false; - if ((rec1.x <= (rec2.x + rec2.width) && (rec1.x + rec1.width) >= rec2.x) && - (rec1.y <= (rec2.y + rec2.height) && (rec1.y + rec1.height) >= rec2.y)) collision = true; + if ((rec1.x < (rec2.x + rec2.width) && (rec1.x + rec1.width) > rec2.x) && + (rec1.y < (rec2.y + rec2.height) && (rec1.y + rec1.height) > rec2.y)) collision = true; return collision; } @@ -1509,9 +1515,9 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) static float EaseCubicInOut(float t, float b, float c, float d) { if ((t /= 0.5f*d) < 1) return 0.5f*c*t*t*t + b; - + t -= 2; - + return 0.5f*c*(t*t*t + 2.0f) + b; } diff --git a/src/text.c b/src/text.c index 1c775c8db..6d55fa8dc 100644 --- a/src/text.c +++ b/src/text.c @@ -10,9 +10,19 @@ * supported by default, to remove support, just comment unrequired #define in this module * * #define SUPPORT_DEFAULT_FONT +* Load default raylib font on initialization to be used by DrawText() and MeasureText(). +* If no default font loaded, DrawTextEx() and MeasureTextEx() are required. +* +* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH +* TextSplit() function static buffer max size +* +* #define TEXTSPLIT_MAX_SUBSTRINGS_COUNT +* TextSplit() function static substrings pointers array (pointing to static buffer) +* * * DEPENDENCIES: -* stb_truetype - Load TTF file and rasterize characters data +* stb_truetype - Load TTF file and rasterize characters data +* stb_rect_pack - Rectangles packing algorythms, required for font atlas generation * * * LICENSE: zlib/libpng @@ -40,7 +50,7 @@ // Check if config flags have been externally provided on compilation line #if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags + #include "config.h" // Defines module configuration flags #endif #include // Required for: malloc(), free() @@ -63,7 +73,18 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers of some Text*() functions +#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: + // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal() + +#define MAX_TEXT_UNICODE_CHARS 512 // Maximum number of unicode codepoints + +#if !defined(TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH) + #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH 1024 // Size of static buffer: TextSplit() +#endif + +#if !defined(TEXTSPLIT_MAX_SUBSTRINGS_COUNT) + #define TEXTSPLIT_MAX_SUBSTRINGS_COUNT 128 // Size of static pointers array: TextSplit() +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -282,7 +303,7 @@ Font LoadFont(const char *fileName) Font font = { 0 }; #if defined(SUPPORT_FILEFORMAT_TTF) - if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, NULL, DEFAULT_TTF_NUMCHARS); + if (IsFileExtension(fileName, ".ttf;.otf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, NULL, DEFAULT_TTF_NUMCHARS); else #endif #if defined(SUPPORT_FILEFORMAT_FNT) @@ -321,7 +342,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou { Image atlas = GenImageFontAtlas(font.chars, &font.recs, font.charsCount, font.baseSize, 2, 0); font.texture = LoadTextureFromImage(atlas); - + // Update chars[i].image to use alpha, required to be used on ImageDrawText() for (int i = 0; i < font.charsCount; i++) { @@ -439,7 +460,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) for (int i = 0; i < spriteFont.charsCount; i++) { spriteFont.chars[i].value = tempCharValues[i]; - + // Get character rectangle in the font atlas texture spriteFont.recs[i] = tempCharRecs[i]; @@ -447,7 +468,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) spriteFont.chars[i].offsetX = 0; spriteFont.chars[i].offsetY = 0; spriteFont.chars[i].advanceX = 0; - + // Fill character image data from fontClear data spriteFont.chars[i].image = ImageFromImage(fontClear, tempCharRecs[i]); } @@ -586,8 +607,8 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo *charRecs = NULL; // In case no chars count provided we suppose default of 95 - charsCount = (charsCount > 0) ? charsCount : 95; - + charsCount = (charsCount > 0)? charsCount : 95; + // NOTE: Rectangles memory is loaded here! Rectangle *recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle)); @@ -597,7 +618,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo // so image size would result bigger than default font type float requiredArea = 0; for (int i = 0; i < charsCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(chars[i].image.height + 2*padding)); - float guessSize = sqrtf(requiredArea)*1.25f; + float guessSize = sqrtf(requiredArea)*1.3f; int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT atlas.width = imageSize; // Atlas bitmap width @@ -711,7 +732,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo RL_FREE(atlas.data); atlas.data = dataGrayAlpha; atlas.format = UNCOMPRESSED_GRAY_ALPHA; - + *charRecs = recs; return atlas; @@ -756,120 +777,6 @@ void DrawFPS(int posX, int posY) DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, LIME); } -// Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found -// When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned -// Total number of bytes processed are returned as a parameter -// NOTE: the standard says U+FFFD should be returned in case of errors -// but that character is not supported by the default font in raylib -// TODO: optimize this code for speed!! -int GetNextCodepoint(const char *text, int *bytesProcessed) -{ -/* - UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt - - Char. number range | UTF-8 octet sequence - (hexadecimal) | (binary) - --------------------+--------------------------------------------- - 0000 0000-0000 007F | 0xxxxxxx - 0000 0080-0000 07FF | 110xxxxx 10xxxxxx - 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx - 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx -*/ - - // NOTE: on decode errors we return as soon as possible - - int code = 0x3f; // Codepoint (defaults to '?') - int octet = (unsigned char)(text[0]); // The first UTF8 octet - *bytesProcessed = 1; - - if (octet <= 0x7f) - { - // Only one octet (ASCII range x00-7F) - code = text[0]; - } - else if ((octet & 0xe0) == 0xc0) - { - // Two octets - // [0]xC2-DF [1]UTF8-tail(x80-BF) - unsigned char octet1 = text[1]; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - if ((octet >= 0xc2) && (octet <= 0xdf)) - { - code = ((octet & 0x1f) << 6) | (octet1 & 0x3f); - *bytesProcessed = 2; - } - } - else if ((octet & 0xf0) == 0xe0) - { - // Three octets - unsigned char octet1 = text[1]; - unsigned char octet2 = '\0'; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - octet2 = text[2]; - - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence - - /* - [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) - [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) - [0]xED [1]x80-9F [2]UTF8-tail(x80-BF) - [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF) - */ - - if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) || - ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; } - - if ((octet >= 0xe0) && (0 <= 0xef)) - { - code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); - *bytesProcessed = 3; - } - } - else if ((octet & 0xf8) == 0xf0) - { - // Four octets - if (octet > 0xf4) return code; - - unsigned char octet1 = text[1]; - unsigned char octet2 = '\0'; - unsigned char octet3 = '\0'; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - octet2 = text[2]; - - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence - - octet3 = text[3]; - - if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence - - /* - [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail - [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail - [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail - */ - - if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) || - ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence - - if (octet >= 0xf0) - { - code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); - *bytesProcessed = 4; - } - } - - if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid - - return code; -} - - // Draw text (using default font) // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used // NOTE: chars spacing is proportional to fontSize @@ -907,12 +814,12 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f int next = 0; letter = GetNextCodepoint(&text[i], &next); index = GetGlyphIndex(font, letter); - + // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set 'next = 1' - if (letter == 0x3f) next = 1; + if (letter == 0x3f) next = 1; i += (next - 1); - + if (letter == '\n') { // NOTE: Fixed line spacing of 1.5 lines @@ -960,17 +867,17 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f int startLine = -1; // Index where to begin drawing (where a line begins) int endLine = -1; // Index where to stop drawing (where a line ends) int lastk = -1; // Holds last value of the character position - + for (int i = 0, k = 0; i < length; i++, k++) { int glyphWidth = 0; int next = 0; letter = GetNextCodepoint(&text[i], &next); index = GetGlyphIndex(font, letter); - + // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1 - if (letter == 0x3f) next = 1; + if (letter == 0x3f) next = 1; i += next - 1; if (letter != '\n') @@ -988,7 +895,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f if (state == MEASURE_STATE) { // TODO: there are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more - // See: http://jkorpela.fi/chars/spaces.html + // See: http://jkorpela.fi/chars/spaces.html if ((letter == ' ') || (letter == '\t') || (letter == '\n')) endLine = i; if ((textOffsetX + glyphWidth + 1) >= rec.width) @@ -1013,7 +920,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f textOffsetX = 0; i = startLine; glyphWidth = 0; - + // Save character position when we switch states int tmp = lastk; lastk = k - 1; @@ -1114,16 +1021,16 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing for (int i = 0; i < len; i++) { lenCounter++; - + int next = 0; letter = GetNextCodepoint(&text[i], &next); index = GetGlyphIndex(font, letter); // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1 - if (letter == 0x3f) next = 1; - i += next - 1; - + if (letter == 0x3f) next = 1; + i += next - 1; + if (letter != '\n') { if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX; @@ -1194,27 +1101,6 @@ unsigned int TextLength(const char *text) return length; } -// Returns total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found -// NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead -unsigned int TextCountCodepoints(const char *text) -{ - unsigned int len = 0; - char *ptr = (char *)&text[0]; - - while (*ptr != '\0') - { - int next = 0; - int letter = GetNextCodepoint(ptr, &next); - - if (letter == 0x3f) ptr += 1; - else ptr += next; - - len++; - } - - return len; -} - // Formatting of text with variables to 'embed' const char *TextFormat(const char *text, ...) { @@ -1338,14 +1224,14 @@ const char *TextJoin(const char **textList, int count, const char *delimiter) for (int i = 0; i < count; i++) { int textListLength = strlen(textList[i]); - + // Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH if ((totalLength + textListLength) < MAX_TEXT_BUFFER_LENGTH) { strcat(text, textList[i]); totalLength += textListLength; - - if ((delimiterLen > 0) && (i < (count - 1))) + + if ((delimiterLen > 0) && (i < (count - 1))) { strcat(text, delimiter); totalLength += delimiterLen; @@ -1362,30 +1248,33 @@ const char **TextSplit(const char *text, char delimiter, int *count) // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, // all used memory is static... it has some limitations: - // 1. Maximum number of possible split strings is set by MAX_SUBSTRINGS_COUNT - // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH + // 1. Maximum number of possible split strings is set by TEXTSPLIT_MAX_SUBSTRINGS_COUNT + // 2. Maximum size of text to split is TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH - #define MAX_SUBSTRINGS_COUNT 64 - - static const char *result[MAX_SUBSTRINGS_COUNT] = { NULL }; - static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; - memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); + static const char *result[TEXTSPLIT_MAX_SUBSTRINGS_COUNT] = { NULL }; + static char buffer[TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(buffer, 0, TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH); result[0] = buffer; - int counter = 1; + int counter = 0; - // Count how many substrings we have on text and point to every one - for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + if (text != NULL) { - buffer[i] = text[i]; - if (buffer[i] == '\0') break; - else if (buffer[i] == delimiter) - { - buffer[i] = '\0'; // Set an end of string at this point - result[counter] = buffer + i + 1; - counter++; + counter = 1; - if (counter == MAX_SUBSTRINGS_COUNT) break; + // Count how many substrings we have on text and point to every one + for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + { + buffer[i] = text[i]; + if (buffer[i] == '\0') break; + else if (buffer[i] == delimiter) + { + buffer[i] = '\0'; // Set an end of string at this point + result[counter] = buffer + i + 1; + counter++; + + if (counter == TEXTSPLIT_MAX_SUBSTRINGS_COUNT) break; + } } } @@ -1487,6 +1376,221 @@ int TextToInteger(const char *text) return result; } + +// Encode text codepoint into utf8 text (memory must be freed!) +char *TextToUtf8(int *codepoints, int length) +{ + // We allocate enough memory fo fit all possible codepoints + // NOTE: 5 bytes for every codepoint should be enough + char *text = (char *)calloc(length*5, 1); + const char *utf8 = NULL; + int size = 0; + + for (int i = 0, bytes = 0; i < length; i++) + { + utf8 = CodepointToUtf8(codepoints[i], &bytes); + strncpy(text + size, utf8, bytes); + size += bytes; + } + + // Resize memory to text length + string NULL terminator + realloc(text, size + 1); + + return text; +} + +// Get all codepoints in a string, codepoints count returned by parameters +int *GetCodepoints(const char *text, int *count) +{ + static int codepoints[MAX_TEXT_UNICODE_CHARS] = { 0 }; + memset(codepoints, 0, MAX_TEXT_UNICODE_CHARS*sizeof(int)); + + int bytesProcessed = 0; + int textLength = strlen(text); + int codepointsCount = 0; + + for (int i = 0; i < textLength; codepointsCount++) + { + codepoints[codepointsCount] = GetNextCodepoint(text + i, &bytesProcessed); + i += bytesProcessed; + } + + *count = codepointsCount; + + return codepoints; +} + +// Returns total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found +// NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead +int GetCodepointsCount(const char *text) +{ + unsigned int len = 0; + char *ptr = (char *)&text[0]; + + while (*ptr != '\0') + { + int next = 0; + int letter = GetNextCodepoint(ptr, &next); + + if (letter == 0x3f) ptr += 1; + else ptr += next; + + len++; + } + + return len; +} + + +// Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found +// When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned +// Total number of bytes processed are returned as a parameter +// NOTE: the standard says U+FFFD should be returned in case of errors +// but that character is not supported by the default font in raylib +// TODO: optimize this code for speed!! +int GetNextCodepoint(const char *text, int *bytesProcessed) +{ +/* + UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt + + Char. number range | UTF-8 octet sequence + (hexadecimal) | (binary) + --------------------+--------------------------------------------- + 0000 0000-0000 007F | 0xxxxxxx + 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx +*/ + // NOTE: on decode errors we return as soon as possible + + int code = 0x3f; // Codepoint (defaults to '?') + int octet = (unsigned char)(text[0]); // The first UTF8 octet + *bytesProcessed = 1; + + if (octet <= 0x7f) + { + // Only one octet (ASCII range x00-7F) + code = text[0]; + } + else if ((octet & 0xe0) == 0xc0) + { + // Two octets + // [0]xC2-DF [1]UTF8-tail(x80-BF) + unsigned char octet1 = text[1]; + + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + + if ((octet >= 0xc2) && (octet <= 0xdf)) + { + code = ((octet & 0x1f) << 6) | (octet1 & 0x3f); + *bytesProcessed = 2; + } + } + else if ((octet & 0xf0) == 0xe0) + { + // Three octets + unsigned char octet1 = text[1]; + unsigned char octet2 = '\0'; + + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + + octet2 = text[2]; + + if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence + + /* + [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) + [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) + [0]xED [1]x80-9F [2]UTF8-tail(x80-BF) + [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF) + */ + + if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) || + ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; } + + if ((octet >= 0xe0) && (0 <= 0xef)) + { + code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); + *bytesProcessed = 3; + } + } + else if ((octet & 0xf8) == 0xf0) + { + // Four octets + if (octet > 0xf4) return code; + + unsigned char octet1 = text[1]; + unsigned char octet2 = '\0'; + unsigned char octet3 = '\0'; + + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + + octet2 = text[2]; + + if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence + + octet3 = text[3]; + + if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence + + /* + [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail + [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail + [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail + */ + + if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) || + ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence + + if (octet >= 0xf0) + { + code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); + *bytesProcessed = 4; + } + } + + if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid + + return code; +} + +// Encode codepoint into utf8 text (char array length returned as parameter) +RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength) +{ + static char utf8[6] = { 0 }; + int length = 0; + + if (codepoint <= 0x7f) + { + utf8[0] = (char)codepoint; + length = 1; + } + else if (codepoint <= 0x7ff) + { + utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0); + utf8[1] = (char)((codepoint & 0x3f) | 0x80); + length = 2; + } + else if (codepoint <= 0xffff) + { + utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0); + utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[2] = (char)((codepoint & 0x3f) | 0x80); + length = 3; + } + else if (codepoint <= 0x10ffff) + { + utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0); + utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80); + utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[3] = (char)((codepoint & 0x3f) | 0x80); + length = 4; + } + + *byteLength = length; + + return utf8; +} //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- @@ -1567,18 +1671,15 @@ static Font LoadBMFont(const char *fileName) TraceLog(LOG_DEBUG, "[%s] Font texture loading path: %s", fileName, texPath); Image imFont = LoadImage(texPath); - Image imFontAlpha = ImageCopy(imFont); if (imFont.format == UNCOMPRESSED_GRAYSCALE) { - for (int i = 0; i < imFontAlpha.width*imFontAlpha.height; i++) ((unsigned char *)imFontAlpha.data)[i] = 0xff; - - ImageAlphaMask(&imFontAlpha, imFont); - font.texture = LoadTextureFromImage(imFontAlpha); + // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel + ImageAlphaMask(&imFont, imFont); + for (int p = 0; p < (imFont.width*imFont.height*2); p += 2) ((unsigned char *)(imFont.data))[p] = 0xff; } - else font.texture = LoadTextureFromImage(imFont); - - UnloadImage(imFont); + + font.texture = LoadTextureFromImage(imFont); RL_FREE(texPath); @@ -1595,7 +1696,7 @@ static Font LoadBMFont(const char *fileName) fgets(buffer, MAX_BUFFER_SIZE, fntFile); sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); - + // Get character rectangle in the font atlas texture font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; @@ -1604,12 +1705,12 @@ static Font LoadBMFont(const char *fileName) font.chars[i].offsetX = charOffsetX; font.chars[i].offsetY = charOffsetY; font.chars[i].advanceX = charAdvanceX; - + // Fill character image data from imFont data - font.chars[i].image = ImageFromImage(imFontAlpha, font.recs[i]); + font.chars[i].image = ImageFromImage(imFont, font.recs[i]); } - UnloadImage(imFontAlpha); + UnloadImage(imFont); fclose(fntFile); diff --git a/src/textures.c b/src/textures.c index 53e22341b..ec08e3ac8 100644 --- a/src/textures.c +++ b/src/textures.c @@ -161,6 +161,9 @@ //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- +#if defined(SUPPORT_FILEFORMAT_GIF) +static Image LoadAnimatedGIF(const char *fileName, int *frames, int **delays); // Load animated GIF file +#endif #if defined(SUPPORT_FILEFORMAT_DDS) static Image LoadDDS(const char *fileName); // Load DDS file #endif @@ -253,13 +256,10 @@ Image LoadImage(const char *fileName) FILE *imFile = fopen(fileName, "rb"); - stbi_set_flip_vertically_on_load(true); - // Load 32 bit per channel floats data + //stbi_set_flip_vertically_on_load(true); image.data = stbi_loadf_from_file(imFile, &image.width, &image.height, &imgBpp, 0); - stbi_set_flip_vertically_on_load(false); - fclose(imFile); image.mipmaps = 1; @@ -551,7 +551,7 @@ Color *GetImageData(Image image) pixels[i].a = 255; k += 3; - } + } break; case UNCOMPRESSED_R32G32B32A32: { pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f); @@ -560,7 +560,7 @@ Color *GetImageData(Image image) pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f); k += 4; - } + } break; default: break; } } @@ -680,6 +680,37 @@ Vector4 *GetImageDataNormalized(Image image) return pixels; } +// Get image alpha border rectangle +Rectangle GetImageAlphaBorder(Image image, float threshold) +{ + Color *pixels = GetImageData(image); + + int xMin = 65536; // Define a big enough number + int xMax = 0; + int yMin = 65536; + int yMax = 0; + + for (int y = 0; y < image.height; y++) + { + for (int x = 0; x < image.width; x++) + { + if (pixels[y*image.width + x].a > (unsigned char)(threshold*255.0f)) + { + if (x < xMin) xMin = x; + if (x > xMax) xMax = x; + if (y < yMin) yMin = y; + if (y > yMax) yMax = y; + } + } + } + + Rectangle crop = { xMin, yMin, (xMax + 1) - xMin, (yMax + 1) - yMin }; + + RL_FREE(pixels); + + return crop; +} + // Get pixel data size in bytes (image or texture) // NOTE: Size depends on pixel format int GetPixelDataSize(int width, int height, int format) @@ -818,37 +849,40 @@ void ExportImageAsCode(Image image, const char *fileName) { #define BYTES_TEXT_PER_LINE 20 - char varFileName[256] = { 0 }; - int dataSize = GetPixelDataSize(image.width, image.height, image.format); - FILE *txtFile = fopen(fileName, "wt"); - fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////////\n"); - fprintf(txtFile, "// //\n"); - fprintf(txtFile, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes //\n"); - fprintf(txtFile, "// //\n"); - fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n"); - fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n"); - fprintf(txtFile, "// //\n"); - fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n"); - fprintf(txtFile, "// //\n"); - fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); + if (txtFile != NULL) + { + char varFileName[256] = { 0 }; + int dataSize = GetPixelDataSize(image.width, image.height, image.format); - // Get file name from path and convert variable name to uppercase - strcpy(varFileName, GetFileNameWithoutExt(fileName)); - for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } + fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n"); + fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// Copyright (c) 2019 Ramon Santamaria (@raysan5) //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); - // Add image information - fprintf(txtFile, "// Image data information\n"); - fprintf(txtFile, "#define %s_WIDTH %i\n", varFileName, image.width); - fprintf(txtFile, "#define %s_HEIGHT %i\n", varFileName, image.height); - fprintf(txtFile, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format); + // Get file name from path and convert variable name to uppercase + strcpy(varFileName, GetFileNameWithoutExt(fileName)); + for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } - fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize); - for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]); - fprintf(txtFile, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]); + // Add image information + fprintf(txtFile, "// Image data information\n"); + fprintf(txtFile, "#define %s_WIDTH %i\n", varFileName, image.width); + fprintf(txtFile, "#define %s_HEIGHT %i\n", varFileName, image.height); + fprintf(txtFile, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format); - fclose(txtFile); + fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize); + for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]); + fprintf(txtFile, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]); + + fclose(txtFile); + } } // Copy an image to a new image @@ -892,9 +926,11 @@ Image ImageCopy(Image image) Image ImageFromImage(Image image, Rectangle rec) { Image result = ImageCopy(image); - + +#if defined(SUPPORT_IMAGE_MANIPULATION) ImageCrop(&result, rec); - +#endif + return result; } @@ -1145,13 +1181,18 @@ void ImageAlphaMask(Image *image, Image alphaMask) // In case image is only grayscale, we just add alpha channel if (image->format == UNCOMPRESSED_GRAYSCALE) { - ImageFormat(image, UNCOMPRESSED_GRAY_ALPHA); + unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2); // Apply alpha mask to alpha channel - for (int i = 0, k = 1; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2) + for (int i = 0, k = 0; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2) { - ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i]; + data[k] = ((unsigned char *)image->data)[i]; + data[k + 1] = ((unsigned char *)mask.data)[i]; } + + RL_FREE(image->data); + image->data = data; + image->format = UNCOMPRESSED_GRAY_ALPHA; } else { @@ -1303,18 +1344,11 @@ void ImageCrop(Image *image, Rectangle crop) // Security check to avoid program crash if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; - // Security checks to make sure cropping rectangle is inside margins - if ((crop.x + crop.width) > image->width) - { - crop.width = image->width - crop.x; - TraceLog(LOG_WARNING, "Crop rectangle width out of bounds, rescaled crop width: %i", crop.width); - } - - if ((crop.y + crop.height) > image->height) - { - crop.height = image->height - crop.y; - TraceLog(LOG_WARNING, "Crop rectangle height out of bounds, rescaled crop height: %i", crop.height); - } + // Security checks to validate crop rectangle + if (crop.x < 0) { crop.width += crop.x; crop.x = 0; } + if (crop.y < 0) { crop.height += crop.y; crop.y = 0; } + if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x; + if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y; if ((crop.x < image->width) && (crop.y < image->height)) { @@ -1343,10 +1377,7 @@ void ImageCrop(Image *image, Rectangle crop) // Reformat 32bit RGBA image to original format ImageFormat(image, format); } - else - { - TraceLog(LOG_WARNING, "Image can not be cropped, crop rectangle out of bounds"); - } + else TraceLog(LOG_WARNING, "Image can not be cropped, crop rectangle out of bounds"); } // Crop image depending on alpha value @@ -1792,14 +1823,16 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color } Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it - ImageCrop(&srcCopy, srcRec); // Crop source image to desired source rectangle + + // Crop source image to desired source rectangle (if required) + if ((src.width != (int)srcRec.width) && (src.height != (int)srcRec.height)) ImageCrop(&srcCopy, srcRec); // Scale source image in case destination rec size is different than source rec size if (((int)dstRec.width != (int)srcRec.width) || ((int)dstRec.height != (int)srcRec.height)) { ImageResize(&srcCopy, (int)dstRec.width, (int)dstRec.height); } - + // Check that dstRec is inside dst image // Allow negative position within destination with cropping if (dstRec.x < 0) @@ -1808,7 +1841,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color dstRec.width = dstRec.width + dstRec.x; dstRec.x = 0; } - + if ((dstRec.x + dstRec.width) > dst->width) { ImageCrop(&srcCopy, (Rectangle) { 0, 0, dst->width - dstRec.x, dstRec.height }); @@ -1821,8 +1854,8 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color dstRec.height = dstRec.height + dstRec.y; dstRec.y = 0; } - - if (dstRec.y > (dst->height - dstRec.height)) + + if ((dstRec.y + dstRec.height) > dst->height) { ImageCrop(&srcCopy, (Rectangle) { 0, 0, dstRec.width, dst->height - dstRec.y }); dstRec.height = dst->height - dstRec.y; @@ -1847,7 +1880,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color fdst = ColorNormalize(dstPixels[j*(int)dst->width + i]); fsrc = ColorNormalize(srcPixels[(j - (int)dstRec.y)*(int)dstRec.width + (i - (int)dstRec.x)]); - + // Apply color tint to source image fsrc.x *= ftint.x; fsrc.y *= ftint.y; fsrc.z *= ftint.z; fsrc.w *= ftint.w; @@ -1910,16 +1943,16 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Create image to store text Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); - + for (int i = 0; i < length; i++) { int next = 0; letter = GetNextCodepoint(&text[i], &next); index = GetGlyphIndex(font, letter); - - if (letter == 0x3f) next = 1; + + if (letter == 0x3f) next = 1; i += (next - 1); - + if (letter == '\n') { // TODO: Support line break @@ -1929,7 +1962,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co if (letter != ' ') { ImageDraw(&imText, font.chars[index].image, (Rectangle){ 0, 0, font.chars[index].image.width, font.chars[index].image.height }, - (Rectangle){ (float)(positionX + font.chars[index].offsetX),(float)font.chars[index].offsetY, + (Rectangle){ (float)(positionX + font.chars[index].offsetX),(float)font.chars[index].offsetY, font.chars[index].image.width, font.chars[index].image.height }, tint); } @@ -1969,7 +2002,7 @@ void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color) ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y, rec.width, thick }, color); ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + thick, thick, rec.height - thick*2 }, color); ImageDrawRectangle(dst, (Rectangle){ rec.x + rec.width - thick, rec.y + thick, thick, rec.height - thick*2 }, color); - ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.height - thick, rec.width, thick }, color); + ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + rec.height - thick, rec.width, thick }, color); } // Draw text (default font) within an image (destination) @@ -2684,9 +2717,9 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V { float width = (float)texture.width; float height = (float)texture.height; - + bool flipX = false; - + if (sourceRec.width < 0) { flipX = true; sourceRec.width *= -1; } if (sourceRec.height < 0) sourceRec.y -= sourceRec.height; @@ -2927,6 +2960,45 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destR //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- +#if defined(SUPPORT_FILEFORMAT_GIF) +// Load animated GIF data +// - Image.data buffer includes all frames: [image#0][image#1][image#2][...] +// - Number of frames is returned through 'frames' parameter +// - Frames delay is returned through 'delays' parameter (int array) +// - All frames are returned in RGBA format +static Image LoadAnimatedGIF(const char *fileName, int *frames, int **delays) +{ + Image image = { 0 }; + + FILE *gifFile = fopen(fileName, "rb"); + + if (gifFile == NULL) + { + TraceLog(LOG_WARNING, "[%s] Animated GIF file could not be opened", fileName); + } + else + { + fseek(gifFile, 0L, SEEK_END); + int size = ftell(gifFile); + fseek(gifFile, 0L, SEEK_SET); + + unsigned char *buffer = (unsigned char *)RL_CALLOC(size, sizeof(char)); + fread(buffer, sizeof(char), size, gifFile); + + fclose(gifFile); // Close file pointer + + int comp = 0; + image.data = stbi_load_gif_from_memory(buffer, size, delays, &image.width, &image.height, frames, &comp, 4); + + image.mipmaps = 1; + image.format = UNCOMPRESSED_R8G8B8A8; + + free(buffer); + } + + return image; +} +#endif #if defined(SUPPORT_FILEFORMAT_DDS) // Loading DDS image data (compressed or uncompressed) diff --git a/src/utils.h b/src/utils.h index 1611b02ca..98c2f59c2 100644 --- a/src/utils.h +++ b/src/utils.h @@ -36,7 +36,7 @@ // Some basic Defines //---------------------------------------------------------------------------------- #if defined(PLATFORM_ANDROID) - #define fopen(name, mode) android_fopen(name, mode) + #define fopen(name, mode) android_fopen(name, mode) #endif //---------------------------------------------------------------------------------- @@ -86,8 +86,8 @@ typedef enum { typedef struct UWPMessage { UWPMessageType type; // Message type - - Vector2 paramVector0; // Vector parameters + + Vector2 paramVector0; // Vector parameters int paramInt0; // Int parameter int paramInt1; // Int parameter char paramChar0; // Char parameters