Update raylib

This commit is contained in:
sergeypdev 2025-07-19 18:18:38 +04:00
commit 2ee0842778
44 changed files with 16968 additions and 13072 deletions

View File

@ -13,10 +13,12 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib-cs](https://github.com/raylib-cs/raylib-cs) | **5.5** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib |
| [Raylib-CsLo](https://github.com/NotNotTech/Raylib-CsLo) | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 |
| [Raylib-CSharp-Vinculum](https://github.com/ZeroElectric/Raylib-CSharp-Vinculum) | **5.0** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 |
| [Raylib-CSharp](https://github.com/MrScautHD/Raylib-CSharp) | **5.1-dev** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MIT |
| [Raylib-CSharp](https://github.com/MrScautHD/Raylib-CSharp) | **5.5** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MIT |
| [Raylib-cs.BleedingEdge](https://github.com/danilwhale/Raylib-cs.BleedingEdge) | **5.6-dev** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib |
| [cl-raylib](https://github.com/longlene/cl-raylib) | 4.0 | [Common Lisp](https://common-lisp.net) | MIT |
| [claylib/wrap](https://github.com/defun-games/claylib) | 4.5 | [Common Lisp](https://common-lisp.net) | Zlib |
| [claw-raylib](https://github.com/bohonghuang/claw-raylib) | **auto** | [Common Lisp](https://common-lisp.net) | Apache-2.0 |
| [raylib](https://github.com/fosskers/raylib) | 5.5 | [Common Lisp](https://common-lisp.net) | MPL-2.0 |
| [chez-raylib](https://github.com/Yunoinsky/chez-raylib) | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme) | GPLv3 |
| [CLIPSraylib](https://github.com/mrryanjohnston/CLIPSraylib) | **auto** | [CLIPS](https://www.clipsrules.net/) | MIT |
| [raylib-cr](https://github.com/sol-vin/raylib-cr) | 4.6-dev (5e1a81) | [Crystal](https://crystal-lang.org) | Apache-2.0 |
@ -49,7 +51,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib-luajit-generated](https://github.com/james2doyle/raylib-luajit-generated) | 5.5 | [Lua](http://www.lua.org) | MIT |
| [raylib-matte](https://github.com/jcorks/raylib-matte) | 4.6-dev | [Matte](https://github.com/jcorks/matte) | **???** |
| [Raylib.nelua](https://github.com/AuzFox/Raylib.nelua) | **5.5** | [nelua](https://nelua.io) | Zlib |
| [raylib-bindings](https://github.com/vaiorabbit/raylib-bindings) | 5.6-dev | [Ruby](https://www.ruby-lang.org/en) | Zlib |
| [raylib-bindings](https://github.com/vaiorabbit/raylib-bindings) | 5.6-dev | [Ruby](https://www.ruby-lang.org/en) | Zlib |
| [naylib](https://github.com/planetis-m/naylib) | **5.6-dev** | [Nim](https://nim-lang.org) | MIT |
| [node-raylib](https://github.com/RobLoach/node-raylib) | 4.5 | [Node.js](https://nodejs.org/en) | Zlib |
| [raylib-odin](https://github.com/odin-lang/Odin/tree/master/vendor/raylib) | **5.5** | [Odin](https://odin-lang.org) | BSD-3Clause |
@ -93,6 +95,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib-apl](https://github.com/Brian-ED/raylib-apl) | **5.0** | [Dyalog APL](https://www.dyalog.com/) | MIT |
| [raylib-jai](https://github.com/ahmedqarmout2/raylib-jai) | **5.5** | [Jai](https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md) | MIT |
| [fnl-raylib](https://github.com/0riginaln0/fnl-raylib) | **5.5** | [Fennel](https://fennel-lang.org/) | MIT |
| [Rayua](https://github.com/uiua-lang/rayua) | **5.5** | [Uiua](https://www.uiua.org/) | **???** |
### Utility Wrapers
@ -103,6 +106,7 @@ These are utility wrappers for specific languages, they are not required to use
| [claylib](https://github.com/defun-games/claylib) | 4.5 | [Common Lisp](https://common-lisp.net) | Zlib |
| [rayed-bqn](https://github.com/Brian-ED/rayed-bqn) | **5.0** | [BQN](https://mlochbaum.github.io/BQN) | MIT |
| [DOOR](https://github.com/RealDoigt/DOOR) | 4.0 | [D](https://dlang.org) | MIT |
| [Iris](https://github.com/Marcos-cat/iris) | **5.5** | [Uiua](https://www.uiua.org/) | MIT |
### Older or Unmaintained Language Bindings

View File

@ -4,6 +4,9 @@ const builtin = @import("builtin");
/// Minimum supported version of Zig
const min_ver = "0.13.0";
const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str;
const emccOutputFile = "index.html";
comptime {
const order = std.SemanticVersion.order;
const parse = std.SemanticVersion.parse;
@ -45,6 +48,26 @@ fn emSdkSetupStep(b: *std.Build, emsdk: *std.Build.Dependency) !?*std.Build.Step
}
}
// Adapted from Not-Nik/raylib-zig
fn emscriptenRunStep(b: *std.Build, emsdk: *std.Build.Dependency, examplePath: []const u8) !*std.Build.Step.Run {
const dot_emsc_path = emsdk.path("upstream/emscripten/cache/sysroot/include").getPath(b);
// If compiling on windows , use emrun.bat.
const emrunExe = switch (builtin.os.tag) {
.windows => "emrun.bat",
else => "emrun",
};
var emrun_run_arg = try b.allocator.alloc(u8, dot_emsc_path.len + emrunExe.len + 1);
defer b.allocator.free(emrun_run_arg);
if (b.sysroot == null) {
emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ emsdk.path("upstream/emscripten").getPath(b), emrunExe });
} else {
emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ dot_emsc_path, emrunExe });
}
const run_cmd = b.addSystemCommand(&.{ emrun_run_arg, examplePath });
return run_cmd;
}
/// A list of all flags from `src/config.h` that one may override
const config_h_flags = outer: {
// Set this value higher if compile errors happen as `src/config.h` gets larger
@ -205,7 +228,7 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
raylib.root_module.addCMacro("PLATFORM_DRM", "");
raylib.root_module.addCMacro("EGL_NO_X11", "");
raylib.root_module.addCMacro("DEFAULT_BATCH_BUFFER_ELEMENT", "");
} else if (target.result.abi == .android) {
} else if (target.result.abi.isAndroid()) {
//these are the only tag options per https://developer.android.com/ndk/guides/other_build_systems
const hostTuple = switch (builtin.target.os.tag) {
@ -215,7 +238,14 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
else => @panic("unsupported host OS"),
};
const androidTriple = try target.result.linuxTriple(b.allocator);
const androidTriple = switch (target.result.cpu.arch) {
.x86 => "i686-linux-android",
.x86_64 => "x86_64-linux-android",
.arm => "arm-linux-androideabi",
.aarch64 => "aarch64-linux-android",
.riscv64 => "riscv64-linux-android",
else => error.InvalidAndroidTarget,
} catch @panic("invalid android target!");
const androidNdkPathString: []const u8 = options.android_ndk;
if (androidNdkPathString.len < 1) @panic("no ndk path provided and ANDROID_NDK_HOME is not set");
const androidApiLevel: []const u8 = options.android_api_version;
@ -249,6 +279,7 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
raylib.root_module.linkSystemLibrary("GLESv2", .{});
raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES2", "");
}
raylib.root_module.linkSystemLibrary("EGL", .{});
setDesktopPlatform(raylib, .android);
} else {
@ -331,7 +362,6 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
setDesktopPlatform(raylib, options.platform);
},
.emscripten => {
// Include emscripten for cross compilation
if (b.lazyDependency("emsdk", .{})) |dep| {
if (try emSdkSetupStep(b, dep)) |emSdkStep| {
raylib.step.dependOn(&emSdkStep.step);
@ -358,8 +388,8 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
return raylib;
}
pub fn addRaygui(b: *std.Build, raylib: *std.Build.Step.Compile, raygui_dep: *std.Build.Dependency) void {
const raylib_dep = b.dependencyFromBuildZig(@This(), .{});
pub fn addRaygui(b: *std.Build, raylib: *std.Build.Step.Compile, raygui_dep: *std.Build.Dependency, options: Options) void {
const raylib_dep = b.dependencyFromBuildZig(@This(), options);
var gen_step = b.addWriteFiles();
raylib.step.dependOn(&gen_step.step);
@ -503,12 +533,9 @@ fn addExamples(
optimize: std.builtin.OptimizeMode,
raylib: *std.Build.Step.Compile,
) !*std.Build.Step {
if (target.result.os.tag == .emscripten) {
return &b.addFail("Emscripten building via Zig unsupported").step;
}
const all = b.step(module, "All " ++ module ++ " examples");
const module_subpath = b.pathJoin(&.{ "examples", module });
const module_resources = b.pathJoin(&.{ module_subpath, "resources@resources" });
var dir = try std.fs.cwd().openDir(b.pathFromRoot(module_subpath), .{ .iterate = true });
defer if (comptime builtin.zig_version.minor >= 12) dir.close();
@ -522,71 +549,154 @@ fn addExamples(
// zig's mingw headers do not include pthread.h
if (std.mem.eql(u8, "core_loading_thread", name) and target.result.os.tag == .windows) continue;
const exe = b.addExecutable(.{
.name = name,
.target = target,
.optimize = optimize,
});
exe.addCSourceFile(.{ .file = b.path(path), .flags = &.{} });
exe.linkLibC();
if (target.result.os.tag == .emscripten) {
const exe_lib = b.addStaticLibrary(.{
.name = name,
.target = target,
.optimize = optimize,
});
exe_lib.addCSourceFile(.{
.file = b.path(path),
.flags = &.{},
});
exe_lib.linkLibC();
// special examples that test using these external dependencies directly
// alongside raylib
if (std.mem.eql(u8, name, "rlgl_standalone")) {
exe.addIncludePath(b.path("src"));
exe.addIncludePath(b.path("src/external/glfw/include"));
if (!hasCSource(raylib.root_module, "rglfw.c")) {
exe.addCSourceFile(.{ .file = b.path("src/rglfw.c"), .flags = &.{} });
if (std.mem.eql(u8, name, "rlgl_standalone")) {
//TODO: Make rlgl_standalone example work
continue;
}
if (std.mem.eql(u8, name, "raylib_opengl_interop")) {
//TODO: Make raylib_opengl_interop example work
continue;
}
exe_lib.linkLibrary(raylib);
// Include emscripten for cross compilation
if (b.lazyDependency("emsdk", .{})) |emsdk_dep| {
if (try emSdkSetupStep(b, emsdk_dep)) |emSdkStep| {
exe_lib.step.dependOn(&emSdkStep.step);
}
exe_lib.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include"));
// Create the output directory because emcc can't do it.
const emccOutputDirExample = b.pathJoin(&.{ emccOutputDir, name, std.fs.path.sep_str });
const mkdir_command = switch (builtin.os.tag) {
.windows => b.addSystemCommand(&.{ "cmd.exe", "/c", "if", "not", "exist", emccOutputDirExample, "mkdir", emccOutputDirExample }),
else => b.addSystemCommand(&.{ "mkdir", "-p", emccOutputDirExample }),
};
const emcc_exe = switch (builtin.os.tag) {
.windows => "emcc.bat",
else => "emcc",
};
const emcc_exe_path = b.pathJoin(&.{ emsdk_dep.path("upstream/emscripten").getPath(b), emcc_exe });
const emcc_command = b.addSystemCommand(&[_][]const u8{emcc_exe_path});
emcc_command.step.dependOn(&mkdir_command.step);
const emccOutputDirExampleWithFile = b.pathJoin(&.{ emccOutputDir, name, std.fs.path.sep_str, emccOutputFile });
emcc_command.addArgs(&[_][]const u8{
"-o",
emccOutputDirExampleWithFile,
"-sFULL-ES3=1",
"-sUSE_GLFW=3",
"-sSTACK_OVERFLOW_CHECK=1",
"-sEXPORTED_RUNTIME_METHODS=['requestFullscreen']",
"-sASYNCIFY",
"-O0",
"--emrun",
"--preload-file",
module_resources,
"--shell-file",
b.path("src/shell.html").getPath(b),
});
const link_items: []const *std.Build.Step.Compile = &.{
raylib,
exe_lib,
};
for (link_items) |item| {
emcc_command.addFileArg(item.getEmittedBin());
emcc_command.step.dependOn(&item.step);
}
const run_step = try emscriptenRunStep(b, emsdk_dep, emccOutputDirExampleWithFile);
run_step.step.dependOn(&emcc_command.step);
run_step.addArg("--no_browser");
const run_option = b.step(name, name);
run_option.dependOn(&run_step.step);
all.dependOn(&emcc_command.step);
}
} else {
const exe = b.addExecutable(.{
.name = name,
.target = target,
.optimize = optimize,
});
exe.addCSourceFile(.{ .file = b.path(path), .flags = &.{} });
exe.linkLibC();
// special examples that test using these external dependencies directly
// alongside raylib
if (std.mem.eql(u8, name, "rlgl_standalone")) {
exe.addIncludePath(b.path("src"));
exe.addIncludePath(b.path("src/external/glfw/include"));
if (!hasCSource(raylib.root_module, "rglfw.c")) {
exe.addCSourceFile(.{ .file = b.path("src/rglfw.c"), .flags = &.{} });
}
}
if (std.mem.eql(u8, name, "raylib_opengl_interop")) {
exe.addIncludePath(b.path("src/external"));
}
exe.linkLibrary(raylib);
switch (target.result.os.tag) {
.windows => {
exe.linkSystemLibrary("winmm");
exe.linkSystemLibrary("gdi32");
exe.linkSystemLibrary("opengl32");
exe.root_module.addCMacro("PLATFORM_DESKTOP", "");
},
.linux => {
exe.linkSystemLibrary("GL");
exe.linkSystemLibrary("rt");
exe.linkSystemLibrary("dl");
exe.linkSystemLibrary("m");
exe.linkSystemLibrary("X11");
exe.root_module.addCMacro("PLATFORM_DESKTOP", "");
},
.macos => {
exe.linkFramework("Foundation");
exe.linkFramework("Cocoa");
exe.linkFramework("OpenGL");
exe.linkFramework("CoreAudio");
exe.linkFramework("CoreVideo");
exe.linkFramework("IOKit");
exe.root_module.addCMacro("PLATFORM_DESKTOP", "");
},
else => {
@panic("Unsupported OS");
},
}
const install_cmd = b.addInstallArtifact(exe, .{});
const run_cmd = b.addRunArtifact(exe);
run_cmd.cwd = b.path(module_subpath);
run_cmd.step.dependOn(&install_cmd.step);
const run_step = b.step(name, name);
run_step.dependOn(&run_cmd.step);
all.dependOn(&install_cmd.step);
}
if (std.mem.eql(u8, name, "raylib_opengl_interop")) {
exe.addIncludePath(b.path("src/external"));
}
exe.linkLibrary(raylib);
switch (target.result.os.tag) {
.windows => {
exe.linkSystemLibrary("winmm");
exe.linkSystemLibrary("gdi32");
exe.linkSystemLibrary("opengl32");
exe.root_module.addCMacro("PLATFORM_DESKTOP", "");
},
.linux => {
exe.linkSystemLibrary("GL");
exe.linkSystemLibrary("rt");
exe.linkSystemLibrary("dl");
exe.linkSystemLibrary("m");
exe.linkSystemLibrary("X11");
exe.root_module.addCMacro("PLATFORM_DESKTOP", "");
},
.macos => {
exe.linkFramework("Foundation");
exe.linkFramework("Cocoa");
exe.linkFramework("OpenGL");
exe.linkFramework("CoreAudio");
exe.linkFramework("CoreVideo");
exe.linkFramework("IOKit");
exe.root_module.addCMacro("PLATFORM_DESKTOP", "");
},
else => {
@panic("Unsupported OS");
},
}
const install_cmd = b.addInstallArtifact(exe, .{});
const run_cmd = b.addRunArtifact(exe);
run_cmd.cwd = b.path(module_subpath);
run_cmd.step.dependOn(&install_cmd.step);
const run_step = b.step(name, name);
run_step.dependOn(&run_cmd.step);
all.dependOn(&install_cmd.step);
}
return all;
}

View File

@ -12,8 +12,8 @@
.lazy = true,
},
.emsdk = .{
.url = "git+https://github.com/emscripten-core/emsdk#3.1.50",
.hash = "N-V-__8AALRTBQDo_pUJ8IQ-XiIyYwDKQVwnr7-7o5kvPDGE",
.url = "git+https://github.com/emscripten-core/emsdk#4.0.9",
.hash = "N-V-__8AAJl1DwBezhYo_VE6f53mPVm00R-Fk28NPW7P14EQ",
.lazy = true,
},
},
@ -23,5 +23,6 @@
"build.zig.zon",
"src",
"examples",
"LICENSE",
},
}

View File

@ -547,7 +547,8 @@ SHAPES = \
shapes/shapes_rectangle_advanced \
shapes/shapes_rectangle_scaling \
shapes/shapes_splines_drawing \
shapes/shapes_top_down_lights
shapes/shapes_top_down_lights \
shapes/shapes_digital_clock
TEXTURES = \
textures/textures_background_scrolling \

View File

@ -429,7 +429,8 @@ SHAPES = \
shapes/shapes_rectangle_advanced \
shapes/shapes_rectangle_scaling \
shapes/shapes_splines_drawing \
shapes/shapes_top_down_lights
shapes/shapes_top_down_lights \
shapes/shapes_digital_clock
TEXTURES = \
textures/textures_background_scrolling \

View File

@ -85,39 +85,39 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes](
| 51 | [shapes_top_down_lights](shapes/shapes_top_down_lights.c) | <img src="shapes/shapes_top_down_lights.png" alt="shapes_top_down_lights" width="80"> | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Jeffery Myers](https://github.com/JeffM2501) |
| 52 | [shapes_rectangle_advanced](shapes/shapes_rectangle_advanced.c) | <img src="shapes/shapes_rectangle_advanced.png" alt="shapes_rectangle_advanced" width="80"> | ⭐️⭐️⭐️⭐️ | 5.5 | 5.5 | [Everton Jr.](https://github.com/evertonse) |
| 53 | [shapes_splines_drawing](shapes/shapes_splines_drawing.c) | <img src="shapes/shapes_splines_drawing.png" alt="shapes_splines_drawing" width="80"> | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Ray](https://github.com/raysan5) |
| 54 | [shapes_digital_clock](shapes/shapes_digital_clock.c) | <img src="shapes/shapes_digital_clock.png" alt="shapes_digital_clock" width="80"> | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Hamza RAHAL](https://github.com/rhmz-rhl) |
### category: textures
Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/textures.c) modul
| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|
| 54 | [textures_logo_raylib](textures/textures_logo_raylib.c) | <img src="textures/textures_logo_raylib.png" alt="textures_logo_raylib" width="80"> | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) |
| 55 | [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | <img src="textures/textures_srcrec_dstrec.png" alt="textures_srcrec_dstrec" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) |
| 56 | [textures_image_drawing](textures/textures_image_drawing.c) | <img src="textures/textures_image_drawing.png" alt="textures_image_drawing" width="80"> | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) |
| 57 | [textures_image_generation](textures/textures_image_generation.c) | <img src="textures/textures_image_generation.png" alt="textures_image_generation" width="80"> | ⭐️⭐️☆☆ | 1.8 | 1.8 | [Wilhem Barbier](https://github.com/nounoursheureux) |
| 58 | [textures_image_loading](textures/textures_image_loading.c) | <img src="textures/textures_image_loading.png" alt="textures_image_loading" width="80"> | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) |
| 59 | [textures_image_processing](textures/textures_image_processing.c) | <img src="textures/textures_image_processing.png" alt="textures_image_processing" width="80"> | ⭐️⭐️⭐️☆ | 1.4 | 3.5 | [Ray](https://github.com/raysan5) |
| 60 | [textures_image_text](textures/textures_image_text.c) | <img src="textures/textures_image_text.png" alt="textures_image_text" width="80"> | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) |
| 61 | [textures_to_image](textures/textures_to_image.c) | <img src="textures/textures_to_image.png" alt="textures_to_image" width="80"> | ⭐️☆☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) |
| 62 | [textures_raw_data](textures/textures_raw_data.c) | <img src="textures/textures_raw_data.png" alt="textures_raw_data" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
| 63 | [textures_particles_blending](textures/textures_particles_blending.c) | <img src="textures/textures_particles_blending.png" alt="textures_particles_blending" width="80"> | ⭐️☆☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) |
| 64 | [textures_npatch_drawing](textures/textures_npatch_drawing.c) | <img src="textures/textures_npatch_drawing.png" alt="textures_npatch_drawing" width="80"> | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) |
| 65 | [textures_background_scrolling](textures/textures_background_scrolling.c) | <img src="textures/textures_background_scrolling.png" alt="textures_background_scrolling" width="80"> | ⭐️☆☆☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) |
| 66 | [textures_sprite_anim](textures/textures_sprite_anim.c) | <img src="textures/textures_sprite_anim.png" alt="textures_sprite_anim" width="80"> | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) |
| 67 | [textures_sprite_button](textures/textures_sprite_button.c) | <img src="textures/textures_sprite_button.png" alt="textures_sprite_button" width="80"> | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) |
| 68 | [textures_sprite_explosion](textures/textures_sprite_explosion.c) | <img src="textures/textures_sprite_explosion.png" alt="textures_sprite_explosion" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 69 | [textures_bunnymark](textures/textures_bunnymark.c) | <img src="textures/textures_bunnymark.png" alt="textures_bunnymark" width="80"> | ⭐️⭐️⭐️☆ | 1.6 | 2.5 | [Ray](https://github.com/raysan5) |
| 70 | [textures_mouse_painting](textures/textures_mouse_painting.c) | <img src="textures/textures_mouse_painting.png" alt="textures_mouse_painting" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) |
| 71 | [textures_blend_modes](textures/textures_blend_modes.c) | <img src="textures/textures_blend_modes.png" alt="textures_blend_modes" width="80"> | ⭐️☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) |
| 72 | [textures_draw_tiled](textures/textures_draw_tiled.c) | <img src="textures/textures_draw_tiled.png" alt="textures_draw_tiled" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 4.2 | [Vlad Adrian](https://github.com/demizdor) |
| 73 | [textures_polygon](textures/textures_polygon.c) | <img src="textures/textures_polygon.png" alt="textures_polygon" width="80"> | ⭐️☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) |
| 74 | [textures_fog_of_war](textures/textures_fog_of_war.c) | <img src="textures/textures_fog_of_war.png" alt="textures_fog_of_war" width="80"> | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) |
| 75 | [textures_gif_player](textures/textures_gif_player.c) | <img src="textures/textures_gif_player.png" alt="textures_gif_player" width="80"> | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) |
| 76 | [textures_image_kernel](textures/textures_image_kernel.c) | <img src="textures/textures_image_kernel.png" alt="textures_image_kernel" width="80"> | ⭐️⭐️⭐️⭐️ | 1.3 | 1.3 | [Karim Salem](https://github.com/kimo-s) |
| 77 | [textures_image_channel](textures/textures_image_channel.c) | <img src="textures/textures_image_channel.png" alt="textures_image_channel" width="80"> | ⭐️⭐️☆☆ | 5.1-dev | 5.1-dev | [Bruno Cabral](https://github.com/brccabral) |
| 78 | [textures_image_rotate](textures/textures_image_rotate.c) | <img src="textures/textures_image_rotate.png" alt="textures_image_rotate" width="80"> | ⭐️⭐️☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) |
| 79 | [textures_textured_curve](textures/textures_textured_curve.c) | <img src="textures/textures_textured_curve.png" alt="textures_textured_curve" width="80"> | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) |
| 55 | [textures_logo_raylib](textures/textures_logo_raylib.c) | <img src="textures/textures_logo_raylib.png" alt="textures_logo_raylib" width="80"> | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) |
| 56 | [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | <img src="textures/textures_srcrec_dstrec.png" alt="textures_srcrec_dstrec" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) |
| 57 | [textures_image_drawing](textures/textures_image_drawing.c) | <img src="textures/textures_image_drawing.png" alt="textures_image_drawing" width="80"> | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) |
| 58 | [textures_image_generation](textures/textures_image_generation.c) | <img src="textures/textures_image_generation.png" alt="textures_image_generation" width="80"> | ⭐️⭐️☆☆ | 1.8 | 1.8 | [Wilhem Barbier](https://github.com/nounoursheureux) |
| 59 | [textures_image_loading](textures/textures_image_loading.c) | <img src="textures/textures_image_loading.png" alt="textures_image_loading" width="80"> | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) |
| 60 | [textures_image_processing](textures/textures_image_processing.c) | <img src="textures/textures_image_processing.png" alt="textures_image_processing" width="80"> | ⭐️⭐️⭐️☆ | 1.4 | 3.5 | [Ray](https://github.com/raysan5) |
| 61 | [textures_image_text](textures/textures_image_text.c) | <img src="textures/textures_image_text.png" alt="textures_image_text" width="80"> | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) |
| 62 | [textures_to_image](textures/textures_to_image.c) | <img src="textures/textures_to_image.png" alt="textures_to_image" width="80"> | ⭐️☆☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) |
| 63 | [textures_raw_data](textures/textures_raw_data.c) | <img src="textures/textures_raw_data.png" alt="textures_raw_data" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
| 64 | [textures_particles_blending](textures/textures_particles_blending.c) | <img src="textures/textures_particles_blending.png" alt="textures_particles_blending" width="80"> | ⭐️☆☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) |
| 65 | [textures_npatch_drawing](textures/textures_npatch_drawing.c) | <img src="textures/textures_npatch_drawing.png" alt="textures_npatch_drawing" width="80"> | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) |
| 66 | [textures_background_scrolling](textures/textures_background_scrolling.c) | <img src="textures/textures_background_scrolling.png" alt="textures_background_scrolling" width="80"> | ⭐️☆☆☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) |
| 67 | [textures_sprite_anim](textures/textures_sprite_anim.c) | <img src="textures/textures_sprite_anim.png" alt="textures_sprite_anim" width="80"> | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) |
| 68 | [textures_sprite_button](textures/textures_sprite_button.c) | <img src="textures/textures_sprite_button.png" alt="textures_sprite_button" width="80"> | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) |
| 69 | [textures_sprite_explosion](textures/textures_sprite_explosion.c) | <img src="textures/textures_sprite_explosion.png" alt="textures_sprite_explosion" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 70 | [textures_bunnymark](textures/textures_bunnymark.c) | <img src="textures/textures_bunnymark.png" alt="textures_bunnymark" width="80"> | ⭐️⭐️⭐️☆ | 1.6 | 2.5 | [Ray](https://github.com/raysan5) |
| 71 | [textures_mouse_painting](textures/textures_mouse_painting.c) | <img src="textures/textures_mouse_painting.png" alt="textures_mouse_painting" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) |
| 72 | [textures_blend_modes](textures/textures_blend_modes.c) | <img src="textures/textures_blend_modes.png" alt="textures_blend_modes" width="80"> | ⭐️☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) |
| 73 | [textures_draw_tiled](textures/textures_draw_tiled.c) | <img src="textures/textures_draw_tiled.png" alt="textures_draw_tiled" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 4.2 | [Vlad Adrian](https://github.com/demizdor) |
| 74 | [textures_polygon](textures/textures_polygon.c) | <img src="textures/textures_polygon.png" alt="textures_polygon" width="80"> | ⭐️☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) |
| 75 | [textures_fog_of_war](textures/textures_fog_of_war.c) | <img src="textures/textures_fog_of_war.png" alt="textures_fog_of_war" width="80"> | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) |
| 76 | [textures_gif_player](textures/textures_gif_player.c) | <img src="textures/textures_gif_player.png" alt="textures_gif_player" width="80"> | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) |
| 77 | [textures_image_kernel](textures/textures_image_kernel.c) | <img src="textures/textures_image_kernel.png" alt="textures_image_kernel" width="80"> | ⭐️⭐️⭐️⭐️ | 1.3 | 1.3 | [Karim Salem](https://github.com/kimo-s) |
| 78 | [textures_image_channel](textures/textures_image_channel.c) | <img src="textures/textures_image_channel.png" alt="textures_image_channel" width="80"> | ⭐️⭐️☆☆ | 5.1-dev | 5.1-dev | [Bruno Cabral](https://github.com/brccabral) |
| 79 | [textures_image_rotate](textures/textures_image_rotate.c) | <img src="textures/textures_image_rotate.png" alt="textures_image_rotate" width="80"> | ⭐️⭐️☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) |
| 80 | [textures_textured_curve](textures/textures_textured_curve.c) | <img src="textures/textures_textured_curve.png" alt="textures_textured_curve" width="80"> | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) |
### category: text
@ -125,18 +125,18 @@ Examples using raylib text functionality, including sprite fonts loading/generat
| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|
| 80 | [text_raylib_fonts](text/text_raylib_fonts.c) | <img src="text/text_raylib_fonts.png" alt="text_raylib_fonts" width="80"> | ⭐️☆☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) |
| 81 | [text_font_spritefont](text/text_font_spritefont.c) | <img src="text/text_font_spritefont.png" alt="text_font_spritefont" width="80"> | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) |
| 82 | [text_font_filters](text/text_font_filters.c) | <img src="text/text_font_filters.png" alt="text_font_filters" width="80"> | ⭐️⭐️☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) |
| 83 | [text_font_loading](text/text_font_loading.c) | <img src="text/text_font_loading.png" alt="text_font_loading" width="80"> | ⭐️☆☆☆ | 1.4 | 3.0 | [Ray](https://github.com/raysan5) |
| 84 | [text_font_sdf](text/text_font_sdf.c) | <img src="text/text_font_sdf.png" alt="text_font_sdf" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) |
| 85 | [text_format_text](text/text_format_text.c) | <img src="text/text_format_text.png" alt="text_format_text" width="80"> | ⭐️☆☆☆ | 1.1 | 3.0 | [Ray](https://github.com/raysan5) |
| 86 | [text_input_box](text/text_input_box.c) | <img src="text/text_input_box.png" alt="text_input_box" width="80"> | ⭐️⭐️☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) |
| 87 | [text_writing_anim](text/text_writing_anim.c) | <img src="text/text_writing_anim.png" alt="text_writing_anim" width="80"> | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) |
| 88 | [text_rectangle_bounds](text/text_rectangle_bounds.c) | <img src="text/text_rectangle_bounds.png" alt="text_rectangle_bounds" width="80"> | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) |
| 89 | [text_unicode](text/text_unicode.c) | <img src="text/text_unicode.png" alt="text_unicode" width="80"> | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) |
| 90 | [text_draw_3d](text/text_draw_3d.c) | <img src="text/text_draw_3d.png" alt="text_draw_3d" width="80"> | ⭐️⭐️⭐️⭐️ | 3.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) |
| 91 | [text_codepoints_loading](text/text_codepoints_loading.c) | <img src="text/text_codepoints_loading.png" alt="text_codepoints_loading" width="80"> | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) |
| 81 | [text_raylib_fonts](text/text_raylib_fonts.c) | <img src="text/text_raylib_fonts.png" alt="text_raylib_fonts" width="80"> | ⭐️☆☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) |
| 82 | [text_font_spritefont](text/text_font_spritefont.c) | <img src="text/text_font_spritefont.png" alt="text_font_spritefont" width="80"> | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) |
| 83 | [text_font_filters](text/text_font_filters.c) | <img src="text/text_font_filters.png" alt="text_font_filters" width="80"> | ⭐️⭐️☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) |
| 84 | [text_font_loading](text/text_font_loading.c) | <img src="text/text_font_loading.png" alt="text_font_loading" width="80"> | ⭐️☆☆☆ | 1.4 | 3.0 | [Ray](https://github.com/raysan5) |
| 85 | [text_font_sdf](text/text_font_sdf.c) | <img src="text/text_font_sdf.png" alt="text_font_sdf" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) |
| 86 | [text_format_text](text/text_format_text.c) | <img src="text/text_format_text.png" alt="text_format_text" width="80"> | ⭐️☆☆☆ | 1.1 | 3.0 | [Ray](https://github.com/raysan5) |
| 87 | [text_input_box](text/text_input_box.c) | <img src="text/text_input_box.png" alt="text_input_box" width="80"> | ⭐️⭐️☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) |
| 88 | [text_writing_anim](text/text_writing_anim.c) | <img src="text/text_writing_anim.png" alt="text_writing_anim" width="80"> | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) |
| 89 | [text_rectangle_bounds](text/text_rectangle_bounds.c) | <img src="text/text_rectangle_bounds.png" alt="text_rectangle_bounds" width="80"> | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) |
| 90 | [text_unicode](text/text_unicode.c) | <img src="text/text_unicode.png" alt="text_unicode" width="80"> | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) |
| 91 | [text_draw_3d](text/text_draw_3d.c) | <img src="text/text_draw_3d.png" alt="text_draw_3d" width="80"> | ⭐️⭐️⭐️⭐️ | 3.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) |
| 92 | [text_codepoints_loading](text/text_codepoints_loading.c) | <img src="text/text_codepoints_loading.png" alt="text_codepoints_loading" width="80"> | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) |
### category: models
@ -144,29 +144,29 @@ Examples using raylib models functionality, including models loading/generation
| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|
| 92 | [models_animation](models/models_animation.c) | <img src="models/models_animation.png" alt="models_animation" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) |
| 93 | [models_billboard](models/models_billboard.c) | <img src="models/models_billboard.png" alt="models_billboard" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
| 94 | [models_box_collisions](models/models_box_collisions.c) | <img src="models/models_box_collisions.png" alt="models_box_collisions" width="80"> | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
| 95 | [models_cubicmap](models/models_cubicmap.c) | <img src="models/models_cubicmap.png" alt="models_cubicmap" width="80"> | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
| 96 | [models_first_person_maze](models/models_first_person_maze.c) | <img src="models/models_first_person_maze.png" alt="models_first_person_maze" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 97 | [models_geometric_shapes](models/models_geometric_shapes.c) | <img src="models/models_geometric_shapes.png" alt="models_geometric_shapes" width="80"> | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) |
| 98 | [models_mesh_generation](models/models_mesh_generation.c) | <img src="models/models_mesh_generation.png" alt="models_mesh_generation" width="80"> | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) |
| 99 | [models_mesh_picking](models/models_mesh_picking.c) | <img src="models/models_mesh_picking.png" alt="models_mesh_picking" width="80"> | ⭐️⭐️⭐️☆ | 1.7 | 4.0 | [Joel Davis](https://github.com/joeld42) |
| 100 | [models_loading](models/models_loading.c) | <img src="models/models_loading.png" alt="models_loading" width="80"> | ⭐️☆☆☆ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) |
| 101 | [models_loading_gltf](models/models_loading_gltf.c) | <img src="models/models_loading_gltf.png" alt="models_loading_gltf" width="80"> | ⭐️☆☆☆ | 3.7 | 4.2 | [Ray](https://github.com/raysan5) |
| 102 | [models_loading_vox](models/models_loading_vox.c) | <img src="models/models_loading_vox.png" alt="models_loading_vox" width="80"> | ⭐️☆☆☆ | 4.0 | 4.0 | [Johann Nadalutti](https://github.com/procfxgen) |
| 103 | [models_loading_m3d](models/models_loading_m3d.c) | <img src="models/models_loading_m3d.png" alt="models_loading_m3d" width="80"> | ⭐️⭐️☆☆ | 4.5 | 4.5 | [bzt](https://bztsrc.gitlab.io/model3d) |
| 104 | [models_orthographic_projection](models/models_orthographic_projection.c) | <img src="models/models_orthographic_projection.png" alt="models_orthographic_projection" width="80"> | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) |
| 105 | [models_point_rendering](models/models_point_rendering.c) | <img src="models/models_point_rendering.png" alt="models_point_rendering" width="80"> | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) |
| 106 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | <img src="models/models_rlgl_solar_system.png" alt="models_rlgl_solar_system" width="80"> | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Ray](https://github.com/raysan5) |
| 107 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | <img src="models/models_yaw_pitch_roll.png" alt="models_yaw_pitch_roll" width="80"> | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Berni](https://github.com/Berni8k) |
| 108 | [models_waving_cubes](models/models_waving_cubes.c) | <img src="models/models_waving_cubes.png" alt="models_waving_cubes" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Codecat](https://github.com/codecat) |
| 109 | [models_heightmap](models/models_heightmap.c) | <img src="models/models_heightmap.png" alt="models_heightmap" width="80"> | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
| 110 | [models_skybox](models/models_skybox.c) | <img src="models/models_skybox.png" alt="models_skybox" width="80"> | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) |
| 111 | [models_draw_cube_texture](models/models_draw_cube_texture.c) | <img src="models/models_draw_cube_texture.png" alt="models_draw_cube_texture" width="80"> | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Ray](https://github.com/raysan5) |
| 112 | [models_gpu_skinning](models/models_gpu_skinning.c) | <img src="models/models_gpu_skinning.png" alt="models_gpu_skinning" width="80"> | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) |
| 113 | [models_bone_socket](models/models_bone_socket.c) | <img src="models/models_bone_socket.png" alt="models_bone_socket" width="80"> | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) |
| 114 | [models_tesseract_view](models/models_tesseract_view.c) | <img src="models/models_tesseract_view.png" alt="models_tesseract_view" width="80"> | ⭐️⭐️☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) |
| 93 | [models_animation](models/models_animation.c) | <img src="models/models_animation.png" alt="models_animation" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) |
| 94 | [models_billboard](models/models_billboard.c) | <img src="models/models_billboard.png" alt="models_billboard" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
| 95 | [models_box_collisions](models/models_box_collisions.c) | <img src="models/models_box_collisions.png" alt="models_box_collisions" width="80"> | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
| 96 | [models_cubicmap](models/models_cubicmap.c) | <img src="models/models_cubicmap.png" alt="models_cubicmap" width="80"> | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
| 97 | [models_first_person_maze](models/models_first_person_maze.c) | <img src="models/models_first_person_maze.png" alt="models_first_person_maze" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 98 | [models_geometric_shapes](models/models_geometric_shapes.c) | <img src="models/models_geometric_shapes.png" alt="models_geometric_shapes" width="80"> | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) |
| 99 | [models_mesh_generation](models/models_mesh_generation.c) | <img src="models/models_mesh_generation.png" alt="models_mesh_generation" width="80"> | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) |
| 100 | [models_mesh_picking](models/models_mesh_picking.c) | <img src="models/models_mesh_picking.png" alt="models_mesh_picking" width="80"> | ⭐️⭐️⭐️☆ | 1.7 | 4.0 | [Joel Davis](https://github.com/joeld42) |
| 101 | [models_loading](models/models_loading.c) | <img src="models/models_loading.png" alt="models_loading" width="80"> | ⭐️☆☆☆ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) |
| 102 | [models_loading_gltf](models/models_loading_gltf.c) | <img src="models/models_loading_gltf.png" alt="models_loading_gltf" width="80"> | ⭐️☆☆☆ | 3.7 | 4.2 | [Ray](https://github.com/raysan5) |
| 103 | [models_loading_vox](models/models_loading_vox.c) | <img src="models/models_loading_vox.png" alt="models_loading_vox" width="80"> | ⭐️☆☆☆ | 4.0 | 4.0 | [Johann Nadalutti](https://github.com/procfxgen) |
| 104 | [models_loading_m3d](models/models_loading_m3d.c) | <img src="models/models_loading_m3d.png" alt="models_loading_m3d" width="80"> | ⭐️⭐️☆☆ | 4.5 | 4.5 | [bzt](https://bztsrc.gitlab.io/model3d) |
| 105 | [models_orthographic_projection](models/models_orthographic_projection.c) | <img src="models/models_orthographic_projection.png" alt="models_orthographic_projection" width="80"> | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) |
| 106 | [models_point_rendering](models/models_point_rendering.c) | <img src="models/models_point_rendering.png" alt="models_point_rendering" width="80"> | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) |
| 107 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | <img src="models/models_rlgl_solar_system.png" alt="models_rlgl_solar_system" width="80"> | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Ray](https://github.com/raysan5) |
| 108 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | <img src="models/models_yaw_pitch_roll.png" alt="models_yaw_pitch_roll" width="80"> | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Berni](https://github.com/Berni8k) |
| 109 | [models_waving_cubes](models/models_waving_cubes.c) | <img src="models/models_waving_cubes.png" alt="models_waving_cubes" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Codecat](https://github.com/codecat) |
| 110 | [models_heightmap](models/models_heightmap.c) | <img src="models/models_heightmap.png" alt="models_heightmap" width="80"> | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
| 111 | [models_skybox](models/models_skybox.c) | <img src="models/models_skybox.png" alt="models_skybox" width="80"> | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) |
| 112 | [models_draw_cube_texture](models/models_draw_cube_texture.c) | <img src="models/models_draw_cube_texture.png" alt="models_draw_cube_texture" width="80"> | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Ray](https://github.com/raysan5) |
| 113 | [models_gpu_skinning](models/models_gpu_skinning.c) | <img src="models/models_gpu_skinning.png" alt="models_gpu_skinning" width="80"> | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) |
| 114 | [models_bone_socket](models/models_bone_socket.c) | <img src="models/models_bone_socket.png" alt="models_bone_socket" width="80"> | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) |
| 115 | [models_tesseract_view](models/models_tesseract_view.c) | <img src="models/models_tesseract_view.png" alt="models_tesseract_view" width="80"> | ⭐️⭐️☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) |
### category: shaders
@ -174,34 +174,34 @@ Examples using raylib shaders functionality, including shaders loading, paramete
| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|
| 115 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | <img src="shaders/shaders_basic_lighting.png" alt="shaders_basic_lighting" width="80"> | ⭐️⭐️⭐️⭐️ | 3.0 | 4.2 | [Chris Camacho](https://github.com/chriscamacho) |
| 116 | [shaders_model_shader](shaders/shaders_model_shader.c) | <img src="shaders/shaders_model_shader.png" alt="shaders_model_shader" width="80"> | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) |
| 117 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | <img src="shaders/shaders_shapes_textures.png" alt="shaders_shapes_textures" width="80"> | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) |
| 118 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | <img src="shaders/shaders_custom_uniform.png" alt="shaders_custom_uniform" width="80"> | ⭐️⭐️☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) |
| 119 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | <img src="shaders/shaders_postprocessing.png" alt="shaders_postprocessing" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) |
| 120 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | <img src="shaders/shaders_palette_switch.png" alt="shaders_palette_switch" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) |
| 121 | [shaders_raymarching](shaders/shaders_raymarching.c) | <img src="shaders/shaders_raymarching.png" alt="shaders_raymarching" width="80"> | ⭐️⭐️⭐️⭐️ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) |
| 122 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | <img src="shaders/shaders_texture_drawing.png" alt="shaders_texture_drawing" width="80"> | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/ciessielski) |
| 123 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | <img src="shaders/shaders_texture_outline.png" alt="shaders_texture_outline" width="80"> | ⭐️⭐️⭐️☆ | 4.0 | 4.0 | [Samuel Skiff](https://github.com/GoldenThumbs) |
| 124 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | <img src="shaders/shaders_texture_waves.png" alt="shaders_texture_waves" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) |
| 125 | [shaders_julia_set](shaders/shaders_julia_set.c) | <img src="shaders/shaders_julia_set.png" alt="shaders_julia_set" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [Josh Colclough](https://github.com/joshcol9232) |
| 126 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | <img src="shaders/shaders_eratosthenes.png" alt="shaders_eratosthenes" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [ProfJski](https://github.com/ProfJski) |
| 127 | [shaders_fog](shaders/shaders_fog.c) | <img src="shaders/shaders_fog.png" alt="shaders_fog" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) |
| 128 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | <img src="shaders/shaders_simple_mask.png" alt="shaders_simple_mask" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) |
| 129 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | <img src="shaders/shaders_hot_reloading.png" alt="shaders_hot_reloading" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) |
| 130 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | <img src="shaders/shaders_mesh_instancing.png" alt="shaders_mesh_instancing" width="80"> | ⭐️⭐️⭐️⭐️ | 3.7 | 4.2 | [seanpringle](https://github.com/seanpringle) |
| 131 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | <img src="shaders/shaders_multi_sample2d.png" alt="shaders_multi_sample2d" width="80"> | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 132 | [shaders_spotlight](shaders/shaders_spotlight.c) | <img src="shaders/shaders_spotlight.png" alt="shaders_spotlight" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) |
| 133 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | <img src="shaders/shaders_deferred_render.png" alt="shaders_deferred_render" width="80"> | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) |
| 134 | [shaders_hybrid_render](shaders/shaders_hybrid_render.c) | <img src="shaders/shaders_hybrid_render.png" alt="shaders_hybrid_render" width="80"> | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) |
| 135 | [shaders_texture_tiling](shaders/shaders_texture_tiling.c) | <img src="shaders/shaders_texture_tiling.png" alt="shaders_texture_tiling" width="80"> | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Luis Almeida](https://github.com/luis605) |
| 136 | [shaders_shadowmap](shaders/shaders_shadowmap.c) | <img src="shaders/shaders_shadowmap.png" alt="shaders_shadowmap" width="80"> | ⭐️⭐️⭐️⭐️ | 5.0 | 5.0 | [TheManTheMythTheGameDev](https://github.com/TheManTheMythTheGameDev) |
| 137 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | <img src="shaders/shaders_vertex_displacement.png" alt="shaders_vertex_displacement" width="80"> | ⭐️⭐️⭐️☆ | 5.0 | 4.5 | [Alex ZH](https://github.com/ZzzhHe) |
| 138 | [shaders_write_depth](shaders/shaders_write_depth.c) | <img src="shaders/shaders_write_depth.png" alt="shaders_write_depth" width="80"> | ⭐️⭐️☆☆ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) |
| 139 | [shaders_basic_pbr](shaders/shaders_basic_pbr.c) | <img src="shaders/shaders_basic_pbr.png" alt="shaders_basic_pbr" width="80"> | ⭐️⭐️⭐️⭐️ | 5.0 | 5.1-dev | [Afan OLOVCIC](https://github.com/_DevDad) |
| 140 | [shaders_lightmap](shaders/shaders_lightmap.c) | <img src="shaders/shaders_lightmap.png" alt="shaders_lightmap" width="80"> | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) |
| 141 | [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | <img src="shaders/shaders_rounded_rectangle.png" alt="shaders_rounded_rectangle" width=80> | ⭐️⭐️⭐️☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) |
| 142 | [shaders_view_depth](shaders/shaders_view_depth.c) | <img src="shaders/shaders_view_depth.png" alt="shaders_view_depth" width="80"> | ⭐️⭐️⭐️☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) |
| 116 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | <img src="shaders/shaders_basic_lighting.png" alt="shaders_basic_lighting" width="80"> | ⭐️⭐️⭐️⭐️ | 3.0 | 4.2 | [Chris Camacho](https://github.com/chriscamacho) |
| 117 | [shaders_model_shader](shaders/shaders_model_shader.c) | <img src="shaders/shaders_model_shader.png" alt="shaders_model_shader" width="80"> | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) |
| 118 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | <img src="shaders/shaders_shapes_textures.png" alt="shaders_shapes_textures" width="80"> | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) |
| 119 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | <img src="shaders/shaders_custom_uniform.png" alt="shaders_custom_uniform" width="80"> | ⭐️⭐️☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) |
| 120 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | <img src="shaders/shaders_postprocessing.png" alt="shaders_postprocessing" width="80"> | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) |
| 121 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | <img src="shaders/shaders_palette_switch.png" alt="shaders_palette_switch" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) |
| 122 | [shaders_raymarching](shaders/shaders_raymarching.c) | <img src="shaders/shaders_raymarching.png" alt="shaders_raymarching" width="80"> | ⭐️⭐️⭐️⭐️ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) |
| 123 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | <img src="shaders/shaders_texture_drawing.png" alt="shaders_texture_drawing" width="80"> | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/ciessielski) |
| 124 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | <img src="shaders/shaders_texture_outline.png" alt="shaders_texture_outline" width="80"> | ⭐️⭐️⭐️☆ | 4.0 | 4.0 | [Samuel Skiff](https://github.com/GoldenThumbs) |
| 125 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | <img src="shaders/shaders_texture_waves.png" alt="shaders_texture_waves" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) |
| 126 | [shaders_julia_set](shaders/shaders_julia_set.c) | <img src="shaders/shaders_julia_set.png" alt="shaders_julia_set" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [Josh Colclough](https://github.com/joshcol9232) |
| 127 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | <img src="shaders/shaders_eratosthenes.png" alt="shaders_eratosthenes" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [ProfJski](https://github.com/ProfJski) |
| 128 | [shaders_fog](shaders/shaders_fog.c) | <img src="shaders/shaders_fog.png" alt="shaders_fog" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) |
| 129 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | <img src="shaders/shaders_simple_mask.png" alt="shaders_simple_mask" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) |
| 130 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | <img src="shaders/shaders_hot_reloading.png" alt="shaders_hot_reloading" width="80"> | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) |
| 131 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | <img src="shaders/shaders_mesh_instancing.png" alt="shaders_mesh_instancing" width="80"> | ⭐️⭐️⭐️⭐️ | 3.7 | 4.2 | [seanpringle](https://github.com/seanpringle) |
| 132 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | <img src="shaders/shaders_multi_sample2d.png" alt="shaders_multi_sample2d" width="80"> | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 133 | [shaders_spotlight](shaders/shaders_spotlight.c) | <img src="shaders/shaders_spotlight.png" alt="shaders_spotlight" width="80"> | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) |
| 134 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | <img src="shaders/shaders_deferred_render.png" alt="shaders_deferred_render" width="80"> | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) |
| 135 | [shaders_hybrid_render](shaders/shaders_hybrid_render.c) | <img src="shaders/shaders_hybrid_render.png" alt="shaders_hybrid_render" width="80"> | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) |
| 136 | [shaders_texture_tiling](shaders/shaders_texture_tiling.c) | <img src="shaders/shaders_texture_tiling.png" alt="shaders_texture_tiling" width="80"> | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Luis Almeida](https://github.com/luis605) |
| 137 | [shaders_shadowmap](shaders/shaders_shadowmap.c) | <img src="shaders/shaders_shadowmap.png" alt="shaders_shadowmap" width="80"> | ⭐️⭐️⭐️⭐️ | 5.0 | 5.0 | [TheManTheMythTheGameDev](https://github.com/TheManTheMythTheGameDev) |
| 138 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | <img src="shaders/shaders_vertex_displacement.png" alt="shaders_vertex_displacement" width="80"> | ⭐️⭐️⭐️☆ | 5.0 | 4.5 | [Alex ZH](https://github.com/ZzzhHe) |
| 139 | [shaders_write_depth](shaders/shaders_write_depth.c) | <img src="shaders/shaders_write_depth.png" alt="shaders_write_depth" width="80"> | ⭐️⭐️☆☆ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) |
| 140 | [shaders_basic_pbr](shaders/shaders_basic_pbr.c) | <img src="shaders/shaders_basic_pbr.png" alt="shaders_basic_pbr" width="80"> | ⭐️⭐️⭐️⭐️ | 5.0 | 5.1-dev | [Afan OLOVCIC](https://github.com/_DevDad) |
| 141 | [shaders_lightmap](shaders/shaders_lightmap.c) | <img src="shaders/shaders_lightmap.png" alt="shaders_lightmap" width="80"> | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) |
| 142 | [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | <img src="shaders/shaders_rounded_rectangle.png" alt="shaders_rounded_rectangle" width=80> | ⭐️⭐️⭐️☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) |
| 143 | [shaders_view_depth](shaders/shaders_view_depth.c) | <img src="shaders/shaders_view_depth.png" alt="shaders_view_depth" width="80"> | ⭐️⭐️⭐️☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) |
### category: audio
@ -209,26 +209,26 @@ Examples using raylib audio functionality, including sound/music loading and pla
| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|
| 142 | [audio_module_playing](audio/audio_module_playing.c) | <img src="audio/audio_module_playing.png" alt="audio_module_playing" width="80"> | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 143 | [audio_music_stream](audio/audio_music_stream.c) | <img src="audio/audio_music_stream.png" alt="audio_music_stream" width="80"> | ⭐️☆☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) |
| 144 | [audio_raw_stream](audio/audio_raw_stream.c) | <img src="audio/audio_raw_stream.png" alt="audio_raw_stream" width="80"> | ⭐️⭐️⭐️☆ | 1.6 | 4.2 | [Ray](https://github.com/raysan5) |
| 145 | [audio_sound_loading](audio/audio_sound_loading.c) | <img src="audio/audio_sound_loading.png" alt="audio_sound_loading" width="80"> | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) |
| 146 | [audio_mixed_processor](audio/audio_mixed_processor.c) | <img src="audio/audio_mixed_processor.png" alt="audio_mixed_processor" width="80"> | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) |
| 147 | [audio_stream_effects](audio/audio_stream_effects.c) | <img src="audio/audio_stream_effects.png" alt="audio_stream_effects" width="80"> | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) |
| 148 | [audio_sound_multi](audio/audio_sound_multi.c) | <img src="audio/audio_sound_multi.png" alt="audio_sound_multi" width="80"> | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) |
| 149 | [audio_sound_positioning](audio/audio_sound_positioning.c) | <img src="audio/audio_sound_positioning.png" alt="audio_sound_positioning" width="80"> | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) |
| 144 | [audio_module_playing](audio/audio_module_playing.c) | <img src="audio/audio_module_playing.png" alt="audio_module_playing" width="80"> | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 145 | [audio_music_stream](audio/audio_music_stream.c) | <img src="audio/audio_music_stream.png" alt="audio_music_stream" width="80"> | ⭐️☆☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) |
| 146 | [audio_raw_stream](audio/audio_raw_stream.c) | <img src="audio/audio_raw_stream.png" alt="audio_raw_stream" width="80"> | ⭐️⭐️⭐️☆ | 1.6 | 4.2 | [Ray](https://github.com/raysan5) |
| 147 | [audio_sound_loading](audio/audio_sound_loading.c) | <img src="audio/audio_sound_loading.png" alt="audio_sound_loading" width="80"> | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) |
| 148 | [audio_mixed_processor](audio/audio_mixed_processor.c) | <img src="audio/audio_mixed_processor.png" alt="audio_mixed_processor" width="80"> | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) |
| 149 | [audio_stream_effects](audio/audio_stream_effects.c) | <img src="audio/audio_stream_effects.png" alt="audio_stream_effects" width="80"> | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) |
| 150 [audio_sound_multi](audio/audio_sound_multi.c) | <img src="audio/audio_sound_multi.png" alt="audio_sound_multi" width="80"> | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) |
| 151 | [audio_sound_positioning](audio/audio_sound_positioning.c) | <img src="audio/audio_sound_positioning.png" alt="audio_sound_positioning" width="80"> | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) |
### category: others
Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries.
Ex150amples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries.
| ## | example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|
| 150 | [rlgl_standalone](others/rlgl_standalone.c) | <img src="others/rlgl_standalone.png" alt="rlgl_standalone" width="80"> | ⭐️⭐️⭐️⭐️ | 1.6 | 4.0 | [Ray](https://github.com/raysan5) |
| 151 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | <img src="others/rlgl_compute_shader.png" alt="rlgl_compute_shader" width="80"> | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) |
| 152 | [easings_testbed](others/easings_testbed.c) | <img src="others/easings_testbed.png" alt="easings_testbed" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) |
| 153 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | <img src="others/raylib_opengl_interop.png" alt="raylib_opengl_interop" width="80"> | ⭐️⭐️⭐️⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) |
| 154 | [embedded_files_loading](others/embedded_files_loading.c) | <img src="others/embedded_files_loading.png" alt="embedded_files_loading" width="80"> | ⭐️⭐️☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) |
| 155 | [raymath_vector_angle](others/raymath_vector_angle.c) | <img src="others/raymath_vector_angle.png" alt="raymath_vector_angle" width="80"> | ⭐️⭐️☆☆ | 1.0 | 4.6 | [Ray](https://github.com/raysan5) |
| 152 | [rlgl_standalone](others/rlgl_standalone.c) | <img src="others/rlgl_standalone.png" alt="rlgl_standalone" width="80"> | ⭐️⭐️⭐️⭐️ | 1.6 | 4.0 | [Ray](https://github.com/raysan5) |
| 153 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | <img src="others/rlgl_compute_shader.png" alt="rlgl_compute_shader" width="80"> | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) |
| 154 | [easings_testbed](others/easings_testbed.c) | <img src="others/easings_testbed.png" alt="easings_testbed" width="80"> | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) |
| 155 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | <img src="others/raylib_opengl_interop.png" alt="raylib_opengl_interop" width="80"> | ⭐️⭐️⭐️⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) |
| 156 | [embedded_files_loading](others/embedded_files_loading.c) | <img src="others/embedded_files_loading.png" alt="embedded_files_loading" width="80"> | ⭐️⭐️☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) |
| 157 | [raymath_vector_angle](others/raymath_vector_angle.c) | <img src="others/raymath_vector_angle.png" alt="raymath_vector_angle" width="80"> | ⭐️⭐️☆☆ | 1.0 | 4.6 | [Ray](https://github.com/raysan5) |
As always contributions are welcome, feel free to send new examples! Here is an [examples template](examples_template.c) to start with!

View File

@ -61,7 +61,9 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
PollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
#ifndef PLATFORM_WEB // NOTE: On non web platforms the PollInputEvents just works before the inputs checks
PollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
#endif
if (IsKeyPressed(KEY_SPACE)) pause = !pause;
@ -76,6 +78,10 @@ int main(void)
if (position >= GetScreenWidth()) position = 0;
timeCounter += deltaTime; // We count time (seconds)
}
#ifdef PLATFORM_WEB // NOTE: On web platform for some reason the PollInputEvents only works after the inputs check, so just call it after check all your inputs (on web)
PollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
#endif
//----------------------------------------------------------------------------------
// Draw

View File

@ -4,7 +4,7 @@
attribute vec3 vertexPosition;
attribute vec2 vertexTexCoord;
attribute vec3 vertexNormal;
attribute vec3 vertexTangent;
attribute vec4 vertexTangent;
attribute vec4 vertexColor;
// Input uniform values
@ -52,7 +52,7 @@ mat3 transpose(mat3 m)
void main()
{
// Compute binormal from vertex normal and tangent
vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w;
// Compute fragment normal based on normal transformations
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
@ -62,7 +62,7 @@ void main()
fragTexCoord = vertexTexCoord*2.0;
fragNormal = normalize(normalMatrix*vertexNormal);
vec3 fragTangent = normalize(normalMatrix*vertexTangent);
vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz);
fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
fragBinormal = cross(fragNormal, fragTangent);

View File

@ -4,7 +4,7 @@
attribute vec3 vertexPosition;
attribute vec2 vertexTexCoord;
attribute vec3 vertexNormal;
attribute vec3 vertexTangent;
attribute vec4 vertexTangent;
attribute vec4 vertexColor;
// Input uniform values
@ -52,7 +52,7 @@ mat3 transpose(mat3 m)
void main()
{
// Compute binormal from vertex normal and tangent
vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w;
// Compute fragment normal based on normal transformations
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
@ -62,7 +62,7 @@ void main()
fragTexCoord = vertexTexCoord*2.0;
fragNormal = normalize(normalMatrix*vertexNormal);
vec3 fragTangent = normalize(normalMatrix*vertexTangent);
vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz);
fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
fragBinormal = cross(fragNormal, fragTangent);

View File

@ -4,7 +4,7 @@
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
in vec3 vertexTangent;
in vec4 vertexTangent;
in vec4 vertexColor;
// Input uniform values
@ -26,7 +26,7 @@ const float normalOffset = 0.1;
void main()
{
// Compute binormal from vertex normal and tangent
vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w;
// Compute fragment normal based on normal transformations
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
@ -36,7 +36,7 @@ void main()
fragTexCoord = vertexTexCoord*2.0;
fragNormal = normalize(normalMatrix*vertexNormal);
vec3 fragTangent = normalize(normalMatrix*vertexTangent);
vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz);
fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
fragBinormal = cross(fragNormal, fragTangent);

View File

@ -125,6 +125,8 @@ int main()
SetShaderValue(shader, GetShaderLocation(shader, "ambient"), &ambientIntensity, SHADER_UNIFORM_FLOAT);
// Get location for shader parameters that can be modified in real time
int metallicValueLoc = GetShaderLocation(shader, "metallicValue");
int roughnessValueLoc = GetShaderLocation(shader, "roughnessValue");
int emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower");
int emissiveColorLoc = GetShaderLocation(shader, "emissiveColor");
int textureTilingLoc = GetShaderLocation(shader, "tiling");
@ -141,7 +143,7 @@ int main()
// Setup materials[0].maps default parameters
car.materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE;
car.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.0f;
car.materials[0].maps[MATERIAL_MAP_METALNESS].value = 1.0f;
car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.0f;
car.materials[0].maps[MATERIAL_MAP_OCCLUSION].value = 1.0f;
car.materials[0].maps[MATERIAL_MAP_EMISSION].color = (Color){ 255, 162, 0, 255 };
@ -163,8 +165,8 @@ int main()
floor.materials[0].shader = shader;
floor.materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE;
floor.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.0f;
floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.0f;
floor.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.8f;
floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.1f;
floor.materials[0].maps[MATERIAL_MAP_OCCLUSION].value = 1.0f;
floor.materials[0].maps[MATERIAL_MAP_EMISSION].color = BLACK;
@ -229,6 +231,10 @@ int main()
Vector4 floorEmissiveColor = ColorNormalize(floor.materials[0].maps[MATERIAL_MAP_EMISSION].color);
SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, SHADER_UNIFORM_VEC4);
// Set floor metallic and roughness values
SetShaderValue(shader, metallicValueLoc, &floor.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT);
SetShaderValue(shader, roughnessValueLoc, &floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT);
DrawModel(floor, (Vector3){ 0.0f, 0.0f, 0.0f }, 5.0f, WHITE); // Draw floor model
// Set old car model texture tiling, emissive color and emissive intensity parameters on shader
@ -238,6 +244,10 @@ int main()
float emissiveIntensity = 0.01f;
SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, SHADER_UNIFORM_FLOAT);
// Set old car metallic and roughness values
SetShaderValue(shader, metallicValueLoc, &car.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT);
SetShaderValue(shader, roughnessValueLoc, &car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT);
DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.25f, WHITE); // Draw car model
// Draw spheres to show the lights positions

View File

@ -52,6 +52,8 @@ int main(void)
DrawCircle(screenWidth/5, 120, 35, DARKBLUE);
DrawCircleGradient(screenWidth/5, 220, 60, GREEN, SKYBLUE);
DrawCircleLines(screenWidth/5, 340, 80, DARKBLUE);
DrawEllipse(screenWidth/5, 120, 25, 20, YELLOW);
DrawEllipseLines(screenWidth/5, 120, 30, 25, YELLOW);
// Rectangle shapes and lines
DrawRectangle(screenWidth/4*2 - 60, 100, 120, 60, RED);

View File

@ -0,0 +1,185 @@
/*******************************************************************************************
*
* raylib [shapes] example - fancy clock using basic shapes
*
* Example complexity rating: [] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Hamza RAHAL (@hmz-rhl) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 Hamza RAHAL (@hmz-rhl)
*
********************************************************************************************/
#include "raylib.h"
#include <math.h> // Required for: cosf(), sinf()
#include <time.h> // Required for: time(), localtime()
#define DIGIT_SIZE 30
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef enum {
MODE_NORMAL = 0,
MODE_HANDS_FREE,
} ClockMode;
typedef struct {
int value;
Vector2 origin;
float angle;
int length;
int thickness;
Color color;
} ClockHand;
typedef struct {
ClockMode mode;
ClockHand second;
ClockHand minute;
ClockHand hour;
} Clock;
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
static void UpdateClock(Clock *clock); // Update clock time
static void DrawClock(Clock clock, Vector2 centerPos); // Draw clock at desired position
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - digital clock");
// Initialize clock
Clock myClock = {
.mode = MODE_NORMAL,
.second.angle = 45,
.second.length = 140,
.second.thickness = 3,
.second.color = BEIGE,
.minute.angle = 10,
.minute.length = 130,
.minute.thickness = 7,
.minute.color = DARKGRAY,
.hour.angle = 0,
.hour.length = 100,
.hour.thickness = 7,
.hour.color = BLACK,
};
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 (IsKeyPressed(KEY_SPACE))
{
if (myClock.mode == MODE_HANDS_FREE) myClock.mode = MODE_NORMAL;
else if (myClock.mode == MODE_NORMAL) myClock.mode = MODE_HANDS_FREE;
}
UpdateClock(&myClock);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
DrawCircle(400, 225, 5, BLACK); // Clock center dot
DrawClock(myClock, (Vector2){ 400, 225 }); // Clock in selected mode
DrawText("Press [SPACE] to switch clock mode", 10, 10, 20, DARKGRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Update clock time
static void UpdateClock(Clock *clock)
{
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
// Updating time data
clock->second.value = timeinfo->tm_sec;
clock->minute.value = timeinfo->tm_min;
clock->hour.value = timeinfo->tm_hour;
clock->hour.angle = (timeinfo->tm_hour%12)*180.0/6.0f;
clock->hour.angle += (timeinfo->tm_min%60)*30/60.0f;
clock->hour.angle -= 90;
clock->minute.angle = (timeinfo->tm_min%60)*6.0f;
clock->minute.angle += (timeinfo->tm_sec%60)*6/60.0f;
clock->minute.angle -= 90;
clock->second.angle = (timeinfo->tm_sec%60)*6.0f;
clock->second.angle -= 90;
}
// Draw clock
static void DrawClock(Clock clock, Vector2 centerPosition)
{
if (clock.mode == MODE_HANDS_FREE)
{
DrawCircleLinesV(centerPosition, clock.minute.length, LIGHTGRAY);
DrawText(TextFormat("%i", clock.second.value), centerPosition.x + (clock.second.length - 10)*cosf(clock.second.angle*(float)(PI/180)) - DIGIT_SIZE/2, centerPosition.y + clock.second.length*sinf(clock.second.angle*(float)(PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, GRAY);
DrawText(TextFormat("%i", clock.minute.value), clock.minute.origin.x + clock.minute.length*cosf(clock.minute.angle*(float)(PI/180)) - DIGIT_SIZE/2, centerPosition.y + clock.minute.length*sinf(clock.minute.angle*(float)(PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, RED);
DrawText(TextFormat("%i", clock.hour.value), centerPosition.x + clock.hour.length*cosf(clock.hour.angle*(float)(PI/180)) - DIGIT_SIZE/2, centerPosition.y + clock.hour.length*sinf(clock.hour.angle*(float)(PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, GOLD);
}
else if (clock.mode == MODE_NORMAL)
{
// Draw hand seconds
DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.second.length, clock.second.thickness },
(Vector2){ 0.0f, clock.second.thickness/2.0f }, clock.second.angle, clock.second.color);
// Draw hand minutes
DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.minute.length, clock.minute.thickness },
(Vector2){ 0.0f, clock.minute.thickness/2.0f }, clock.minute.angle, clock.minute.color);
// Draw hand hours
DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.hour.length, clock.hour.thickness },
(Vector2){ 0.0f, clock.hour.thickness/2.0f }, clock.hour.angle, clock.hour.color);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -108,7 +108,7 @@ int main(void)
}
else
{
// Draw provided text with laoded font, containing all required codepoint glyphs
// Draw provided text with loaded font, containing all required codepoint glyphs
DrawTextEx(font, text, (Vector2) { 160, 110 }, 48, 5, BLACK);
}

View File

@ -2300,7 +2300,7 @@
},
{
"name": "GamepadAxis",
"description": "Gamepad axis",
"description": "Gamepad axes",
"values": [
{
"name": "GAMEPAD_AXIS_LEFT_X",
@ -3139,7 +3139,7 @@
"name": "fileName"
},
{
"type": "char *",
"type": "const char *",
"name": "text"
}
]
@ -4409,7 +4409,7 @@
"name": "fileName"
},
{
"type": "char *",
"type": "const char *",
"name": "text"
}
]
@ -4684,7 +4684,7 @@
},
{
"name": "EncodeDataBase64",
"description": "Encode data to Base64 string, memory must be MemFree()",
"description": "Encode data to Base64 string (includes NULL terminator), memory must be MemFree()",
"returnType": "char *",
"params": [
{
@ -4703,12 +4703,12 @@
},
{
"name": "DecodeDataBase64",
"description": "Decode Base64 string data, memory must be MemFree()",
"description": "Decode Base64 string (expected NULL terminated), memory must be MemFree()",
"returnType": "unsigned char *",
"params": [
{
"type": "const unsigned char *",
"name": "data"
"type": "const char *",
"name": "text"
},
{
"type": "int *",
@ -5017,7 +5017,7 @@
},
{
"name": "GetGamepadAxisCount",
"description": "Get gamepad axis count for a gamepad",
"description": "Get axis count for a gamepad",
"returnType": "int",
"params": [
{
@ -5028,7 +5028,7 @@
},
{
"name": "GetGamepadAxisMovement",
"description": "Get axis movement value for a gamepad axis",
"description": "Get movement value for a gamepad axis",
"returnType": "float",
"params": [
{
@ -5702,6 +5702,29 @@
}
]
},
{
"name": "DrawEllipseV",
"description": "Draw ellipse (Vector version)",
"returnType": "void",
"params": [
{
"type": "Vector2",
"name": "center"
},
{
"type": "float",
"name": "radiusH"
},
{
"type": "float",
"name": "radiusV"
},
{
"type": "Color",
"name": "color"
}
]
},
{
"name": "DrawEllipseLines",
"description": "Draw ellipse outline",
@ -5729,6 +5752,29 @@
}
]
},
{
"name": "DrawEllipseLinesV",
"description": "Draw ellipse outline (Vector version)",
"returnType": "void",
"params": [
{
"type": "Vector2",
"name": "center"
},
{
"type": "float",
"name": "radiusH"
},
{
"type": "float",
"name": "radiusV"
},
{
"type": "Color",
"name": "color"
}
]
},
{
"name": "DrawRing",
"description": "Draw ring",
@ -5964,11 +6010,11 @@
},
{
"type": "Color",
"name": "topRight"
"name": "bottomRight"
},
{
"type": "Color",
"name": "bottomRight"
"name": "topRight"
}
]
},
@ -8256,7 +8302,7 @@
"name": "dst"
},
{
"type": "Vector2 *",
"type": "const Vector2 *",
"name": "points"
},
{
@ -8279,7 +8325,7 @@
"name": "dst"
},
{
"type": "Vector2 *",
"type": "const Vector2 *",
"name": "points"
},
{
@ -8483,7 +8529,7 @@
},
{
"name": "UpdateTexture",
"description": "Update GPU texture with new data",
"description": "Update GPU texture with new data (pixels should be able to fill texture)",
"returnType": "void",
"params": [
{
@ -8498,7 +8544,7 @@
},
{
"name": "UpdateTextureRec",
"description": "Update GPU texture rectangle with new data",
"description": "Update GPU texture rectangle with new data (pixels and rec should fit in texture)",
"returnType": "void",
"params": [
{
@ -11465,7 +11511,7 @@
},
{
"name": "UpdateSound",
"description": "Update sound buffer with new data",
"description": "Update sound buffer with new data (data and frame count should fit in sound)",
"returnType": "void",
"params": [
{

View File

@ -2300,7 +2300,7 @@ return {
},
{
name = "GamepadAxis",
description = "Gamepad axis",
description = "Gamepad axes",
values = {
{
name = "GAMEPAD_AXIS_LEFT_X",
@ -3108,7 +3108,7 @@ return {
returnType = "bool",
params = {
{type = "const char *", name = "fileName"},
{type = "char *", name = "text"}
{type = "const char *", name = "text"}
}
},
{
@ -4006,7 +4006,7 @@ return {
returnType = "bool",
params = {
{type = "const char *", name = "fileName"},
{type = "char *", name = "text"}
{type = "const char *", name = "text"}
}
},
{
@ -4198,7 +4198,7 @@ return {
},
{
name = "EncodeDataBase64",
description = "Encode data to Base64 string, memory must be MemFree()",
description = "Encode data to Base64 string (includes NULL terminator), memory must be MemFree()",
returnType = "char *",
params = {
{type = "const unsigned char *", name = "data"},
@ -4208,10 +4208,10 @@ return {
},
{
name = "DecodeDataBase64",
description = "Decode Base64 string data, memory must be MemFree()",
description = "Decode Base64 string (expected NULL terminated), memory must be MemFree()",
returnType = "unsigned char *",
params = {
{type = "const unsigned char *", name = "data"},
{type = "const char *", name = "text"},
{type = "int *", name = "outputSize"}
}
},
@ -4426,7 +4426,7 @@ return {
},
{
name = "GetGamepadAxisCount",
description = "Get gamepad axis count for a gamepad",
description = "Get axis count for a gamepad",
returnType = "int",
params = {
{type = "int", name = "gamepad"}
@ -4434,7 +4434,7 @@ return {
},
{
name = "GetGamepadAxisMovement",
description = "Get axis movement value for a gamepad axis",
description = "Get movement value for a gamepad axis",
returnType = "float",
params = {
{type = "int", name = "gamepad"},
@ -4838,6 +4838,17 @@ return {
{type = "Color", name = "color"}
}
},
{
name = "DrawEllipseV",
description = "Draw ellipse (Vector version)",
returnType = "void",
params = {
{type = "Vector2", name = "center"},
{type = "float", name = "radiusH"},
{type = "float", name = "radiusV"},
{type = "Color", name = "color"}
}
},
{
name = "DrawEllipseLines",
description = "Draw ellipse outline",
@ -4850,6 +4861,17 @@ return {
{type = "Color", name = "color"}
}
},
{
name = "DrawEllipseLinesV",
description = "Draw ellipse outline (Vector version)",
returnType = "void",
params = {
{type = "Vector2", name = "center"},
{type = "float", name = "radiusH"},
{type = "float", name = "radiusV"},
{type = "Color", name = "color"}
}
},
{
name = "DrawRing",
description = "Draw ring",
@ -4954,8 +4976,8 @@ return {
{type = "Rectangle", name = "rec"},
{type = "Color", name = "topLeft"},
{type = "Color", name = "bottomLeft"},
{type = "Color", name = "topRight"},
{type = "Color", name = "bottomRight"}
{type = "Color", name = "bottomRight"},
{type = "Color", name = "topRight"}
}
},
{
@ -6119,7 +6141,7 @@ return {
returnType = "void",
params = {
{type = "Image *", name = "dst"},
{type = "Vector2 *", name = "points"},
{type = "const Vector2 *", name = "points"},
{type = "int", name = "pointCount"},
{type = "Color", name = "color"}
}
@ -6130,7 +6152,7 @@ return {
returnType = "void",
params = {
{type = "Image *", name = "dst"},
{type = "Vector2 *", name = "points"},
{type = "const Vector2 *", name = "points"},
{type = "int", name = "pointCount"},
{type = "Color", name = "color"}
}
@ -6242,7 +6264,7 @@ return {
},
{
name = "UpdateTexture",
description = "Update GPU texture with new data",
description = "Update GPU texture with new data (pixels should be able to fill texture)",
returnType = "void",
params = {
{type = "Texture2D", name = "texture"},
@ -6251,7 +6273,7 @@ return {
},
{
name = "UpdateTextureRec",
description = "Update GPU texture rectangle with new data",
description = "Update GPU texture rectangle with new data (pixels and rec should fit in texture)",
returnType = "void",
params = {
{type = "Texture2D", name = "texture"},
@ -7850,7 +7872,7 @@ return {
},
{
name = "UpdateSound",
description = "Update sound buffer with new data",
description = "Update sound buffer with new data (data and frame count should fit in sound)",
returnType = "void",
params = {
{type = "Sound", name = "sound"},

File diff suppressed because it is too large Load Diff

View File

@ -486,7 +486,7 @@
<Value name="GAMEPAD_BUTTON_LEFT_THUMB" integer="16" desc="Gamepad joystick pressed button left" />
<Value name="GAMEPAD_BUTTON_RIGHT_THUMB" integer="17" desc="Gamepad joystick pressed button right" />
</Enum>
<Enum name="GamepadAxis" valueCount="6" desc="Gamepad axis">
<Enum name="GamepadAxis" valueCount="6" desc="Gamepad axes">
<Value name="GAMEPAD_AXIS_LEFT_X" integer="0" desc="Gamepad left stick X axis" />
<Value name="GAMEPAD_AXIS_LEFT_Y" integer="1" desc="Gamepad left stick Y axis" />
<Value name="GAMEPAD_AXIS_RIGHT_X" integer="2" desc="Gamepad right stick X axis" />
@ -672,14 +672,14 @@
</Callback>
<Callback name="SaveFileTextCallback" retType="bool" paramCount="2" desc="FileIO: Save text data">
<Param type="const char *" name="fileName" desc="" />
<Param type="char *" name="text" desc="" />
<Param type="const char *" name="text" desc="" />
</Callback>
<Callback name="AudioCallback" retType="void" paramCount="2" desc="">
<Param type="void *" name="bufferData" desc="" />
<Param type="unsigned int" name="frames" desc="" />
</Callback>
</Callbacks>
<Functions count="582">
<Functions count="584">
<Function name="InitWindow" retType="void" paramCount="3" desc="Initialize window and OpenGL context">
<Param type="int" name="width" desc="" />
<Param type="int" name="height" desc="" />
@ -1046,7 +1046,7 @@
</Function>
<Function name="SaveFileText" retType="bool" paramCount="2" desc="Save text data to file (write), string must be '\0' terminated, returns true on success">
<Param type="const char *" name="fileName" desc="" />
<Param type="char *" name="text" desc="" />
<Param type="const char *" name="text" desc="" />
</Function>
<Function name="FileExists" retType="bool" paramCount="1" desc="Check if file exists">
<Param type="const char *" name="fileName" desc="" />
@ -1123,13 +1123,13 @@
<Param type="int" name="compDataSize" desc="" />
<Param type="int *" name="dataSize" desc="" />
</Function>
<Function name="EncodeDataBase64" retType="char *" paramCount="3" desc="Encode data to Base64 string, memory must be MemFree()">
<Function name="EncodeDataBase64" retType="char *" paramCount="3" desc="Encode data to Base64 string (includes NULL terminator), memory must be MemFree()">
<Param type="const unsigned char *" name="data" desc="" />
<Param type="int" name="dataSize" desc="" />
<Param type="int *" name="outputSize" desc="" />
</Function>
<Function name="DecodeDataBase64" retType="unsigned char *" paramCount="2" desc="Decode Base64 string data, memory must be MemFree()">
<Param type="const unsigned char *" name="data" desc="" />
<Function name="DecodeDataBase64" retType="unsigned char *" paramCount="2" desc="Decode Base64 string (expected NULL terminated), memory must be MemFree()">
<Param type="const char *" name="text" desc="" />
<Param type="int *" name="outputSize" desc="" />
</Function>
<Function name="ComputeCRC32" retType="unsigned int" paramCount="2" desc="Compute CRC32 hash code">
@ -1216,10 +1216,10 @@
</Function>
<Function name="GetGamepadButtonPressed" retType="int" paramCount="0" desc="Get the last gamepad button pressed">
</Function>
<Function name="GetGamepadAxisCount" retType="int" paramCount="1" desc="Get gamepad axis count for a gamepad">
<Function name="GetGamepadAxisCount" retType="int" paramCount="1" desc="Get axis count for a gamepad">
<Param type="int" name="gamepad" desc="" />
</Function>
<Function name="GetGamepadAxisMovement" retType="float" paramCount="2" desc="Get axis movement value for a gamepad axis">
<Function name="GetGamepadAxisMovement" retType="float" paramCount="2" desc="Get movement value for a gamepad axis">
<Param type="int" name="gamepad" desc="" />
<Param type="int" name="axis" desc="" />
</Function>
@ -1409,6 +1409,12 @@
<Param type="float" name="radiusV" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawEllipseV" retType="void" paramCount="4" desc="Draw ellipse (Vector version)">
<Param type="Vector2" name="center" desc="" />
<Param type="float" name="radiusH" desc="" />
<Param type="float" name="radiusV" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawEllipseLines" retType="void" paramCount="5" desc="Draw ellipse outline">
<Param type="int" name="centerX" desc="" />
<Param type="int" name="centerY" desc="" />
@ -1416,6 +1422,12 @@
<Param type="float" name="radiusV" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawEllipseLinesV" retType="void" paramCount="4" desc="Draw ellipse outline (Vector version)">
<Param type="Vector2" name="center" desc="" />
<Param type="float" name="radiusH" desc="" />
<Param type="float" name="radiusV" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawRing" retType="void" paramCount="7" desc="Draw ring">
<Param type="Vector2" name="center" desc="" />
<Param type="float" name="innerRadius" desc="" />
@ -1476,8 +1488,8 @@
<Param type="Rectangle" name="rec" desc="" />
<Param type="Color" name="topLeft" desc="" />
<Param type="Color" name="bottomLeft" desc="" />
<Param type="Color" name="topRight" desc="" />
<Param type="Color" name="bottomRight" desc="" />
<Param type="Color" name="topRight" desc="" />
</Function>
<Function name="DrawRectangleLines" retType="void" paramCount="5" desc="Draw rectangle outline">
<Param type="int" name="posX" desc="" />
@ -2078,13 +2090,13 @@
</Function>
<Function name="ImageDrawTriangleFan" retType="void" paramCount="4" desc="Draw a triangle fan defined by points within an image (first vertex is the center)">
<Param type="Image *" name="dst" desc="" />
<Param type="Vector2 *" name="points" desc="" />
<Param type="const Vector2 *" name="points" desc="" />
<Param type="int" name="pointCount" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="ImageDrawTriangleStrip" retType="void" paramCount="4" desc="Draw a triangle strip defined by points within an image">
<Param type="Image *" name="dst" desc="" />
<Param type="Vector2 *" name="points" desc="" />
<Param type="const Vector2 *" name="points" desc="" />
<Param type="int" name="pointCount" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
@ -2138,11 +2150,11 @@
<Function name="UnloadRenderTexture" retType="void" paramCount="1" desc="Unload render texture from GPU memory (VRAM)">
<Param type="RenderTexture2D" name="target" desc="" />
</Function>
<Function name="UpdateTexture" retType="void" paramCount="2" desc="Update GPU texture with new data">
<Function name="UpdateTexture" retType="void" paramCount="2" desc="Update GPU texture with new data (pixels should be able to fill texture)">
<Param type="Texture2D" name="texture" desc="" />
<Param type="const void *" name="pixels" desc="" />
</Function>
<Function name="UpdateTextureRec" retType="void" paramCount="3" desc="Update GPU texture rectangle with new data">
<Function name="UpdateTextureRec" retType="void" paramCount="3" desc="Update GPU texture rectangle with new data (pixels and rec should fit in texture)">
<Param type="Texture2D" name="texture" desc="" />
<Param type="Rectangle" name="rec" desc="" />
<Param type="const void *" name="pixels" desc="" />
@ -2928,7 +2940,7 @@
<Function name="IsSoundValid" retType="bool" paramCount="1" desc="Checks if a sound is valid (data loaded and buffers initialized)">
<Param type="Sound" name="sound" desc="" />
</Function>
<Function name="UpdateSound" retType="void" paramCount="3" desc="Update sound buffer with new data">
<Function name="UpdateSound" retType="void" paramCount="3" desc="Update sound buffer with new data (data and frame count should fit in sound)">
<Param type="Sound" name="sound" desc="" />
<Param type="const void *" name="data" desc="" />
<Param type="int" name="sampleCount" desc="" />

View File

@ -440,8 +440,8 @@ RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color c
RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle within an image
RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image
RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline within an image
RLAPI void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center)
RLAPI void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image
RLAPI void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center)
RLAPI void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image
RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source)
RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination)
RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination)

View File

@ -868,7 +868,7 @@ clean: clean_shell_$(PLATFORM_SHELL)
@echo "removed all generated files!"
clean_shell_sh:
rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h
rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).web.a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h
ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o
endif
@ -879,6 +879,7 @@ clean_shell_cmd:
del *.o /s
cd $(RAYLIB_RELEASE_PATH) & \
del lib$(RAYLIB_LIB_NAME).a /s & \
del lib$(RAYLIB_LIB_NAME).web.a /s & \
del lib$(RAYLIB_LIB_NAME)dll.a /s & \
del $(RAYLIB_LIB_NAME).dll /s & \
del raygui.c /s & \

View File

@ -104,7 +104,7 @@
#define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
#define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
#define MAX_GAMEPADS 4 // Maximum number of gamepads supported
#define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad)
#define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad)
#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
#define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds
#define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
@ -189,7 +189,7 @@
//------------------------------------------------------------------------------------
// Module: rtextures - Configuration Flags
//------------------------------------------------------------------------------------
// Selecte desired fileformats to be supported for image data loading
// Selected desired fileformats to be supported for image data loading
#define SUPPORT_FILEFORMAT_PNG 1
//#define SUPPORT_FILEFORMAT_BMP 1
//#define SUPPORT_FILEFORMAT_TGA 1

File diff suppressed because it is too large Load Diff

View File

@ -1,121 +1,12 @@
/*
FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_flac - v0.12.42 - 2023-11-02
dr_flac - v0.13.0 - TBD
David Reid - mackron@gmail.com
GitHub: https://github.com/mackron/dr_libs
*/
/*
RELEASE NOTES - v0.12.0
=======================
Version 0.12.0 has breaking API changes including changes to the existing API and the removal of deprecated APIs.
Improved Client-Defined Memory Allocation
-----------------------------------------
The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The
existing system of DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE are still in place and will be used by default when no custom
allocation callbacks are specified.
To use the new system, you pass in a pointer to a drflac_allocation_callbacks object to drflac_open() and family, like this:
void* my_malloc(size_t sz, void* pUserData)
{
return malloc(sz);
}
void* my_realloc(void* p, size_t sz, void* pUserData)
{
return realloc(p, sz);
}
void my_free(void* p, void* pUserData)
{
free(p);
}
...
drflac_allocation_callbacks allocationCallbacks;
allocationCallbacks.pUserData = &myData;
allocationCallbacks.onMalloc = my_malloc;
allocationCallbacks.onRealloc = my_realloc;
allocationCallbacks.onFree = my_free;
drflac* pFlac = drflac_open_file("my_file.flac", &allocationCallbacks);
The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines.
Passing in null for the allocation callbacks object will cause dr_flac to use defaults which is the same as DRFLAC_MALLOC,
DRFLAC_REALLOC and DRFLAC_FREE and the equivalent of how it worked in previous versions.
Every API that opens a drflac object now takes this extra parameter. These include the following:
drflac_open()
drflac_open_relaxed()
drflac_open_with_metadata()
drflac_open_with_metadata_relaxed()
drflac_open_file()
drflac_open_file_with_metadata()
drflac_open_memory()
drflac_open_memory_with_metadata()
drflac_open_and_read_pcm_frames_s32()
drflac_open_and_read_pcm_frames_s16()
drflac_open_and_read_pcm_frames_f32()
drflac_open_file_and_read_pcm_frames_s32()
drflac_open_file_and_read_pcm_frames_s16()
drflac_open_file_and_read_pcm_frames_f32()
drflac_open_memory_and_read_pcm_frames_s32()
drflac_open_memory_and_read_pcm_frames_s16()
drflac_open_memory_and_read_pcm_frames_f32()
Optimizations
-------------
Seeking performance has been greatly improved. A new binary search based seeking algorithm has been introduced which significantly
improves performance over the brute force method which was used when no seek table was present. Seek table based seeking also takes
advantage of the new binary search seeking system to further improve performance there as well. Note that this depends on CRC which
means it will be disabled when DR_FLAC_NO_CRC is used.
The SSE4.1 pipeline has been cleaned up and optimized. You should see some improvements with decoding speed of 24-bit files in
particular. 16-bit streams should also see some improvement.
drflac_read_pcm_frames_s16() has been optimized. Previously this sat on top of drflac_read_pcm_frames_s32() and performed it's s32
to s16 conversion in a second pass. This is now all done in a single pass. This includes SSE2 and ARM NEON optimized paths.
A minor optimization has been implemented for drflac_read_pcm_frames_s32(). This will now use an SSE2 optimized pipeline for stereo
channel reconstruction which is the last part of the decoding process.
The ARM build has seen a few improvements. The CLZ (count leading zeroes) and REV (byte swap) instructions are now used when
compiling with GCC and Clang which is achieved using inline assembly. The CLZ instruction requires ARM architecture version 5 at
compile time and the REV instruction requires ARM architecture version 6.
An ARM NEON optimized pipeline has been implemented. To enable this you'll need to add -mfpu=neon to the command line when compiling.
Removed APIs
------------
The following APIs were deprecated in version 0.11.0 and have been completely removed in version 0.12.0:
drflac_read_s32() -> drflac_read_pcm_frames_s32()
drflac_read_s16() -> drflac_read_pcm_frames_s16()
drflac_read_f32() -> drflac_read_pcm_frames_f32()
drflac_seek_to_sample() -> drflac_seek_to_pcm_frame()
drflac_open_and_decode_s32() -> drflac_open_and_read_pcm_frames_s32()
drflac_open_and_decode_s16() -> drflac_open_and_read_pcm_frames_s16()
drflac_open_and_decode_f32() -> drflac_open_and_read_pcm_frames_f32()
drflac_open_and_decode_file_s32() -> drflac_open_file_and_read_pcm_frames_s32()
drflac_open_and_decode_file_s16() -> drflac_open_file_and_read_pcm_frames_s16()
drflac_open_and_decode_file_f32() -> drflac_open_file_and_read_pcm_frames_f32()
drflac_open_and_decode_memory_s32() -> drflac_open_memory_and_read_pcm_frames_s32()
drflac_open_and_decode_memory_s16() -> drflac_open_memory_and_read_pcm_frames_s16()
drflac_open_and_decode_memory_f32() -> drflac_open_memroy_and_read_pcm_frames_f32()
Prior versions of dr_flac operated on a per-sample basis whereas now it operates on PCM frames. The removed APIs all relate
to the old per-sample APIs. You now need to use the "pcm_frame" versions.
*/
/*
Introduction
============
@ -234,8 +125,8 @@ extern "C" {
#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x)
#define DRFLAC_VERSION_MAJOR 0
#define DRFLAC_VERSION_MINOR 12
#define DRFLAC_VERSION_REVISION 42
#define DRFLAC_VERSION_MINOR 13
#define DRFLAC_VERSION_REVISION 0
#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
#include <stddef.h> /* For size_t. */
@ -348,11 +239,11 @@ but also more memory. In my testing there is diminishing returns after about 4KB
#define DRFLAC_64BIT
#endif
#if defined(__x86_64__) || defined(_M_X64)
#if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC))
#define DRFLAC_X64
#elif defined(__i386) || defined(_M_IX86)
#define DRFLAC_X86
#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
#define DRFLAC_ARM
#endif
/* End Architecture Detection */
@ -406,8 +297,9 @@ typedef enum
typedef enum
{
drflac_seek_origin_start,
drflac_seek_origin_current
DRFLAC_SEEK_SET,
DRFLAC_SEEK_CUR,
DRFLAC_SEEK_END
} drflac_seek_origin;
/* The order of members in this structure is important because we map this directly to the raw data within the SEEKTABLE metadata block. */
@ -547,7 +439,7 @@ offset (in)
The number of bytes to move, relative to the origin. Will never be negative.
origin (in)
The origin of the seek - the current position or the start of the stream.
The origin of the seek - the current position, the start of the stream, or the end of the stream.
Return Value
@ -557,14 +449,32 @@ Whether or not the seek was successful.
Remarks
-------
The offset will never be negative. Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be
either drflac_seek_origin_start or drflac_seek_origin_current.
Seeking relative to the start and the current position must always be supported. If seeking from the end of the stream is not supported, return DRFLAC_FALSE.
When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of the FLAC stream. This needs to be detected
and handled by returning DRFLAC_FALSE.
*/
typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
/*
Callback for when the current position in the stream needs to be retrieved.
Parameters
----------
pUserData (in)
The user data that was passed to drflac_open() and family.
pCursor (out)
A pointer to a variable to receive the current position in the stream.
Return Value
------------
Whether or not the operation was successful.
*/
typedef drflac_bool32 (* drflac_tell_proc)(void* pUserData, drflac_int64* pCursor);
/*
Callback for when a metadata block is read.
@ -603,6 +513,9 @@ typedef struct
/* The function to call when the current read position needs to be moved. */
drflac_seek_proc onSeek;
/* The function to call when the current read position needs to be retrieved. */
drflac_tell_proc onTell;
/* The user data to pass around to onRead and onSeek. */
void* pUserData;
@ -828,7 +741,7 @@ drflac_open_memory()
drflac_open_with_metadata()
drflac_close()
*/
DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
/*
Opens a FLAC stream with relaxed validation of the header block.
@ -869,7 +782,7 @@ force your `onRead` callback to return 0, which dr_flac will use as an indicator
Use `drflac_open_with_metadata_relaxed()` if you need access to metadata.
*/
DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
/*
Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.).
@ -926,7 +839,7 @@ drflac_open_memory_with_metadata()
drflac_open()
drflac_close()
*/
DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
/*
The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present.
@ -936,7 +849,7 @@ See Also
drflac_open_with_metadata()
drflac_open_relaxed()
*/
DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
/*
Closes the given FLAC decoder.
@ -1234,13 +1147,13 @@ read samples into a dynamically sized buffer on the heap until no samples are le
Do not call this function on a broadcast type of stream (like internet radio streams and whatnot).
*/
DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
/* Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */
DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
/* Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */
DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
#ifndef DR_FLAC_NO_STDIO
/* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */
@ -2960,25 +2873,25 @@ static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFro
*/
if (offsetFromStart > 0x7FFFFFFF) {
drflac_uint64 bytesRemaining = offsetFromStart;
if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_SET)) {
return DRFLAC_FALSE;
}
bytesRemaining -= 0x7FFFFFFF;
while (bytesRemaining > 0x7FFFFFFF) {
if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
bytesRemaining -= 0x7FFFFFFF;
}
if (bytesRemaining > 0) {
if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) {
if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
}
} else {
if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) {
if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, DRFLAC_SEEK_SET)) {
return DRFLAC_FALSE;
}
}
@ -5393,6 +5306,12 @@ static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe
return DRFLAC_FALSE;
}
/*
Default to 0 for the LPC order. It's important that we always set this to 0 for non LPC
and FIXED subframes because we'll be using it in a generic validation check later.
*/
pSubframe->lpcOrder = 0;
type = (header & 0x7E) >> 1;
if (type == 0) {
pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
@ -5465,6 +5384,18 @@ static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame,
pSubframe->pSamplesS32 = pDecodedSamplesOut;
/*
pDecodedSamplesOut will be pointing to a buffer that was allocated with enough memory to store
maxBlockSizeInPCMFrames samples (as specified in the FLAC header). We need to guard against an
overflow here. At a higher level we are checking maxBlockSizeInPCMFrames from the header, but
here we need to do an additional check to ensure this frame's block size fully encompasses any
warmup samples which is determined by the LPC order. For non LPC and FIXED subframes, the LPC
order will be have been set to 0 in drflac__read_subframe_header().
*/
if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) {
return DRFLAC_FALSE;
}
switch (pSubframe->subframeType)
{
case DRFLAC_SUBFRAME_CONSTANT:
@ -6312,6 +6243,7 @@ typedef struct
{
drflac_read_proc onRead;
drflac_seek_proc onSeek;
drflac_tell_proc onTell;
drflac_meta_proc onMeta;
drflac_container container;
void* pUserData;
@ -6479,7 +6411,7 @@ static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbac
}
static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks)
static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks)
{
/*
We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that
@ -6489,6 +6421,8 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
drflac_uint64 seektablePos = 0;
drflac_uint32 seektableSize = 0;
(void)onTell;
for (;;) {
drflac_metadata metadata;
drflac_uint8 isLastBlock = 0;
@ -6840,7 +6774,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
metadata.data.padding.unused = 0;
/* Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. */
if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) {
isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */
} else {
onMeta(pUserDataMD, &metadata);
@ -6852,7 +6786,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
{
/* Invalid chunk. Just skip over this one. */
if (onMeta) {
if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) {
isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */
}
}
@ -6886,7 +6820,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
/* If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. */
if (onMeta == NULL && blockSize > 0) {
if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) {
isLastBlock = DRFLAC_TRUE;
}
}
@ -7220,6 +7154,7 @@ typedef struct
{
drflac_read_proc onRead; /* The original onRead callback from drflac_open() and family. */
drflac_seek_proc onSeek; /* The original onSeek callback from drflac_open() and family. */
drflac_tell_proc onTell; /* The original onTell callback from drflac_open() and family. */
void* pUserData; /* The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. */
drflac_uint64 currentBytePos; /* The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. */
drflac_uint64 firstBytePos; /* The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. */
@ -7241,32 +7176,32 @@ static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut,
static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin)
{
if (origin == drflac_seek_origin_start) {
if (origin == DRFLAC_SEEK_SET) {
if (offset <= 0x7FFFFFFF) {
if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) {
if (!oggbs->onSeek(oggbs->pUserData, (int)offset, DRFLAC_SEEK_SET)) {
return DRFLAC_FALSE;
}
oggbs->currentBytePos = offset;
return DRFLAC_TRUE;
} else {
if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_SET)) {
return DRFLAC_FALSE;
}
oggbs->currentBytePos = offset;
return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current);
return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, DRFLAC_SEEK_CUR);
}
} else {
while (offset > 0x7FFFFFFF) {
if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
oggbs->currentBytePos += 0x7FFFFFFF;
offset -= 0x7FFFFFFF;
}
if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { /* <-- Safe cast thanks to the loop above. */
if (!oggbs->onSeek(oggbs->pUserData, (int)offset, DRFLAC_SEEK_CUR)) { /* <-- Safe cast thanks to the loop above. */
return DRFLAC_FALSE;
}
oggbs->currentBytePos += offset;
@ -7298,7 +7233,7 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og
if (header.serialNumber != oggbs->serialNumber) {
/* It's not a FLAC page. Skip it. */
if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) {
if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
continue;
@ -7384,7 +7319,7 @@ static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
At this point we will have found either the packet or the end of the page. If were at the end of the page we'll
want to load the next page and keep searching for the end of the packet.
*/
drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, DRFLAC_SEEK_CUR);
oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
if (atEndOfPage) {
@ -7462,8 +7397,8 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see
DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */
/* Seeking is always forward which makes things a lot simpler. */
if (origin == drflac_seek_origin_start) {
if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) {
if (origin == DRFLAC_SEEK_SET) {
if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, DRFLAC_SEEK_SET)) {
return DRFLAC_FALSE;
}
@ -7471,38 +7406,50 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see
return DRFLAC_FALSE;
}
return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
}
return drflac__on_seek_ogg(pUserData, offset, DRFLAC_SEEK_CUR);
} else if (origin == DRFLAC_SEEK_CUR) {
while (bytesSeeked < offset) {
int bytesRemainingToSeek = offset - bytesSeeked;
DRFLAC_ASSERT(bytesRemainingToSeek >= 0);
DRFLAC_ASSERT(origin == drflac_seek_origin_current);
if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
bytesSeeked += bytesRemainingToSeek;
(void)bytesSeeked; /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */
oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
break;
}
while (bytesSeeked < offset) {
int bytesRemainingToSeek = offset - bytesSeeked;
DRFLAC_ASSERT(bytesRemainingToSeek >= 0);
/* If we get here it means some of the requested data is contained in the next pages. */
if (oggbs->bytesRemainingInPage > 0) {
bytesSeeked += (int)oggbs->bytesRemainingInPage;
oggbs->bytesRemainingInPage = 0;
}
if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
bytesSeeked += bytesRemainingToSeek;
(void)bytesSeeked; /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */
oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
break;
}
/* If we get here it means some of the requested data is contained in the next pages. */
if (oggbs->bytesRemainingInPage > 0) {
bytesSeeked += (int)oggbs->bytesRemainingInPage;
oggbs->bytesRemainingInPage = 0;
}
DRFLAC_ASSERT(bytesRemainingToSeek > 0);
if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
/* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */
return DRFLAC_FALSE;
DRFLAC_ASSERT(bytesRemainingToSeek > 0);
if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
/* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */
return DRFLAC_FALSE;
}
}
} else if (origin == DRFLAC_SEEK_END) {
/* Seeking to the end is not supported. */
return DRFLAC_FALSE;
}
return DRFLAC_TRUE;
}
static drflac_bool32 drflac__on_tell_ogg(void* pUserData, drflac_int64* pCursor)
{
/*
Not implemented for Ogg containers because we don't currently track the byte position of the logical bitstream. To support this, we'll need
to track the position in drflac__on_read_ogg and drflac__on_seek_ogg.
*/
(void)pUserData;
(void)pCursor;
return DRFLAC_FALSE;
}
static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
{
@ -7525,7 +7472,7 @@ static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64
runningGranulePosition = 0;
for (;;) {
if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
drflac_oggbs__seek_physical(oggbs, originalBytePos, DRFLAC_SEEK_SET);
return DRFLAC_FALSE; /* Never did find that sample... */
}
@ -7559,7 +7506,7 @@ static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64
a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until
we find the one containing the target sample.
*/
if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, DRFLAC_SEEK_SET)) {
return DRFLAC_FALSE;
}
if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
@ -7726,7 +7673,7 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r
The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to
be handling it in a generic way based on the serial number and packet types.
*/
if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
if (!onSeek(pUserData, 2, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
@ -7783,18 +7730,18 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r
}
} else {
/* Not a FLAC header. Skip it. */
if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
if (!onSeek(pUserData, bytesRemainingInPage, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
}
} else {
/* Not a FLAC header. Seek past the entire page and move on to the next. */
if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
if (!onSeek(pUserData, bytesRemainingInPage, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
}
} else {
if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) {
if (!onSeek(pUserData, pageBodySize, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
}
@ -7819,18 +7766,19 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r
}
#endif
static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
{
drflac_bool32 relaxed;
drflac_uint8 id[4];
if (pInit == NULL || onRead == NULL || onSeek == NULL) {
if (pInit == NULL || onRead == NULL || onSeek == NULL) { /* <-- onTell is optional. */
return DRFLAC_FALSE;
}
DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit));
pInit->onRead = onRead;
pInit->onSeek = onSeek;
pInit->onTell = onTell;
pInit->onMeta = onMeta;
pInit->container = container;
pInit->pUserData = pUserData;
@ -7838,6 +7786,7 @@ static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_p
pInit->bs.onRead = onRead;
pInit->bs.onSeek = onSeek;
pInit->bs.onTell = onTell;
pInit->bs.pUserData = pUserData;
drflac__reset_cache(&pInit->bs);
@ -7870,7 +7819,7 @@ static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_p
headerSize += 10;
}
if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) {
if (!onSeek(pUserData, headerSize, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE; /* Failed to seek past the tag. */
}
pInit->runningFilePos += headerSize;
@ -7922,7 +7871,7 @@ static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit)
}
static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks)
static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks)
{
drflac_init_info init;
drflac_uint32 allocationSize;
@ -7940,7 +7889,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac
/* CPU support first. */
drflac__init_cpu_caps();
if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
if (!drflac__init_private(&init, onRead, onSeek, onTell, onMeta, container, pUserData, pUserDataMD)) {
return NULL;
}
@ -7996,6 +7945,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac
DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs));
pOggbs->onRead = onRead;
pOggbs->onSeek = onSeek;
pOggbs->onTell = onTell;
pOggbs->pUserData = pUserData;
pOggbs->currentBytePos = init.oggFirstBytePos;
pOggbs->firstBytePos = init.oggFirstBytePos;
@ -8016,17 +7966,19 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac
if (init.hasMetadataBlocks) {
drflac_read_proc onReadOverride = onRead;
drflac_seek_proc onSeekOverride = onSeek;
drflac_tell_proc onTellOverride = onTell;
void* pUserDataOverride = pUserData;
#ifndef DR_FLAC_NO_OGG
if (init.container == drflac_container_ogg) {
onReadOverride = drflac__on_read_ogg;
onSeekOverride = drflac__on_seek_ogg;
onTellOverride = drflac__on_tell_ogg;
pUserDataOverride = (void*)pOggbs;
}
#endif
if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) {
if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onTellOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) {
#ifndef DR_FLAC_NO_OGG
drflac__free_from_callbacks(pOggbs, &allocationCallbacks);
#endif
@ -8061,6 +8013,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac
/* The Ogg bistream needs to be layered on top of the original bitstream. */
pFlac->bs.onRead = drflac__on_read_ogg;
pFlac->bs.onSeek = drflac__on_seek_ogg;
pFlac->bs.onTell = drflac__on_tell_ogg;
pFlac->bs.pUserData = (void*)pInternalOggbs;
pFlac->_oggbs = (void*)pInternalOggbs;
}
@ -8087,7 +8040,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac
DRFLAC_ASSERT(pFlac->bs.onRead != NULL);
/* Seek to the seektable, then just read directly into our seektable buffer. */
if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) {
if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, DRFLAC_SEEK_SET)) {
drflac_uint32 iSeekpoint;
for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) {
@ -8105,7 +8058,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac
}
/* We need to seek back to where we were. If this fails it's a critical error. */
if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) {
if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, DRFLAC_SEEK_SET)) {
drflac__free_from_callbacks(pFlac, &allocationCallbacks);
return NULL;
}
@ -8276,7 +8229,7 @@ static drflac_result drflac_result_from_errno(int e)
#ifdef ENOSYS
case ENOSYS: return DRFLAC_NOT_IMPLEMENTED;
#endif
#ifdef ENOTEMPTY
#if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST /* In AIX, ENOTEMPTY and EEXIST use the same value. */
case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY;
#endif
#ifdef ELOOP
@ -8727,11 +8680,41 @@ static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t byt
static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
{
DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */
int whence = SEEK_SET;
if (origin == DRFLAC_SEEK_CUR) {
whence = SEEK_CUR;
} else if (origin == DRFLAC_SEEK_END) {
whence = SEEK_END;
}
return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
return fseek((FILE*)pUserData, offset, whence) == 0;
}
static drflac_bool32 drflac__on_tell_stdio(void* pUserData, drflac_int64* pCursor)
{
FILE* pFileStdio = (FILE*)pUserData;
drflac_int64 result;
/* These were all validated at a higher level. */
DRFLAC_ASSERT(pFileStdio != NULL);
DRFLAC_ASSERT(pCursor != NULL);
#if defined(_WIN32)
#if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio);
#else
result = ftell(pFileStdio);
#endif
#else
result = ftell(pFileStdio);
#endif
*pCursor = result;
return DRFLAC_TRUE;
}
DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
{
@ -8742,7 +8725,7 @@ DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocati
return NULL;
}
pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
if (pFlac == NULL) {
fclose(pFile);
return NULL;
@ -8761,7 +8744,7 @@ DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_all
return NULL;
}
pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
if (pFlac == NULL) {
fclose(pFile);
return NULL;
@ -8780,7 +8763,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_
return NULL;
}
pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
if (pFlac == NULL) {
fclose(pFile);
return pFlac;
@ -8799,7 +8782,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, dr
return NULL;
}
pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
if (pFlac == NULL) {
fclose(pFile);
return pFlac;
@ -8834,28 +8817,45 @@ static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t by
static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
{
drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
drflac_int64 newCursor;
DRFLAC_ASSERT(memoryStream != NULL);
DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */
if (offset > (drflac_int64)memoryStream->dataSize) {
newCursor = memoryStream->currentReadPos;
if (origin == DRFLAC_SEEK_SET) {
newCursor = 0;
} else if (origin == DRFLAC_SEEK_CUR) {
newCursor = (drflac_int64)memoryStream->currentReadPos;
} else if (origin == DRFLAC_SEEK_END) {
newCursor = (drflac_int64)memoryStream->dataSize;
} else {
DRFLAC_ASSERT(!"Invalid seek origin");
return DRFLAC_FALSE;
}
if (origin == drflac_seek_origin_current) {
if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
memoryStream->currentReadPos += offset;
} else {
return DRFLAC_FALSE; /* Trying to seek too far forward. */
}
} else {
if ((drflac_uint32)offset <= memoryStream->dataSize) {
memoryStream->currentReadPos = offset;
} else {
return DRFLAC_FALSE; /* Trying to seek too far forward. */
}
newCursor += offset;
if (newCursor < 0) {
return DRFLAC_FALSE; /* Trying to seek prior to the start of the buffer. */
}
if ((size_t)newCursor > memoryStream->dataSize) {
return DRFLAC_FALSE; /* Trying to seek beyond the end of the buffer. */
}
memoryStream->currentReadPos = (size_t)newCursor;
return DRFLAC_TRUE;
}
static drflac_bool32 drflac__on_tell_memory(void* pUserData, drflac_int64* pCursor)
{
drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
DRFLAC_ASSERT(memoryStream != NULL);
DRFLAC_ASSERT(pCursor != NULL);
*pCursor = (drflac_int64)memoryStream->currentReadPos;
return DRFLAC_TRUE;
}
@ -8867,7 +8867,7 @@ DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const
memoryStream.data = (const drflac_uint8*)pData;
memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0;
pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks);
pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, drflac__on_tell_memory, &memoryStream, pAllocationCallbacks);
if (pFlac == NULL) {
return NULL;
}
@ -8898,7 +8898,7 @@ DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t da
memoryStream.data = (const drflac_uint8*)pData;
memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0;
pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, drflac__on_tell_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
if (pFlac == NULL) {
return NULL;
}
@ -8923,22 +8923,22 @@ DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t da
DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
{
return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
return drflac_open_with_metadata_private(onRead, onSeek, onTell, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
}
DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
{
return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks);
return drflac_open_with_metadata_private(onRead, onSeek, onTell, NULL, container, pUserData, pUserData, pAllocationCallbacks);
}
DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
{
return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
return drflac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
}
DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
{
return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
return drflac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
}
DRFLAC_API void drflac_close(drflac* pFlac)
@ -11770,7 +11770,7 @@ DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32)
DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16)
DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
{
drflac* pFlac;
@ -11784,7 +11784,7 @@ DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc on
*totalPCMFrameCountOut = 0;
}
pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
pFlac = drflac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
if (pFlac == NULL) {
return NULL;
}
@ -11792,7 +11792,7 @@ DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc on
return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
}
DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
{
drflac* pFlac;
@ -11806,7 +11806,7 @@ DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc on
*totalPCMFrameCountOut = 0;
}
pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
pFlac = drflac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
if (pFlac == NULL) {
return NULL;
}
@ -11814,7 +11814,7 @@ DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc on
return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
}
DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
{
drflac* pFlac;
@ -11828,7 +11828,7 @@ DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, d
*totalPCMFrameCountOut = 0;
}
pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
pFlac = drflac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
if (pFlac == NULL) {
return NULL;
}
@ -12077,6 +12077,26 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat
/*
REVISION HISTORY
================
v0.13.0 - TBD
- API CHANGE: Seek origin enums have been renamed to match the naming convention used by other dr_libs libraries:
- drflac_seek_origin_start -> DRFLAC_SEEK_SET
- drflac_seek_origin_current -> DRFLAC_SEEK_CUR
- DRFLAC_SEEK_END (new)
- API CHANGE: A new seek origin has been added to allow seeking from the end of the file. If you implement your own `onSeek` callback, you should now detect and handle `DRFLAC_SEEK_END`. If seeking to the end is not supported, return `DRFLAC_FALSE`. If you only use `*_open_file()` or `*_open_memory()`, you need not change anything.
- API CHANGE: An `onTell` callback has been added to the following functions:
- drflac_open()
- drflac_open_relaxed()
- drflac_open_with_metadata()
- drflac_open_with_metadata_relaxed()
- drflac_open_and_read_pcm_frames_s32()
- drflac_open_and_read_pcm_frames_s16()
- drflac_open_and_read_pcm_frames_f32()
- Fix compilation for AIX OS.
v0.12.43 - 2024-12-17
- Fix a possible buffer overflow during decoding.
- Improve detection of ARM64EC
v0.12.42 - 2023-11-02
- Fix build for ARMv6-M.
- Fix a compilation warning with GCC.

File diff suppressed because it is too large Load Diff

View File

@ -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.13.16 - 2024-02-27
dr_wav - v0.14.0 - TBD
David Reid - mackron@gmail.com
@ -146,8 +146,8 @@ extern "C" {
#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x)
#define DRWAV_VERSION_MAJOR 0
#define DRWAV_VERSION_MINOR 13
#define DRWAV_VERSION_REVISION 16
#define DRWAV_VERSION_MINOR 14
#define DRWAV_VERSION_REVISION 0
#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
#include <stddef.h> /* For size_t. */
@ -176,7 +176,7 @@ typedef unsigned int drwav_uint32;
#pragma GCC diagnostic pop
#endif
#endif
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__)
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__powerpc64__)
typedef drwav_uint64 drwav_uintptr;
#else
typedef drwav_uint32 drwav_uintptr;
@ -305,8 +305,9 @@ typedef struct
typedef enum
{
drwav_seek_origin_start,
drwav_seek_origin_current
DRWAV_SEEK_SET,
DRWAV_SEEK_CUR,
DRWAV_SEEK_END
} drwav_seek_origin;
typedef enum
@ -415,11 +416,21 @@ origin [in] The origin of the seek - the current position or the start of the
Returns whether or not the seek was successful.
Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either drwav_seek_origin_start or
drwav_seek_origin_current.
Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either DRWAV_SEEK_SET or
DRWAV_SEEK_CUR.
*/
typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
/*
Callback for when the current position in the stream needs to be retrieved.
pUserData [in] The user data that was passed to drwav_init() and family.
pCursor [out] A pointer to a variable to receive the current position in the stream.
Returns whether or not the operation was successful.
*/
typedef drwav_bool32 (* drwav_tell_proc)(void* pUserData, drwav_int64* pCursor);
/*
Callback for when drwav_init_ex() finds a chunk.
@ -514,6 +525,11 @@ typedef enum
drwav_metadata_type_list_info_genre = 1 << 15,
drwav_metadata_type_list_info_album = 1 << 16,
drwav_metadata_type_list_info_tracknumber = 1 << 17,
drwav_metadata_type_list_info_location = 1 << 18,
drwav_metadata_type_list_info_organization = 1 << 19,
drwav_metadata_type_list_info_keywords = 1 << 20,
drwav_metadata_type_list_info_medium = 1 << 21,
drwav_metadata_type_list_info_description = 1 << 22,
/* Other type constants for convenience. */
drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software
@ -524,7 +540,12 @@ typedef enum
| drwav_metadata_type_list_info_date
| drwav_metadata_type_list_info_genre
| drwav_metadata_type_list_info_album
| drwav_metadata_type_list_info_tracknumber,
| drwav_metadata_type_list_info_tracknumber
| drwav_metadata_type_list_info_location
| drwav_metadata_type_list_info_organization
| drwav_metadata_type_list_info_keywords
| drwav_metadata_type_list_info_medium
| drwav_metadata_type_list_info_description,
drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label
| drwav_metadata_type_list_note
@ -555,11 +576,11 @@ typedef struct
/* See drwav_smpl_loop_type. */
drwav_uint32 type;
/* The byte offset of the first sample to be played in the loop. */
drwav_uint32 firstSampleByteOffset;
/* The offset of the first sample to be played in the loop. */
drwav_uint32 firstSampleOffset;
/* The byte offset into the audio data of the last sample to be played in the loop. */
drwav_uint32 lastSampleByteOffset;
/* The offset into the audio data of the last sample to be played in the loop. */
drwav_uint32 lastSampleOffset;
/* A value to represent that playback should occur at a point between samples. This value ranges from 0 to UINT32_MAX. Where a value of 0 means no fraction, and a value of (UINT32_MAX / 2) would mean half a sample. */
drwav_uint32 sampleFraction;
@ -637,8 +658,8 @@ typedef struct
/* Set to 0 for uncompressed formats. Else the last byte in compressed wave data where decompression can begin to find the value of the corresponding sample value. */
drwav_uint32 blockStart;
/* For uncompressed formats this is the byte offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */
drwav_uint32 sampleByteOffset;
/* For uncompressed formats this is the offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */
drwav_uint32 sampleOffset;
} drwav_cue_point;
typedef struct
@ -846,6 +867,9 @@ typedef struct
/* A pointer to the function to call when the wav file needs to be seeked. */
drwav_seek_proc onSeek;
/* A pointer to the function to call when the position of the stream needs to be retrieved. */
drwav_tell_proc onTell;
/* The user data to pass to callbacks. */
void* pUserData;
@ -968,9 +992,9 @@ after the function returns.
See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
*/
DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, drwav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
/*
Initializes a pre-allocated drwav object for writing.
@ -1273,9 +1297,9 @@ Opens and reads an entire wav file in a single operation.
The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
*/
DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
#ifndef DR_WAV_NO_STDIO
/*
Opens and decodes an entire wav file in a single operation.
@ -1384,7 +1408,7 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);
#define DRWAV_MAX_SIMD_VECTOR_SIZE 32
/* Architecture Detection */
#if defined(__x86_64__) || defined(_M_X64)
#if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC))
#define DRWAV_X64
#elif defined(__i386) || defined(_M_IX86)
#define DRWAV_X86
@ -1962,12 +1986,12 @@ DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uin
drwav_uint64 bytesRemainingToSeek = offset;
while (bytesRemainingToSeek > 0) {
if (bytesRemainingToSeek > 0x7FFFFFFF) {
if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_CUR)) {
return DRWAV_FALSE;
}
bytesRemainingToSeek -= 0x7FFFFFFF;
} else {
if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
if (!onSeek(pUserData, (int)bytesRemainingToSeek, DRWAV_SEEK_CUR)) {
return DRWAV_FALSE;
}
bytesRemainingToSeek = 0;
@ -1980,21 +2004,21 @@ DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uin
DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
{
if (offset <= 0x7FFFFFFF) {
return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
return onSeek(pUserData, (int)offset, DRWAV_SEEK_SET);
}
/* Larger than 32-bit seek. */
if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_SET)) {
return DRWAV_FALSE;
}
offset -= 0x7FFFFFFF;
for (;;) {
if (offset <= 0x7FFFFFFF) {
return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
return onSeek(pUserData, (int)offset, DRWAV_SEEK_CUR);
}
if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_CUR)) {
return DRWAV_FALSE;
}
offset -= 0x7FFFFFFF;
@ -2028,7 +2052,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserDat
return DRWAV_FALSE;
}
if (origin == drwav_seek_origin_start) {
if (origin == DRWAV_SEEK_SET) {
*pCursor = offset;
} else {
*pCursor += offset;
@ -2189,12 +2213,12 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
if (bytesJustRead == sizeof(smplLoopData)) {
pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0);
pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4);
pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8);
pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12);
pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16);
pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20);
pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0);
pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4);
pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleOffset = drwav_bytes_to_u32(smplLoopData + 8);
pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleOffset = drwav_bytes_to_u32(smplLoopData + 12);
pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16);
pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20);
} else {
break;
}
@ -2254,7 +2278,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse
pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11];
pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12);
pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16);
pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20);
pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset = drwav_bytes_to_u32(cuePointData + 20);
} else {
break;
}
@ -2698,7 +2722,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser*
drwav_uint8 buffer[4];
size_t bytesJustRead;
if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) {
if (!pParser->onSeek(pParser->pReadSeekUserData, 28, DRWAV_SEEK_CUR)) {
return bytesRead;
}
bytesRead += 28;
@ -2811,7 +2835,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser*
return bytesRead;
}
allocSizeNeeded += drwav__strlen(buffer) + 1;
allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; /* Coding history. */
allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES + 1; /* Coding history. */
drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
@ -2916,6 +2940,16 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser*
subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album);
} else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) {
subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber);
} else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_location, "IARL")) {
subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_location);
} else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_organization, "ICMS")) {
subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_organization);
} else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_keywords, "IKEY")) {
subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_keywords);
} else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_medium, "IMED")) {
subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_medium);
} else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_description, "ISBJ")) {
subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_description);
} else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
}
@ -2926,14 +2960,14 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser*
if (subchunkBytesRead < subchunkDataSize) {
drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) {
if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, DRWAV_SEEK_CUR)) {
break;
}
bytesRead += bytesToSeek;
}
if ((subchunkDataSize % 2) == 1) {
if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) {
if (!pParser->onSeek(pParser->pReadSeekUserData, 1, DRWAV_SEEK_CUR)) {
break;
}
bytesRead += 1;
@ -2985,16 +3019,17 @@ DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT)
}
}
DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pReadSeekTellUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
{
if (pWav == NULL || onRead == NULL || onSeek == NULL) {
if (pWav == NULL || onRead == NULL || onSeek == NULL) { /* <-- onTell is optional. */
return DRWAV_FALSE;
}
DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
pWav->onRead = onRead;
pWav->onSeek = onSeek;
pWav->pUserData = pReadSeekUserData;
pWav->onTell = onTell;
pWav->pUserData = pReadSeekTellUserData;
pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
@ -3311,7 +3346,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
fmt.channelMask = drwav_bytes_to_u32_ex(fmtext + 2, pWav->container);
drwav_bytes_to_guid(fmtext + 6, fmt.subFormat);
} else {
if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, drwav_seek_origin_current) == DRWAV_FALSE) {
if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, DRWAV_SEEK_CUR) == DRWAV_FALSE) {
return DRWAV_FALSE;
}
}
@ -3321,7 +3356,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
}
/* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */
if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current) == DRWAV_FALSE) {
if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), DRWAV_SEEK_CUR) == DRWAV_FALSE) {
return DRWAV_FALSE;
}
cursor += (header.sizeInBytes - bytesReadSoFar);
@ -3465,12 +3500,15 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
compressionFormat = DR_WAVE_FORMAT_MULAW;
} else if (drwav_fourcc_equal(type, "ima4")) {
compressionFormat = DR_WAVE_FORMAT_DVI_ADPCM;
sampleSizeInBits = 4;
sampleSizeInBits = 4;
/*
I haven't been able to figure out how to get correct decoding for IMA ADPCM. Until this is figured out
we'll need to abort when we encounter such an encoding. Advice welcome!
*/
(void)compressionFormat;
(void)sampleSizeInBits;
return DRWAV_FALSE;
} else {
return DRWAV_FALSE; /* Unknown or unsupported compression format. Need to abort. */
@ -3533,20 +3571,46 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
return DRWAV_FALSE;
}
/* We need to seek forward by the offset. */
/* The position of the audio data starts at an offset. */
offset = drwav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container);
if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) {
return DRWAV_FALSE;
}
cursor += offset;
pWav->dataChunkDataPos = cursor + offset;
pWav->dataChunkDataPos = cursor;
/* The data chunk size needs to be reduced by the offset or else seeking will break. */
dataChunkSize = chunkSize;
/* If we're running in sequential mode, or we're not reading metadata, we have enough now that we can get out of the loop. */
if (sequential || !isProcessingMetadata) {
break; /* No need to keep reading beyond the data chunk. */
if (dataChunkSize > offset) {
dataChunkSize -= offset;
} else {
dataChunkSize = 0;
}
if (sequential) {
if (foundChunk_fmt) { /* <-- Name is misleading, but will be set to true if the COMM chunk has been parsed. */
/*
Getting here means we're opening in sequential mode and we've found the SSND (data) and COMM (fmt) chunks. We need
to get out of the loop here or else we'll end up going past the data chunk and will have no way of getting back to
it since we're not allowed to seek backwards.
One subtle detail here is that there is an offset with the SSND chunk. We need to make sure we seek past this offset
so we're left sitting on the first byte of actual audio data.
*/
if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) {
return DRWAV_FALSE;
}
cursor += offset;
break;
} else {
/*
Getting here means the COMM chunk was not found. In sequential mode, if we haven't yet found the COMM chunk
we'll need to abort because we can't be doing a backwards seek back to the SSND chunk in order to read the
data. For this reason, this configuration of AIFF files are not supported with sequential mode.
*/
return DRWAV_FALSE;
}
} else {
chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */
chunkSize -= sizeof(offsetAndBlockSizeData); /* <-- This was read earlier. */
if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
break;
}
@ -3557,7 +3621,6 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
}
/* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */
if (isProcessingMetadata) {
drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown);
@ -3647,8 +3710,26 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
pWav->metadataCount = metadataParser.metadataCount;
}
/* At this point we should be sitting on the first byte of the raw audio data. */
/*
It's possible for the size reported in the data chunk to be greater than that of the file. We
need to do a validation check here to make sure we don't exceed the file size. To skip this
check, set the onTell callback to NULL.
*/
if (pWav->onTell != NULL && pWav->onSeek != NULL) {
if (pWav->onSeek(pWav->pUserData, 0, DRWAV_SEEK_END) == DRWAV_TRUE) {
drwav_int64 fileSize;
if (pWav->onTell(pWav->pUserData, &fileSize)) {
if (dataChunkSize + pWav->dataChunkDataPos > (drwav_uint64)fileSize) {
dataChunkSize = (drwav_uint64)fileSize - pWav->dataChunkDataPos;
}
}
} else {
/*
Failed to seek to the end of the file. It might not be supported by the backend so in
this case we cannot perform the validation check.
*/
}
}
/*
I've seen a WAV file in the wild where a RIFF-ecapsulated file has the size of it's "RIFF" and
@ -3670,6 +3751,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
}
}
/* At this point we want to be sitting on the first byte of the raw audio data. */
if (drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == DRWAV_FALSE) {
drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
return DRWAV_FALSE;
@ -3680,8 +3762,26 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
pWav->sampleRate = fmt.sampleRate;
pWav->channels = fmt.channels;
pWav->bitsPerSample = fmt.bitsPerSample;
pWav->bytesRemaining = dataChunkSize;
pWav->translatedFormatTag = translatedFormatTag;
/*
I've had a report where files would start glitching after seeking. The reason for this is the data
chunk is not a clean multiple of the PCM frame size in bytes. Where this becomes a problem is when
seeking, because the number of bytes remaining in the data chunk is used to calculate the current
byte position. If this byte position is not aligned to the number of bytes in a PCM frame, it will
result in the seek not being cleanly positioned at the start of the PCM frame thereby resulting in
all decoded frames after that being corrupted.
To address this, we need to round the data chunk size down to the nearest multiple of the frame size.
*/
if (!drwav__is_compressed_format_tag(translatedFormatTag)) {
drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
if (bytesPerFrame > 0) {
dataChunkSize -= (dataChunkSize % bytesPerFrame);
}
}
pWav->bytesRemaining = dataChunkSize;
pWav->dataChunkDataSize = dataChunkSize;
if (sampleCountFromFactChunk != 0) {
@ -3764,23 +3864,23 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
return DRWAV_TRUE;
}
DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
{
return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);
return drwav_init_ex(pWav, onRead, onSeek, onTell, NULL, pUserData, NULL, 0, pAllocationCallbacks);
}
DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, drwav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
{
if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {
if (!drwav_preinit(pWav, onRead, onSeek, onTell, pReadSeekTellUserData, pAllocationCallbacks)) {
return DRWAV_FALSE;
}
return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
}
DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
{
if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
if (!drwav_preinit(pWav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
return DRWAV_FALSE;
}
@ -3995,8 +4095,8 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata*
for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleOffset);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleOffset);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
}
@ -4036,7 +4136,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata*
bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset);
bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset);
}
} break;
@ -4142,15 +4242,20 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata*
const char* pID = NULL;
switch (pMetadata->type) {
case drwav_metadata_type_list_info_software: pID = "ISFT"; break;
case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break;
case drwav_metadata_type_list_info_title: pID = "INAM"; break;
case drwav_metadata_type_list_info_artist: pID = "IART"; break;
case drwav_metadata_type_list_info_comment: pID = "ICMT"; break;
case drwav_metadata_type_list_info_date: pID = "ICRD"; break;
case drwav_metadata_type_list_info_genre: pID = "IGNR"; break;
case drwav_metadata_type_list_info_album: pID = "IPRD"; break;
case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
case drwav_metadata_type_list_info_software: pID = "ISFT"; break;
case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break;
case drwav_metadata_type_list_info_title: pID = "INAM"; break;
case drwav_metadata_type_list_info_artist: pID = "IART"; break;
case drwav_metadata_type_list_info_comment: pID = "ICMT"; break;
case drwav_metadata_type_list_info_date: pID = "ICRD"; break;
case drwav_metadata_type_list_info_genre: pID = "IGNR"; break;
case drwav_metadata_type_list_info_album: pID = "IPRD"; break;
case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
case drwav_metadata_type_list_info_location: pID = "IARL"; break;
case drwav_metadata_type_list_info_organization: pID = "ICMS"; break;
case drwav_metadata_type_list_info_keywords: pID = "IKEY"; break;
case drwav_metadata_type_list_info_medium: pID = "IMED"; break;
case drwav_metadata_type_list_info_description: pID = "ISBJ"; break;
default: break;
}
@ -4434,7 +4539,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_d
/* "RIFF" chunk. */
if (pFormat->container == drwav_container_riff) {
drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; /* +28 = "WAVE" + [sizeof "fmt " chunk] */
drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; /* +36 = "WAVE" + [sizeof "fmt " chunk] + [data chunk header] */
runningPos += drwav__write(pWav, "RIFF", 4);
runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);
runningPos += drwav__write(pWav, "WAVE", 4);
@ -4704,7 +4809,7 @@ DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e)
#ifdef ENOSYS
case ENOSYS: return DRWAV_NOT_IMPLEMENTED;
#endif
#ifdef ENOTEMPTY
#if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST /* In AIX, ENOTEMPTY and EEXIST use the same value. */
case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY;
#endif
#ifdef ELOOP
@ -5161,7 +5266,38 @@ DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, s
DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
{
return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
int whence = SEEK_SET;
if (origin == DRWAV_SEEK_CUR) {
whence = SEEK_CUR;
} else if (origin == DRWAV_SEEK_END) {
whence = SEEK_END;
}
return fseek((FILE*)pUserData, offset, whence) == 0;
}
DRWAV_PRIVATE drwav_bool32 drwav__on_tell_stdio(void* pUserData, drwav_int64* pCursor)
{
FILE* pFileStdio = (FILE*)pUserData;
drwav_int64 result;
/* These were all validated at a higher level. */
DRWAV_ASSERT(pFileStdio != NULL);
DRWAV_ASSERT(pCursor != NULL);
#if defined(_WIN32)
#if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio);
#else
result = ftell(pFileStdio);
#endif
#else
result = ftell(pFileStdio);
#endif
*pCursor = result;
return DRWAV_TRUE;
}
DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
@ -5174,7 +5310,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFi
{
drwav_bool32 result;
result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, drwav__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
if (result != DRWAV_TRUE) {
fclose(pFile);
return result;
@ -5352,29 +5488,34 @@ DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, si
DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
{
drwav* pWav = (drwav*)pUserData;
drwav_int64 newCursor;
DRWAV_ASSERT(pWav != NULL);
if (origin == drwav_seek_origin_current) {
if (offset > 0) {
if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {
return DRWAV_FALSE; /* Trying to seek too far forward. */
}
} else {
if (pWav->memoryStream.currentReadPos < (size_t)-offset) {
return DRWAV_FALSE; /* Trying to seek too far backwards. */
}
}
newCursor = pWav->memoryStream.currentReadPos;
/* This will never underflow thanks to the clamps above. */
pWav->memoryStream.currentReadPos += offset;
if (origin == DRWAV_SEEK_SET) {
newCursor = 0;
} else if (origin == DRWAV_SEEK_CUR) {
newCursor = (drwav_int64)pWav->memoryStream.currentReadPos;
} else if (origin == DRWAV_SEEK_END) {
newCursor = (drwav_int64)pWav->memoryStream.dataSize;
} else {
if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) {
pWav->memoryStream.currentReadPos = offset;
} else {
return DRWAV_FALSE; /* Trying to seek too far forward. */
}
DRWAV_ASSERT(!"Invalid seek origin");
return DRWAV_FALSE;
}
newCursor += offset;
if (newCursor < 0) {
return DRWAV_FALSE; /* Trying to seek prior to the start of the buffer. */
}
if ((size_t)newCursor > pWav->memoryStream.dataSize) {
return DRWAV_FALSE; /* Trying to seek beyond the end of the buffer. */
}
pWav->memoryStream.currentReadPos = (size_t)newCursor;
return DRWAV_TRUE;
}
@ -5421,29 +5562,45 @@ DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn
DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
{
drwav* pWav = (drwav*)pUserData;
drwav_int64 newCursor;
DRWAV_ASSERT(pWav != NULL);
if (origin == drwav_seek_origin_current) {
if (offset > 0) {
if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {
offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); /* Trying to seek too far forward. */
}
} else {
if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {
offset = -(int)pWav->memoryStreamWrite.currentWritePos; /* Trying to seek too far backwards. */
}
}
newCursor = pWav->memoryStreamWrite.currentWritePos;
/* This will never underflow thanks to the clamps above. */
pWav->memoryStreamWrite.currentWritePos += offset;
if (origin == DRWAV_SEEK_SET) {
newCursor = 0;
} else if (origin == DRWAV_SEEK_CUR) {
newCursor = (drwav_int64)pWav->memoryStreamWrite.currentWritePos;
} else if (origin == DRWAV_SEEK_END) {
newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize;
} else {
if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) {
pWav->memoryStreamWrite.currentWritePos = offset;
} else {
pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; /* Trying to seek too far forward. */
}
DRWAV_ASSERT(!"Invalid seek origin");
return DRWAV_INVALID_ARGS;
}
newCursor += offset;
if (newCursor < 0) {
return DRWAV_FALSE; /* Trying to seek prior to the start of the buffer. */
}
if ((size_t)newCursor > pWav->memoryStreamWrite.dataSize) {
return DRWAV_FALSE; /* Trying to seek beyond the end of the buffer. */
}
pWav->memoryStreamWrite.currentWritePos = (size_t)newCursor;
return DRWAV_TRUE;
}
DRWAV_PRIVATE drwav_bool32 drwav__on_tell_memory(void* pUserData, drwav_int64* pCursor)
{
drwav* pWav = (drwav*)pUserData;
DRWAV_ASSERT(pWav != NULL);
DRWAV_ASSERT(pCursor != NULL);
*pCursor = (drwav_int64)pWav->memoryStream.currentReadPos;
return DRWAV_TRUE;
}
@ -5458,7 +5615,7 @@ DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_
return DRWAV_FALSE;
}
if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, drwav__on_tell_memory, pWav, pAllocationCallbacks)) {
return DRWAV_FALSE;
}
@ -5475,7 +5632,7 @@ DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void*
return DRWAV_FALSE;
}
if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, drwav__on_tell_memory, pWav, pAllocationCallbacks)) {
return DRWAV_FALSE;
}
@ -5565,25 +5722,25 @@ DRWAV_API drwav_result drwav_uninit(drwav* pWav)
if (pWav->onSeek && !pWav->isSequentialWrite) {
if (pWav->container == drwav_container_riff) {
/* The "RIFF" chunk size. */
if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
if (pWav->onSeek(pWav->pUserData, 4, DRWAV_SEEK_SET)) {
drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
drwav__write_u32ne_to_le(pWav, riffChunkSize);
}
/* The "data" chunk size. */
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) {
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, DRWAV_SEEK_SET)) {
drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
drwav__write_u32ne_to_le(pWav, dataChunkSize);
}
} else if (pWav->container == drwav_container_w64) {
/* The "RIFF" chunk size. */
if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
if (pWav->onSeek(pWav->pUserData, 16, DRWAV_SEEK_SET)) {
drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
drwav__write_u64ne_to_le(pWav, riffChunkSize);
}
/* The "data" chunk size. */
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) {
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, DRWAV_SEEK_SET)) {
drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
drwav__write_u64ne_to_le(pWav, dataChunkSize);
}
@ -5592,13 +5749,13 @@ DRWAV_API drwav_result drwav_uninit(drwav* pWav)
int ds64BodyPos = 12 + 8;
/* The "RIFF" chunk size. */
if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) {
if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, DRWAV_SEEK_SET)) {
drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
drwav__write_u64ne_to_le(pWav, riffChunkSize);
}
/* The "data" chunk size. */
if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) {
if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, DRWAV_SEEK_SET)) {
drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize);
drwav__write_u64ne_to_le(pWav, dataChunkSize);
}
@ -5663,7 +5820,7 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu
bytesToSeek = 0x7FFFFFFF;
}
if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) {
if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, DRWAV_SEEK_CUR) == DRWAV_FALSE) {
break;
}
@ -5810,7 +5967,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)
return DRWAV_FALSE; /* No seeking in write mode. */
}
if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, DRWAV_SEEK_SET)) {
return DRWAV_FALSE;
}
@ -5928,7 +6085,7 @@ DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetF
while (offset > 0) {
int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
if (!pWav->onSeek(pWav->pUserData, offset32, DRWAV_SEEK_CUR)) {
return DRWAV_FALSE;
}
@ -6101,6 +6258,13 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
{
drwav_uint64 totalFramesRead = 0;
static drwav_int32 adaptationTable[] = {
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
DRWAV_ASSERT(pWav != NULL);
DRWAV_ASSERT(framesToRead > 0);
@ -6126,6 +6290,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.cachedFrameCount = 2;
/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table)) {
return totalFramesRead; /* Invalid file. */
}
} else {
/* Stereo. */
drwav_uint8 header[14];
@ -6148,6 +6317,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
pWav->msadpcm.cachedFrameCount = 2;
/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Invalid file. */
}
}
}
@ -6181,13 +6355,6 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
if (pWav->msadpcm.bytesRemainingInBlock == 0) {
continue;
} else {
static drwav_int32 adaptationTable[] = {
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
drwav_uint8 nibbles;
drwav_int32 nibble0;
drwav_int32 nibble1;
@ -6320,7 +6487,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin
pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
if (header[2] >= drwav_countof(stepTable)) {
pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, DRWAV_SEEK_CUR);
pWav->ima.bytesRemainingInBlock = 0;
return totalFramesRead; /* Invalid data. */
}
@ -6338,7 +6505,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin
pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) {
pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, DRWAV_SEEK_CUR);
pWav->ima.bytesRemainingInBlock = 0;
return totalFramesRead; /* Invalid data. */
}
@ -8006,7 +8173,7 @@ DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, uns
DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
{
drwav wav;
@ -8020,14 +8187,14 @@ DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead
*totalFrameCountOut = 0;
}
if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
return NULL;
}
return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
}
DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
{
drwav wav;
@ -8041,14 +8208,14 @@ DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwa
*totalFrameCountOut = 0;
}
if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
return NULL;
}
return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
}
DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
{
drwav wav;
@ -8062,7 +8229,7 @@ DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead
*totalFrameCountOut = 0;
}
if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
return NULL;
}
@ -8350,6 +8517,27 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
/*
REVISION HISTORY
================
v0.14.0 - TBD
- API CHANGE: Seek origin enums have been renamed to the following:
- drwav_seek_origin_start -> DRWAV_SEEK_SET
- drwav_seek_origin_current -> DRWAV_SEEK_CUR
- DRWAV_SEEK_END (new)
- API CHANGE: A new seek origin has been added to allow seeking from the end of the file. If you implement your own `onSeek` callback, you must now handle `DRWAV_SEEK_END`. If you only use `*_init_file()` or `*_init_memory()`, you need not change anything.
- API CHANGE: An `onTell` callback has been added to the following functions:
- drwav_init()
- drwav_init_ex()
- drwav_init_with_metadata()
- drwav_open_and_read_pcm_frames_s16()
- drwav_open_and_read_pcm_frames_f32()
- drwav_open_and_read_pcm_frames_s32()
- API CHANGE: The `firstSampleByteOffset`, `lastSampleByteOffset` and `sampleByteOffset` members of `drwav_cue_point` have been renamed to `firstSampleOffset`, `lastSampleOffset` and `sampleOffset`, respectively.
- Fix a static analysis warning.
- Fix compilation for AIX OS.
v0.13.17 - 2024-12-17
- Fix a possible crash when reading from MS-ADPCM encoded files.
- Improve detection of ARM64EC
v0.13.16 - 2024-02-27
- Fix a Wdouble-promotion warning.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -90,7 +90,7 @@
#define RPRAND_FREE(ptr) free(ptr)
#endif
// Simple log system to avoid RPNG_LOG() calls if required
// Simple log system to avoid log calls if required
// NOTE: Avoiding those calls, also avoids const strings memory usage
#define RPRAND_SHOW_LOG_INFO
#if defined(RPNG_SHOW_LOG_INFO)

View File

@ -70,6 +70,16 @@ typedef struct {
EGLConfig config; // Graphic config
} PlatformData;
typedef struct {
// Store data for both Hover and Touch events
// Used to ignore Hover events which are interpreted as Touch events
int32_t pointCount; // Number of touch points active
int32_t pointId[MAX_TOUCH_POINTS]; // Point identifiers
Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen
int32_t hoverPoints[MAX_TOUCH_POINTS]; // Hover Points
} TouchRaw;
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
@ -246,6 +256,8 @@ static const KeyboardKey mapKeycode[KEYCODE_MAP_SIZE] = {
KEY_KP_EQUAL // AKEYCODE_NUMPAD_EQUALS
};
static TouchRaw touchRaw = { 0 };
//----------------------------------------------------------------------------------
// Module Internal Functions Declaration
//----------------------------------------------------------------------------------
@ -801,6 +813,8 @@ int InitPlatform(void)
}
}
for (int i = 0; i < MAX_TOUCH_POINTS; i++) touchRaw.hoverPoints[i] = -1;
return 0;
}
@ -1269,25 +1283,85 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
}
// Register touch points count
CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event);
touchRaw.pointCount = AMotionEvent_getPointerCount(event);
for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++)
{
// Register touch points id
CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i);
touchRaw.pointId[i] = AMotionEvent_getPointerId(event, i);
// Register touch points position
CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
touchRaw.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
// Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height
float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x)/(float)CORE.Window.display.width;
float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y)/(float)CORE.Window.display.height;
CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2;
CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2;
touchRaw.position[i].x = touchRaw.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2;
touchRaw.position[i].y = touchRaw.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2;
}
int32_t action = AMotionEvent_getAction(event);
unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
if (flags == AMOTION_EVENT_ACTION_HOVER_ENTER)
{
// The new pointer is hover
// So add it to hoverPoints
for (int i = 0; i < MAX_TOUCH_POINTS; i++)
{
if (touchRaw.hoverPoints[i] == -1)
{
touchRaw.hoverPoints[i] = touchRaw.pointId[pointerIndex];
break;
}
}
}
if ((flags == AMOTION_EVENT_ACTION_POINTER_UP) || (flags == AMOTION_EVENT_ACTION_UP) || (flags == AMOTION_EVENT_ACTION_HOVER_EXIT))
{
// One of the touchpoints is released, remove it from touch point arrays
if (flags == AMOTION_EVENT_ACTION_HOVER_EXIT)
{
// If the touchPoint is hover, remove it from hoverPoints
for (int i = 0; i < MAX_TOUCH_POINTS; i++)
{
if (touchRaw.hoverPoints[i] == touchRaw.pointId[pointerIndex])
{
touchRaw.hoverPoints[i] = -1;
break;
}
}
}
for (int i = pointerIndex; (i < touchRaw.pointCount - 1) && (i < MAX_TOUCH_POINTS - 1); i++)
{
touchRaw.pointId[i] = touchRaw.pointId[i+1];
touchRaw.position[i] = touchRaw.position[i+1];
}
touchRaw.pointCount--;
}
int pointCount = 0;
for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++)
{
// If the touchPoint is hover, Ignore it
bool hover = false;
for (int j = 0; j < MAX_TOUCH_POINTS; j++)
{
// Check if the touchPoint is in hoverPointers
if (touchRaw.hoverPoints[j] == touchRaw.pointId[i])
{
hover = true;
break;
}
}
if (hover) continue;
CORE.Input.Touch.pointId[pointCount] = touchRaw.pointId[i];
CORE.Input.Touch.position[pointCount] = touchRaw.position[i];
pointCount++;
}
CORE.Input.Touch.pointCount = pointCount;
#if defined(SUPPORT_GESTURES_SYSTEM)
GestureEvent gestureEvent = { 0 };
@ -1312,20 +1386,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
ProcessGestureEvent(gestureEvent);
#endif
int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP)
{
// One of the touchpoints is released, remove it from touch point arrays
for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++)
{
CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1];
CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1];
}
CORE.Input.Touch.pointCount--;
}
// When all touchpoints are tapped and released really quickly, this event is generated
if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0;

View File

@ -1,6 +1,6 @@
/**********************************************************************************************
*
* rcore_desktop - Functions to manage window, graphics device and inputs
* rcore_desktop_glfw - Functions to manage window, graphics device and inputs
*
* PLATFORM: DESKTOP: GLFW
* - Windows (Win32, Win64)
@ -130,9 +130,9 @@ static void CursorEnterCallback(GLFWwindow *window, int enter);
static void JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback
// Wrappers used by glfwInitAllocator
static void* AllocateWrapper(size_t size, void* user); // GLFW3 GLFWallocatefun, wrapps around RL_MALLOC macro
static void* ReallocateWrapper(void* block, size_t size, void* user); // GLFW3 GLFWreallocatefun, wrapps around RL_MALLOC macro
static void DeallocateWrapper(void* block, void* user); // GLFW3 GLFWdeallocatefun, wraps around RL_FREE macro
static void *AllocateWrapper(size_t size, void *user); // GLFW3 GLFWallocatefun, wrapps around RL_MALLOC macro
static void *ReallocateWrapper(void *block, size_t size, void *user); // GLFW3 GLFWreallocatefun, wrapps around RL_MALLOC macro
static void DeallocateWrapper(void *block, void *user); // GLFW3 GLFWdeallocatefun, wraps around RL_FREE macro
//----------------------------------------------------------------------------------
// Module Functions Declaration
@ -576,7 +576,7 @@ void SetWindowIcons(Image *images, int count)
else
{
int valid = 0;
GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage));
GLFWimage *icons = (GLFWimage *)RL_CALLOC(count, sizeof(GLFWimage));
for (int i = 0; i < count; i++)
{
@ -1238,7 +1238,7 @@ void PollInputEvents(void)
}
}
// Get current axis state
// Get current state of axes
const float *axes = state.axes;
for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1); k++)
@ -1246,7 +1246,7 @@ void PollInputEvents(void)
CORE.Input.Gamepad.axisState[i][k] = axes[k];
}
// Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis)
// Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather as axes)
if (CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f)
{
CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = 1;
@ -1300,17 +1300,17 @@ static void SetDimensionsFromMonitor(GLFWmonitor *monitor)
// We need to provide these because GLFWallocator expects function pointers with specific signatures.
// Similar wrappers exist in utils.c but we cannot reuse them here due to declaration mismatch.
// https://www.glfw.org/docs/latest/intro_guide.html#init_allocator
static void* AllocateWrapper(size_t size, void* user)
static void *AllocateWrapper(size_t size, void *user)
{
(void)user;
return RL_MALLOC(size);
}
static void* ReallocateWrapper(void* block, size_t size, void* user)
static void *ReallocateWrapper(void *block, size_t size, void *user)
{
(void)user;
return RL_REALLOC(block, size);
}
static void DeallocateWrapper(void* block, void* user)
static void DeallocateWrapper(void *block, void *user)
{
(void)user;
RL_FREE(block);
@ -1327,6 +1327,7 @@ int InitPlatform(void)
.reallocate = ReallocateWrapper,
.user = NULL, // RL_*ALLOC macros are not capable of handling user-provided data
};
glfwInitAllocator(&allocator);
#if defined(__APPLE__)
@ -1696,6 +1697,8 @@ int InitPlatform(void)
// Retrieve gamepad names
for (int i = 0; i < MAX_GAMEPADS; i++)
{
// WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH,
// we can get a not-NULL terminated string, so, we only copy up to (MAX_GAMEPAD_NAME_LENGTH - 1)
if (glfwJoystickPresent(i)) strncpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i), MAX_GAMEPAD_NAME_LENGTH - 1);
}
//----------------------------------------------------------------------------
@ -1752,6 +1755,10 @@ static void ErrorCallback(int error, const char *description)
// NOTE: Window resizing not enabled by default, use SetConfigFlags()
static void WindowSizeCallback(GLFWwindow *window, int width, int height)
{
// WARNING: On window minimization, callback is called,
// but we don't want to change internal screen values, it breaks things
if ((width == 0) || (height == 0)) return;
// Reset viewport and projection matrix for new size
SetupViewport(width, height);
@ -1969,6 +1976,9 @@ static void JoystickCallback(int jid, int event)
{
if (event == GLFW_CONNECTED)
{
// WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH,
// we can get a not-NULL terminated string, so, we clean destination and only copy up to -1
memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH);
strncpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid), MAX_GAMEPAD_NAME_LENGTH - 1);
}
else if (event == GLFW_DISCONNECTED)

View File

@ -175,7 +175,9 @@ static const unsigned short keyMappingRGFW[] = {
[RGFW_superL] = KEY_LEFT_SUPER,
#ifndef RGFW_MACOS
[RGFW_shiftR] = KEY_RIGHT_SHIFT,
[RGFW_controlR] = KEY_RIGHT_CONTROL,
[RGFW_altR] = KEY_RIGHT_ALT,
[RGFW_superR] = KEY_RIGHT_SUPER,
#endif
[RGFW_space] = KEY_SPACE,
@ -583,7 +585,7 @@ void SetWindowPosition(int x, int y)
// Set monitor for the current window
void SetWindowMonitor(int monitor)
{
RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors()[monitor]);
RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors(NULL)[monitor]);
}
// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
@ -640,7 +642,7 @@ int GetMonitorCount(void)
#define MAX_MONITORS_SUPPORTED 6
int count = MAX_MONITORS_SUPPORTED;
RGFW_monitor *mons = RGFW_getMonitors();
RGFW_monitor *mons = RGFW_getMonitors(NULL);
for (int i = 0; i < 6; i++)
{
@ -657,7 +659,7 @@ int GetMonitorCount(void)
// Get current monitor where window is placed
int GetCurrentMonitor(void)
{
RGFW_monitor *mons = RGFW_getMonitors();
RGFW_monitor *mons = RGFW_getMonitors(NULL);
RGFW_monitor mon = { 0 };
if (platform.window) mon = RGFW_window_getMonitor(platform.window);
@ -674,7 +676,7 @@ int GetCurrentMonitor(void)
// Get selected monitor position
Vector2 GetMonitorPosition(int monitor)
{
RGFW_monitor *mons = RGFW_getMonitors();
RGFW_monitor *mons = RGFW_getMonitors(NULL);
return (Vector2){ (float)mons[monitor].x, (float)mons[monitor].y };
}
@ -682,7 +684,7 @@ Vector2 GetMonitorPosition(int monitor)
// Get selected monitor width (currently used by monitor)
int GetMonitorWidth(int monitor)
{
RGFW_monitor *mons = RGFW_getMonitors();
RGFW_monitor *mons = RGFW_getMonitors(NULL);
return mons[monitor].mode.area.w;
}
@ -690,7 +692,7 @@ int GetMonitorWidth(int monitor)
// Get selected monitor height (currently used by monitor)
int GetMonitorHeight(int monitor)
{
RGFW_monitor *mons = RGFW_getMonitors();
RGFW_monitor *mons = RGFW_getMonitors(NULL);
return mons[monitor].mode.area.h;
}
@ -698,7 +700,7 @@ int GetMonitorHeight(int monitor)
// Get selected monitor physical width in millimetres
int GetMonitorPhysicalWidth(int monitor)
{
RGFW_monitor *mons = RGFW_getMonitors();
RGFW_monitor *mons = RGFW_getMonitors(NULL);
return mons[monitor].physW;
}
@ -706,7 +708,7 @@ int GetMonitorPhysicalWidth(int monitor)
// Get selected monitor physical height in millimetres
int GetMonitorPhysicalHeight(int monitor)
{
RGFW_monitor *mons = RGFW_getMonitors();
RGFW_monitor *mons = RGFW_getMonitors(NULL);
return (int)mons[monitor].physH;
}
@ -714,7 +716,7 @@ int GetMonitorPhysicalHeight(int monitor)
// Get selected monitor refresh rate
int GetMonitorRefreshRate(int monitor)
{
RGFW_monitor *mons = RGFW_getMonitors();
RGFW_monitor *mons = RGFW_getMonitors(NULL);
return (int)mons[monitor].mode.refreshRate;
}
@ -722,7 +724,7 @@ int GetMonitorRefreshRate(int monitor)
// Get the human-readable, UTF-8 encoded name of the selected monitor
const char *GetMonitorName(int monitor)
{
RGFW_monitor *mons = RGFW_getMonitors();
RGFW_monitor *mons = RGFW_getMonitors(NULL);
return mons[monitor].name;
}
@ -987,7 +989,7 @@ void PollInputEvents(void)
if ((CORE.Window.eventWaiting) || (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)))
{
RGFW_window_eventWait(platform.window, 0); // Wait for input events: keyboard/mouse/window events (callbacks) -> Update keys state
RGFW_window_eventWait(platform.window, -1); // Wait for input events: keyboard/mouse/window events (callbacks) -> Update keys state
CORE.Time.previous = GetTime();
}
@ -1319,6 +1321,13 @@ int InitPlatform(void)
platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags);
platform.mon.mode.area.w = 0;
if (platform.window != NULL)
{
// NOTE: RGFW's exit key is distinct from raylib's exit key (which can
// be set with SetExitKey()) and defaults to Escape
platform.window->exitKey = RGFW_keyNULL;
}
#ifndef PLATFORM_WEB_RGFW
RGFW_area screenSize = RGFW_getScreenSize();
CORE.Window.display.width = screenSize.w;

View File

@ -424,6 +424,8 @@ void ClosePlatform(void); // Close platform
static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help convert SDL scancodes to raylib key
static int GetCodepointNextSDL(const char *text, int *codepointSize); // Get next codepoint in a byte sequence and bytes processed
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
@ -1601,13 +1603,18 @@ void PollInputEvents(void)
{
// NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
int codepointSize = 0;
// Check if there is space available in the queue
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
{
// Add character (codepoint) to the queue
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = GetCodepointNext(event.text.text, &codepointSize);
#if defined(PLATFORM_DESKTOP_SDL3)
unsigned int textLen = strlen(event.text.text);
unsigned int codepoint = (unsigned int)SDL_StepUTF8(&event.text.text, textLen);
#else
int codepointSize = 0;
int codepoint = GetCodepointNextSDL(event.text.text, &codepointSize);
#endif
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint;
CORE.Input.Keyboard.charPressedQueueCount++;
}
} break;
@ -1697,8 +1704,8 @@ void PollInputEvents(void)
CORE.Input.Gamepad.axisCount[jid] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[jid]));
CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH);
strncpy(CORE.Input.Gamepad.name[jid], SDL_GameControllerNameForIndex(jid), MAX_GAMEPAD_NAME_LENGTH - 1);
CORE.Input.Gamepad.name[jid][MAX_GAMEPAD_NAME_LENGTH - 1] = '\0';
}
else
{
@ -1825,7 +1832,7 @@ void PollInputEvents(void)
{
if (platform.gamepadId[i] == event.jaxis.which)
{
// SDL axis value range is -32768 to 32767, we normalize it to RayLib's -1.0 to 1.0f range
// SDL axis value range is -32768 to 32767, we normalize it to raylib's -1.0 to 1.0f range
float value = event.jaxis.value/(float)32767;
CORE.Input.Gamepad.axisState[i][axis] = value;
@ -2093,4 +2100,42 @@ static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode)
return KEY_NULL; // No equivalent key in Raylib
}
// EOF
// Get next codepoint in a byte sequence and bytes processed
static int GetCodepointNextSDL(const char *text, int *codepointSize)
{
const char *ptr = text;
int codepoint = 0x3f; // Codepoint (defaults to '?')
*codepointSize = 1;
// Get current codepoint and bytes processed
if (0xf0 == (0xf8 & ptr[0]))
{
// 4 byte UTF-8 codepoint
if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } // 10xxxxxx checks
codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
*codepointSize = 4;
}
else if (0xe0 == (0xf0 & ptr[0]))
{
// 3 byte UTF-8 codepoint */
if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } // 10xxxxxx checks
codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
*codepointSize = 3;
}
else if (0xc0 == (0xe0 & ptr[0]))
{
// 2 byte UTF-8 codepoint
if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } // 10xxxxxx checks
codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
*codepointSize = 2;
}
else if (0x00 == (0x80 & ptr[0]))
{
// 1 byte UTF-8 codepoint
codepoint = ptr[0];
*codepointSize = 1;
}
return codepoint;
}

View File

@ -124,7 +124,7 @@ typedef struct {
// Gamepad data
int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor
int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXIS][2]; // [0] = min, [1] = range value of the axis
int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXES][2]; // [0] = min, [1] = range value of the axes
int gamepadAbsAxisMap[MAX_GAMEPADS][ABS_CNT]; // Maps the axes gamepads from the evdev api to a sequential one
int gamepadCount; // The number of gamepads registered
} PlatformData;
@ -948,7 +948,7 @@ int InitPlatform(void)
TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs);
EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs));
EGLConfig *configs = (EGLConfig *)RL_CALLOC(numConfigs, sizeof(*configs));
if (!configs)
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
@ -1374,7 +1374,7 @@ static void InitEvdevInput(void)
if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*"
(strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*"
{
sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
snprintf(path, MAX_FILEPATH_LENGTH, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
ConfigureEvdevDevice(path); // Configure the device if appropriate
}
}
@ -1460,7 +1460,7 @@ static void ConfigureEvdevDevice(char *device)
// matter if we support them
else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
// If any of the common joystick axis is present, we assume it's a gamepad
// If any of the common joystick axes are present, we assume it's a gamepad
else
{
for (int axis = (hasAbsXY? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++)
@ -1546,7 +1546,7 @@ static void ConfigureEvdevDevice(char *device)
if (absAxisCount > 0)
{
// TODO / NOTE
// So gamepad axis (as in the actual linux joydev.c) are just simply enumerated
// So gamepad axes (as in the actual linux joydev.c) are just simply enumerated
// and (at least for some input drivers like xpat) it's convention to use
// ABS_X, ABX_Y for one joystick ABS_RX, ABS_RY for the other and the Z axes for the
// shoulder buttons
@ -1681,7 +1681,7 @@ static void PollGamepadEvents(void)
TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: Axis: %2i Value: %i", i, axisRaylib, event.value);
if (axisRaylib < MAX_GAMEPAD_AXIS)
if (axisRaylib < MAX_GAMEPAD_AXES)
{
int min = platform.gamepadAbsAxisRange[i][event.code][0];
int range = platform.gamepadAbsAxisRange[i][event.code][1];

View File

@ -1081,7 +1081,7 @@ void PollInputEvents(void)
}
// Register axis data for every connected gamepad
for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++)
for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXES); j++)
{
CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j];
}
@ -1746,7 +1746,7 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE
if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS))
{
CORE.Input.Gamepad.ready[gamepadEvent->index] = true;
sprintf(CORE.Input.Gamepad.name[gamepadEvent->index], "%s", gamepadEvent->id);
snprintf(CORE.Input.Gamepad.name[gamepadEvent->index], MAX_GAMEPAD_NAME_LENGTH, "%s", gamepadEvent->id);
}
else CORE.Input.Gamepad.ready[gamepadEvent->index] = false;

View File

@ -81,7 +81,7 @@
#include "utils.h" // Required for: fopen() Android mapping
#endif
#if defined(SUPPORT_MODULE_RAUDIO)
#if defined(SUPPORT_MODULE_RAUDIO) || defined(RAUDIO_STANDALONE)
#if defined(_WIN32)
// To avoid conflicting windows.h symbols with raylib, some flags are defined
@ -1037,6 +1037,8 @@ void UnloadSoundAlias(Sound alias)
}
// Update sound buffer with new data
// NOTE 1: data format must match sound.stream.sampleSize
// NOTE 2: frameCount must not exceed sound.frameCount
void UpdateSound(Sound sound, const void *data, int frameCount)
{
if (sound.stream.buffer != NULL)
@ -1337,7 +1339,7 @@ Music LoadMusicStream(const char *fileName)
#if defined(SUPPORT_FILEFORMAT_WAV)
else if (IsFileExtension(fileName, ".wav"))
{
drwav *ctxWav = RL_CALLOC(1, sizeof(drwav));
drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav));
bool success = drwav_init_file(ctxWav, fileName, NULL);
if (success)
@ -1387,7 +1389,7 @@ Music LoadMusicStream(const char *fileName)
#if defined(SUPPORT_FILEFORMAT_MP3)
else if (IsFileExtension(fileName, ".mp3"))
{
drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3));
drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3));
int result = drmp3_init_file(ctxMp3, fileName, NULL);
if (result > 0)
@ -1478,7 +1480,7 @@ Music LoadMusicStream(const char *fileName)
#if defined(SUPPORT_FILEFORMAT_MOD)
else if (IsFileExtension(fileName, ".mod"))
{
jar_mod_context_t *ctxMod = RL_CALLOC(1, sizeof(jar_mod_context_t));
jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_CALLOC(1, sizeof(jar_mod_context_t));
jar_mod_init(ctxMod);
int result = jar_mod_load_file(ctxMod, fileName);
@ -1529,7 +1531,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
#if defined(SUPPORT_FILEFORMAT_WAV)
else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0))
{
drwav *ctxWav = RL_CALLOC(1, sizeof(drwav));
drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav));
bool success = drwav_init_memory(ctxWav, (const void *)data, dataSize, NULL);
@ -1580,7 +1582,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
#if defined(SUPPORT_FILEFORMAT_MP3)
else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0))
{
drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3));
drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3));
int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL);
if (success)
@ -1869,6 +1871,7 @@ void SeekMusicStream(Music music, float position)
void UpdateMusicStream(Music music)
{
if (music.stream.buffer == NULL) return;
if (!music.stream.buffer->playing) return;
ma_mutex_lock(&AUDIO.System.lock);
@ -1888,14 +1891,28 @@ void UpdateMusicStream(Music music)
// Check both sub-buffers to check if they require refilling
for (int i = 0; i < 2; i++)
{
if (!music.stream.buffer->isSubBufferProcessed[i]) continue; // No refilling required, move to next sub-buffer
unsigned int framesLeft = music.frameCount - music.stream.buffer->framesProcessed; // Frames left to be processed
unsigned int framesToStream = 0; // Total frames to be streamed
if ((framesLeft >= subBufferSizeInFrames) || music.looping) framesToStream = subBufferSizeInFrames;
else framesToStream = framesLeft;
if (framesToStream == 0)
{
// Check if both buffers have been processed
if (music.stream.buffer->isSubBufferProcessed[0] && music.stream.buffer->isSubBufferProcessed[1])
{
ma_mutex_unlock(&AUDIO.System.lock);
StopMusicStream(music);
return;
}
ma_mutex_unlock(&AUDIO.System.lock);
return;
}
if (!music.stream.buffer->isSubBufferProcessed[i]) continue; // No refilling required, move to next sub-buffer
int frameCountStillNeeded = framesToStream;
int frameCountReadTotal = 0;
@ -2008,19 +2025,6 @@ void UpdateMusicStream(Music music)
}
UpdateAudioStreamInLockedState(music.stream, AUDIO.System.pcmBuffer, framesToStream);
music.stream.buffer->framesProcessed = music.stream.buffer->framesProcessed%music.frameCount;
if (framesLeft <= subBufferSizeInFrames)
{
if (!music.looping)
{
ma_mutex_unlock(&AUDIO.System.lock);
// Streaming is ending, we filled latest frames from input
StopMusicStream(music);
return;
}
}
}
ma_mutex_unlock(&AUDIO.System.lock);
@ -2083,8 +2087,12 @@ float GetMusicTimePlayed(Music music)
int subBufferSize = (int)music.stream.buffer->sizeInFrames/2;
int framesInFirstBuffer = music.stream.buffer->isSubBufferProcessed[0]? 0 : subBufferSize;
int framesInSecondBuffer = music.stream.buffer->isSubBufferProcessed[1]? 0 : subBufferSize;
int framesInBuffers = framesInFirstBuffer + framesInSecondBuffer;
if (framesInBuffers > music.frameCount) {
if (!music.looping) framesInBuffers = music.frameCount;
}
int framesSentToMix = music.stream.buffer->frameCursorPos%subBufferSize;
int framesPlayed = (framesProcessed - framesInFirstBuffer - framesInSecondBuffer + framesSentToMix)%(int)music.frameCount;
int framesPlayed = (framesProcessed - framesInBuffers + framesSentToMix)%(int)music.frameCount;
if (framesPlayed < 0) framesPlayed += music.frameCount;
secondsPlayed = (float)framesPlayed/music.stream.sampleRate;
ma_mutex_unlock(&AUDIO.System.lock);
@ -2681,8 +2689,7 @@ static void UpdateAudioStreamInLockedState(AudioStream stream, const void *data,
ma_uint32 subBufferSizeInFrames = stream.buffer->sizeInFrames/2;
unsigned char *subBuffer = stream.buffer->data + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate);
// Total frames processed in buffer is always the complete size, filled with 0 if required
stream.buffer->framesProcessed += subBufferSizeInFrames;
stream.buffer->framesProcessed += frameCount;
// Does this API expect a whole buffer to be updated in one go?
// Assuming so, but if not will need to change this logic

View File

@ -743,7 +743,7 @@ typedef enum {
GAMEPAD_BUTTON_RIGHT_THUMB // Gamepad joystick pressed button right
} GamepadButton;
// Gamepad axis
// Gamepad axes
typedef enum {
GAMEPAD_AXIS_LEFT_X = 0, // Gamepad left stick X axis
GAMEPAD_AXIS_LEFT_Y = 1, // Gamepad left stick Y axis
@ -954,7 +954,7 @@ typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args);
typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load binary data
typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize); // FileIO: Save binary data
typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data
typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data
typedef bool (*SaveFileTextCallback)(const char *fileName, const char *text); // FileIO: Save text data
//------------------------------------------------------------------------------------
// Global Variables Definition
@ -1125,7 +1125,7 @@ RLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save
RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName); // Export data to code (.h), returns true on success
RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string
RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText()
RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success
RLAPI bool SaveFileText(const char *fileName, const char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success
//------------------------------------------------------------------
// File system functions
@ -1155,8 +1155,8 @@ RLAPI long GetFileModTime(const char *fileName); // Get file mo
// Compression/Encoding functionality
RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree()
RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree()
RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree()
RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree()
RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string (includes NULL terminator), memory must be MemFree()
RLAPI unsigned char *DecodeDataBase64(const char *text, int *outputSize); // Decode Base64 string (expected NULL terminated), memory must be MemFree()
RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize); // Compute CRC32 hash code
RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize); // Compute MD5 hash code, returns static int[4] (16 bytes)
RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize); // Compute SHA1 hash code, returns static int[5] (20 bytes)
@ -1194,8 +1194,8 @@ RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a game
RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once
RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed
RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed
RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad
RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis
RLAPI int GetGamepadAxisCount(int gamepad); // Get axis count for a gamepad
RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get movement value for a gamepad axis
RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB)
RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration); // Set gamepad vibration for both motors (duration in seconds)
@ -1266,7 +1266,9 @@ RLAPI void DrawCircleV(Vector2 center, float radius, Color color);
RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline
RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version)
RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse
RLAPI void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse (Vector version)
RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline
RLAPI void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse outline (Vector version)
RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring
RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline
RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle
@ -1275,7 +1277,7 @@ RLAPI void DrawRectangleRec(Rectangle rec, Color color);
RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters
RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom); // Draw a vertical-gradient-filled rectangle
RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right); // Draw a horizontal-gradient-filled rectangle
RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors
RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color bottomRight, Color topRight); // Draw a gradient-filled rectangle with custom vertex colors
RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline
RLAPI void DrawRectangleLinesEx(Rectangle rec, float 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
@ -1408,8 +1410,8 @@ RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color c
RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle within an image
RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image
RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline within an image
RLAPI void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center)
RLAPI void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image
RLAPI void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center)
RLAPI void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image
RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source)
RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination)
RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination)
@ -1424,8 +1426,8 @@ RLAPI bool IsTextureValid(Texture2D texture);
RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM)
RLAPI bool IsRenderTextureValid(RenderTexture2D target); // Check if a render texture is valid (loaded in GPU)
RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM)
RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data
RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data
RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data (pixels should be able to fill texture)
RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data (pixels and rec should fit in texture)
// Texture configuration functions
RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture
@ -1646,7 +1648,7 @@ RLAPI Sound LoadSound(const char *fileName); // Load so
RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data
RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data
RLAPI bool IsSoundValid(Sound sound); // Checks if a sound is valid (data loaded and buffers initialized)
RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data
RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data (data and frame count should fit in sound)
RLAPI void UnloadWave(Wave wave); // Unload wave data
RLAPI void UnloadSound(Sound sound); // Unload sound
RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data)

View File

@ -235,10 +235,10 @@ __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod)
#define MAX_GAMEPADS 4 // Maximum number of gamepads supported
#endif
#ifndef MAX_GAMEPAD_NAME_LENGTH
#define MAX_GAMEPAD_NAME_LENGTH 128 // Maximum number of characters of gamepad name (byte size)
#define MAX_GAMEPAD_NAME_LENGTH 128 // Maximum number of characters in a gamepad name (byte size)
#endif
#ifndef MAX_GAMEPAD_AXIS
#define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad)
#ifndef MAX_GAMEPAD_AXES
#define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad)
#endif
#ifndef MAX_GAMEPAD_BUTTONS
#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
@ -354,12 +354,12 @@ typedef struct CoreData {
} Touch;
struct {
int lastButtonPressed; // Register last gamepad button pressed
int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis
int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axes
bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready
char name[MAX_GAMEPADS][MAX_GAMEPAD_NAME_LENGTH]; // Gamepad name holder
char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state
char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state
float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXES]; // Gamepad axes state
} Gamepad;
} Input;
@ -523,25 +523,25 @@ const char *TextFormat(const char *text, ...); // Formatting of tex
#define PLATFORM_DESKTOP_GLFW
#endif
// We're using `#pragma message` because `#warning` is not adopted by MSVC.
// We're using '#pragma message' because '#warning' is not adopted by MSVC
#if defined(SUPPORT_CLIPBOARD_IMAGE)
#if !defined(SUPPORT_MODULE_RTEXTURES)
#pragma message ("Warning: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly")
#pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly")
#endif
// It's nice to have support Bitmap on Linux as well, but not as necessary as Windows
#if !defined(SUPPORT_FILEFORMAT_BMP) && defined(_WIN32)
#pragma message ("Warning: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows")
#pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows")
#endif
// From what I've tested applications on Wayland saves images on clipboard as PNG.
// From what I've tested applications on Wayland saves images on clipboard as PNG
#if (!defined(SUPPORT_FILEFORMAT_PNG) || !defined(SUPPORT_FILEFORMAT_JPG)) && !defined(_WIN32)
#pragma message ("Warning: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG")
#pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG")
#endif
// Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined.
// Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined
// #if !defined(STBI_REQUIRED)
// #pragma message ("Warning: "STBI_REQUIRED is not defined, that means we can't load images from clipbard"
// #pragma message ("WARNING: "STBI_REQUIRED is not defined, that means we can't load images from clipbard"
// #endif
#endif // SUPPORT_CLIPBOARD_IMAGE
@ -2329,9 +2329,12 @@ FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool
// WARNING: files.count is not reseted to 0 after unloading
void UnloadDirectoryFiles(FilePathList files)
{
for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
if (files.paths != NULL)
{
for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
RL_FREE(files.paths);
RL_FREE(files.paths);
}
}
// Create directories (including full path requested), returns 0 on success
@ -2512,7 +2515,7 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa
#if defined(SUPPORT_COMPRESSION_API)
// Compress data and generate a valid DEFLATE stream
struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB
struct sdefl *sdefl = (struct sdefl *)RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB
int bounds = sdefl_bound(dataSize);
compData = (unsigned char *)RL_CALLOC(bounds, 1);
@ -2551,96 +2554,112 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i
}
// Encode data to Base64 string
// NOTE: Returned string includes NULL terminator, considered on outputSize
char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
{
static const unsigned char base64encodeTable[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
// Base64 conversion table from RFC 4648 [0..63]
// NOTE: They represent 64 values (6 bits), to encode 3 bytes of data into 4 "sixtets" (6bit characters)
static const char base64EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int modTable[] = { 0, 2, 1 };
// Compute expected size and padding
int paddedSize = dataSize;
while (paddedSize%3 != 0) paddedSize++; // Padding bytes to round 4*(dataSize/3) to 4 bytes
int estimatedOutputSize = 4*(paddedSize/3);
int padding = paddedSize - dataSize;
*outputSize = 4*((dataSize + 2)/3);
// Adding null terminator to string
estimatedOutputSize += 1;
char *encodedData = (char *)RL_MALLOC(*outputSize);
// Load some memory to store encoded string
char *encodedData = (char *)RL_CALLOC(estimatedOutputSize, 1);
if (encodedData == NULL) return NULL;
if (encodedData == NULL) return NULL; // Security check
for (int i = 0, j = 0; i < dataSize;)
int outputCount = 0;
for (int i = 0; i < dataSize;)
{
unsigned int octetA = (i < dataSize)? (unsigned char)data[i++] : 0;
unsigned int octetB = (i < dataSize)? (unsigned char)data[i++] : 0;
unsigned int octetC = (i < dataSize)? (unsigned char)data[i++] : 0;
unsigned int octetA = 0;
unsigned int octetB = 0;
unsigned int octetC = 0;
unsigned int octetPack = 0;
unsigned int triple = (octetA << 0x10) + (octetB << 0x08) + octetC;
octetA = data[i]; // Generates 2 sextets
octetB = ((i + 1) < dataSize)? data[i + 1] : 0; // Generates 3 sextets
octetC = ((i + 2) < dataSize)? data[i + 2] : 0; // Generates 4 sextets
encodedData[j++] = base64encodeTable[(triple >> 3*6) & 0x3F];
encodedData[j++] = base64encodeTable[(triple >> 2*6) & 0x3F];
encodedData[j++] = base64encodeTable[(triple >> 1*6) & 0x3F];
encodedData[j++] = base64encodeTable[(triple >> 0*6) & 0x3F];
octetPack = (octetA << 16) | (octetB << 8) | octetC;
encodedData[outputCount + 0] = (unsigned char)(base64EncodeTable[(octetPack >> 18) & 0x3f]);
encodedData[outputCount + 1] = (unsigned char)(base64EncodeTable[(octetPack >> 12) & 0x3f]);
encodedData[outputCount + 2] = (unsigned char)(base64EncodeTable[(octetPack >> 6) & 0x3f]);
encodedData[outputCount + 3] = (unsigned char)(base64EncodeTable[octetPack & 0x3f]);
outputCount += 4;
i += 3;
}
for (int i = 0; i < modTable[dataSize%3]; i++) encodedData[*outputSize - 1 - i] = '='; // Padding character
// Add required padding bytes
for (int p = 0; p < padding; p++) encodedData[outputCount - p - 1] = '=';
// Add null terminator to string
encodedData[outputCount] = '\0';
outputCount++;
if (outputCount != estimatedOutputSize) TRACELOG(LOG_WARNING, "BASE64: Output size differs from estimation");
*outputSize = estimatedOutputSize;
return encodedData;
}
// Decode Base64 string data
unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize)
// Decode Base64 string (expected NULL terminated)
unsigned char *DecodeDataBase64(const char *text, int *outputSize)
{
static const unsigned char base64decodeTable[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
// Base64 decode table
// NOTE: Following ASCII order [0..255] assigning the expected sixtet value to
// every character in the corresponding ASCII position
static const unsigned char base64DecodeTable[256] = {
['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7,
['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15,
['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, ['Z'] = 25,
['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33,
['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41,
['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51,
['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63
};
// Get output size of Base64 input data
int outSize = 0;
for (int i = 0; data[4*i] != 0; i++)
// Compute expected size and padding
int dataSize = (int)strlen(text); // WARNING: Expecting NULL terminated strings!
int ending = dataSize - 1;
int padding = 0;
while (text[ending] == '=') { padding++; ending--; }
int estimatedOutputSize = 3*(dataSize/4) - padding;
int maxOutputSize = 3*(dataSize/4);
// Load some memory to store decoded data
// NOTE: Allocated enough size to include padding
unsigned char *decodedData = (unsigned char *)RL_CALLOC(maxOutputSize, 1);
if (decodedData == NULL) return NULL;
int outputCount = 0;
for (int i = 0; i < dataSize;)
{
if (data[4*i + 3] == '=')
{
if (data[4*i + 2] == '=') outSize += 1;
else outSize += 2;
}
else outSize += 3;
// Every 4 sixtets must generate 3 octets
unsigned int sixtetA = base64DecodeTable[(unsigned char)text[i]];
unsigned int sixtetB = base64DecodeTable[(unsigned char)text[i + 1]];
unsigned int sixtetC = ((unsigned char)text[i + 2] != '=')? base64DecodeTable[(unsigned char)text[i + 2]] : 0;
unsigned int sixtetD = ((unsigned char)text[i + 3] != '=')? base64DecodeTable[(unsigned char)text[i + 3]] : 0;
unsigned int octetPack = (sixtetA << 18) | (sixtetB << 12) | (sixtetC << 6) | sixtetD;
decodedData[outputCount + 0] = (octetPack >> 16) & 0xff;
decodedData[outputCount + 1] = (octetPack >> 8) & 0xff;
decodedData[outputCount + 2] = octetPack & 0xff;
outputCount += 3;
i += 4;
}
// Allocate memory to store decoded Base64 data
unsigned char *decodedData = (unsigned char *)RL_MALLOC(outSize);
if (estimatedOutputSize != (outputCount - padding)) TRACELOG(LOG_WARNING, "BASE64: Decoded size differs from estimation");
for (int i = 0; i < outSize/3; i++)
{
unsigned char a = base64decodeTable[(int)data[4*i]];
unsigned char b = base64decodeTable[(int)data[4*i + 1]];
unsigned char c = base64decodeTable[(int)data[4*i + 2]];
unsigned char d = base64decodeTable[(int)data[4*i + 3]];
decodedData[3*i] = (a << 2) | (b >> 4);
decodedData[3*i + 1] = (b << 4) | (c >> 2);
decodedData[3*i + 2] = (c << 6) | d;
}
if (outSize%3 == 1)
{
int n = outSize/3;
unsigned char a = base64decodeTable[(int)data[4*n]];
unsigned char b = base64decodeTable[(int)data[4*n + 1]];
decodedData[outSize - 1] = (a << 2) | (b >> 4);
}
else if (outSize%3 == 2)
{
int n = outSize/3;
unsigned char a = base64decodeTable[(int)data[4*n]];
unsigned char b = base64decodeTable[(int)data[4*n + 1]];
unsigned char c = base64decodeTable[(int)data[4*n + 2]];
decodedData[outSize - 2] = (a << 2) | (b >> 4);
decodedData[outSize - 1] = (b << 4) | (c >> 2);
}
*outputSize = outSize;
*outputSize = estimatedOutputSize;
return decodedData;
}
@ -2648,38 +2667,38 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize)
unsigned int ComputeCRC32(unsigned char *data, int dataSize)
{
static unsigned int crcTable[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
unsigned int crc = ~0u;
@ -2743,7 +2762,7 @@ unsigned int *ComputeMD5(unsigned char *data, int dataSize)
int newDataSize = ((((dataSize + 8)/64) + 1)*64) - 8;
unsigned char *msg = RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes
unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes
memcpy(msg, data, dataSize);
msg[dataSize] = 128; // Write the '1' bit
@ -2833,7 +2852,7 @@ unsigned int *ComputeSHA1(unsigned char *data, int dataSize)
int newDataSize = ((((dataSize + 8)/64) + 1)*64);
unsigned char *msg = RL_CALLOC(newDataSize, 1); // Initialize with '0' bits
unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize, 1); // Initialize with '0' bits
memcpy(msg, data, dataSize);
msg[dataSize] = 128; // Write the '1' bit
@ -3364,11 +3383,11 @@ int GetGamepadAxisCount(int gamepad)
// Get axis movement vector for a gamepad
float GetGamepadAxisMovement(int gamepad, int axis)
{
float value = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
float value = ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))? -1.0f : 0.0f;
if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS))
if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXES))
{
float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]);
float movement = (value < 0.0f)? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]);
if (movement > value) value = CORE.Input.Gamepad.axisState[gamepad][axis];
}
@ -3753,6 +3772,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const
// Scan all files and directories recursively from a base path
static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter)
{
// WARNING: Path can not be static or it will be reused between recursive function calls!
char path[MAX_FILEPATH_LENGTH] = { 0 };
memset(path, 0, MAX_FILEPATH_LENGTH);
@ -4046,10 +4066,10 @@ static void RecordAutomationEvent(void)
if (currentEventList->count == currentEventList->capacity) return; // Security check
}
for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
for (int axis = 0; axis < MAX_GAMEPAD_AXES; axis++)
{
// Event type: INPUT_GAMEPAD_AXIS_MOTION
float defaultMovement = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
float defaultMovement = ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))? -1.0f : 0.0f;
if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement)
{
currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;

View File

@ -56,8 +56,8 @@
*
* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack
* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported
* #define RL_CULL_DISTANCE_NEAR 0.001 // Default projection matrix near cull distance
* #define RL_CULL_DISTANCE_FAR 10000.0 // Default projection matrix far cull distance
* #define RL_CULL_DISTANCE_NEAR 0.05 // Default projection matrix near cull distance
* #define RL_CULL_DISTANCE_FAR 4000.0 // Default projection matrix far cull distance
*
* When loading a shader, the following vertex attributes and uniform
* location names are tried to be set automatically:
@ -234,10 +234,10 @@
// Projection matrix culling
#ifndef RL_CULL_DISTANCE_NEAR
#define RL_CULL_DISTANCE_NEAR 0.001 // Default near cull distance
#define RL_CULL_DISTANCE_NEAR 0.05 // Default near cull distance
#endif
#ifndef RL_CULL_DISTANCE_FAR
#define RL_CULL_DISTANCE_FAR 10000.0 // Default far cull distance
#define RL_CULL_DISTANCE_FAR 4000.0 // Default far cull distance
#endif
// Texture parameters (equivalent to OpenGL defines)
@ -1748,7 +1748,6 @@ void rlTextureParameters(unsigned int id, int param, int value)
#endif
}
else glTexParameteri(GL_TEXTURE_2D, param, value);
} break;
case RL_TEXTURE_MAG_FILTER:
case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break;
@ -1793,7 +1792,6 @@ void rlCubemapParameters(unsigned int id, int param, int value)
else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)");
}
else glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value);
} break;
case RL_TEXTURE_MAG_FILTER:
case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); break;
@ -1887,7 +1885,7 @@ void rlActiveDrawBuffers(int count)
if (count > 0)
{
if (count > 8) TRACELOG(LOG_WARNING, "GL: Max color buffers limited to 8");
if (count > 8) TRACELOG(RL_LOG_WARNING, "GL: Max color buffers limited to 8");
else
{
unsigned int buffers[8] = {
@ -1904,7 +1902,7 @@ void rlActiveDrawBuffers(int count)
glDrawBuffers(count, buffers);
}
}
else TRACELOG(LOG_WARNING, "GL: One color buffer active by default");
else TRACELOG(RL_LOG_WARNING, "GL: One color buffer active by default");
#endif
}
@ -2112,14 +2110,12 @@ void rlSetBlendMode(int mode)
{
// NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactors()
glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation);
} break;
case RL_BLEND_CUSTOM_SEPARATE:
{
// NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactorsSeparate()
glBlendFuncSeparate(RLGL.State.glBlendSrcFactorRGB, RLGL.State.glBlendDestFactorRGB, RLGL.State.glBlendSrcFactorAlpha, RLGL.State.glBlendDestFactorAlpha);
glBlendEquationSeparate(RLGL.State.glBlendEquationRGB, RLGL.State.glBlendEquationAlpha);
} break;
default: break;
}
@ -2223,10 +2219,10 @@ static void GLAPIENTRY rlDebugMessageCallback(GLenum source, GLenum type, GLuint
default: break;
}
TRACELOG(LOG_WARNING, "GL: OpenGL debug message: %s", message);
TRACELOG(LOG_WARNING, " > Type: %s", msgType);
TRACELOG(LOG_WARNING, " > Source = %s", msgSource);
TRACELOG(LOG_WARNING, " > Severity = %s", msgSeverity);
TRACELOG(RL_LOG_WARNING, "GL: OpenGL debug message: %s", message);
TRACELOG(RL_LOG_WARNING, " > Type: %s", msgType);
TRACELOG(RL_LOG_WARNING, " > Source = %s", msgSource);
TRACELOG(RL_LOG_WARNING, " > Severity = %s", msgSeverity);
}
#endif
@ -2429,7 +2425,7 @@ void rlLoadExtensions(void *loader)
// Get supported extensions list
GLint numExt = 0;
const char **extList = RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB)
const char **extList = (const char **)RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB)
const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string
// NOTE: We have to duplicate string because glGetString() returns a const string
@ -3747,19 +3743,16 @@ void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType,
if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, texId, mipLevel);
else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_RENDERBUFFER, texId);
else if (texType >= RL_ATTACHMENT_CUBEMAP_POSITIVE_X) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_CUBE_MAP_POSITIVE_X + texType, texId, mipLevel);
} break;
case RL_ATTACHMENT_DEPTH:
{
if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel);
else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, texId);
} break;
case RL_ATTACHMENT_STENCIL:
{
if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel);
else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, texId);
} break;
default: break;
}
@ -4185,6 +4178,9 @@ unsigned int rlCompileShader(const char *shaderCode, int type)
RL_FREE(log);
}
// Unload object allocated by glCreateShader(),
// despite failing in the compilation process
glDeleteShader(shader);
shader = 0;
}
else

View File

@ -496,12 +496,18 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
vertices[2] = (Vector3){ cosslice*vertices[2].x - sinslice*vertices[2].z, vertices[2].y, sinslice*vertices[2].x + cosslice*vertices[2].z }; // Rotation matrix around y axis
vertices[3] = (Vector3){ cosslice*vertices[3].x - sinslice*vertices[3].z, vertices[3].y, sinslice*vertices[3].x + cosslice*vertices[3].z };
rlNormal3f(vertices[0].x, vertices[0].y, vertices[0].z);
rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z);
rlNormal3f(vertices[3].x, vertices[3].y, vertices[3].z);
rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z);
rlNormal3f(vertices[1].x, vertices[1].y, vertices[1].z);
rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z);
rlNormal3f(vertices[0].x, vertices[0].y, vertices[0].z);
rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z);
rlNormal3f(vertices[2].x, vertices[2].y, vertices[2].z);
rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z);
rlNormal3f(vertices[3].x, vertices[3].y, vertices[3].z);
rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z);
}
@ -1423,9 +1429,13 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id);
rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices);
if (mesh.animVertices) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices);
else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices);
rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords);
rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals);
if (mesh.normals) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animNormals);
else rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals);
rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors);
rlPushMatrix();
@ -2164,7 +2174,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount)
int result = tinyobj_parse_mtl_file(&mats, &count, fileName);
if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName);
materials = RL_MALLOC(count*sizeof(Material));
materials = (Material *)RL_MALLOC(count*sizeof(Material));
ProcessMaterialsOBJ(materials, mats, count);
tinyobj_materials_free(mats, count);
@ -3598,16 +3608,16 @@ BoundingBox GetMeshBoundingBox(Mesh mesh)
}
// Compute mesh tangents
// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates
// Implementation based on: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html
void GenMeshTangents(Mesh *mesh)
{
if ((mesh->vertices == NULL) || (mesh->texcoords == NULL))
// Check if input mesh data is useful
if ((mesh == NULL) || (mesh->vertices == NULL) || (mesh->texcoords == NULL) || (mesh->normals == NULL))
{
TRACELOG(LOG_WARNING, "MESH: Tangents generation requires texcoord vertex attribute data");
TRACELOG(LOG_WARNING, "MESH: Tangents generation requires vertices, texcoords and normals vertex attribute data");
return;
}
// Allocate or reallocate tangents data
if (mesh->tangents == NULL) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
else
{
@ -3615,26 +3625,51 @@ void GenMeshTangents(Mesh *mesh)
mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
}
Vector3 *tan1 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
Vector3 *tan2 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
// Allocate temporary arrays for tangents calculation
Vector3 *tan1 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3));
Vector3 *tan2 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3));
if (mesh->vertexCount % 3 != 0)
if (tan1 == NULL || tan2 == NULL)
{
TRACELOG(LOG_WARNING, "MESH: vertexCount expected to be a multiple of 3. Expect uninitialized values.");
TRACELOG(LOG_WARNING, "MESH: Failed to allocate temporary memory for tangent calculation");
if (tan1) RL_FREE(tan1);
if (tan2) RL_FREE(tan2);
return;
}
for (int i = 0; i <= mesh->vertexCount - 3; i += 3)
// Process all triangles of the mesh
// 'triangleCount' must be always valid
for (int t = 0; t < mesh->triangleCount; t++)
{
// Get triangle vertices
Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] };
Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] };
Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] };
// Get triangle vertex indices
int i0, i1, i2;
if (mesh->indices != NULL)
{
// Use indices if available
i0 = mesh->indices[t*3 + 0];
i1 = mesh->indices[t*3 + 1];
i2 = mesh->indices[t*3 + 2];
}
else
{
// Sequential access for non-indexed mesh
i0 = t*3 + 0;
i1 = t*3 + 1;
i2 = t*3 + 2;
}
// Get triangle vertices position
Vector3 v1 = { mesh->vertices[i0*3 + 0], mesh->vertices[i0*3 + 1], mesh->vertices[i0*3 + 2] };
Vector3 v2 = { mesh->vertices[i1*3 + 0], mesh->vertices[i1*3 + 1], mesh->vertices[i1*3 + 2] };
Vector3 v3 = { mesh->vertices[i2*3 + 0], mesh->vertices[i2*3 + 1], mesh->vertices[i2*3 + 2] };
// Get triangle texcoords
Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] };
Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] };
Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] };
Vector2 uv1 = { mesh->texcoords[i0*2 + 0], mesh->texcoords[i0*2 + 1] };
Vector2 uv2 = { mesh->texcoords[i1*2 + 0], mesh->texcoords[i1*2 + 1] };
Vector2 uv3 = { mesh->texcoords[i2*2 + 0], mesh->texcoords[i2*2 + 1] };
// Calculate triangle edges
float x1 = v2.x - v1.x;
float y1 = v2.y - v1.y;
float z1 = v2.z - v1.z;
@ -3642,65 +3677,95 @@ void GenMeshTangents(Mesh *mesh)
float y2 = v3.y - v1.y;
float z2 = v3.z - v1.z;
// Calculate texture coordinate differences
float s1 = uv2.x - uv1.x;
float t1 = uv2.y - uv1.y;
float s2 = uv3.x - uv1.x;
float t2 = uv3.y - uv1.y;
// Calculate denominator and check for degenerate UV
float div = s1*t2 - s2*t1;
float r = (div == 0.0f)? 0.0f : 1.0f/div;
float r = (fabsf(div) < 0.0001f)? 0.0f : 1.0f/div;
// Calculate tangent and bitangent directions
Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r };
Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r };
tan1[i + 0] = sdir;
tan1[i + 1] = sdir;
tan1[i + 2] = sdir;
// Accumulate tangents and bitangents for each vertex of the triangle
tan1[i0] = Vector3Add(tan1[i0], sdir);
tan1[i1] = Vector3Add(tan1[i1], sdir);
tan1[i2] = Vector3Add(tan1[i2], sdir);
tan2[i + 0] = tdir;
tan2[i + 1] = tdir;
tan2[i + 2] = tdir;
tan2[i0] = Vector3Add(tan2[i0], tdir);
tan2[i1] = Vector3Add(tan2[i1], tdir);
tan2[i2] = Vector3Add(tan2[i2], tdir);
}
// Compute tangents considering normals
// Calculate final tangents for each vertex
for (int i = 0; i < mesh->vertexCount; i++)
{
Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] };
Vector3 tangent = tan1[i];
// TODO: Review, not sure if tangent computation is right, just used reference proposed maths...
#if defined(COMPUTE_TANGENTS_METHOD_01)
Vector3 tmp = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent)));
tmp = Vector3Normalize(tmp);
mesh->tangents[i*4 + 0] = tmp.x;
mesh->tangents[i*4 + 1] = tmp.y;
mesh->tangents[i*4 + 2] = tmp.z;
mesh->tangents[i*4 + 3] = 1.0f;
#else
Vector3OrthoNormalize(&normal, &tangent);
mesh->tangents[i*4 + 0] = tangent.x;
mesh->tangents[i*4 + 1] = tangent.y;
mesh->tangents[i*4 + 2] = tangent.z;
mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f;
#endif
// Handle zero tangent (can happen with degenerate UVs)
if (Vector3Length(tangent) < 0.0001f)
{
// Create a tangent perpendicular to the normal
if (fabsf(normal.z) > 0.707f) tangent = (Vector3){ 1.0f, 0.0f, 0.0f };
else tangent = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f });
mesh->tangents[i*4 + 0] = tangent.x;
mesh->tangents[i*4 + 1] = tangent.y;
mesh->tangents[i*4 + 2] = tangent.z;
mesh->tangents[i*4 + 3] = 1.0f;
continue;
}
// Gram-Schmidt orthogonalization to make tangent orthogonal to normal
// T_prime = T - N * dot(N, T)
Vector3 orthogonalized = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent)));
// Handle cases where orthogonalized vector is too small
if (Vector3Length(orthogonalized) < 0.0001f)
{
// Create a tangent perpendicular to the normal
if (fabsf(normal.z) > 0.707f) orthogonalized = (Vector3){ 1.0f, 0.0f, 0.0f };
else orthogonalized = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f });
}
else
{
// Normalize the orthogonalized tangent
orthogonalized = Vector3Normalize(orthogonalized);
}
// Store the calculated tangent
mesh->tangents[i*4 + 0] = orthogonalized.x;
mesh->tangents[i*4 + 1] = orthogonalized.y;
mesh->tangents[i*4 + 2] = orthogonalized.z;
// Calculate the handedness (w component)
mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, orthogonalized), tan2[i]) < 0.0f)? -1.0f : 1.0f;
}
// Free temporary arrays
RL_FREE(tan1);
RL_FREE(tan2);
// Update vertex buffers if available
if (mesh->vboId != NULL)
{
if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0)
{
// Update existing vertex buffer
// Update existing tangent vertex buffer
rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0);
}
else
{
// Load a new tangent attributes buffer
// Create new tangent vertex buffer
mesh->vboId[SHADER_LOC_VERTEX_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), false);
}
// Set up vertex attributes for shader
rlEnableVertexArray(mesh->vaoId);
rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0);
rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT);
@ -4577,13 +4642,13 @@ static Model LoadIQM(const char *fileName)
//fileDataPtr += sizeof(IQMHeader); // Move file data pointer
// Meshes data processing
imesh = RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh));
imesh = (IQMMesh *)RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh));
//fseek(iqmFile, iqmHeader->ofs_meshes, SEEK_SET);
//fread(imesh, sizeof(IQMMesh)*iqmHeader->num_meshes, 1, iqmFile);
memcpy(imesh, fileDataPtr + iqmHeader->ofs_meshes, iqmHeader->num_meshes*sizeof(IQMMesh));
model.meshCount = iqmHeader->num_meshes;
model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
model.materialCount = model.meshCount;
model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
@ -4611,24 +4676,24 @@ static Model LoadIQM(const char *fileName)
model.meshes[i].vertexCount = imesh[i].num_vertexes;
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].vertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex positions
model.meshes[i].normals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex normals
model.meshes[i].texcoords = (float *)RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); // Default vertex texcoords
model.meshes[i].boneIds = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char)); // 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].boneIds = (unsigned char *)RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char)); // Up-to 4 bones supported!
model.meshes[i].boneWeights = (float *)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_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short));
model.meshes[i].indices = (unsigned short *)RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short));
// Animated vertex 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_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
model.meshes[i].animVertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
model.meshes[i].animNormals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
}
// Triangles data processing
tri = RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle));
tri = (IQMTriangle *)RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle));
//fseek(iqmFile, iqmHeader->ofs_triangles, SEEK_SET);
//fread(tri, sizeof(IQMTriangle), iqmHeader->num_triangles, iqmFile);
memcpy(tri, fileDataPtr + iqmHeader->ofs_triangles, iqmHeader->num_triangles*sizeof(IQMTriangle));
@ -4650,7 +4715,7 @@ static Model LoadIQM(const char *fileName)
}
// Vertex arrays data processing
va = RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray));
va = (IQMVertexArray *)RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray));
//fseek(iqmFile, iqmHeader->ofs_vertexarrays, SEEK_SET);
//fread(va, sizeof(IQMVertexArray), iqmHeader->num_vertexarrays, iqmFile);
memcpy(va, fileDataPtr + iqmHeader->ofs_vertexarrays, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray));
@ -4661,7 +4726,7 @@ static Model LoadIQM(const char *fileName)
{
case IQM_POSITION:
{
vertex = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
vertex = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(vertex, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
memcpy(vertex, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
@ -4679,7 +4744,7 @@ static Model LoadIQM(const char *fileName)
} break;
case IQM_NORMAL:
{
normal = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
normal = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(normal, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
memcpy(normal, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
@ -4697,7 +4762,7 @@ static Model LoadIQM(const char *fileName)
} break;
case IQM_TEXCOORD:
{
text = RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float));
text = (float *)RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float));
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(text, iqmHeader->num_vertexes*2*sizeof(float), 1, iqmFile);
memcpy(text, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*2*sizeof(float));
@ -4714,7 +4779,7 @@ static Model LoadIQM(const char *fileName)
} break;
case IQM_BLENDINDEXES:
{
blendi = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char));
blendi = (char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char));
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(blendi, iqmHeader->num_vertexes*4*sizeof(char), 1, iqmFile);
memcpy(blendi, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(char));
@ -4731,7 +4796,7 @@ static Model LoadIQM(const char *fileName)
} break;
case IQM_BLENDWEIGHTS:
{
blendw = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
blendw = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
memcpy(blendw, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char));
@ -4748,14 +4813,14 @@ static Model LoadIQM(const char *fileName)
} break;
case IQM_COLOR:
{
color = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
color = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
//fseek(iqmFile, va[i].offset, SEEK_SET);
//fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
memcpy(color, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char));
for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
{
model.meshes[m].colors = RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char));
model.meshes[m].colors = (unsigned char *)RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char));
int vCounter = 0;
for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
@ -4769,14 +4834,14 @@ static Model LoadIQM(const char *fileName)
}
// Bones (joints) data processing
ijoint = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
ijoint = (IQMJoint *)RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
//fseek(iqmFile, iqmHeader->ofs_joints, SEEK_SET);
//fread(ijoint, sizeof(IQMJoint), iqmHeader->num_joints, iqmFile);
memcpy(ijoint, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint));
model.boneCount = iqmHeader->num_joints;
model.bones = RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo));
model.bindPose = RL_MALLOC(iqmHeader->num_joints*sizeof(Transform));
model.bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo));
model.bindPose = (Transform *)RL_MALLOC(iqmHeader->num_joints*sizeof(Transform));
for (unsigned int i = 0; i < iqmHeader->num_joints; i++)
{
@ -4806,7 +4871,7 @@ static Model LoadIQM(const char *fileName)
for (int i = 0; i < model.meshCount; i++)
{
model.meshes[i].boneCount = model.boneCount;
model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
for (int j = 0; j < model.meshes[i].boneCount; j++)
{
@ -4898,36 +4963,36 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
}
// Get bones data
IQMPose *poses = RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose));
IQMPose *poses = (IQMPose *)RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose));
//fseek(iqmFile, iqmHeader->ofs_poses, SEEK_SET);
//fread(poses, sizeof(IQMPose), iqmHeader->num_poses, iqmFile);
memcpy(poses, fileDataPtr + iqmHeader->ofs_poses, iqmHeader->num_poses*sizeof(IQMPose));
// Get animations data
*animCount = iqmHeader->num_anims;
IQMAnim *anim = RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim));
IQMAnim *anim = (IQMAnim *)RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim));
//fseek(iqmFile, iqmHeader->ofs_anims, SEEK_SET);
//fread(anim, sizeof(IQMAnim), iqmHeader->num_anims, iqmFile);
memcpy(anim, fileDataPtr + iqmHeader->ofs_anims, iqmHeader->num_anims*sizeof(IQMAnim));
ModelAnimation *animations = RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation));
ModelAnimation *animations = (ModelAnimation *)RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation));
// frameposes
unsigned short *framedata = RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
unsigned short *framedata = (unsigned short *)RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
//fseek(iqmFile, iqmHeader->ofs_frames, SEEK_SET);
//fread(framedata, sizeof(unsigned short), iqmHeader->num_frames*iqmHeader->num_framechannels, iqmFile);
memcpy(framedata, fileDataPtr + iqmHeader->ofs_frames, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
// joints
IQMJoint *joints = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
IQMJoint *joints = (IQMJoint *)RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
memcpy(joints, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint));
for (unsigned int a = 0; a < iqmHeader->num_anims; a++)
{
animations[a].frameCount = anim[a].num_frames;
animations[a].boneCount = iqmHeader->num_poses;
animations[a].bones = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo));
animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *));
animations[a].bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo));
animations[a].framePoses = (Transform **)RL_MALLOC(anim[a].num_frames*sizeof(Transform *));
memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32); // I don't like this 32 here
TraceLog(LOG_INFO, "IQM Anim %s", animations[a].name);
// animations[a].framerate = anim.framerate; // TODO: Use animation framerate data?
@ -4942,7 +5007,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
animations[a].bones[j].parent = poses[j].parent;
}
for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = RL_MALLOC(iqmHeader->num_poses*sizeof(Transform));
for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = (Transform *)RL_MALLOC(iqmHeader->num_poses*sizeof(Transform));
int dcounter = anim[a].first_frame*iqmHeader->num_framechannels;
@ -5133,7 +5198,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat
}
else if ((cgltfImage->buffer_view != NULL) && (cgltfImage->buffer_view->buffer->data != NULL)) // Check if image is provided as data buffer
{
unsigned char *data = RL_MALLOC(cgltfImage->buffer_view->size);
unsigned char *data = (unsigned char *)RL_MALLOC(cgltfImage->buffer_view->size);
int offset = (int)cgltfImage->buffer_view->offset;
int stride = (int)cgltfImage->buffer_view->stride? (int)cgltfImage->buffer_view->stride : 1;
@ -5166,16 +5231,12 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat
static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount)
{
*boneCount = (int)skin.joints_count;
BoneInfo *bones = RL_MALLOC(skin.joints_count*sizeof(BoneInfo));
BoneInfo *bones = (BoneInfo *)RL_CALLOC(skin.joints_count, sizeof(BoneInfo));
for (unsigned int i = 0; i < skin.joints_count; i++)
{
cgltf_node node = *skin.joints[i];
if (node.name != NULL)
{
strncpy(bones[i].name, node.name, sizeof(bones[i].name));
bones[i].name[sizeof(bones[i].name) - 1] = '\0';
}
if (node.name != NULL) strncpy(bones[i].name, node.name, sizeof(bones[i].name) - 1);
// Find parent bone index
int parentIndex = -1;
@ -5201,7 +5262,7 @@ static Model LoadGLTF(const char *fileName)
/*********************************************************************************************
Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend)
Transform handling implemented by Paul Melis (@paulmelis).
Transform handling implemented by Paul Melis (@paulmelis)
Reviewed by Ramon Santamaria (@raysan5)
FEATURES:
@ -5211,10 +5272,10 @@ static Model LoadGLTF(const char *fileName)
PBR specular/glossiness flow and extended texture flows not supported
- Supports multiple meshes per model (every primitives is loaded as a separate mesh)
- Supports basic animations
- Transforms, including parent-child relations, are applied on the mesh data, but the
hierarchy is not kept (as it can't be represented).
- Transforms, including parent-child relations, are applied on the mesh data,
but the hierarchy is not kept (as it can't be represented)
- Mesh instances in the glTF file (i.e. same mesh linked from multiple nodes)
are turned into separate raylib Meshes.
are turned into separate raylib Meshes
RESTRICTIONS:
- Only triangle meshes supported
@ -5297,15 +5358,15 @@ static Model LoadGLTF(const char *fileName)
// Load our model data: meshes and materials
model.meshCount = primitivesCount;
model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
// NOTE: We keep an extra slot for default material, in case some mesh requires it
model.materialCount = (int)data->materials_count + 1;
model.materials = RL_CALLOC(model.materialCount, sizeof(Material));
model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
model.materials[0] = LoadMaterialDefault(); // Load default material (index: 0)
// Load mesh-material indices, by default all meshes are mapped to material index: 0
model.meshMaterial = RL_CALLOC(model.meshCount, sizeof(int));
model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
// Load materials data
//----------------------------------------------------------------------------------------------------
@ -5430,7 +5491,7 @@ static Model LoadGLTF(const char *fileName)
// Any glTF mesh linked from more than one Node (i.e. instancing)
// is turned into multiple Mesh's, as each Node will have its own
// transform applied.
// Note: the code below disregards the scenes defined in the file, all nodes are used.
// NOTE: The code below disregards the scenes defined in the file, all nodes are used.
//----------------------------------------------------------------------------------------------------
int meshIndex = 0;
for (unsigned int i = 0; i < data->nodes_count; i++)
@ -5475,7 +5536,7 @@ static Model LoadGLTF(const char *fileName)
{
// Init raylib mesh vertices to copy glTF attribute data
model.meshes[meshIndex].vertexCount = (int)attribute->count;
model.meshes[meshIndex].vertices = RL_MALLOC(attribute->count*3*sizeof(float));
model.meshes[meshIndex].vertices = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
// Load 3 components of float data type into mesh.vertices
LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].vertices)
@ -5499,7 +5560,7 @@ static Model LoadGLTF(const char *fileName)
if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f))
{
// Init raylib mesh normals to copy glTF attribute data
model.meshes[meshIndex].normals = RL_MALLOC(attribute->count*3*sizeof(float));
model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
// Load 3 components of float data type into mesh.normals
LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].normals)
@ -5516,14 +5577,14 @@ static Model LoadGLTF(const char *fileName)
}
else TRACELOG(LOG_WARNING, "MODEL: [%s] Normal attribute data format not supported, use vec3 float", fileName);
}
else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_tangent) // TANGENT, vec3, float
else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_tangent) // TANGENT, vec4, float, w is tangent basis sign
{
cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
if ((attribute->type == cgltf_type_vec4) && (attribute->component_type == cgltf_component_type_r_32f))
{
// Init raylib mesh tangent to copy glTF attribute data
model.meshes[meshIndex].tangents = RL_MALLOC(attribute->count*4*sizeof(float));
model.meshes[meshIndex].tangents = (float *)RL_MALLOC(attribute->count*4*sizeof(float));
// Load 4 components of float data type into mesh.tangents
LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].tangents)
@ -5532,10 +5593,10 @@ static Model LoadGLTF(const char *fileName)
float *tangents = model.meshes[meshIndex].tangents;
for (unsigned int k = 0; k < attribute->count; k++)
{
Vector3 tt = Vector3Transform((Vector3){ tangents[3*k], tangents[3*k+1], tangents[3*k+2] }, worldMatrix);
tangents[3*k] = tt.x;
tangents[3*k+1] = tt.y;
tangents[3*k+2] = tt.z;
Vector3 tt = Vector3Transform((Vector3){ tangents[4*k], tangents[4*k+1], tangents[4*k+2] }, worldMatrix);
tangents[4*k] = tt.x;
tangents[4*k+1] = tt.y;
tangents[4*k+2] = tt.z;
}
}
else TRACELOG(LOG_WARNING, "MODEL: [%s] Tangent attribute data format not supported, use vec4 float", fileName);
@ -5609,10 +5670,10 @@ static Model LoadGLTF(const char *fileName)
if (attribute->component_type == cgltf_component_type_r_8u)
{
// Init raylib mesh color to copy glTF attribute data
model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
// Load data into a temp buffer to be converted to raylib data type
unsigned char *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned char));
unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*3*sizeof(unsigned char));
LOAD_ATTRIBUTE(attribute, 3, unsigned char, temp);
// Convert data to raylib color data type (4 bytes)
@ -5629,10 +5690,10 @@ static Model LoadGLTF(const char *fileName)
else if (attribute->component_type == cgltf_component_type_r_16u)
{
// Init raylib mesh color to copy glTF attribute data
model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
// Load data into a temp buffer to be converted to raylib data type
unsigned short *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned short));
unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*3*sizeof(unsigned short));
LOAD_ATTRIBUTE(attribute, 3, unsigned short, temp);
// Convert data to raylib color data type (4 bytes)
@ -5649,10 +5710,10 @@ static Model LoadGLTF(const char *fileName)
else if (attribute->component_type == cgltf_component_type_r_32f)
{
// Init raylib mesh color to copy glTF attribute data
model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
// Load data into a temp buffer to be converted to raylib data type
float *temp = RL_MALLOC(attribute->count*3*sizeof(float));
float *temp = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
LOAD_ATTRIBUTE(attribute, 3, float, temp);
// Convert data to raylib color data type (4 bytes)
@ -5673,7 +5734,7 @@ static Model LoadGLTF(const char *fileName)
if (attribute->component_type == cgltf_component_type_r_8u)
{
// Init raylib mesh color to copy glTF attribute data
model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
// Load 4 components of unsigned char data type into mesh.colors
LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].colors)
@ -5681,10 +5742,10 @@ static Model LoadGLTF(const char *fileName)
else if (attribute->component_type == cgltf_component_type_r_16u)
{
// Init raylib mesh color to copy glTF attribute data
model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
// Load data into a temp buffer to be converted to raylib data type
unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short));
unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*4*sizeof(unsigned short));
LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
// Convert data to raylib color data type (4 bytes)
@ -5695,10 +5756,10 @@ static Model LoadGLTF(const char *fileName)
else if (attribute->component_type == cgltf_component_type_r_32f)
{
// Init raylib mesh color to copy glTF attribute data
model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
// Load data into a temp buffer to be converted to raylib data type
float *temp = RL_MALLOC(attribute->count*4*sizeof(float));
float *temp = (float *)RL_MALLOC(attribute->count*4*sizeof(float));
LOAD_ATTRIBUTE(attribute, 4, float, temp);
// Convert data to raylib color data type (4 bytes), we expect the color data normalized
@ -5724,7 +5785,7 @@ static Model LoadGLTF(const char *fileName)
if (attribute->component_type == cgltf_component_type_r_16u)
{
// Init raylib mesh indices to copy glTF attribute data
model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short));
model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short));
// Load unsigned short data type into mesh.indices
LOAD_ATTRIBUTE(attribute, 1, unsigned short, model.meshes[meshIndex].indices)
@ -5732,14 +5793,14 @@ static Model LoadGLTF(const char *fileName)
else if (attribute->component_type == cgltf_component_type_r_8u)
{
// Init raylib mesh indices to copy glTF attribute data
model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short));
model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short));
LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned char, model.meshes[meshIndex].indices, unsigned short)
}
else if (attribute->component_type == cgltf_component_type_r_32u)
{
// Init raylib mesh indices to copy glTF attribute data
model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short));
model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short));
LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned int, model.meshes[meshIndex].indices, unsigned short);
TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data converted from u32 to u16, possible loss of data", fileName);
@ -5783,7 +5844,7 @@ static Model LoadGLTF(const char *fileName)
{
cgltf_skin skin = data->skins[0];
model.bones = LoadBoneInfoGLTF(skin, &model.boneCount);
model.bindPose = RL_MALLOC(model.boneCount*sizeof(Transform));
model.bindPose = (Transform *)RL_MALLOC(model.boneCount*sizeof(Transform));
for (int i = 0; i < model.boneCount; i++)
{
@ -5815,15 +5876,17 @@ static Model LoadGLTF(const char *fileName)
for (unsigned int p = 0; p < mesh->primitives_count; p++)
{
bool hasJoints = false;
// NOTE: We only support primitives defined by triangles
if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue;
for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++)
{
// NOTE: JOINTS_1 + WEIGHT_1 will be used for +4 joints influencing a vertex -> Not supported by raylib
if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_joints) // JOINTS_n (vec4: 4 bones max per vertex / u8, u16)
{
hasJoints = true;
cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
// NOTE: JOINTS_n can only be vec4 and u8/u16
@ -5838,7 +5901,7 @@ static Model LoadGLTF(const char *fileName)
if (attribute->component_type == cgltf_component_type_r_8u)
{
// Init raylib mesh boneIds to copy glTF attribute data
model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
// Load attribute: vec4, u8 (unsigned char)
LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].boneIds)
@ -5846,10 +5909,10 @@ static Model LoadGLTF(const char *fileName)
else if (attribute->component_type == cgltf_component_type_r_16u)
{
// Init raylib mesh boneIds to copy glTF attribute data
model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
// Load data into a temp buffer to be converted to raylib data type
unsigned short *temp = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short));
unsigned short *temp = (unsigned short *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short));
LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
// Convert data to raylib color data type (4 bytes)
@ -5878,14 +5941,13 @@ static Model LoadGLTF(const char *fileName)
if (attribute->type == cgltf_type_vec4)
{
// TODO: Support component types: u8, u16?
if (attribute->component_type == cgltf_component_type_r_8u)
{
// Init raylib mesh bone weight to copy glTF attribute data
model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
// Load data into a temp buffer to be converted to raylib data type
unsigned char *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
LOAD_ATTRIBUTE(attribute, 4, unsigned char, temp);
// Convert data to raylib bone weight data type (4 bytes)
@ -5896,10 +5958,10 @@ static Model LoadGLTF(const char *fileName)
else if (attribute->component_type == cgltf_component_type_r_16u)
{
// Init raylib mesh bone weight to copy glTF attribute data
model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
// Load data into a temp buffer to be converted to raylib data type
unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short));
unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*4*sizeof(unsigned short));
LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
// Convert data to raylib bone weight data type
@ -5910,10 +5972,11 @@ static Model LoadGLTF(const char *fileName)
else if (attribute->component_type == cgltf_component_type_r_32f)
{
// Init raylib mesh bone weight to copy glTF attribute data
model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
// Load 4 components of float data type into mesh.boneWeights
// for cgltf_attribute_type_weights we have:
// - data.meshes[0] (256 vertices)
// - 256 values, provided as cgltf_type_vec4 of float (4 byte per joint, stride 16)
LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].boneWeights)
@ -5924,10 +5987,37 @@ static Model LoadGLTF(const char *fileName)
}
}
// Check if we are animated, and the mesh was not given any bone assignments, but is the child of a bone node
// in this case we need to fully attach all the verts to the parent bone so it will animate with the bone
if (data->skins_count > 0 && !hasJoints && node->parent != NULL && node->parent->mesh == NULL)
{
int parentBoneId = -1;
for (int joint = 0; joint < model.boneCount; joint++)
{
if (data->skins[0].joints[joint] == node->parent)
{
parentBoneId = joint;
break;
}
}
if (parentBoneId >= 0)
{
model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
for (int vertexIndex = 0; vertexIndex < model.meshes[meshIndex].vertexCount*4; vertexIndex += 4)
{
model.meshes[meshIndex].boneIds[vertexIndex] = (unsigned char)parentBoneId;
model.meshes[meshIndex].boneWeights[vertexIndex] = 1.0f;
}
}
}
// Animated vertex data
model.meshes[meshIndex].animVertices = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
model.meshes[meshIndex].animVertices = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
memcpy(model.meshes[meshIndex].animVertices, model.meshes[meshIndex].vertices, model.meshes[meshIndex].vertexCount*3*sizeof(float));
model.meshes[meshIndex].animNormals = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
model.meshes[meshIndex].animNormals = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
if (model.meshes[meshIndex].normals != NULL)
{
memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float));
@ -5935,16 +6025,15 @@ static Model LoadGLTF(const char *fileName)
// Bone Transform Matrices
model.meshes[meshIndex].boneCount = model.boneCount;
model.meshes[meshIndex].boneMatrices = RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix));
model.meshes[meshIndex].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix));
for (int j = 0; j < model.meshes[meshIndex].boneCount; j++)
{
model.meshes[meshIndex].boneMatrices[j] = MatrixIdentity();
}
meshIndex++; // Move to next mesh
meshIndex++; // Move to next mesh
}
}
// Free all cgltf loaded data
@ -6126,7 +6215,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
{
cgltf_skin skin = data->skins[0];
*animCount = (int)data->animations_count;
animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation));
animations = (ModelAnimation *)RL_CALLOC(data->animations_count, sizeof(ModelAnimation));
for (unsigned int i = 0; i < data->animations_count; i++)
{
@ -6141,7 +6230,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
cgltf_interpolation_type interpolationType;
};
struct Channels *boneChannels = RL_CALLOC(animations[i].boneCount, sizeof(struct Channels));
struct Channels *boneChannels = (struct Channels *)RL_CALLOC(animations[i].boneCount, sizeof(struct Channels));
float animDuration = 0.0f;
for (unsigned int j = 0; j < animData.channels_count; j++)
@ -6199,18 +6288,14 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
animDuration = (t > animDuration)? t : animDuration;
}
if (animData.name != NULL)
{
strncpy(animations[i].name, animData.name, sizeof(animations[i].name));
animations[i].name[sizeof(animations[i].name) - 1] = '\0';
}
if (animData.name != NULL) strncpy(animations[i].name, animData.name, sizeof(animations[i].name) - 1);
animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY) + 1;
animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *));
animations[i].framePoses = (Transform **)RL_MALLOC(animations[i].frameCount*sizeof(Transform *));
for (int j = 0; j < animations[i].frameCount; j++)
{
animations[i].framePoses[j] = RL_MALLOC(animations[i].boneCount*sizeof(Transform));
animations[i].framePoses[j] = (Transform *)RL_MALLOC(animations[i].boneCount*sizeof(Transform));
float time = ((float) j*GLTF_ANIMDELAY)/1000.0f;
for (int k = 0; k < animations[i].boneCount; k++)
@ -6360,7 +6445,7 @@ static Model LoadVOX(const char *fileName)
// Copy colors
size = pmesh->vertexCount*sizeof(Color);
pmesh->colors = RL_MALLOC(size);
pmesh->colors = (unsigned char *)RL_MALLOC(size);
memcpy(pmesh->colors, pcolors, size);
// First material index
@ -6496,7 +6581,7 @@ static Model LoadM3D(const char *fileName)
// If no map is provided, or we have colors defined, we allocate storage for vertex colors
// M3D specs only consider vertex colors if no material is provided, however raylib uses both and mixes the colors
if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
// If no map is provided and we allocated vertex colors, set them to white
if ((mi == M3D_UNDEF) && (model.meshes[k].colors != NULL))
@ -6530,11 +6615,11 @@ static Model LoadM3D(const char *fileName)
// Without vertex color (full transparency), we use the default color
if (model.meshes[k].colors != NULL)
{
if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xFF000000)
if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xff000000)
memcpy(&model.meshes[k].colors[l*12 + 0], &m3d->vertex[m3d->face[i].vertex[0]].color, 4);
if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xFF000000)
if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xff000000)
memcpy(&model.meshes[k].colors[l*12 + 4], &m3d->vertex[m3d->face[i].vertex[1]].color, 4);
if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xFF000000)
if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xff000000)
memcpy(&model.meshes[k].colors[l*12 + 8], &m3d->vertex[m3d->face[i].vertex[2]].color, 4);
}
@ -6663,13 +6748,13 @@ static Model LoadM3D(const char *fileName)
if (m3d->numbone)
{
model.boneCount = m3d->numbone + 1;
model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo));
model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform));
model.bones = (BoneInfo *)RL_CALLOC(model.boneCount, sizeof(BoneInfo));
model.bindPose = (Transform *)RL_CALLOC(model.boneCount, sizeof(Transform));
for (i = 0; i < (int)m3d->numbone; i++)
{
model.bones[i].parent = m3d->bone[i].parent;
strncpy(model.bones[i].name, m3d->bone[i].name, sizeof(model.bones[i].name));
strncpy(model.bones[i].name, m3d->bone[i].name, sizeof(model.bones[i].name) - 1);
model.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x*m3d->scale;
model.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y*m3d->scale;
model.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z*m3d->scale;
@ -6715,7 +6800,7 @@ static Model LoadM3D(const char *fileName)
memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float));
model.meshes[i].boneCount = model.boneCount;
model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
for (j = 0; j < model.meshes[i].boneCount; j++)
{
model.meshes[i].boneMatrices[j] = MatrixIdentity();
@ -6765,24 +6850,23 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
return NULL;
}
animations = RL_MALLOC(m3d->numaction*sizeof(ModelAnimation));
animations = (ModelAnimation *)RL_CALLOC(m3d->numaction, sizeof(ModelAnimation));
*animCount = m3d->numaction;
for (unsigned int a = 0; a < m3d->numaction; a++)
{
animations[a].frameCount = m3d->action[a].durationmsec/M3D_ANIMDELAY;
animations[a].boneCount = m3d->numbone + 1;
animations[a].bones = RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo));
animations[a].framePoses = RL_MALLOC(animations[a].frameCount*sizeof(Transform *));
strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name));
animations[a].name[sizeof(animations[a].name) - 1] = '\0';
animations[a].bones = (BoneInfo *)RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo));
animations[a].framePoses = (Transform **)RL_MALLOC(animations[a].frameCount*sizeof(Transform *));
strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name) - 1);
TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount);
for (i = 0; i < (int)m3d->numbone; i++)
{
animations[a].bones[i].parent = m3d->bone[i].parent;
strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name));
strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name) - 1);
}
// A special, never transformed "no bone" bone, used for boneless vertices
@ -6793,7 +6877,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
// regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones
for (i = 0; i < animations[a].frameCount; i++)
{
animations[a].framePoses[i] = RL_MALLOC((m3d->numbone + 1)*sizeof(Transform));
animations[a].framePoses[i] = (Transform *)RL_MALLOC((m3d->numbone + 1)*sizeof(Transform));
m3db_t *pose = m3d_pose(m3d, a, i*M3D_ANIMDELAY);

View File

@ -470,27 +470,39 @@ void DrawCircleLinesV(Vector2 center, float radius, Color color)
// Draw ellipse
void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color)
{
DrawEllipseV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color);
}
// Draw ellipse (Vector version)
void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color)
{
rlBegin(RL_TRIANGLES);
for (int i = 0; i < 360; i += 10)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f((float)centerX, (float)centerY);
rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radiusH, (float)centerY + sinf(DEG2RAD*(i + 10))*radiusV);
rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radiusH, (float)centerY + sinf(DEG2RAD*i)*radiusV);
rlVertex2f(center.x, center.y);
rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV);
rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV);
}
rlEnd();
}
// Draw ellipse outline
void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color)
{
DrawEllipseLinesV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color);
}
// Draw ellipse outline
void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color)
{
rlBegin(RL_LINES);
for (int i = 0; i < 360; i += 10)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radiusH, centerY + sinf(DEG2RAD*(i + 10))*radiusV);
rlVertex2f(centerX + cosf(DEG2RAD*i)*radiusH, centerY + sinf(DEG2RAD*i)*radiusV);
rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV);
rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV);
}
rlEnd();
}
@ -774,7 +786,7 @@ void DrawRectangleGradientH(int posX, int posY, int width, int height, Color lef
}
// Draw a gradient-filled rectangle
void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight)
void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color bottomRight, Color topRight)
{
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@ -791,11 +803,11 @@ void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Col
rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(rec.x, rec.y + rec.height);
rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a);
rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
rlVertex2f(rec.x + rec.width, rec.y + rec.height);
rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a);
rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a);
rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(rec.x + rec.width, rec.y);
rlEnd();

View File

@ -161,6 +161,10 @@ extern void LoadFontDefault(void)
{
#define BIT_CHECK(a,b) ((a) & (1u << (b)))
// check to see if we have allready allocated the font for an image, and if we don't need to upload, then just return
if (defaultFont.glyphs != NULL && !isGpuReady)
return;
// NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
// Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl
@ -249,7 +253,7 @@ extern void LoadFontDefault(void)
}
else
{
((unsigned char *)imFont.data)[(i + j)*sizeof(short)] = 0xFF;
((unsigned char *)imFont.data)[(i + j)*sizeof(short)] = 0xff;
((unsigned char *)imFont.data)[(i + j)*sizeof(short) + 1] = 0x00;
}
}
@ -257,7 +261,18 @@ extern void LoadFontDefault(void)
counter++;
}
if (isGpuReady) defaultFont.texture = LoadTextureFromImage(imFont);
if (isGpuReady)
{
defaultFont.texture = LoadTextureFromImage(imFont);
// we have already loaded the font glyph data an image, and the GPU is ready, we are done
// if we don't do this, we will leak memory by reallocating the glyphs and rects
if (defaultFont.glyphs != NULL)
{
UnloadImage(imFont);
return;
}
}
// Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, glyphCount
//------------------------------------------------------------------------------
@ -282,7 +297,7 @@ extern void LoadFontDefault(void)
testPosX += (int)(defaultFont.recs[i].width + (float)charsDivisor);
if (testPosX >= defaultFont.texture.width)
if (testPosX >= imFont.width)
{
currentLine++;
currentPosX = 2*charsDivisor + charsWidth[i];
@ -316,6 +331,9 @@ extern void UnloadFontDefault(void)
if (isGpuReady) UnloadTexture(defaultFont.texture);
RL_FREE(defaultFont.glyphs);
RL_FREE(defaultFont.recs);
defaultFont.glyphCount = 0;
defaultFont.glyphs = NULL;
defaultFont.recs = NULL;
}
#endif // SUPPORT_DEFAULT_FONT
@ -373,7 +391,7 @@ Font LoadFont(const char *fileName)
else
{
SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, we set point filter (the best performance)
TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", FONT_TTF_DEFAULT_SIZE, FONT_TTF_DEFAULT_NUMCHARS);
TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
}
}
@ -1560,7 +1578,7 @@ const char *TextSubtext(const char *text, int position, int length)
}
// Replace text string
// REQUIRES: strlen(), strstr(), strncpy(), strcpy()
// REQUIRES: strstr(), strncpy(), strcpy()
// WARNING: Allocated memory must be manually freed
char *TextReplace(const char *text, const char *replace, const char *by)
{

View File

@ -832,10 +832,11 @@ Image GenImageGradientLinear(int width, int height, int direction, Color start,
// Calculate how far the top-left pixel is along the gradient direction from the center of said gradient
float startingPos = 0.5f - (cosDir*width/2) - (sinDir*height/2);
// With directions that lie in the first or third quadrant (i.e. from top-left to
// bottom-right or vice-versa), pixel (0, 0) is the farthest point on the gradient
// (i.e. the pixel which should become one of the gradient's ends color); while for
// directions that lie in the second or fourth quadrant, that point is pixel (width, 0).
// directions that lie in the second or fourth quadrant, that point is pixel (width, 0)
float maxPosValue = ((signbit(sinDir) != 0) == (signbit(cosDir) != 0))? fabsf(startingPos) : fabsf(startingPos + width*cosDir);
for (int i = 0; i < width; i++)
{
@ -2101,8 +2102,8 @@ void ImageBlurGaussian(Image *image, int blurSize)
Color *pixels = LoadImageColors(*image);
// Loop switches between pixelsCopy1 and pixelsCopy2
Vector4 *pixelsCopy1 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
Vector4 *pixelsCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
Vector4 *pixelsCopy1 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
Vector4 *pixelsCopy2 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
for (int i = 0; i < (image->height*image->width); i++)
{
@ -2216,9 +2217,9 @@ void ImageBlurGaussian(Image *image, int blurSize)
else if (pixelsCopy1[i].w <= 255.0f)
{
float alpha = (float)pixelsCopy1[i].w/255.0f;
pixels[i].r = (unsigned char)((float)pixelsCopy1[i].x/alpha);
pixels[i].g = (unsigned char)((float)pixelsCopy1[i].y/alpha);
pixels[i].b = (unsigned char)((float)pixelsCopy1[i].z/alpha);
pixels[i].r = (unsigned char)fminf((float)pixelsCopy1[i].x/alpha, 255.0);
pixels[i].g = (unsigned char)fminf((float)pixelsCopy1[i].y/alpha, 255.0);
pixels[i].b = (unsigned char)fminf((float)pixelsCopy1[i].z/alpha, 255.0);
pixels[i].a = (unsigned char) pixelsCopy1[i].w;
}
}
@ -2250,8 +2251,8 @@ void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize)
Color *pixels = LoadImageColors(*image);
Vector4 *imageCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
Vector4 *temp = RL_MALLOC(kernelSize*sizeof(Vector4));
Vector4 *imageCopy2 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
Vector4 *temp = (Vector4 *)RL_MALLOC(kernelSize*sizeof(Vector4));
for (int i = 0; i < kernelSize; i++)
{
@ -2926,7 +2927,16 @@ void ImageColorReplace(Image *image, Color color, Color replace)
image->data = pixels;
image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
ImageFormat(image, format);
// Only convert back to original format if it supported alpha
if ((format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) ||
(format == PIXELFORMAT_UNCOMPRESSED_R5G6B5) ||
(format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ||
(format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) ||
(format == PIXELFORMAT_UNCOMPRESSED_R16G16B16) ||
(format == PIXELFORMAT_COMPRESSED_DXT1_RGB) ||
(format == PIXELFORMAT_COMPRESSED_ETC1_RGB) ||
(format == PIXELFORMAT_COMPRESSED_ETC2_RGB) ||
(format == PIXELFORMAT_COMPRESSED_PVRT_RGB)) ImageFormat(image, format);
}
#endif // SUPPORT_IMAGE_MANIPULATION
@ -3564,34 +3574,43 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co
int dx = x2 - x1;
int dy = y2 - y1;
// Draw the main line between (x1, y1) and (x2, y2)
ImageDrawLine(dst, x1, y1, x2, y2, color);
// Determine if the line is more horizontal or vertical
if ((dx != 0) && (abs(dy/dx) < 1))
{
// Line is more horizontal
// Calculate half the width of the line
int wy = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dx));
// Draw additional lines above and below the main line
for (int i = 1; i <= wy; i++)
// How many additional lines to draw
int wy = thick - 1;
// Draw the main line and lower half
for (int i = 0; i <= ((wy+1)/2); i++)
{
ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); // Draw above the main line
ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); // Draw below the main line
ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color);
}
// Draw the upper half
for (int i = 1; i <= (wy/2); i++)
{
ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color);
}
}
else if (dy != 0)
{
// Line is more vertical or perfectly horizontal
// Calculate half the width of the line
int wx = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dy));
// Draw additional lines to the left and right of the main line
for (int i = 1; i <= wx; i++)
// How many additional lines to draw
int wx = thick - 1;
//Draw the main line and right half
for (int i = 0; i <= ((wx+1)/2); i++)
{
ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); // Draw left of the main line
ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); // Draw right of the main line
ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color);
}
// Draw the left half
for (int i = 1; i <= (wx/2); i++)
{
ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color);
}
}
}
@ -3835,7 +3854,7 @@ void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c
// Calculate the inverse of the sum of the barycentric coordinates for normalization
// NOTE 1: Here, we act as if we multiply by 255 the reciprocal, which avoids additional
// calculations in the loop. This is acceptable because we are only interpolating colors.
// calculations in the loop. This is acceptable because we are only interpolating colors
// NOTE 2: This sum remains constant throughout the triangle
float wInvSum = 255.0f/(w1Row + w2Row + w3Row);
@ -3890,7 +3909,7 @@ void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Colo
}
// Draw a triangle fan defined by points within an image (first vertex is the center)
void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color)
void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color)
{
if (pointCount >= 3)
{
@ -3902,7 +3921,7 @@ void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color col
}
// Draw a triangle strip defined by points within an image
void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color)
void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color)
{
if (pointCount >= 3)
{
@ -4337,14 +4356,17 @@ void UnloadRenderTexture(RenderTexture2D target)
}
// Update GPU texture with new data
// NOTE: pixels data must match texture.format
// NOTE 1: pixels data must match texture.format
// NOTE 2: pixels data must contain at least as many pixels as texture
void UpdateTexture(Texture2D texture, const void *pixels)
{
rlUpdateTexture(texture.id, 0, 0, texture.width, texture.height, texture.format, pixels);
}
// Update GPU texture rectangle with new data
// NOTE: pixels data must match texture.format
// NOTE 1: pixels data must match texture.format
// NOTE 2: pixels data must contain as many pixels as rec contains
// NOTE 3: rec must fit completely within texture's width and height
void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels)
{
rlUpdateTexture(texture.id, (int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, texture.format, pixels);
@ -5147,10 +5169,10 @@ Color GetColor(unsigned int hexValue)
{
Color color;
color.r = (unsigned char)(hexValue >> 24) & 0xFF;
color.g = (unsigned char)(hexValue >> 16) & 0xFF;
color.b = (unsigned char)(hexValue >> 8) & 0xFF;
color.a = (unsigned char)hexValue & 0xFF;
color.r = (unsigned char)(hexValue >> 24) & 0xff;
color.g = (unsigned char)(hexValue >> 16) & 0xff;
color.b = (unsigned char)(hexValue >> 8) & 0xff;
color.a = (unsigned char)hexValue & 0xff;
return color;
}
@ -5390,17 +5412,16 @@ static float HalfToFloat(unsigned short x)
{
float result = 0.0f;
union
{
union {
float fm;
unsigned int ui;
} uni;
const unsigned int e = (x & 0x7C00) >> 10; // Exponent
const unsigned int m = (x & 0x03FF) << 13; // Mantissa
const unsigned int e = (x & 0x7c00) >> 10; // Exponent
const unsigned int m = (x & 0x03ff) << 13; // Mantissa
uni.fm = (float)m;
const unsigned int v = uni.ui >> 23; // Evil log2 bit hack to count leading zeros in denormalized format
uni.ui = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000)); // sign : normalized : denormalized
uni.ui = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007fe000)); // sign : normalized : denormalized
result = uni.fm;
@ -5412,18 +5433,17 @@ static unsigned short FloatToHalf(float x)
{
unsigned short result = 0;
union
{
union {
float fm;
unsigned int ui;
} uni;
uni.fm = x;
const unsigned int b = uni.ui + 0x00001000; // Round-to-nearest-even: add last bit after truncated mantissa
const unsigned int e = (b & 0x7F800000) >> 23; // Exponent
const unsigned int m = b & 0x007FFFFF; // Mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
const unsigned int e = (b & 0x7f800000) >> 23; // Exponent
const unsigned int m = b & 0x007fffff; // Mantissa; in line below: 0x007ff000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7FFF; // sign : normalized : denormalized : saturate
result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7c00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007ff000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7fff; // sign : normalized : denormalized : saturate
return result;
}

View File

@ -405,7 +405,7 @@ void UnloadFileText(char *text)
}
// Save text data to file (write), string must be '\0' terminated
bool SaveFileText(const char *fileName, char *text)
bool SaveFileText(const char *fileName, const char *text)
{
bool success = false;