Merge branch 'master' into rcamera_redesign_vector
This commit is contained in:
commit
056e0b857c
|
|
@ -27,7 +27,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to
|
||||||
| raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile |
|
| raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile |
|
||||||
| gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib |
|
| gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib |
|
||||||
| h-raylib | 4.5-dev | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib |
|
| h-raylib | 4.5-dev | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib |
|
||||||
| raylib-hx | 4.0 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx |
|
| raylib-hx | **4.2** | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx |
|
||||||
| hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib |
|
| hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib |
|
||||||
| jaylib | **4.2** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ |
|
| jaylib | **4.2** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ |
|
||||||
| raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J |
|
| raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J |
|
||||||
|
|
|
||||||
20
CHANGELOG
20
CHANGELOG
|
|
@ -9,14 +9,13 @@ Release: raylib 4.5 (xx February 2023) -WIP-
|
||||||
KEY CHANGES:
|
KEY CHANGES:
|
||||||
- ADDED: M3D model format support with animations
|
- ADDED: M3D model format support with animations
|
||||||
- ADDED: GLTF animation support
|
- ADDED: GLTF animation support
|
||||||
|
- ADDED: QOA audio format support (import/export) -WIP-
|
||||||
- rlgl redesign to avoid render batch triangles limits pre-check: rlCheckRenderBatchLimit()
|
- rlgl redesign to avoid render batch triangles limits pre-check: rlCheckRenderBatchLimit()
|
||||||
- rshapes simplification to minimize the requirement of rlgl functionality, now it only depends on 6 functions
|
- rshapes simplification to minimize the requirement of rlgl functionality, now it only depends on 6 functions
|
||||||
- rl_gputex.h: Compressed textures loading, required by rtextures module, has been moved to a separate self-contained library
|
- rl_gputex.h: Compressed textures loading, required by rtextures module, has been moved to a separate self-contained library
|
||||||
- raygui 3.5: New version of the immediate-mode gui system for tools development with raylib
|
- raygui 3.5: New version of the immediate-mode gui system for tools development with raylib
|
||||||
|
|
||||||
|
|
||||||
Detailed changes:
|
Detailed changes:
|
||||||
|
|
||||||
[core] ADDED: RAYLIB_VERSION_* values to raylib.h (#2856) by @RobLoach
|
[core] ADDED: RAYLIB_VERSION_* values to raylib.h (#2856) by @RobLoach
|
||||||
[core] ADDED: Basic gamepad support for Android (#2709) by @deniska
|
[core] ADDED: Basic gamepad support for Android (#2709) by @deniska
|
||||||
[core] ADDED: Support CAPS/NUM lock keys registering if locked
|
[core] ADDED: Support CAPS/NUM lock keys registering if locked
|
||||||
|
|
@ -43,6 +42,7 @@ Detailed changes:
|
||||||
[core] REVIEWED: GetClipboardText() on PLATFORM_WEB, permissions issues
|
[core] REVIEWED: GetClipboardText() on PLATFORM_WEB, permissions issues
|
||||||
[core] REVIEWED: Initial window position for display-sized fullscreen (#2742) by @daipom
|
[core] REVIEWED: Initial window position for display-sized fullscreen (#2742) by @daipom
|
||||||
[core] REVIEWED: Sticky touches input (#2857) by @ImazighenGhost
|
[core] REVIEWED: Sticky touches input (#2857) by @ImazighenGhost
|
||||||
|
[core] REVIEWED: Enable GetWindowHandle() on macOS (#2915) by @Not-Nik
|
||||||
[rlgl] ADDED: OpenGL ES 2.0 support on PLATFORM_DESKTOP (#2840) by @wtnbgo
|
[rlgl] ADDED: OpenGL ES 2.0 support on PLATFORM_DESKTOP (#2840) by @wtnbgo
|
||||||
[rlgl] ADDED: Separate blending modes for color and alpha, BLEND_CUSTOM_SEPARATE (#2741)
|
[rlgl] ADDED: Separate blending modes for color and alpha, BLEND_CUSTOM_SEPARATE (#2741)
|
||||||
[rlgl] ADDED: rlSetBlendFactorsSeparate and custom blend mode modification checks (#2741) by @pure01fx
|
[rlgl] ADDED: rlSetBlendFactorsSeparate and custom blend mode modification checks (#2741) by @pure01fx
|
||||||
|
|
@ -58,7 +58,8 @@ Detailed changes:
|
||||||
[rlgl] REVIEWED: rlMultMatrixf(), use const pointer (#2807) by @planetis-m
|
[rlgl] REVIEWED: rlMultMatrixf(), use const pointer (#2807) by @planetis-m
|
||||||
[rlgl] REVIEWED: Expose OpenGL blending mode factors and functions/equations
|
[rlgl] REVIEWED: Expose OpenGL blending mode factors and functions/equations
|
||||||
[rlgl] REVIEWED: rLoadTextureDepth(), issue with depth textures on WebGL (#2824)
|
[rlgl] REVIEWED: rLoadTextureDepth(), issue with depth textures on WebGL (#2824)
|
||||||
[raymath] REVIEWED: Vector2Angle() (#2829, #2832) by @AlxHnr and @planetis-m
|
[raymath] ADDED: Vector2LineAngle() (#2887)
|
||||||
|
[raymath] REVIEWED: Vector2Angle() (#2829, #2832) by @AlxHnr, @planetis-m
|
||||||
[shapes] ADDED: CheckCollisionPointPoly() (#2685) by @acejacek
|
[shapes] ADDED: CheckCollisionPointPoly() (#2685) by @acejacek
|
||||||
[shapes] REVIEWED: DrawPixel*(), use RL_QUADS/RL_TRIANGLES (#2750) by @hatkidchan
|
[shapes] REVIEWED: DrawPixel*(), use RL_QUADS/RL_TRIANGLES (#2750) by @hatkidchan
|
||||||
[shapes] REVIEWED: DrawLineBezier*(), fix bezier line breaking (#2735, #2767) by @nobytesgiven
|
[shapes] REVIEWED: DrawLineBezier*(), fix bezier line breaking (#2735, #2767) by @nobytesgiven
|
||||||
|
|
@ -88,21 +89,29 @@ Detailed changes:
|
||||||
[models] ADDED: GLTF animation support (#2844) by @charles-l
|
[models] ADDED: GLTF animation support (#2844) by @charles-l
|
||||||
[models] ADDED: DrawCapsule() and DrawCapsuleWires() (#2761) by @IanBand
|
[models] ADDED: DrawCapsule() and DrawCapsuleWires() (#2761) by @IanBand
|
||||||
[models] ADDED: LoadMaterials(), MTL files loading, same code as OBJ loader (#2872) by @JeffM2501
|
[models] ADDED: LoadMaterials(), MTL files loading, same code as OBJ loader (#2872) by @JeffM2501
|
||||||
|
[models] REVIEWED: DrawMesh(), using SHADER_LOC_COLOR_SPECULAR as a material map (#2908) by @haved
|
||||||
|
[models] REVIEWED: LoadM3D() vertex color support (#2878) by @GithubPrankster, @bztsrc
|
||||||
[models] REVIEWED: GenMeshHeightmap() (#2716)
|
[models] REVIEWED: GenMeshHeightmap() (#2716)
|
||||||
[models] REVIEWED: LoadIQM() (#2676)
|
[models] REVIEWED: LoadIQM() (#2676)
|
||||||
[models] REVIEWED: Simplify .vox signature check (#2752) by @CrezyDud
|
[models] REVIEWED: Simplify .vox signature check (#2752) by @CrezyDud
|
||||||
[models] REVIEWED: LoadIQM(), support bone names loading if available (#2882) by @PencilAmazing
|
[models] REVIEWED: LoadIQM(), support bone names loading if available (#2882) by @PencilAmazing
|
||||||
[models] `WARNING`: REMOVED: DrawCubeTexture(), DrawCubeTextureRec(), functions moved to new example: `models_draw_cube_texture`
|
[models] `WARNING`: REMOVED: DrawCubeTexture(), DrawCubeTextureRec(), functions moved to new example: `models_draw_cube_texture`
|
||||||
|
[audio] ADDED: IsWaveReady()`, IsSoundReady(), IsMusicReady() (#2892) by @RobLoach
|
||||||
[audio] REVIEWED: Clear PCM buffer state when closing audio device (#2736) by @veins1
|
[audio] REVIEWED: Clear PCM buffer state when closing audio device (#2736) by @veins1
|
||||||
[audio] REVIEWED: Android backend selected (#2118, #2875) by @planetis-m
|
[audio] REVIEWED: Android backend selected (#2118, #2875) by @planetis-m
|
||||||
|
[multi] ADDED: IsShaderReady(), IsImageReady(), IsFontReady() (#2892) by @RobLoach
|
||||||
|
[multi] ADDED: IsModelReady(), IsMaterialReady(), IsTextureReady(), IsRenderTextureReady() (#2895) by @RobLoach
|
||||||
[multi] REVIEWED: Multiple code/comment typos by @sDos280
|
[multi] REVIEWED: Multiple code/comment typos by @sDos280
|
||||||
|
[multi] REVIEWED: Grammar mistakes and typos (#2914) by @stickM4N
|
||||||
[multi] REVIEWED: Use TRACELOG() macro instead of TraceLog() in internal modules (#2881) by @RobLoach
|
[multi] REVIEWED: Use TRACELOG() macro instead of TraceLog() in internal modules (#2881) by @RobLoach
|
||||||
[examples] ADDED: textures_textured_curve (#2821) by @JeffM2501
|
[examples] ADDED: textures_textured_curve (#2821) by @JeffM2501
|
||||||
[examples] ADDED: shaders_write_depth (#2836) by @BugraAlptekinSari
|
[examples] ADDED: shaders_write_depth (#2836) by @BugraAlptekinSari
|
||||||
|
[examples] ADDED: shaders_hybrid_render (#2919) by @BugraAlptekinSari
|
||||||
[examples] RENAMED: Several shaders for naming consistency (#2707)
|
[examples] RENAMED: Several shaders for naming consistency (#2707)
|
||||||
[examples] RENAMED: lighting_instanced.fs to lighting_instancing.fs (glsl100) (#2805) by @gtrxAC
|
[examples] RENAMED: lighting_instanced.fs to lighting_instancing.fs (glsl100) (#2805) by @gtrxAC
|
||||||
[examples] REVIEWED: core_custom_logging.c (#2692) by @hartmannathan
|
[examples] REVIEWED: core_custom_logging.c (#2692) by @hartmannathan
|
||||||
[examples] REVIEWED: core_camera_2d_platformer (#2687) by @skylar779
|
[examples] REVIEWED: core_camera_2d_platformer (#2687) by @skylar779
|
||||||
|
[examples] REVIEWED: core_input_gamepad.c (#2903) by @planetis-m
|
||||||
[examples] REVIEWED: core_custom_frame_control
|
[examples] REVIEWED: core_custom_frame_control
|
||||||
[examples] REVIEWED: text_rectangle_bounds (#2746) by @SzieberthAdam
|
[examples] REVIEWED: text_rectangle_bounds (#2746) by @SzieberthAdam
|
||||||
[examples] REVIEWED: textures_image_processing, added gaussian blurring (#2775) by @nobytesgiven
|
[examples] REVIEWED: textures_image_processing, added gaussian blurring (#2775) by @nobytesgiven
|
||||||
|
|
@ -114,6 +123,9 @@ Detailed changes:
|
||||||
[build] ADDED: Packaging for distros with deb-based and rpm-based packages (#2877) by @KOLANICH
|
[build] ADDED: Packaging for distros with deb-based and rpm-based packages (#2877) by @KOLANICH
|
||||||
[build] ADDED: Linkage library -latomic on Linux (only required for ARM32)
|
[build] ADDED: Linkage library -latomic on Linux (only required for ARM32)
|
||||||
[build] ADDED: Required frameworks on macOS (#2793) by @SpexGuy
|
[build] ADDED: Required frameworks on macOS (#2793) by @SpexGuy
|
||||||
|
[build] ADDED: WASM support for Zig build (#2901) by @Not-Nik
|
||||||
|
[build] REVIEWED: config.h format and inconsistencies
|
||||||
|
[build] REVIEWED: Zig build to latest master, avoid deprecated functions (#2910) by @star-tek-mb
|
||||||
[build] REVIEWED: CMake project template to easily target raylib version (#2700) by @RobLoach
|
[build] REVIEWED: CMake project template to easily target raylib version (#2700) by @RobLoach
|
||||||
[build] REVIEWED: PATH for PLATFORM_WEB target (#2647) by @futureapricot
|
[build] REVIEWED: PATH for PLATFORM_WEB target (#2647) by @futureapricot
|
||||||
[build] REVIEWED: build.zig to let user decide how to set build mode and linker fixes by @InKryption
|
[build] REVIEWED: build.zig to let user decide how to set build mode and linker fixes by @InKryption
|
||||||
|
|
@ -141,7 +153,7 @@ Detailed changes:
|
||||||
[bindings] ADDED: TurboRaylib (Object Pascal) by @turborium
|
[bindings] ADDED: TurboRaylib (Object Pascal) by @turborium
|
||||||
[bindings] ADDED: Kaylib (Kotlin/Native) by @Its-Kenta
|
[bindings] ADDED: Kaylib (Kotlin/Native) by @Its-Kenta
|
||||||
[bindings] ADDED: Raylib-Nelua (Nelua) by @Its-Kenta
|
[bindings] ADDED: Raylib-Nelua (Nelua) by @Its-Kenta
|
||||||
[misc] REVIEWED: Update some external libraries to latest versions
|
[misc] REVIEWED: Update external libraries to latest versions
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
Release: raylib 4.2 (11 August 2022)
|
Release: raylib 4.2 (11 August 2022)
|
||||||
|
|
|
||||||
18
build.zig
Normal file
18
build.zig
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const raylib = @import("src/build.zig");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const lib = raylib.addRaylib(b, target, optimize);
|
||||||
|
lib.installHeader("src/raylib.h", "raylib.h");
|
||||||
|
lib.install();
|
||||||
|
}
|
||||||
|
|
@ -523,7 +523,8 @@ SHADERS = \
|
||||||
shaders/shaders_hot_reloading \
|
shaders/shaders_hot_reloading \
|
||||||
shaders/shaders_mesh_instancing \
|
shaders/shaders_mesh_instancing \
|
||||||
shaders/shaders_multi_sample2d \
|
shaders/shaders_multi_sample2d \
|
||||||
shaders/shaders_write_depth
|
shaders/shaders_write_depth \
|
||||||
|
shaders/shaders_hybrid_render
|
||||||
|
|
||||||
AUDIO = \
|
AUDIO = \
|
||||||
audio/audio_module_playing \
|
audio/audio_module_playing \
|
||||||
|
|
@ -581,10 +582,10 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
|
||||||
rm -fv *.o
|
rm -fv *.o
|
||||||
endif
|
endif
|
||||||
ifeq ($(PLATFORM),PLATFORM_WEB)
|
ifeq ($(PLATFORM),PLATFORM_WEB)
|
||||||
ifeq ($(PLATFORM_OS),WINDOWS)
|
ifeq ($(PLATFORM_OS),WINDOWS)
|
||||||
del *.wasm *.html *.js *.data
|
del *.wasm *.html *.js *.data
|
||||||
else
|
else
|
||||||
rm -f */*.wasm */*.html */*.js */*.data
|
rm -f */*.wasm */*.html */*.js */*.data
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
@echo Cleaning done
|
@echo Cleaning done
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zig.CrossTarget) !*std.build.Step {
|
fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step {
|
||||||
if (target.getOsTag() == .emscripten) {
|
if (target.getOsTag() == .emscripten) {
|
||||||
@panic("Emscripten building via Zig unsupported");
|
@panic("Emscripten building via Zig unsupported");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard release options allow the person running `zig build` to select
|
|
||||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
|
||||||
const mode = b.standardReleaseOptions();
|
|
||||||
|
|
||||||
const all = b.step(module, "All " ++ module ++ " examples");
|
const all = b.step(module, "All " ++ module ++ " examples");
|
||||||
const dir = try std.fs.cwd().openIterableDir(module, .{});
|
const dir = try std.fs.cwd().openIterableDir(module, .{});
|
||||||
var iter = dir.iterate();
|
var iter = dir.iterate();
|
||||||
|
|
@ -22,10 +18,12 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi
|
||||||
// zig's mingw headers do not include pthread.h
|
// zig's mingw headers do not include pthread.h
|
||||||
if (std.mem.eql(u8, "core_loading_thread", name) and target.getOsTag() == .windows) continue;
|
if (std.mem.eql(u8, "core_loading_thread", name) and target.getOsTag() == .windows) continue;
|
||||||
|
|
||||||
const exe = b.addExecutable(name, null);
|
const exe = b.addExecutable(.{
|
||||||
|
.name = name,
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
exe.addCSourceFile(path, &[_][]const u8{});
|
exe.addCSourceFile(path, &[_][]const u8{});
|
||||||
exe.setTarget(target);
|
|
||||||
exe.setBuildMode(mode);
|
|
||||||
exe.linkLibC();
|
exe.linkLibC();
|
||||||
exe.addObjectFile(switch (target.getOsTag()) {
|
exe.addObjectFile(switch (target.getOsTag()) {
|
||||||
.windows => "../src/raylib.lib",
|
.windows => "../src/raylib.lib",
|
||||||
|
|
@ -83,21 +81,25 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi
|
||||||
return all;
|
return all;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(b: *std.build.Builder) !void {
|
pub fn build(b: *std.Build) !void {
|
||||||
// Standard target options allows the person running `zig build` to choose
|
// Standard target options allows the person running `zig build` to choose
|
||||||
// what target to build for. Here we do not override the defaults, which
|
// what target to build for. Here we do not override the defaults, which
|
||||||
// means any target is allowed, and the default is native. Other options
|
// means any target is allowed, and the default is native. Other options
|
||||||
// for restricting supported target set are available.
|
// for restricting supported target set are available.
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
const all = b.getInstallStep();
|
const all = b.getInstallStep();
|
||||||
|
|
||||||
all.dependOn(try add_module("audio", b, target));
|
all.dependOn(try add_module("audio", b, target, optimize));
|
||||||
all.dependOn(try add_module("core", b, target));
|
all.dependOn(try add_module("core", b, target, optimize));
|
||||||
all.dependOn(try add_module("models", b, target));
|
all.dependOn(try add_module("models", b, target, optimize));
|
||||||
all.dependOn(try add_module("others", b, target));
|
all.dependOn(try add_module("others", b, target, optimize));
|
||||||
all.dependOn(try add_module("shaders", b, target));
|
all.dependOn(try add_module("shaders", b, target, optimize));
|
||||||
all.dependOn(try add_module("shapes", b, target));
|
all.dependOn(try add_module("shapes", b, target, optimize));
|
||||||
all.dependOn(try add_module("text", b, target));
|
all.dependOn(try add_module("text", b, target, optimize));
|
||||||
all.dependOn(try add_module("textures", b, target));
|
all.dependOn(try add_module("textures", b, target, optimize));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ int main(void)
|
||||||
DrawText(TextFormat("AXIS %i: %.02f", i, GetGamepadAxisMovement(0, i)), 20, 70 + 20*i, 10, DARKGRAY);
|
DrawText(TextFormat("AXIS %i: %.02f", i, GetGamepadAxisMovement(0, i)), 20, 70 + 20*i, 10, DARKGRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetGamepadButtonPressed() != -1) DrawText(TextFormat("DETECTED BUTTON: %i", GetGamepadButtonPressed()), 10, 430, 10, RED);
|
if (GetGamepadButtonPressed() != GAMEPAD_BUTTON_UNKNOWN) DrawText(TextFormat("DETECTED BUTTON: %i", GetGamepadButtonPressed()), 10, 430, 10, RED);
|
||||||
else DrawText("DETECTED BUTTON: NONE", 10, 430, 10, GRAY);
|
else DrawText("DETECTED BUTTON: NONE", 10, 430, 10, GRAY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
16
examples/shaders/resources/shaders/glsl100/hybrid_raster.fs
Normal file
16
examples/shaders/resources/shaders/glsl100/hybrid_raster.fs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#version 100
|
||||||
|
#extension GL_EXT_frag_depth : enable // Extension required for writing depth
|
||||||
|
precision mediump float; // Precision required for OpenGL ES2 (WebGL)
|
||||||
|
|
||||||
|
varying vec2 fragTexCoord;
|
||||||
|
varying vec4 fragColor;
|
||||||
|
|
||||||
|
uniform sampler2D texture0;
|
||||||
|
uniform vec4 colDiffuse;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texelColor = texture2D(texture0, fragTexCoord);
|
||||||
|
gl_FragColor = texelColor*colDiffuse*fragColor;
|
||||||
|
gl_FragDepthEXT = gl_FragCoord.z;
|
||||||
|
}
|
||||||
288
examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs
Normal file
288
examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs
Normal file
|
|
@ -0,0 +1,288 @@
|
||||||
|
#version 100
|
||||||
|
#extension GL_EXT_frag_depth : enable //Extension required for writing depth
|
||||||
|
#extension GL_OES_standard_derivatives : enable //Extension used for fwidth()
|
||||||
|
precision mediump float; // Precision required for OpenGL ES2 (WebGL)
|
||||||
|
|
||||||
|
|
||||||
|
// Input vertex attributes (from vertex shader)
|
||||||
|
varying vec2 fragTexCoord;
|
||||||
|
varying vec4 fragColor;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform sampler2D texture0;
|
||||||
|
uniform vec4 colDiffuse;
|
||||||
|
|
||||||
|
// Custom Input Uniform
|
||||||
|
uniform vec3 camPos;
|
||||||
|
uniform vec3 camDir;
|
||||||
|
uniform vec2 screenCenter;
|
||||||
|
|
||||||
|
#define ZERO 0
|
||||||
|
|
||||||
|
// https://learnopengl.com/Advanced-OpenGL/Depth-testing
|
||||||
|
float CalcDepth(in vec3 rd, in float Idist){
|
||||||
|
float local_z = dot(normalize(camDir),rd)*Idist;
|
||||||
|
return (1.0/(local_z) - 1.0/0.01)/(1.0/1000.0 -1.0/0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/distfunctions/
|
||||||
|
float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w )
|
||||||
|
{
|
||||||
|
p.x = abs(p.x);
|
||||||
|
float l = length(p.xy);
|
||||||
|
p.xy = mat2(-c.x, c.y,
|
||||||
|
c.y, c.x)*p.xy;
|
||||||
|
p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x),
|
||||||
|
(p.x>0.0)?p.y:l );
|
||||||
|
p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0);
|
||||||
|
|
||||||
|
vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z);
|
||||||
|
vec2 d = abs(q) - w;
|
||||||
|
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// r = sphere's radius
|
||||||
|
// h = cutting's plane's position
|
||||||
|
// t = thickness
|
||||||
|
float sdSixWayCutHollowSphere( vec3 p, float r, float h, float t )
|
||||||
|
{
|
||||||
|
// Six way symetry Transformation
|
||||||
|
vec3 ap = abs(p);
|
||||||
|
if(ap.x < max(ap.y, ap.z)){
|
||||||
|
if(ap.y < ap.z) ap.xz = ap.zx;
|
||||||
|
else ap.xy = ap.yx;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 q = vec2( length(ap.yz), ap.x );
|
||||||
|
|
||||||
|
float w = sqrt(r*r-h*h);
|
||||||
|
|
||||||
|
return ((h*q.x<w*q.y) ? length(q-vec2(w,h)) :
|
||||||
|
abs(length(q)-r) ) - t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/boxfunctions
|
||||||
|
vec2 iBox( in vec3 ro, in vec3 rd, in vec3 rad )
|
||||||
|
{
|
||||||
|
vec3 m = 1.0/rd;
|
||||||
|
vec3 n = m*ro;
|
||||||
|
vec3 k = abs(m)*rad;
|
||||||
|
vec3 t1 = -n - k;
|
||||||
|
vec3 t2 = -n + k;
|
||||||
|
return vec2( max( max( t1.x, t1.y ), t1.z ),
|
||||||
|
min( min( t2.x, t2.y ), t2.z ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 opU( vec2 d1, vec2 d2 )
|
||||||
|
{
|
||||||
|
return (d1.x<d2.x) ? d1 : d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 map( in vec3 pos ){
|
||||||
|
vec2 res = vec2( sdHorseshoe( pos-vec3(-1.0,0.08, 1.0), vec2(cos(1.3),sin(1.3)), 0.2, 0.3, vec2(0.03,0.5) ), 11.5 ) ;
|
||||||
|
res = opU(res, vec2( sdSixWayCutHollowSphere( pos-vec3(0.0, 1.0, 0.0), 4.0, 3.5, 0.5 ), 4.5 )) ;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.shadertoy.com/view/Xds3zN
|
||||||
|
vec2 raycast( in vec3 ro, in vec3 rd ){
|
||||||
|
vec2 res = vec2(-1.0,-1.0);
|
||||||
|
|
||||||
|
float tmin = 1.0;
|
||||||
|
float tmax = 20.0;
|
||||||
|
|
||||||
|
// raytrace floor plane
|
||||||
|
float tp1 = (-ro.y)/rd.y;
|
||||||
|
if( tp1>0.0 )
|
||||||
|
{
|
||||||
|
tmax = min( tmax, tp1 );
|
||||||
|
res = vec2( tp1, 1.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
float t = tmin;
|
||||||
|
for( int i=0; i<70 ; i++ )
|
||||||
|
{
|
||||||
|
if(t>tmax) break;
|
||||||
|
vec2 h = map( ro+rd*t );
|
||||||
|
if( abs(h.x)<(0.0001*t) )
|
||||||
|
{
|
||||||
|
res = vec2(t,h.y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t += h.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/rmshadows
|
||||||
|
float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax )
|
||||||
|
{
|
||||||
|
// bounding volume
|
||||||
|
float tp = (0.8-ro.y)/rd.y; if( tp>0.0 ) tmax = min( tmax, tp );
|
||||||
|
|
||||||
|
float res = 1.0;
|
||||||
|
float t = mint;
|
||||||
|
for( int i=ZERO; i<24; i++ )
|
||||||
|
{
|
||||||
|
float h = map( ro + rd*t ).x;
|
||||||
|
float s = clamp(8.0*h/t,0.0,1.0);
|
||||||
|
res = min( res, s );
|
||||||
|
t += clamp( h, 0.01, 0.2 );
|
||||||
|
if( res<0.004 || t>tmax ) break;
|
||||||
|
}
|
||||||
|
res = clamp( res, 0.0, 1.0 );
|
||||||
|
return res*res*(3.0-2.0*res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/normalsSDF
|
||||||
|
vec3 calcNormal( in vec3 pos )
|
||||||
|
{
|
||||||
|
vec2 e = vec2(1.0,-1.0)*0.5773*0.0005;
|
||||||
|
return normalize( e.xyy*map( pos + e.xyy ).x +
|
||||||
|
e.yyx*map( pos + e.yyx ).x +
|
||||||
|
e.yxy*map( pos + e.yxy ).x +
|
||||||
|
e.xxx*map( pos + e.xxx ).x );
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/nvscene2008/rwwtt.pdf
|
||||||
|
float calcAO( in vec3 pos, in vec3 nor )
|
||||||
|
{
|
||||||
|
float occ = 0.0;
|
||||||
|
float sca = 1.0;
|
||||||
|
for( int i=ZERO; i<5; i++ )
|
||||||
|
{
|
||||||
|
float h = 0.01 + 0.12*float(i)/4.0;
|
||||||
|
float d = map( pos + h*nor ).x;
|
||||||
|
occ += (h-d)*sca;
|
||||||
|
sca *= 0.95;
|
||||||
|
if( occ>0.35 ) break;
|
||||||
|
}
|
||||||
|
return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/checkerfiltering
|
||||||
|
float checkersGradBox( in vec2 p )
|
||||||
|
{
|
||||||
|
// filter kernel
|
||||||
|
vec2 w = fwidth(p) + 0.001;
|
||||||
|
// analytical integral (box filter)
|
||||||
|
vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w;
|
||||||
|
// xor pattern
|
||||||
|
return 0.5 - 0.5*i.x*i.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.shadertoy.com/view/tdS3DG
|
||||||
|
vec4 render( in vec3 ro, in vec3 rd)
|
||||||
|
{
|
||||||
|
// background
|
||||||
|
vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3;
|
||||||
|
|
||||||
|
// raycast scene
|
||||||
|
vec2 res = raycast(ro,rd);
|
||||||
|
float t = res.x;
|
||||||
|
float m = res.y;
|
||||||
|
if( m>-0.5 )
|
||||||
|
{
|
||||||
|
vec3 pos = ro + t*rd;
|
||||||
|
vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos );
|
||||||
|
vec3 ref = reflect( rd, nor );
|
||||||
|
|
||||||
|
// material
|
||||||
|
col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) );
|
||||||
|
float ks = 1.0;
|
||||||
|
|
||||||
|
if( m<1.5 )
|
||||||
|
{
|
||||||
|
float f = checkersGradBox( 3.0*pos.xz);
|
||||||
|
col = 0.15 + f*vec3(0.05);
|
||||||
|
ks = 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lighting
|
||||||
|
float occ = calcAO( pos, nor );
|
||||||
|
|
||||||
|
vec3 lin = vec3(0.0);
|
||||||
|
|
||||||
|
// sun
|
||||||
|
{
|
||||||
|
vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) );
|
||||||
|
vec3 hal = normalize( lig-rd );
|
||||||
|
float dif = clamp( dot( nor, lig ), 0.0, 1.0 );
|
||||||
|
//if( dif>0.0001 )
|
||||||
|
dif *= calcSoftshadow( pos, lig, 0.02, 2.5 );
|
||||||
|
float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0);
|
||||||
|
spe *= dif;
|
||||||
|
spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0);
|
||||||
|
//spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0);
|
||||||
|
lin += col*2.20*dif*vec3(1.30,1.00,0.70);
|
||||||
|
lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks;
|
||||||
|
}
|
||||||
|
// sky
|
||||||
|
{
|
||||||
|
float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 ));
|
||||||
|
dif *= occ;
|
||||||
|
float spe = smoothstep( -0.2, 0.2, ref.y );
|
||||||
|
spe *= dif;
|
||||||
|
spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 );
|
||||||
|
//if( spe>0.001 )
|
||||||
|
spe *= calcSoftshadow( pos, ref, 0.02, 2.5 );
|
||||||
|
lin += col*0.60*dif*vec3(0.40,0.60,1.15);
|
||||||
|
lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks;
|
||||||
|
}
|
||||||
|
// back
|
||||||
|
{
|
||||||
|
float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
|
||||||
|
dif *= occ;
|
||||||
|
lin += col*0.55*dif*vec3(0.25,0.25,0.25);
|
||||||
|
}
|
||||||
|
// sss
|
||||||
|
{
|
||||||
|
float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0);
|
||||||
|
dif *= occ;
|
||||||
|
lin += col*0.25*dif*vec3(1.00,1.00,1.00);
|
||||||
|
}
|
||||||
|
|
||||||
|
col = lin;
|
||||||
|
|
||||||
|
col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec4(vec3( clamp(col,0.0,1.0) ),t);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 CalcRayDir(vec2 nCoord){
|
||||||
|
vec3 horizontal = normalize(cross(camDir,vec3(.0 , 1.0, .0)));
|
||||||
|
vec3 vertical = normalize(cross(horizontal,camDir));
|
||||||
|
return normalize(camDir + horizontal*nCoord.x + vertical*nCoord.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
mat3 setCamera()
|
||||||
|
{
|
||||||
|
vec3 cw = normalize(camDir);
|
||||||
|
vec3 cp = vec3(0.0, 1.0 ,0.0);
|
||||||
|
vec3 cu = normalize( cross(cw,cp) );
|
||||||
|
vec3 cv = ( cross(cu,cw) );
|
||||||
|
return mat3( cu, cv, cw );
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 nCoord = (gl_FragCoord.xy - screenCenter.xy)/screenCenter.y;
|
||||||
|
mat3 ca = setCamera();
|
||||||
|
|
||||||
|
// focal length
|
||||||
|
float fl = length(camDir);
|
||||||
|
vec3 rd = ca * normalize( vec3(nCoord,fl) );
|
||||||
|
vec3 color = vec3(nCoord/2.0 + 0.5, 0.0);
|
||||||
|
float depth = gl_FragCoord.z;
|
||||||
|
{
|
||||||
|
vec4 res = render( camPos - vec3(0.0, 0.0, 0.0) , rd );
|
||||||
|
color = res.xyz;
|
||||||
|
depth = CalcDepth(rd,res.w);
|
||||||
|
}
|
||||||
|
gl_FragColor = vec4(color , 1.0);
|
||||||
|
gl_FragDepthEXT = depth;
|
||||||
|
}
|
||||||
14
examples/shaders/resources/shaders/glsl330/hybrid_raster.fs
Normal file
14
examples/shaders/resources/shaders/glsl330/hybrid_raster.fs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 fragTexCoord;
|
||||||
|
in vec4 fragColor;
|
||||||
|
|
||||||
|
uniform sampler2D texture0;
|
||||||
|
uniform vec4 colDiffuse;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texelColor = texture2D(texture0, fragTexCoord);
|
||||||
|
gl_FragColor = texelColor*colDiffuse*fragColor;
|
||||||
|
gl_FragDepth = gl_FragCoord.z;
|
||||||
|
}
|
||||||
284
examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs
Normal file
284
examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs
Normal file
|
|
@ -0,0 +1,284 @@
|
||||||
|
# version 330
|
||||||
|
|
||||||
|
// Input vertex attributes (from vertex shader)
|
||||||
|
in vec2 fragTexCoord;
|
||||||
|
in vec4 fragColor;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform sampler2D texture0;
|
||||||
|
uniform vec4 colDiffuse;
|
||||||
|
|
||||||
|
// Custom Input Uniform
|
||||||
|
uniform vec3 camPos;
|
||||||
|
uniform vec3 camDir;
|
||||||
|
uniform vec2 screenCenter;
|
||||||
|
|
||||||
|
#define ZERO 0
|
||||||
|
|
||||||
|
// https://learnopengl.com/Advanced-OpenGL/Depth-testing
|
||||||
|
float CalcDepth(in vec3 rd, in float Idist){
|
||||||
|
float local_z = dot(normalize(camDir),rd)*Idist;
|
||||||
|
return (1.0/(local_z) - 1.0/0.01)/(1.0/1000.0 -1.0/0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/distfunctions/
|
||||||
|
float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w )
|
||||||
|
{
|
||||||
|
p.x = abs(p.x);
|
||||||
|
float l = length(p.xy);
|
||||||
|
p.xy = mat2(-c.x, c.y,
|
||||||
|
c.y, c.x)*p.xy;
|
||||||
|
p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x),
|
||||||
|
(p.x>0.0)?p.y:l );
|
||||||
|
p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0);
|
||||||
|
|
||||||
|
vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z);
|
||||||
|
vec2 d = abs(q) - w;
|
||||||
|
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// r = sphere's radius
|
||||||
|
// h = cutting's plane's position
|
||||||
|
// t = thickness
|
||||||
|
float sdSixWayCutHollowSphere( vec3 p, float r, float h, float t )
|
||||||
|
{
|
||||||
|
// Six way symetry Transformation
|
||||||
|
vec3 ap = abs(p);
|
||||||
|
if(ap.x < max(ap.y, ap.z)){
|
||||||
|
if(ap.y < ap.z) ap.xz = ap.zx;
|
||||||
|
else ap.xy = ap.yx;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 q = vec2( length(ap.yz), ap.x );
|
||||||
|
|
||||||
|
float w = sqrt(r*r-h*h);
|
||||||
|
|
||||||
|
return ((h*q.x<w*q.y) ? length(q-vec2(w,h)) :
|
||||||
|
abs(length(q)-r) ) - t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/boxfunctions
|
||||||
|
vec2 iBox( in vec3 ro, in vec3 rd, in vec3 rad )
|
||||||
|
{
|
||||||
|
vec3 m = 1.0/rd;
|
||||||
|
vec3 n = m*ro;
|
||||||
|
vec3 k = abs(m)*rad;
|
||||||
|
vec3 t1 = -n - k;
|
||||||
|
vec3 t2 = -n + k;
|
||||||
|
return vec2( max( max( t1.x, t1.y ), t1.z ),
|
||||||
|
min( min( t2.x, t2.y ), t2.z ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 opU( vec2 d1, vec2 d2 )
|
||||||
|
{
|
||||||
|
return (d1.x<d2.x) ? d1 : d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 map( in vec3 pos ){
|
||||||
|
vec2 res = vec2( sdHorseshoe( pos-vec3(-1.0,0.08, 1.0), vec2(cos(1.3),sin(1.3)), 0.2, 0.3, vec2(0.03,0.5) ), 11.5 ) ;
|
||||||
|
res = opU(res, vec2( sdSixWayCutHollowSphere( pos-vec3(0.0, 1.0, 0.0), 4.0, 3.5, 0.5 ), 4.5 )) ;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.shadertoy.com/view/Xds3zN
|
||||||
|
vec2 raycast( in vec3 ro, in vec3 rd ){
|
||||||
|
vec2 res = vec2(-1.0,-1.0);
|
||||||
|
|
||||||
|
float tmin = 1.0;
|
||||||
|
float tmax = 20.0;
|
||||||
|
|
||||||
|
// raytrace floor plane
|
||||||
|
float tp1 = (-ro.y)/rd.y;
|
||||||
|
if( tp1>0.0 )
|
||||||
|
{
|
||||||
|
tmax = min( tmax, tp1 );
|
||||||
|
res = vec2( tp1, 1.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
float t = tmin;
|
||||||
|
for( int i=0; i<70 ; i++ )
|
||||||
|
{
|
||||||
|
if(t>tmax) break;
|
||||||
|
vec2 h = map( ro+rd*t );
|
||||||
|
if( abs(h.x)<(0.0001*t) )
|
||||||
|
{
|
||||||
|
res = vec2(t,h.y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t += h.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/rmshadows
|
||||||
|
float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax )
|
||||||
|
{
|
||||||
|
// bounding volume
|
||||||
|
float tp = (0.8-ro.y)/rd.y; if( tp>0.0 ) tmax = min( tmax, tp );
|
||||||
|
|
||||||
|
float res = 1.0;
|
||||||
|
float t = mint;
|
||||||
|
for( int i=ZERO; i<24; i++ )
|
||||||
|
{
|
||||||
|
float h = map( ro + rd*t ).x;
|
||||||
|
float s = clamp(8.0*h/t,0.0,1.0);
|
||||||
|
res = min( res, s );
|
||||||
|
t += clamp( h, 0.01, 0.2 );
|
||||||
|
if( res<0.004 || t>tmax ) break;
|
||||||
|
}
|
||||||
|
res = clamp( res, 0.0, 1.0 );
|
||||||
|
return res*res*(3.0-2.0*res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/normalsSDF
|
||||||
|
vec3 calcNormal( in vec3 pos )
|
||||||
|
{
|
||||||
|
vec2 e = vec2(1.0,-1.0)*0.5773*0.0005;
|
||||||
|
return normalize( e.xyy*map( pos + e.xyy ).x +
|
||||||
|
e.yyx*map( pos + e.yyx ).x +
|
||||||
|
e.yxy*map( pos + e.yxy ).x +
|
||||||
|
e.xxx*map( pos + e.xxx ).x );
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/nvscene2008/rwwtt.pdf
|
||||||
|
float calcAO( in vec3 pos, in vec3 nor )
|
||||||
|
{
|
||||||
|
float occ = 0.0;
|
||||||
|
float sca = 1.0;
|
||||||
|
for( int i=ZERO; i<5; i++ )
|
||||||
|
{
|
||||||
|
float h = 0.01 + 0.12*float(i)/4.0;
|
||||||
|
float d = map( pos + h*nor ).x;
|
||||||
|
occ += (h-d)*sca;
|
||||||
|
sca *= 0.95;
|
||||||
|
if( occ>0.35 ) break;
|
||||||
|
}
|
||||||
|
return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://iquilezles.org/articles/checkerfiltering
|
||||||
|
float checkersGradBox( in vec2 p )
|
||||||
|
{
|
||||||
|
// filter kernel
|
||||||
|
vec2 w = fwidth(p) + 0.001;
|
||||||
|
// analytical integral (box filter)
|
||||||
|
vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w;
|
||||||
|
// xor pattern
|
||||||
|
return 0.5 - 0.5*i.x*i.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.shadertoy.com/view/tdS3DG
|
||||||
|
vec4 render( in vec3 ro, in vec3 rd)
|
||||||
|
{
|
||||||
|
// background
|
||||||
|
vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3;
|
||||||
|
|
||||||
|
// raycast scene
|
||||||
|
vec2 res = raycast(ro,rd);
|
||||||
|
float t = res.x;
|
||||||
|
float m = res.y;
|
||||||
|
if( m>-0.5 )
|
||||||
|
{
|
||||||
|
vec3 pos = ro + t*rd;
|
||||||
|
vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos );
|
||||||
|
vec3 ref = reflect( rd, nor );
|
||||||
|
|
||||||
|
// material
|
||||||
|
col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) );
|
||||||
|
float ks = 1.0;
|
||||||
|
|
||||||
|
if( m<1.5 )
|
||||||
|
{
|
||||||
|
float f = checkersGradBox( 3.0*pos.xz);
|
||||||
|
col = 0.15 + f*vec3(0.05);
|
||||||
|
ks = 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lighting
|
||||||
|
float occ = calcAO( pos, nor );
|
||||||
|
|
||||||
|
vec3 lin = vec3(0.0);
|
||||||
|
|
||||||
|
// sun
|
||||||
|
{
|
||||||
|
vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) );
|
||||||
|
vec3 hal = normalize( lig-rd );
|
||||||
|
float dif = clamp( dot( nor, lig ), 0.0, 1.0 );
|
||||||
|
//if( dif>0.0001 )
|
||||||
|
dif *= calcSoftshadow( pos, lig, 0.02, 2.5 );
|
||||||
|
float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0);
|
||||||
|
spe *= dif;
|
||||||
|
spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0);
|
||||||
|
//spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0);
|
||||||
|
lin += col*2.20*dif*vec3(1.30,1.00,0.70);
|
||||||
|
lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks;
|
||||||
|
}
|
||||||
|
// sky
|
||||||
|
{
|
||||||
|
float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 ));
|
||||||
|
dif *= occ;
|
||||||
|
float spe = smoothstep( -0.2, 0.2, ref.y );
|
||||||
|
spe *= dif;
|
||||||
|
spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 );
|
||||||
|
//if( spe>0.001 )
|
||||||
|
spe *= calcSoftshadow( pos, ref, 0.02, 2.5 );
|
||||||
|
lin += col*0.60*dif*vec3(0.40,0.60,1.15);
|
||||||
|
lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks;
|
||||||
|
}
|
||||||
|
// back
|
||||||
|
{
|
||||||
|
float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
|
||||||
|
dif *= occ;
|
||||||
|
lin += col*0.55*dif*vec3(0.25,0.25,0.25);
|
||||||
|
}
|
||||||
|
// sss
|
||||||
|
{
|
||||||
|
float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0);
|
||||||
|
dif *= occ;
|
||||||
|
lin += col*0.25*dif*vec3(1.00,1.00,1.00);
|
||||||
|
}
|
||||||
|
|
||||||
|
col = lin;
|
||||||
|
|
||||||
|
col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec4(vec3( clamp(col,0.0,1.0) ),t);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 CalcRayDir(vec2 nCoord){
|
||||||
|
vec3 horizontal = normalize(cross(camDir,vec3(.0 , 1.0, .0)));
|
||||||
|
vec3 vertical = normalize(cross(horizontal,camDir));
|
||||||
|
return normalize(camDir + horizontal*nCoord.x + vertical*nCoord.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
mat3 setCamera()
|
||||||
|
{
|
||||||
|
vec3 cw = normalize(camDir);
|
||||||
|
vec3 cp = vec3(0.0, 1.0 ,0.0);
|
||||||
|
vec3 cu = normalize( cross(cw,cp) );
|
||||||
|
vec3 cv = ( cross(cu,cw) );
|
||||||
|
return mat3( cu, cv, cw );
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 nCoord = (gl_FragCoord.xy - screenCenter.xy)/screenCenter.y;
|
||||||
|
mat3 ca = setCamera();
|
||||||
|
|
||||||
|
// focal length
|
||||||
|
float fl = length(camDir);
|
||||||
|
vec3 rd = ca * normalize( vec3(nCoord,fl) );
|
||||||
|
vec3 color = vec3(nCoord/2.0 + 0.5, 0.0);
|
||||||
|
float depth = gl_FragCoord.z;
|
||||||
|
{
|
||||||
|
vec4 res = render( camPos - vec3(0.0, 0.0, 0.0) , rd );
|
||||||
|
color = res.xyz;
|
||||||
|
depth = CalcDepth(rd,res.w);
|
||||||
|
}
|
||||||
|
gl_FragColor = vec4(color , 1.0);
|
||||||
|
gl_FragDepth = depth;
|
||||||
|
}
|
||||||
212
examples/shaders/shaders_hybrid_render.c
Normal file
212
examples/shaders/shaders_hybrid_render.c
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* raylib [shaders] example - Hybrid Rendering
|
||||||
|
*
|
||||||
|
* Example originally created with raylib 4.2, last time updated with raylib 4.2
|
||||||
|
*
|
||||||
|
* Example contributed by Buğra Alptekin Sarı (@BugraAlptekinSari) 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) 2022-2023 Buğra Alptekin Sarı (@BugraAlptekinSari)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "rlgl.h"
|
||||||
|
#include "math.h" // Used for tan()
|
||||||
|
#include "raymath.h" // Used to calculate camera Direction
|
||||||
|
|
||||||
|
#if defined(PLATFORM_DESKTOP)
|
||||||
|
#define GLSL_VERSION 330
|
||||||
|
#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
|
||||||
|
#define GLSL_VERSION 100
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
// Declare custom functions required for the example
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
// Load custom render texture, create a writable depth texture buffer
|
||||||
|
static RenderTexture2D LoadRenderTextureDepthTex(int width, int height);
|
||||||
|
// Unload render texture from GPU memory (VRAM)
|
||||||
|
static void UnloadRenderTextureDepthTex(RenderTexture2D target);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
// Declare custom Structs
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int camPos, camDir, screenCenter;
|
||||||
|
}RayLocs ;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
// Program main entry point
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
// Initialization
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
const int screenWidth = 800;
|
||||||
|
const int screenHeight = 450;
|
||||||
|
|
||||||
|
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - write depth buffer");
|
||||||
|
|
||||||
|
// This Shader calculates pixel depth and color using raymarch.
|
||||||
|
Shader raymarch_shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/hybrid_raymarch.fs", GLSL_VERSION));
|
||||||
|
// This Shader is a standard rasterization fragment shader with the addition of depth writing. You are required to write depth for all shaders if one shader does it.
|
||||||
|
Shader raster_shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/hybrid_raster.fs", GLSL_VERSION));
|
||||||
|
|
||||||
|
// Declare Struct used to store camera locs.
|
||||||
|
RayLocs march_locs = {0};
|
||||||
|
|
||||||
|
// Fill the struct with shader locs.
|
||||||
|
march_locs.camPos = GetShaderLocation(raymarch_shader, "camPos");
|
||||||
|
march_locs.camDir = GetShaderLocation(raymarch_shader, "camDir");
|
||||||
|
march_locs.screenCenter = GetShaderLocation(raymarch_shader, "screenCenter");
|
||||||
|
|
||||||
|
{ // Transfer screenCenter position to shader. Which is used to calculate ray direction.
|
||||||
|
Vector2 screenCenter = {.x = screenWidth/2.0, .y = screenHeight/2.0};
|
||||||
|
SetShaderValue(raymarch_shader, march_locs.screenCenter , &screenCenter , SHADER_UNIFORM_VEC2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Use Customized function to create writable depth texture buffer
|
||||||
|
RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight);
|
||||||
|
|
||||||
|
// Define the camera to look into our 3d world
|
||||||
|
Camera camera = {
|
||||||
|
.position = (Vector3){ 0.5f, 1.0f, 1.5f }, // Camera position
|
||||||
|
.target = (Vector3){ 0.0f, 0.5f, 0.0f }, // Camera looking at point
|
||||||
|
.up = (Vector3){ 0.0f, 1.0f, 0.0f }, // Camera up vector (rotation towards target)
|
||||||
|
.fovy = 45.0f, // Camera field-of-view Y
|
||||||
|
.projection = CAMERA_PERSPECTIVE // Camera mode type
|
||||||
|
};
|
||||||
|
|
||||||
|
// Camera FOV is pre-calculated in the camera Distance.
|
||||||
|
double camDist = 1.0/(tan(camera.fovy*0.5*DEG2RAD));
|
||||||
|
|
||||||
|
SetCameraMode(camera, CAMERA_FIRST_PERSON);
|
||||||
|
|
||||||
|
SetTargetFPS(60);
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Main game loop
|
||||||
|
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||||
|
{
|
||||||
|
// Update
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
UpdateCamera(&camera);
|
||||||
|
|
||||||
|
//Update Camera Postion in the ray march shader.
|
||||||
|
SetShaderValue(raymarch_shader, march_locs.camPos, &(camera.position), RL_SHADER_UNIFORM_VEC3);
|
||||||
|
|
||||||
|
{ // Update Camera Looking Vector. Vector length determines FOV.
|
||||||
|
Vector3 camDir = Vector3Scale( Vector3Normalize( Vector3Subtract(camera.target, camera.position)) , camDist);
|
||||||
|
SetShaderValue(raymarch_shader, march_locs.camDir, &(camDir), RL_SHADER_UNIFORM_VEC3);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Draw into our custom render texture (framebuffer)
|
||||||
|
BeginTextureMode(target);
|
||||||
|
ClearBackground(WHITE);
|
||||||
|
|
||||||
|
// Raymarch Scene
|
||||||
|
rlEnableDepthTest(); //Manually enable Depth Test to handle multiple rendering methods.
|
||||||
|
BeginShaderMode(raymarch_shader);
|
||||||
|
DrawRectangleRec((Rectangle){0,0,screenWidth,screenHeight},WHITE);
|
||||||
|
EndShaderMode();
|
||||||
|
|
||||||
|
// Raserize Scene
|
||||||
|
BeginMode3D(camera);
|
||||||
|
BeginShaderMode(raster_shader);
|
||||||
|
DrawCubeWiresV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, RED);
|
||||||
|
DrawCubeV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, PURPLE);
|
||||||
|
DrawCubeWiresV((Vector3){ 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, DARKGREEN);
|
||||||
|
DrawCubeV((Vector3) { 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, YELLOW);
|
||||||
|
DrawGrid(10, 1.0f);
|
||||||
|
EndShaderMode();
|
||||||
|
EndMode3D();
|
||||||
|
EndTextureMode();
|
||||||
|
|
||||||
|
// Draw into screen our custom render texture
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
DrawTextureRec(target.texture, (Rectangle) { 0, 0, screenWidth, -screenHeight }, (Vector2) { 0, 0 }, WHITE);
|
||||||
|
DrawFPS(10, 10);
|
||||||
|
EndDrawing();
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
}
|
||||||
|
|
||||||
|
// De-Initialization
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
UnloadRenderTextureDepthTex(target);
|
||||||
|
UnloadShader(raymarch_shader);
|
||||||
|
UnloadShader(raster_shader);
|
||||||
|
|
||||||
|
CloseWindow(); // Close window and OpenGL context
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
// Define custom functions required for the example
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
// Load custom render texture, create a writable depth texture buffer
|
||||||
|
RenderTexture2D LoadRenderTextureDepthTex(int width, int height)
|
||||||
|
{
|
||||||
|
RenderTexture2D target = { 0 };
|
||||||
|
|
||||||
|
target.id = rlLoadFramebuffer(width, height); // Load an empty framebuffer
|
||||||
|
|
||||||
|
if (target.id > 0)
|
||||||
|
{
|
||||||
|
rlEnableFramebuffer(target.id);
|
||||||
|
|
||||||
|
// Create color texture (default to RGBA)
|
||||||
|
target.texture.id = rlLoadTexture(0, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
|
||||||
|
target.texture.width = width;
|
||||||
|
target.texture.height = height;
|
||||||
|
target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
|
||||||
|
target.texture.mipmaps = 1;
|
||||||
|
|
||||||
|
// Create depth texture buffer (instead of raylib default renderbuffer)
|
||||||
|
target.depth.id = rlLoadTextureDepth(width, height, false);
|
||||||
|
target.depth.width = width;
|
||||||
|
target.depth.height = height;
|
||||||
|
target.depth.format = 19; //DEPTH_COMPONENT_24BIT?
|
||||||
|
target.depth.mipmaps = 1;
|
||||||
|
|
||||||
|
// Attach color texture and depth texture to FBO
|
||||||
|
rlFramebufferAttach(target.id, target.texture.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
|
||||||
|
rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
|
||||||
|
|
||||||
|
// Check if fbo is complete with attachments (valid)
|
||||||
|
if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
|
||||||
|
|
||||||
|
rlDisableFramebuffer();
|
||||||
|
}
|
||||||
|
else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unload render texture from GPU memory (VRAM)
|
||||||
|
void UnloadRenderTextureDepthTex(RenderTexture2D target)
|
||||||
|
{
|
||||||
|
if (target.id > 0)
|
||||||
|
{
|
||||||
|
// Color texture attached to FBO is deleted
|
||||||
|
rlUnloadTexture(target.texture.id);
|
||||||
|
rlUnloadTexture(target.depth.id);
|
||||||
|
|
||||||
|
// NOTE: Depth texture is automatically
|
||||||
|
// queried and deleted before deleting framebuffer
|
||||||
|
rlUnloadFramebuffer(target.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
examples/shaders/shaders_hybrid_render.png
Normal file
BIN
examples/shaders/shaders_hybrid_render.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
BIN
examples/shaders/shaders_write_depth.png
Normal file
BIN
examples/shaders/shaders_write_depth.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
|
|
@ -1,6 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.LibExeObjStep {
|
pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
|
||||||
const raylib_flags = &[_][]const u8{
|
const raylib_flags = &[_][]const u8{
|
||||||
"-std=gnu99",
|
"-std=gnu99",
|
||||||
"-D_GNU_SOURCE",
|
"-D_GNU_SOURCE",
|
||||||
|
|
@ -8,8 +8,11 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.
|
||||||
"-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891
|
"-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891
|
||||||
};
|
};
|
||||||
|
|
||||||
const raylib = b.addStaticLibrary("raylib", null);
|
const raylib = b.addStaticLibrary(.{
|
||||||
raylib.setTarget(target);
|
.name = "raylib",
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
raylib.linkLibC();
|
raylib.linkLibC();
|
||||||
|
|
||||||
raylib.addIncludePath(srcdir ++ "/external/glfw/include");
|
raylib.addIncludePath(srcdir ++ "/external/glfw/include");
|
||||||
|
|
@ -100,14 +103,18 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.
|
||||||
return raylib;
|
return raylib;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(b: *std.build.Builder) void {
|
pub fn build(b: *std.Build) void {
|
||||||
// Standard target options allows the person running `zig build` to choose
|
// Standard target options allows the person running `zig build` to choose
|
||||||
// what target to build for. Here we do not override the defaults, which
|
// what target to build for. Here we do not override the defaults, which
|
||||||
// means any target is allowed, and the default is native. Other options
|
// means any target is allowed, and the default is native. Other options
|
||||||
// for restricting supported target set are available.
|
// for restricting supported target set are available.
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
const lib = addRaylib(b, target);
|
const lib = addRaylib(b, target, optimize);
|
||||||
lib.setOutputDir(srcdir);
|
lib.setOutputDir(srcdir);
|
||||||
lib.install();
|
lib.install();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
165
src/config.h
165
src/config.h
|
|
@ -24,66 +24,68 @@
|
||||||
* 3. This notice may not be removed or altered from any source distribution.
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
*
|
*
|
||||||
**********************************************************************************************/
|
**********************************************************************************************/
|
||||||
|
|
||||||
#ifndef CONFIG_H
|
#ifndef CONFIG_H
|
||||||
#define CONFIG_H
|
#define CONFIG_H
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Module selection - Some modules could be avoided
|
// Module selection - Some modules could be avoided
|
||||||
// Mandatory modules: rcore, rlgl, utils
|
// Mandatory modules: rcore, rlgl, utils
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
#define SUPPORT_MODULE_RSHAPES 1
|
#define SUPPORT_MODULE_RSHAPES 1
|
||||||
#define SUPPORT_MODULE_RTEXTURES 1
|
#define SUPPORT_MODULE_RTEXTURES 1
|
||||||
#define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures
|
#define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures
|
||||||
#define SUPPORT_MODULE_RMODELS 1
|
#define SUPPORT_MODULE_RMODELS 1
|
||||||
#define SUPPORT_MODULE_RAUDIO 1
|
#define SUPPORT_MODULE_RAUDIO 1
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Module: rcore - Configuration Flags
|
// Module: rcore - Configuration Flags
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital
|
// Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital
|
||||||
#define SUPPORT_CAMERA_SYSTEM 1
|
#define SUPPORT_CAMERA_SYSTEM 1
|
||||||
// Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
|
// Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
|
||||||
#define SUPPORT_GESTURES_SYSTEM 1
|
#define SUPPORT_GESTURES_SYSTEM 1
|
||||||
// Mouse gestures are directly mapped like touches and processed by gestures system
|
// Mouse gestures are directly mapped like touches and processed by gestures system
|
||||||
#define SUPPORT_MOUSE_GESTURES 1
|
#define SUPPORT_MOUSE_GESTURES 1
|
||||||
// Reconfigure standard input to receive key inputs, works with SSH connection.
|
// Reconfigure standard input to receive key inputs, works with SSH connection.
|
||||||
#define SUPPORT_SSH_KEYBOARD_RPI 1
|
#define SUPPORT_SSH_KEYBOARD_RPI 1
|
||||||
// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
|
// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
|
||||||
// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
|
// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
|
||||||
#define SUPPORT_WINMM_HIGHRES_TIMER 1
|
#define SUPPORT_WINMM_HIGHRES_TIMER 1
|
||||||
// Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
|
// Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used
|
||||||
//#define SUPPORT_BUSY_WAIT_LOOP 1
|
//#define SUPPORT_BUSY_WAIT_LOOP 1
|
||||||
// Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy
|
// Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy
|
||||||
#define SUPPORT_PARTIALBUSY_WAIT_LOOP
|
#define SUPPORT_PARTIALBUSY_WAIT_LOOP
|
||||||
// Wait for events passively (sleeping while no events) instead of polling them actively every frame
|
// Wait for events passively (sleeping while no events) instead of polling them actively every frame
|
||||||
//#define SUPPORT_EVENTS_WAITING 1
|
//#define SUPPORT_EVENTS_WAITING 1
|
||||||
// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
|
// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
|
||||||
#define SUPPORT_SCREEN_CAPTURE 1
|
#define SUPPORT_SCREEN_CAPTURE 1
|
||||||
// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
|
// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
|
||||||
#define SUPPORT_GIF_RECORDING 1
|
#define SUPPORT_GIF_RECORDING 1
|
||||||
// Support CompressData() and DecompressData() functions
|
// Support CompressData() and DecompressData() functions
|
||||||
#define SUPPORT_COMPRESSION_API 1
|
#define SUPPORT_COMPRESSION_API 1
|
||||||
// Support automatic generated events, loading and recording of those events when required
|
// Support automatic generated events, loading and recording of those events when required
|
||||||
//#define SUPPORT_EVENTS_AUTOMATION 1
|
//#define SUPPORT_EVENTS_AUTOMATION 1
|
||||||
// Support custom frame control, only for advance users
|
// Support custom frame control, only for advance users
|
||||||
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents()
|
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
|
||||||
// Enabling this flag allows manual control of the frame processes, use at your own risk
|
// Enabling this flag allows manual control of the frame processes, use at your own risk
|
||||||
//#define SUPPORT_CUSTOM_FRAME_CONTROL 1
|
//#define SUPPORT_CUSTOM_FRAME_CONTROL 1
|
||||||
|
|
||||||
// rcore: Configuration values
|
// rcore: Configuration values
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
#define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity
|
#define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity
|
||||||
#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
|
#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
|
||||||
|
|
||||||
#define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
|
#define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
|
||||||
#define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
|
#define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
|
||||||
#define MAX_GAMEPADS 4 // Maximum number of gamepads 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_AXIS 8 // Maximum number of axis supported (per gamepad)
|
||||||
#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
|
#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
|
||||||
#define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
|
#define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
|
||||||
#define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
|
#define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
|
||||||
#define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
|
#define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
|
||||||
|
|
||||||
#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB
|
#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
|
|
@ -110,12 +112,12 @@
|
||||||
|
|
||||||
// Default shader vertex attribute names to set location points
|
// Default shader vertex attribute names to set location points
|
||||||
// NOTE: When a new shader is loaded, the following locations are tried to be set for convenience
|
// NOTE: When a new shader is loaded, the following locations are tried to be set for convenience
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Binded by default to shader location: 0
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Binded by default to shader location: 1
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Binded by default to shader location: 2
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Binded by default to shader location: 3
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Binded by default to shader location: 4
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Binded by default to shader location: 5
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5
|
||||||
|
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
|
||||||
|
|
@ -133,36 +135,36 @@
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Use QUADS instead of TRIANGLES for drawing when possible
|
// Use QUADS instead of TRIANGLES for drawing when possible
|
||||||
// Some lines-based shapes could still use lines
|
// Some lines-based shapes could still use lines
|
||||||
#define SUPPORT_QUADS_DRAW_MODE 1
|
#define SUPPORT_QUADS_DRAW_MODE 1
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Module: rtextures - Configuration Flags
|
// Module: rtextures - Configuration Flags
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Selecte desired fileformats to be supported for image data loading
|
// Select the desired fileformats to be supported for image data loading
|
||||||
#define SUPPORT_FILEFORMAT_PNG 1
|
#define SUPPORT_FILEFORMAT_PNG 1
|
||||||
//#define SUPPORT_FILEFORMAT_BMP 1
|
//#define SUPPORT_FILEFORMAT_BMP 1
|
||||||
//#define SUPPORT_FILEFORMAT_TGA 1
|
//#define SUPPORT_FILEFORMAT_TGA 1
|
||||||
//#define SUPPORT_FILEFORMAT_JPG 1
|
//#define SUPPORT_FILEFORMAT_JPG 1
|
||||||
#define SUPPORT_FILEFORMAT_GIF 1
|
#define SUPPORT_FILEFORMAT_GIF 1
|
||||||
#define SUPPORT_FILEFORMAT_QOI 1
|
#define SUPPORT_FILEFORMAT_QOI 1
|
||||||
//#define SUPPORT_FILEFORMAT_PSD 1
|
//#define SUPPORT_FILEFORMAT_PSD 1
|
||||||
#define SUPPORT_FILEFORMAT_DDS 1
|
#define SUPPORT_FILEFORMAT_DDS 1
|
||||||
#define SUPPORT_FILEFORMAT_HDR 1
|
#define SUPPORT_FILEFORMAT_HDR 1
|
||||||
//#define SUPPORT_FILEFORMAT_PIC 1
|
//#define SUPPORT_FILEFORMAT_PIC 1
|
||||||
//#define SUPPORT_FILEFORMAT_PNM 1
|
//#define SUPPORT_FILEFORMAT_PNM 1
|
||||||
//#define SUPPORT_FILEFORMAT_KTX 1
|
//#define SUPPORT_FILEFORMAT_KTX 1
|
||||||
//#define SUPPORT_FILEFORMAT_ASTC 1
|
//#define SUPPORT_FILEFORMAT_ASTC 1
|
||||||
//#define SUPPORT_FILEFORMAT_PKM 1
|
//#define SUPPORT_FILEFORMAT_PKM 1
|
||||||
//#define SUPPORT_FILEFORMAT_PVR 1
|
//#define SUPPORT_FILEFORMAT_PVR 1
|
||||||
|
|
||||||
// Support image export functionality (.png, .bmp, .tga, .jpg, .qoi)
|
// Support image export functionality (.png, .bmp, .tga, .jpg, .qoi)
|
||||||
#define SUPPORT_IMAGE_EXPORT 1
|
#define SUPPORT_IMAGE_EXPORT 1
|
||||||
// Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)
|
// Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)
|
||||||
#define SUPPORT_IMAGE_GENERATION 1
|
#define SUPPORT_IMAGE_GENERATION 1
|
||||||
// Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
|
// Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
|
||||||
// If not defined, still some functions are supported: ImageFormat(), ImageCrop(), ImageToPOT()
|
// If not defined, still some functions are supported: ImageFormat(), ImageCrop(), ImageToPOT()
|
||||||
#define SUPPORT_IMAGE_MANIPULATION 1
|
#define SUPPORT_IMAGE_MANIPULATION 1
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
|
|
@ -170,51 +172,52 @@
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Default font is loaded on window initialization to be available for the user to render simple text
|
// Default font is loaded on window initialization to be available for the user to render simple text
|
||||||
// NOTE: If enabled, uses external module functions to load default raylib font
|
// NOTE: If enabled, uses external module functions to load default raylib font
|
||||||
#define SUPPORT_DEFAULT_FONT 1
|
#define SUPPORT_DEFAULT_FONT 1
|
||||||
// Selected desired font fileformats to be supported for loading
|
// Selected desired font fileformats to be supported for loading
|
||||||
#define SUPPORT_FILEFORMAT_FNT 1
|
#define SUPPORT_FILEFORMAT_FNT 1
|
||||||
#define SUPPORT_FILEFORMAT_TTF 1
|
#define SUPPORT_FILEFORMAT_TTF 1
|
||||||
|
|
||||||
// Support text management functions
|
// Support text management functions
|
||||||
// If not defined, still some functions are supported: TextLength(), TextFormat()
|
// If not defined, still some functions are supported: TextLength(), TextFormat()
|
||||||
#define SUPPORT_TEXT_MANIPULATION 1
|
#define SUPPORT_TEXT_MANIPULATION 1
|
||||||
|
|
||||||
// rtext: Configuration values
|
// rtext: Configuration values
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
|
#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
|
||||||
// TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
|
// TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
|
||||||
#define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit()
|
#define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit()
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Module: rmodels - Configuration Flags
|
// Module: rmodels - Configuration Flags
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Selected desired model fileformats to be supported for loading
|
// Selected desired model fileformats to be supported for loading
|
||||||
#define SUPPORT_FILEFORMAT_OBJ 1
|
#define SUPPORT_FILEFORMAT_OBJ 1
|
||||||
#define SUPPORT_FILEFORMAT_MTL 1
|
#define SUPPORT_FILEFORMAT_MTL 1
|
||||||
#define SUPPORT_FILEFORMAT_IQM 1
|
#define SUPPORT_FILEFORMAT_IQM 1
|
||||||
#define SUPPORT_FILEFORMAT_GLTF 1
|
#define SUPPORT_FILEFORMAT_GLTF 1
|
||||||
#define SUPPORT_FILEFORMAT_VOX 1
|
#define SUPPORT_FILEFORMAT_VOX 1
|
||||||
#define SUPPORT_FILEFORMAT_M3D 1
|
#define SUPPORT_FILEFORMAT_M3D 1
|
||||||
// Support procedural mesh generation functions, uses external par_shapes.h library
|
// Support procedural mesh generation functions, uses external par_shapes.h library
|
||||||
// NOTE: Some generated meshes DO NOT include generated texture coordinates
|
// NOTE: Some generated meshes DO NOT include generated texture coordinates
|
||||||
#define SUPPORT_MESH_GENERATION 1
|
#define SUPPORT_MESH_GENERATION 1
|
||||||
|
|
||||||
// rmodels: Configuration values
|
// rmodels: Configuration values
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
#define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported
|
#define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported
|
||||||
#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh
|
#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Module: raudio - Configuration Flags
|
// Module: raudio - Configuration Flags
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Desired audio fileformats to be supported for loading
|
// Desired audio fileformats to be supported for loading
|
||||||
#define SUPPORT_FILEFORMAT_WAV 1
|
#define SUPPORT_FILEFORMAT_WAV 1
|
||||||
#define SUPPORT_FILEFORMAT_OGG 1
|
#define SUPPORT_FILEFORMAT_OGG 1
|
||||||
#define SUPPORT_FILEFORMAT_XM 1
|
#define SUPPORT_FILEFORMAT_MP3 1
|
||||||
#define SUPPORT_FILEFORMAT_MOD 1
|
//#define SUPPORT_FILEFORMAT_QOA 1
|
||||||
#define SUPPORT_FILEFORMAT_MP3 1
|
//#define SUPPORT_FILEFORMAT_FLAC 1
|
||||||
//#define SUPPORT_FILEFORMAT_FLAC 1
|
#define SUPPORT_FILEFORMAT_XM 1
|
||||||
|
#define SUPPORT_FILEFORMAT_MOD 1
|
||||||
|
|
||||||
// raudio: Configuration values
|
// raudio: Configuration values
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
|
|
@ -228,14 +231,14 @@
|
||||||
// Module: utils - Configuration Flags
|
// Module: utils - Configuration Flags
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Standard file io library (stdio.h) included
|
// Standard file io library (stdio.h) included
|
||||||
#define SUPPORT_STANDARD_FILEIO
|
#define SUPPORT_STANDARD_FILEIO 1
|
||||||
// Show TRACELOG() output messages
|
// Show TRACELOG() output messages
|
||||||
// NOTE: By default LOG_DEBUG traces not shown
|
// NOTE: By default LOG_DEBUG traces not shown
|
||||||
#define SUPPORT_TRACELOG 1
|
#define SUPPORT_TRACELOG 1
|
||||||
//#define SUPPORT_TRACELOG_DEBUG 1
|
//#define SUPPORT_TRACELOG_DEBUG 1
|
||||||
|
|
||||||
// utils: Configuration values
|
// utils: Configuration values
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
#define MAX_TRACELOG_MSG_LENGTH 128 // Max length of one trace-log message
|
#define MAX_TRACELOG_MSG_LENGTH 128 // Max length of one trace-log message
|
||||||
|
|
||||||
#endif // CONFIG_H
|
#endif // CONFIG_H
|
||||||
|
|
|
||||||
660
src/external/qoa.h
vendored
Normal file
660
src/external/qoa.h
vendored
Normal file
|
|
@ -0,0 +1,660 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2023, Dominic Szablewski - https://phoboslab.org
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
QOA - The "Quite OK Audio" format for fast, lossy audio compression
|
||||||
|
|
||||||
|
|
||||||
|
-- Data Format
|
||||||
|
|
||||||
|
A QOA file has an 8 byte file header, followed by a number of frames. Each frame
|
||||||
|
consists of an 8 byte frame header, the current 8 byte en-/decoder state per
|
||||||
|
channel and 256 slices per channel. Each slice is 8 bytes wide and encodes 20
|
||||||
|
samples of audio data.
|
||||||
|
|
||||||
|
Note that the last frame of a file may contain less than 256 slices per channel.
|
||||||
|
The last slice (per channel) in the last frame may contain less 20 samples, but
|
||||||
|
the slice will still be 8 bytes wide, with the unused samples zeroed out.
|
||||||
|
|
||||||
|
The samplerate and number of channels is only stated in the frame headers, but
|
||||||
|
not in the file header. A decoder may peek into the first frame of the file to
|
||||||
|
find these values.
|
||||||
|
|
||||||
|
In a valid QOA file all frames have the same number of channels and the same
|
||||||
|
samplerate. These restriction may be releaxed for streaming. This remains to
|
||||||
|
be decided.
|
||||||
|
|
||||||
|
All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file,
|
||||||
|
including the headers, is 64 bit aligned, so it's possible to read files with
|
||||||
|
just a read_u64() that does the byte swapping if neccessary.
|
||||||
|
|
||||||
|
In pseudocode, the file layout is as follows:
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
char magic[4]; // magic bytes 'qoaf'
|
||||||
|
uint32_t samples; // number of samples per channel in this file
|
||||||
|
} file_header; // = 64 bits
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
uint8_t num_channels; // number of channels
|
||||||
|
uint24_t samplerate; // samplerate in hz
|
||||||
|
uint16_t fsamples; // sample count per channel in this frame
|
||||||
|
uint16_t fsize; // frame size (including the frame header)
|
||||||
|
} frame_header; // = 64 bits
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int16_t history[4]; // = 64 bits
|
||||||
|
int16_t weights[4]; // = 64 bits
|
||||||
|
} lms_state[num_channels];
|
||||||
|
|
||||||
|
qoa_slice_t slices[256][num_channels]; // = 64 bits each
|
||||||
|
} frames[samples * channels / qoa_max_framesize()];
|
||||||
|
} qoa_file;
|
||||||
|
|
||||||
|
Wheras the 64bit qoa_slice_t is defined as follows:
|
||||||
|
|
||||||
|
.- QOA_SLICE -- 64 bits, 20 samples --------------------------/ /------------.
|
||||||
|
| Byte[0] | Byte[1] | Byte[2] \ \ Byte[7] |
|
||||||
|
| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 / / 2 1 0 |
|
||||||
|
|------------+--------+--------+--------+---------+---------+-\ \--+---------|
|
||||||
|
| sf_index | r00 | r01 | r02 | r03 | r04 | / / | r19 |
|
||||||
|
`-------------------------------------------------------------\ \------------`
|
||||||
|
|
||||||
|
`sf_index` defines the scalefactor to use for this slice as an index into the
|
||||||
|
qoa_scalefactor_tab[16]
|
||||||
|
|
||||||
|
`r00`--`r19` are the residuals for the individiual samples, divided by the
|
||||||
|
scalefactor and quantized by the qoa_quant_tab[].
|
||||||
|
|
||||||
|
In the decoder, a prediction of the next sample is computed by multiplying the
|
||||||
|
state (the last four output samples) with the predictor. The residual from the
|
||||||
|
slice is then dequantized using the qoa_dequant_tab[] and added to the
|
||||||
|
prediction. The result is clamped to int16 to form the final output sample.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Header - Public functions */
|
||||||
|
|
||||||
|
#ifndef QOA_H
|
||||||
|
#define QOA_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define QOA_MIN_FILESIZE 16
|
||||||
|
#define QOA_MAX_CHANNELS 8
|
||||||
|
|
||||||
|
#define QOA_SLICE_LEN 20
|
||||||
|
#define QOA_SLICES_PER_FRAME 256
|
||||||
|
#define QOA_FRAME_LEN (QOA_SLICES_PER_FRAME * QOA_SLICE_LEN)
|
||||||
|
#define QOA_LMS_LEN 4
|
||||||
|
#define QOA_MAGIC 0x716f6166 /* 'qoaf' */
|
||||||
|
|
||||||
|
#define QOA_FRAME_SIZE(channels, slices) \
|
||||||
|
(8 + QOA_LMS_LEN * 4 * channels + 8 * slices * channels)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int history[QOA_LMS_LEN];
|
||||||
|
int weights[QOA_LMS_LEN];
|
||||||
|
} qoa_lms_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int channels;
|
||||||
|
unsigned int samplerate;
|
||||||
|
unsigned int samples;
|
||||||
|
qoa_lms_t lms[QOA_MAX_CHANNELS];
|
||||||
|
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||||
|
double error;
|
||||||
|
#endif
|
||||||
|
} qoa_desc;
|
||||||
|
|
||||||
|
unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes);
|
||||||
|
unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes);
|
||||||
|
void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len);
|
||||||
|
|
||||||
|
unsigned int qoa_max_frame_size(qoa_desc *qoa);
|
||||||
|
unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa);
|
||||||
|
unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len);
|
||||||
|
short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *file);
|
||||||
|
|
||||||
|
#ifndef QOA_NO_STDIO
|
||||||
|
|
||||||
|
int qoa_write(const char *filename, const short *sample_data, qoa_desc *qoa);
|
||||||
|
void *qoa_read(const char *filename, qoa_desc *qoa);
|
||||||
|
|
||||||
|
#endif /* QOA_NO_STDIO */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* QOA_H */
|
||||||
|
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Implementation */
|
||||||
|
|
||||||
|
#ifdef QOA_IMPLEMENTATION
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef QOA_MALLOC
|
||||||
|
#define QOA_MALLOC(sz) malloc(sz)
|
||||||
|
#define QOA_FREE(p) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef unsigned long long qoa_uint64_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* The quant_tab provides an index into the dequant_tab for residuals in the
|
||||||
|
range of -8 .. 8. It maps this range to just 3bits and becommes less accurate at
|
||||||
|
the higher end. Note that the residual zero is identical to the lowest positive
|
||||||
|
value. This is mostly fine, since the qoa_div() function always rounds away
|
||||||
|
from zero. */
|
||||||
|
|
||||||
|
static int qoa_quant_tab[17] = {
|
||||||
|
7, 7, 7, 5, 5, 3, 3, 1, /* -8..-1 */
|
||||||
|
0, /* 0 */
|
||||||
|
0, 2, 2, 4, 4, 6, 6, 6 /* 1.. 8 */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* We have 16 different scalefactors. Like the quantized residuals these become
|
||||||
|
less accurate at the higher end. In theory, the highest scalefactor that we
|
||||||
|
would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we
|
||||||
|
rely on the LMS filter to predict samples accurately enough that a maximum
|
||||||
|
residual of one quarter of the 16 bit range is high sufficent. I.e. with the
|
||||||
|
scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14.
|
||||||
|
|
||||||
|
The scalefactor values are computed as:
|
||||||
|
scalefactor_tab[s] <- round(pow(s + 1, 2.75)) */
|
||||||
|
|
||||||
|
static int qoa_scalefactor_tab[16] = {
|
||||||
|
1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* The reciprocal_tab maps each of the 16 scalefactors to their rounded
|
||||||
|
reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in
|
||||||
|
the encoder with just one multiplication instead of an expensive division. We
|
||||||
|
do this in .16 fixed point with integers, instead of floats.
|
||||||
|
|
||||||
|
The reciprocal_tab is computed as:
|
||||||
|
reciprocal_tab[s] <- ((1<<16) + scalefactor_tab[s] - 1) / scalefactor_tab[s] */
|
||||||
|
|
||||||
|
static int qoa_reciprocal_tab[16] = {
|
||||||
|
65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* The dequant_tab maps each of the scalefactors and quantized residuals to
|
||||||
|
their unscaled & dequantized version.
|
||||||
|
|
||||||
|
Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4
|
||||||
|
instead of 1. The dequant_tab assumes the following dequantized values for each
|
||||||
|
of the quant_tab indices and is computed as:
|
||||||
|
float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7};
|
||||||
|
dequant_tab[s][q] <- round(scalefactor_tab[s] * dqt[q]) */
|
||||||
|
|
||||||
|
static int qoa_dequant_tab[16][8] = {
|
||||||
|
{ 1, -1, 3, -3, 5, -5, 7, -7},
|
||||||
|
{ 5, -5, 18, -18, 32, -32, 49, -49},
|
||||||
|
{ 16, -16, 53, -53, 95, -95, 147, -147},
|
||||||
|
{ 34, -34, 113, -113, 203, -203, 315, -315},
|
||||||
|
{ 63, -63, 210, -210, 378, -378, 588, -588},
|
||||||
|
{ 104, -104, 345, -345, 621, -621, 966, -966},
|
||||||
|
{ 158, -158, 528, -528, 950, -950, 1477, -1477},
|
||||||
|
{ 228, -228, 760, -760, 1368, -1368, 2128, -2128},
|
||||||
|
{ 316, -316, 1053, -1053, 1895, -1895, 2947, -2947},
|
||||||
|
{ 422, -422, 1405, -1405, 2529, -2529, 3934, -3934},
|
||||||
|
{ 548, -548, 1828, -1828, 3290, -3290, 5117, -5117},
|
||||||
|
{ 696, -696, 2320, -2320, 4176, -4176, 6496, -6496},
|
||||||
|
{ 868, -868, 2893, -2893, 5207, -5207, 8099, -8099},
|
||||||
|
{1064, -1064, 3548, -3548, 6386, -6386, 9933, -9933},
|
||||||
|
{1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005},
|
||||||
|
{1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* The Least Mean Squares Filter is the heart of QOA. It predicts the next
|
||||||
|
sample based on the previous 4 reconstructed samples. It does so by continuously
|
||||||
|
adjusting 4 weights based on the residual of the previous prediction.
|
||||||
|
|
||||||
|
The next sample is predicted as the sum of (weight[i] * history[i]).
|
||||||
|
|
||||||
|
The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or
|
||||||
|
subtracts the residual to each weight, based on the corresponding sample from
|
||||||
|
the history. This, suprisingly, is sufficent to get worthwhile predictions.
|
||||||
|
|
||||||
|
This is all done with fixed point integers. Hence the right-shifts when updating
|
||||||
|
the weights and calculating the prediction. */
|
||||||
|
|
||||||
|
static int qoa_lms_predict(qoa_lms_t *lms) {
|
||||||
|
int prediction = 0;
|
||||||
|
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||||
|
prediction += lms->weights[i] * lms->history[i];
|
||||||
|
}
|
||||||
|
return prediction >> 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qoa_lms_update(qoa_lms_t *lms, int sample, int residual) {
|
||||||
|
int delta = residual >> 4;
|
||||||
|
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||||
|
lms->weights[i] += lms->history[i] < 0 ? -delta : delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < QOA_LMS_LEN-1; i++) {
|
||||||
|
lms->history[i] = lms->history[i+1];
|
||||||
|
}
|
||||||
|
lms->history[QOA_LMS_LEN-1] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* qoa_div() implements a rounding division, but avoids rounding to zero for
|
||||||
|
small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still
|
||||||
|
returns as 0, which is handled in the qoa_quant_tab[].
|
||||||
|
qoa_div() takes an index into the .16 fixed point qoa_reciprocal_tab as an
|
||||||
|
argument, so it can do the division with a cheaper integer multiplication. */
|
||||||
|
|
||||||
|
static inline int qoa_div(int v, int scalefactor) {
|
||||||
|
int reciprocal = qoa_reciprocal_tab[scalefactor];
|
||||||
|
int n = (v * reciprocal + (1 << 15)) >> 16;
|
||||||
|
n = n + ((v > 0) - (v < 0)) - ((n > 0) - (n < 0)); /* round away from 0 */
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int qoa_clamp(int v, int min, int max) {
|
||||||
|
return (v < min) ? min : (v > max) ? max : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) {
|
||||||
|
bytes += *p;
|
||||||
|
*p += 8;
|
||||||
|
return
|
||||||
|
((qoa_uint64_t)(bytes[0]) << 56) | ((qoa_uint64_t)(bytes[1]) << 48) |
|
||||||
|
((qoa_uint64_t)(bytes[2]) << 40) | ((qoa_uint64_t)(bytes[3]) << 32) |
|
||||||
|
((qoa_uint64_t)(bytes[4]) << 24) | ((qoa_uint64_t)(bytes[5]) << 16) |
|
||||||
|
((qoa_uint64_t)(bytes[6]) << 8) | ((qoa_uint64_t)(bytes[7]) << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void qoa_write_u64(qoa_uint64_t v, unsigned char *bytes, unsigned int *p) {
|
||||||
|
bytes += *p;
|
||||||
|
*p += 8;
|
||||||
|
bytes[0] = (v >> 56) & 0xff;
|
||||||
|
bytes[1] = (v >> 48) & 0xff;
|
||||||
|
bytes[2] = (v >> 40) & 0xff;
|
||||||
|
bytes[3] = (v >> 32) & 0xff;
|
||||||
|
bytes[4] = (v >> 24) & 0xff;
|
||||||
|
bytes[5] = (v >> 16) & 0xff;
|
||||||
|
bytes[6] = (v >> 8) & 0xff;
|
||||||
|
bytes[7] = (v >> 0) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Encoder */
|
||||||
|
|
||||||
|
unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes) {
|
||||||
|
unsigned int p = 0;
|
||||||
|
qoa_write_u64(((qoa_uint64_t)QOA_MAGIC << 32) | qoa->samples, bytes, &p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes) {
|
||||||
|
unsigned int channels = qoa->channels;
|
||||||
|
|
||||||
|
unsigned int p = 0;
|
||||||
|
unsigned int slices = (frame_len + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN;
|
||||||
|
unsigned int frame_size = QOA_FRAME_SIZE(channels, slices);
|
||||||
|
|
||||||
|
/* Write the frame header */
|
||||||
|
qoa_write_u64((
|
||||||
|
(qoa_uint64_t)qoa->channels << 56 |
|
||||||
|
(qoa_uint64_t)qoa->samplerate << 32 |
|
||||||
|
(qoa_uint64_t)frame_len << 16 |
|
||||||
|
(qoa_uint64_t)frame_size
|
||||||
|
), bytes, &p);
|
||||||
|
|
||||||
|
/* Write the current LMS state */
|
||||||
|
for (int c = 0; c < channels; c++) {
|
||||||
|
qoa_uint64_t weights = 0;
|
||||||
|
qoa_uint64_t history = 0;
|
||||||
|
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||||
|
history = (history << 16) | (qoa->lms[c].history[i] & 0xffff);
|
||||||
|
weights = (weights << 16) | (qoa->lms[c].weights[i] & 0xffff);
|
||||||
|
}
|
||||||
|
qoa_write_u64(history, bytes, &p);
|
||||||
|
qoa_write_u64(weights, bytes, &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We encode all samples with the channels interleaved on a slice level.
|
||||||
|
E.g. for stereo: (ch-0, slice 0), (ch 1, slice 0), (ch 0, slice 1), ...*/
|
||||||
|
for (int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) {
|
||||||
|
|
||||||
|
for (int c = 0; c < channels; c++) {
|
||||||
|
int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index);
|
||||||
|
int slice_start = sample_index * channels + c;
|
||||||
|
int slice_end = (sample_index + slice_len) * channels + c;
|
||||||
|
|
||||||
|
/* Brute for search for the best scalefactor. Just go through all
|
||||||
|
16 scalefactors, encode all samples for the current slice and
|
||||||
|
meassure the total squared error. */
|
||||||
|
qoa_uint64_t best_error = -1;
|
||||||
|
qoa_uint64_t best_slice;
|
||||||
|
qoa_lms_t best_lms;
|
||||||
|
|
||||||
|
for (int scalefactor = 0; scalefactor < 16; scalefactor++) {
|
||||||
|
|
||||||
|
/* We have to reset the LMS state to the last known good one
|
||||||
|
before trying each scalefactor, as each pass updates the LMS
|
||||||
|
state when encoding. */
|
||||||
|
qoa_lms_t lms = qoa->lms[c];
|
||||||
|
qoa_uint64_t slice = scalefactor;
|
||||||
|
qoa_uint64_t current_error = 0;
|
||||||
|
|
||||||
|
for (int si = slice_start; si < slice_end; si += channels) {
|
||||||
|
int sample = sample_data[si];
|
||||||
|
int predicted = qoa_lms_predict(&lms);
|
||||||
|
|
||||||
|
int residual = sample - predicted;
|
||||||
|
int scaled = qoa_div(residual, scalefactor);
|
||||||
|
int clamped = qoa_clamp(scaled, -8, 8);
|
||||||
|
int quantized = qoa_quant_tab[clamped + 8];
|
||||||
|
int dequantized = qoa_dequant_tab[scalefactor][quantized];
|
||||||
|
int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767);
|
||||||
|
|
||||||
|
int error = (sample - reconstructed);
|
||||||
|
current_error += error * error;
|
||||||
|
if (current_error > best_error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qoa_lms_update(&lms, reconstructed, dequantized);
|
||||||
|
slice = (slice << 3) | quantized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_error < best_error) {
|
||||||
|
best_error = current_error;
|
||||||
|
best_slice = slice;
|
||||||
|
best_lms = lms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qoa->lms[c] = best_lms;
|
||||||
|
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||||
|
qoa->error += best_error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If this slice was shorter than QOA_SLICE_LEN, we have to left-
|
||||||
|
shift all encoded data, to ensure the rightmost bits are the empty
|
||||||
|
ones. This should only happen in the last frame of a file as all
|
||||||
|
slices are completely filled otherwise. */
|
||||||
|
best_slice <<= (QOA_SLICE_LEN - slice_len) * 3;
|
||||||
|
qoa_write_u64(best_slice, bytes, &p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) {
|
||||||
|
if (
|
||||||
|
qoa->samples == 0 ||
|
||||||
|
qoa->samplerate == 0 || qoa->samplerate > 0xffffff ||
|
||||||
|
qoa->channels == 0 || qoa->channels > QOA_MAX_CHANNELS
|
||||||
|
) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the encoded size and allocate */
|
||||||
|
unsigned int num_frames = (qoa->samples + QOA_FRAME_LEN-1) / QOA_FRAME_LEN;
|
||||||
|
unsigned int num_slices = (qoa->samples + QOA_SLICE_LEN-1) / QOA_SLICE_LEN;
|
||||||
|
unsigned int encoded_size = 8 + /* 8 byte file header */
|
||||||
|
num_frames * 8 + /* 8 byte frame headers */
|
||||||
|
num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */
|
||||||
|
num_slices * 8 * qoa->channels; /* 8 byte slices */
|
||||||
|
|
||||||
|
unsigned char *bytes = QOA_MALLOC(encoded_size);
|
||||||
|
|
||||||
|
for (int c = 0; c < qoa->channels; c++) {
|
||||||
|
/* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the
|
||||||
|
prediction of the first few ms of a file. */
|
||||||
|
qoa->lms[c].weights[0] = 0;
|
||||||
|
qoa->lms[c].weights[1] = 0;
|
||||||
|
qoa->lms[c].weights[2] = -(1<<13);
|
||||||
|
qoa->lms[c].weights[3] = (1<<14);
|
||||||
|
|
||||||
|
/* Explicitly set the history samples to 0, as we might have some
|
||||||
|
garbage in there. */
|
||||||
|
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||||
|
qoa->lms[c].history[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Encode the header and go through all frames */
|
||||||
|
unsigned int p = qoa_encode_header(qoa, bytes);
|
||||||
|
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||||
|
qoa->error = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int frame_len = QOA_FRAME_LEN;
|
||||||
|
for (int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) {
|
||||||
|
frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index);
|
||||||
|
const short *frame_samples = sample_data + sample_index * qoa->channels;
|
||||||
|
unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p);
|
||||||
|
p += frame_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_len = p;
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Decoder */
|
||||||
|
|
||||||
|
unsigned int qoa_max_frame_size(qoa_desc *qoa) {
|
||||||
|
return QOA_FRAME_SIZE(qoa->channels, QOA_SLICES_PER_FRAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa) {
|
||||||
|
unsigned int p = 0;
|
||||||
|
if (size < QOA_MIN_FILESIZE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Read the file header, verify the magic number ('qoaf') and read the
|
||||||
|
total number of samples. */
|
||||||
|
qoa_uint64_t file_header = qoa_read_u64(bytes, &p);
|
||||||
|
|
||||||
|
if ((file_header >> 32) != QOA_MAGIC) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
qoa->samples = file_header & 0xffffffff;
|
||||||
|
if (!qoa->samples) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Peek into the first frame header to get the number of channels and
|
||||||
|
the samplerate. */
|
||||||
|
qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
|
||||||
|
qoa->channels = (frame_header >> 56) & 0x0000ff;
|
||||||
|
qoa->samplerate = (frame_header >> 32) & 0xffffff;
|
||||||
|
|
||||||
|
if (qoa->channels == 0 || qoa->samples == 0 || qoa->samplerate == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len) {
|
||||||
|
unsigned int p = 0;
|
||||||
|
*frame_len = 0;
|
||||||
|
|
||||||
|
if (size < 8 + QOA_LMS_LEN * 4 * qoa->channels) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read and verify the frame header */
|
||||||
|
qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
|
||||||
|
int channels = (frame_header >> 56) & 0x0000ff;
|
||||||
|
int samplerate = (frame_header >> 32) & 0xffffff;
|
||||||
|
int samples = (frame_header >> 16) & 0x00ffff;
|
||||||
|
int frame_size = (frame_header ) & 0x00ffff;
|
||||||
|
|
||||||
|
int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels;
|
||||||
|
int num_slices = data_size / 8;
|
||||||
|
int max_total_samples = num_slices * QOA_SLICE_LEN;
|
||||||
|
|
||||||
|
if (
|
||||||
|
channels != qoa->channels ||
|
||||||
|
samplerate != qoa->samplerate ||
|
||||||
|
frame_size > size ||
|
||||||
|
samples * channels > max_total_samples
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */
|
||||||
|
for (int c = 0; c < channels; c++) {
|
||||||
|
qoa_uint64_t history = qoa_read_u64(bytes, &p);
|
||||||
|
qoa_uint64_t weights = qoa_read_u64(bytes, &p);
|
||||||
|
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||||
|
qoa->lms[c].history[i] = ((signed short)(history >> 48));
|
||||||
|
history <<= 16;
|
||||||
|
qoa->lms[c].weights[i] = ((signed short)(weights >> 48));
|
||||||
|
weights <<= 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Decode all slices for all channels in this frame */
|
||||||
|
for (int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) {
|
||||||
|
for (int c = 0; c < channels; c++) {
|
||||||
|
qoa_uint64_t slice = qoa_read_u64(bytes, &p);
|
||||||
|
|
||||||
|
int scalefactor = (slice >> 60) & 0xf;
|
||||||
|
int slice_start = sample_index * channels + c;
|
||||||
|
int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c;
|
||||||
|
|
||||||
|
for (int si = slice_start; si < slice_end; si += channels) {
|
||||||
|
int predicted = qoa_lms_predict(&qoa->lms[c]);
|
||||||
|
int quantized = (slice >> 57) & 0x7;
|
||||||
|
int dequantized = qoa_dequant_tab[scalefactor][quantized];
|
||||||
|
int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767);
|
||||||
|
|
||||||
|
sample_data[si] = reconstructed;
|
||||||
|
slice <<= 3;
|
||||||
|
|
||||||
|
qoa_lms_update(&qoa->lms[c], reconstructed, dequantized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*frame_len = samples;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) {
|
||||||
|
unsigned int p = qoa_decode_header(bytes, size, qoa);
|
||||||
|
if (!p) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the required size of the sample buffer and allocate */
|
||||||
|
int total_samples = qoa->samples * qoa->channels;
|
||||||
|
short *sample_data = QOA_MALLOC(total_samples * sizeof(short));
|
||||||
|
|
||||||
|
unsigned int sample_index = 0;
|
||||||
|
unsigned int frame_len;
|
||||||
|
unsigned int frame_size;
|
||||||
|
|
||||||
|
/* Decode all frames */
|
||||||
|
do {
|
||||||
|
short *sample_ptr = sample_data + sample_index * qoa->channels;
|
||||||
|
frame_size = qoa_decode_frame(bytes + p, size - p, qoa, sample_ptr, &frame_len);
|
||||||
|
|
||||||
|
p += frame_size;
|
||||||
|
sample_index += frame_len;
|
||||||
|
} while (frame_size && sample_index < qoa->samples);
|
||||||
|
|
||||||
|
qoa->samples = sample_index;
|
||||||
|
return sample_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
File read/write convenience functions */
|
||||||
|
|
||||||
|
#ifndef QOA_NO_STDIO
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int qoa_write(const char *filename, const short *sample_data, qoa_desc *qoa) {
|
||||||
|
FILE *f = fopen(filename, "wb");
|
||||||
|
unsigned int size;
|
||||||
|
void *encoded;
|
||||||
|
|
||||||
|
if (!f) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded = qoa_encode(sample_data, qoa, &size);
|
||||||
|
if (!encoded) {
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(encoded, 1, size, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
QOA_FREE(encoded);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *qoa_read(const char *filename, qoa_desc *qoa) {
|
||||||
|
FILE *f = fopen(filename, "rb");
|
||||||
|
int size, bytes_read;
|
||||||
|
void *data;
|
||||||
|
short *sample_data;
|
||||||
|
|
||||||
|
if (!f) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
size = ftell(f);
|
||||||
|
if (size <= 0) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
data = QOA_MALLOC(size);
|
||||||
|
if (!data) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_read = fread(data, 1, size, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
sample_data = qoa_decode(data, bytes_read, qoa);
|
||||||
|
QOA_FREE(data);
|
||||||
|
return sample_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* QOA_NO_STDIO */
|
||||||
|
#endif /* QOA_IMPLEMENTATION */
|
||||||
50
src/external/qoi.h
vendored
50
src/external/qoi.h
vendored
|
|
@ -1,31 +1,11 @@
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
QOI - The "Quite OK Image" format for fast, lossless image compression
|
QOI - The "Quite OK Image" format for fast, lossless image compression
|
||||||
|
|
||||||
Dominic Szablewski - https://phoboslab.org
|
|
||||||
|
|
||||||
|
|
||||||
-- LICENSE: The MIT License(MIT)
|
|
||||||
|
|
||||||
Copyright(c) 2021 Dominic Szablewski
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files(the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions :
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
-- About
|
-- About
|
||||||
|
|
||||||
QOI encodes and decodes images in a lossless format. Compared to stb_image and
|
QOI encodes and decodes images in a lossless format. Compared to stb_image and
|
||||||
|
|
@ -424,13 +404,12 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
|
||||||
channels = desc->channels;
|
channels = desc->channels;
|
||||||
|
|
||||||
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
|
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
|
||||||
|
px.rgba.r = pixels[px_pos + 0];
|
||||||
|
px.rgba.g = pixels[px_pos + 1];
|
||||||
|
px.rgba.b = pixels[px_pos + 2];
|
||||||
|
|
||||||
if (channels == 4) {
|
if (channels == 4) {
|
||||||
px = *(qoi_rgba_t *)(pixels + px_pos);
|
px.rgba.a = pixels[px_pos + 3];
|
||||||
}
|
|
||||||
else {
|
|
||||||
px.rgba.r = pixels[px_pos + 0];
|
|
||||||
px.rgba.g = pixels[px_pos + 1];
|
|
||||||
px.rgba.b = pixels[px_pos + 2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (px.v == px_prev.v) {
|
if (px.v == px_prev.v) {
|
||||||
|
|
@ -598,13 +577,12 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
|
||||||
index[QOI_COLOR_HASH(px) % 64] = px;
|
index[QOI_COLOR_HASH(px) % 64] = px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pixels[px_pos + 0] = px.rgba.r;
|
||||||
|
pixels[px_pos + 1] = px.rgba.g;
|
||||||
|
pixels[px_pos + 2] = px.rgba.b;
|
||||||
|
|
||||||
if (channels == 4) {
|
if (channels == 4) {
|
||||||
*(qoi_rgba_t*)(pixels + px_pos) = px;
|
pixels[px_pos + 3] = px.rgba.a;
|
||||||
}
|
|
||||||
else {
|
|
||||||
pixels[px_pos + 0] = px.rgba.r;
|
|
||||||
pixels[px_pos + 1] = px.rgba.g;
|
|
||||||
pixels[px_pos + 2] = px.rgba.b;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
src/external/rl_gputex.h
vendored
6
src/external/rl_gputex.h
vendored
|
|
@ -477,10 +477,10 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo
|
||||||
// Calculate file data_size required
|
// Calculate file data_size required
|
||||||
int data_size = sizeof(ktx_header);
|
int data_size = sizeof(ktx_header);
|
||||||
|
|
||||||
for (int i = 0, width = width, height = height; i < mipmaps; i++)
|
for (int i = 0, w = width, h = height; i < mipmaps; i++)
|
||||||
{
|
{
|
||||||
data_size += get_pixel_data_size(width, height, format);
|
data_size += get_pixel_data_size(w, h, format);
|
||||||
width /= 2; height /= 2;
|
w /= 2; h /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *file_data = RL_CALLOC(data_size, 1);
|
unsigned char *file_data = RL_CALLOC(data_size, 1);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en-us">
|
<html lang="EN-us">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
|
|
||||||
350
src/raudio.c
350
src/raudio.c
|
|
@ -21,10 +21,11 @@
|
||||||
*
|
*
|
||||||
* #define SUPPORT_FILEFORMAT_WAV
|
* #define SUPPORT_FILEFORMAT_WAV
|
||||||
* #define SUPPORT_FILEFORMAT_OGG
|
* #define SUPPORT_FILEFORMAT_OGG
|
||||||
|
* #define SUPPORT_FILEFORMAT_MP3
|
||||||
|
* #define SUPPORT_FILEFORMAT_QOA
|
||||||
|
* #define SUPPORT_FILEFORMAT_FLAC
|
||||||
* #define SUPPORT_FILEFORMAT_XM
|
* #define SUPPORT_FILEFORMAT_XM
|
||||||
* #define SUPPORT_FILEFORMAT_MOD
|
* #define SUPPORT_FILEFORMAT_MOD
|
||||||
* #define SUPPORT_FILEFORMAT_FLAC
|
|
||||||
* #define SUPPORT_FILEFORMAT_MP3
|
|
||||||
* Selected desired fileformats to be supported for loading. Some of those formats are
|
* Selected desired fileformats to be supported for loading. Some of those formats are
|
||||||
* supported by default, to remove support, just comment unrequired #define in this module
|
* supported by default, to remove support, just comment unrequired #define in this module
|
||||||
*
|
*
|
||||||
|
|
@ -196,37 +197,6 @@ typedef struct tagBITMAPINFOHEADER {
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
|
||||||
// TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE
|
|
||||||
#include "external/stb_vorbis.c" // OGG loading functions
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
|
||||||
#define JARXM_MALLOC RL_MALLOC
|
|
||||||
#define JARXM_FREE RL_FREE
|
|
||||||
|
|
||||||
#if defined(_MSC_VER ) // jar xm has warnings on windows, so disable them just for this file
|
|
||||||
#pragma warning( push )
|
|
||||||
#pragma warning( disable : 4244)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define JAR_XM_IMPLEMENTATION
|
|
||||||
#include "external/jar_xm.h" // XM loading functions
|
|
||||||
|
|
||||||
#if defined(_MSC_VER )
|
|
||||||
#pragma warning( pop )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_MOD)
|
|
||||||
#define JARMOD_MALLOC RL_MALLOC
|
|
||||||
#define JARMOD_FREE RL_FREE
|
|
||||||
|
|
||||||
#define JAR_MOD_IMPLEMENTATION
|
|
||||||
#include "external/jar_mod.h" // MOD loading functions
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_WAV)
|
#if defined(SUPPORT_FILEFORMAT_WAV)
|
||||||
#define DRWAV_MALLOC RL_MALLOC
|
#define DRWAV_MALLOC RL_MALLOC
|
||||||
#define DRWAV_REALLOC RL_REALLOC
|
#define DRWAV_REALLOC RL_REALLOC
|
||||||
|
|
@ -236,6 +206,11 @@ typedef struct tagBITMAPINFOHEADER {
|
||||||
#include "external/dr_wav.h" // WAV loading functions
|
#include "external/dr_wav.h" // WAV loading functions
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
|
// TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE
|
||||||
|
#include "external/stb_vorbis.c" // OGG loading functions
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||||
#define DRMP3_MALLOC RL_MALLOC
|
#define DRMP3_MALLOC RL_MALLOC
|
||||||
#define DRMP3_REALLOC RL_REALLOC
|
#define DRMP3_REALLOC RL_REALLOC
|
||||||
|
|
@ -245,6 +220,14 @@ typedef struct tagBITMAPINFOHEADER {
|
||||||
#include "external/dr_mp3.h" // MP3 loading functions
|
#include "external/dr_mp3.h" // MP3 loading functions
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
|
#define QOA_MALLOC RL_MALLOC
|
||||||
|
#define QOA_FREE RL_FREE
|
||||||
|
|
||||||
|
#define QOA_IMPLEMENTATION
|
||||||
|
#include "external/qoa.h" // QOA loading and saving functions
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
#define DRFLAC_MALLOC RL_MALLOC
|
#define DRFLAC_MALLOC RL_MALLOC
|
||||||
#define DRFLAC_REALLOC RL_REALLOC
|
#define DRFLAC_REALLOC RL_REALLOC
|
||||||
|
|
@ -255,6 +238,31 @@ typedef struct tagBITMAPINFOHEADER {
|
||||||
#include "external/dr_flac.h" // FLAC loading functions
|
#include "external/dr_flac.h" // FLAC loading functions
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||||
|
#define JARXM_MALLOC RL_MALLOC
|
||||||
|
#define JARXM_FREE RL_FREE
|
||||||
|
|
||||||
|
#if defined(_MSC_VER ) // jar_xm has warnings on windows, so disable them just for this file
|
||||||
|
#pragma warning( push )
|
||||||
|
#pragma warning( disable : 4244)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define JAR_XM_IMPLEMENTATION
|
||||||
|
#include "external/jar_xm.h" // XM loading functions
|
||||||
|
|
||||||
|
#if defined(_MSC_VER )
|
||||||
|
#pragma warning( pop )
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_MOD)
|
||||||
|
#define JARMOD_MALLOC RL_MALLOC
|
||||||
|
#define JARMOD_FREE RL_FREE
|
||||||
|
|
||||||
|
#define JAR_MOD_IMPLEMENTATION
|
||||||
|
#include "external/jar_mod.h" // MOD loading functions
|
||||||
|
#endif
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
// Defines and Macros
|
// Defines and Macros
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
@ -285,6 +293,7 @@ typedef enum {
|
||||||
MUSIC_AUDIO_OGG, // OGG audio context
|
MUSIC_AUDIO_OGG, // OGG audio context
|
||||||
MUSIC_AUDIO_FLAC, // FLAC audio context
|
MUSIC_AUDIO_FLAC, // FLAC audio context
|
||||||
MUSIC_AUDIO_MP3, // MP3 audio context
|
MUSIC_AUDIO_MP3, // MP3 audio context
|
||||||
|
MUSIC_AUDIO_QOA, // QOA audio context
|
||||||
MUSIC_MODULE_XM, // XM module audio context
|
MUSIC_MODULE_XM, // XM module audio context
|
||||||
MUSIC_MODULE_MOD // MOD module audio context
|
MUSIC_MODULE_MOD // MOD module audio context
|
||||||
} MusicContextType;
|
} MusicContextType;
|
||||||
|
|
@ -305,7 +314,7 @@ typedef enum {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE: Different logic is used when feeding data to the playback device
|
// NOTE: Different logic is used when feeding data to the playback device
|
||||||
// depending on whether or not data is streamed (Music vs Sound)
|
// depending on whether data is streamed (Music vs Sound)
|
||||||
typedef enum {
|
typedef enum {
|
||||||
AUDIO_BUFFER_USAGE_STATIC = 0,
|
AUDIO_BUFFER_USAGE_STATIC = 0,
|
||||||
AUDIO_BUFFER_USAGE_STREAM
|
AUDIO_BUFFER_USAGE_STREAM
|
||||||
|
|
@ -465,7 +474,7 @@ void InitAudioDevice(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may
|
// Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may
|
||||||
// want to look at something a bit smarter later on to keep everything real-time, if that's necessary.
|
// want to look at something a bit smarter later on to keep everything real-time, if that's necessary.
|
||||||
if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS)
|
if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
|
|
@ -795,19 +804,6 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
|
||||||
else TRACELOG(LOG_WARNING, "WAVE: Failed to load OGG data");
|
else TRACELOG(LOG_WARNING, "WAVE: Failed to load OGG data");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
|
||||||
else if (strcmp(fileType, ".flac") == 0)
|
|
||||||
{
|
|
||||||
unsigned long long int totalFrameCount = 0;
|
|
||||||
|
|
||||||
// NOTE: We are forcing conversion to 16bit sample size on reading
|
|
||||||
wave.data = drflac_open_memory_and_read_pcm_frames_s16(fileData, dataSize, &wave.channels, &wave.sampleRate, &totalFrameCount, NULL);
|
|
||||||
wave.sampleSize = 16;
|
|
||||||
|
|
||||||
if (wave.data != NULL) wave.frameCount = (unsigned int)totalFrameCount;
|
|
||||||
else TRACELOG(LOG_WARNING, "WAVE: Failed to load FLAC data");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||||
else if (strcmp(fileType, ".mp3") == 0)
|
else if (strcmp(fileType, ".mp3") == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -827,6 +823,38 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
|
||||||
else TRACELOG(LOG_WARNING, "WAVE: Failed to load MP3 data");
|
else TRACELOG(LOG_WARNING, "WAVE: Failed to load MP3 data");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
|
else if (strcmp(fileType, ".qoa") == 0)
|
||||||
|
{
|
||||||
|
qoa_desc qoa = { 0 };
|
||||||
|
|
||||||
|
// NOTE: Returned sample data is always 16 bit?
|
||||||
|
wave.data = qoa_decode(fileData, dataSize, &qoa);
|
||||||
|
wave.sampleSize = 16;
|
||||||
|
|
||||||
|
if (wave.data != NULL)
|
||||||
|
{
|
||||||
|
wave.channels = qoa.channels;
|
||||||
|
wave.sampleRate = qoa.samplerate;
|
||||||
|
wave.frameCount = qoa.samples;
|
||||||
|
}
|
||||||
|
else TRACELOG(LOG_WARNING, "WAVE: Failed to load QOA data");
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
else if (strcmp(fileType, ".flac") == 0)
|
||||||
|
{
|
||||||
|
unsigned long long int totalFrameCount = 0;
|
||||||
|
|
||||||
|
// NOTE: We are forcing conversion to 16bit sample size on reading
|
||||||
|
wave.data = drflac_open_memory_and_read_pcm_frames_s16(fileData, dataSize, &wave.channels, &wave.sampleRate, &totalFrameCount, NULL);
|
||||||
|
wave.sampleSize = 16;
|
||||||
|
|
||||||
|
if (wave.data != NULL) wave.frameCount = (unsigned int)totalFrameCount;
|
||||||
|
else TRACELOG(LOG_WARNING, "WAVE: Failed to load FLAC data");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
else TRACELOG(LOG_WARNING, "WAVE: Data format not supported");
|
else TRACELOG(LOG_WARNING, "WAVE: Data format not supported");
|
||||||
|
|
||||||
|
|
@ -957,6 +985,18 @@ bool ExportWave(Wave wave, const char *fileName)
|
||||||
|
|
||||||
drwav_free(fileData, NULL);
|
drwav_free(fileData, NULL);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
|
else if (IsFileExtension(fileName, ".qoa"))
|
||||||
|
{
|
||||||
|
qoa_desc qoa = { 0 };
|
||||||
|
qoa.channels = wave.channels;
|
||||||
|
qoa.samplerate = wave.sampleRate;
|
||||||
|
qoa.samples = wave.frameCount;
|
||||||
|
|
||||||
|
// TODO: Review wave.data format required for export
|
||||||
|
success = qoa_write(fileName, wave.data, &qoa);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (IsFileExtension(fileName, ".raw"))
|
else if (IsFileExtension(fileName, ".raw"))
|
||||||
{
|
{
|
||||||
|
|
@ -1049,7 +1089,7 @@ void PlaySoundMulti(Sound sound)
|
||||||
unsigned int oldAge = 0;
|
unsigned int oldAge = 0;
|
||||||
int oldIndex = -1;
|
int oldIndex = -1;
|
||||||
|
|
||||||
// find the first non playing pool entry
|
// find the first non-playing pool entry
|
||||||
for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
|
for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
|
||||||
{
|
{
|
||||||
if (AUDIO.MultiChannel.channels[i] > oldAge)
|
if (AUDIO.MultiChannel.channels[i] > oldAge)
|
||||||
|
|
@ -1065,7 +1105,7 @@ void PlaySoundMulti(Sound sound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no none playing pool members can be index choose the oldest
|
// If no none playing pool members can be indexed choose the oldest
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "SOUND: Buffer pool is already full, count: %i", AUDIO.MultiChannel.poolCounter);
|
TRACELOG(LOG_WARNING, "SOUND: Buffer pool is already full, count: %i", AUDIO.MultiChannel.poolCounter);
|
||||||
|
|
@ -1073,7 +1113,7 @@ void PlaySoundMulti(Sound sound)
|
||||||
if (oldIndex == -1)
|
if (oldIndex == -1)
|
||||||
{
|
{
|
||||||
// Shouldn't be able to get here... but just in case something odd happens!
|
// Shouldn't be able to get here... but just in case something odd happens!
|
||||||
TRACELOG(LOG_WARNING, "SOUND: Buffer pool could not determine oldest buffer not playing sound");
|
TRACELOG(LOG_WARNING, "SOUND: Buffer pool could not determine the oldest buffer not playing sound");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1316,23 +1356,6 @@ Music LoadMusicStream(const char *fileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
|
||||||
else if (IsFileExtension(fileName, ".flac"))
|
|
||||||
{
|
|
||||||
music.ctxType = MUSIC_AUDIO_FLAC;
|
|
||||||
music.ctxData = drflac_open_file(fileName, NULL);
|
|
||||||
|
|
||||||
if (music.ctxData != NULL)
|
|
||||||
{
|
|
||||||
drflac *ctxFlac = (drflac *)music.ctxData;
|
|
||||||
|
|
||||||
music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels);
|
|
||||||
music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount;
|
|
||||||
music.looping = true; // Looping enabled by default
|
|
||||||
musicLoaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||||
else if (IsFileExtension(fileName, ".mp3"))
|
else if (IsFileExtension(fileName, ".mp3"))
|
||||||
{
|
{
|
||||||
|
|
@ -1351,6 +1374,46 @@ Music LoadMusicStream(const char *fileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
|
else if (IsFileExtension(fileName, ".qoa"))
|
||||||
|
{
|
||||||
|
qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc));
|
||||||
|
|
||||||
|
// TODO: QOA stream support: Init context from file
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
music.ctxType = MUSIC_AUDIO_QOA;
|
||||||
|
music.ctxData = ctxQoa;
|
||||||
|
|
||||||
|
if (result > 0)
|
||||||
|
{
|
||||||
|
music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels);
|
||||||
|
|
||||||
|
// TODO: Read next frame(s) from QOA stream
|
||||||
|
//music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len);
|
||||||
|
|
||||||
|
music.looping = true; // Looping enabled by default
|
||||||
|
musicLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
else if (IsFileExtension(fileName, ".flac"))
|
||||||
|
{
|
||||||
|
music.ctxType = MUSIC_AUDIO_FLAC;
|
||||||
|
music.ctxData = drflac_open_file(fileName, NULL);
|
||||||
|
|
||||||
|
if (music.ctxData != NULL)
|
||||||
|
{
|
||||||
|
drflac *ctxFlac = (drflac *)music.ctxData;
|
||||||
|
|
||||||
|
music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels);
|
||||||
|
music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount;
|
||||||
|
music.looping = true; // Looping enabled by default
|
||||||
|
musicLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||||
else if (IsFileExtension(fileName, ".xm"))
|
else if (IsFileExtension(fileName, ".xm"))
|
||||||
{
|
{
|
||||||
|
|
@ -1408,12 +1471,15 @@ Music LoadMusicStream(const char *fileName)
|
||||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
|
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
|
||||||
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
|
||||||
#endif
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
|
else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context data*/ RL_FREE(music.ctxData); }
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||||
else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
|
else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1467,18 +1533,23 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
else if (strcmp(fileType, ".flac") == 0)
|
else if (strcmp(fileType, ".ogg") == 0)
|
||||||
{
|
{
|
||||||
music.ctxType = MUSIC_AUDIO_FLAC;
|
// Open ogg audio stream
|
||||||
music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL);
|
music.ctxType = MUSIC_AUDIO_OGG;
|
||||||
|
//music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL);
|
||||||
|
music.ctxData = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL);
|
||||||
|
|
||||||
if (music.ctxData != NULL)
|
if (music.ctxData != NULL)
|
||||||
{
|
{
|
||||||
drflac *ctxFlac = (drflac *)music.ctxData;
|
stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info
|
||||||
|
|
||||||
music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels);
|
// OGG bit rate defaults to 16 bit, it's enough for compressed format
|
||||||
music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount;
|
music.stream = LoadAudioStream(info.sample_rate, 16, info.channels);
|
||||||
|
|
||||||
|
// WARNING: It seems this function returns length in frames, not samples, so we multiply by channels
|
||||||
|
music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData);
|
||||||
music.looping = true; // Looping enabled by default
|
music.looping = true; // Looping enabled by default
|
||||||
musicLoaded = true;
|
musicLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1502,23 +1573,41 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
else if (strcmp(fileType, ".ogg") == 0)
|
else if (strcmp(fileType, ".qoa") == 0)
|
||||||
{
|
{
|
||||||
// Open ogg audio stream
|
qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc));
|
||||||
music.ctxType = MUSIC_AUDIO_OGG;
|
|
||||||
//music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL);
|
// TODO: Init QOA context data
|
||||||
music.ctxData = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL);
|
int result = 0;
|
||||||
|
|
||||||
|
music.ctxType = MUSIC_AUDIO_QOA;
|
||||||
|
music.ctxData = ctxQoa;
|
||||||
|
|
||||||
|
if (result > 0)
|
||||||
|
{
|
||||||
|
music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels);
|
||||||
|
|
||||||
|
// TODO: Read next frame(s) from QOA stream
|
||||||
|
//music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len);
|
||||||
|
|
||||||
|
music.looping = true; // Looping enabled by default
|
||||||
|
musicLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
else if (strcmp(fileType, ".flac") == 0)
|
||||||
|
{
|
||||||
|
music.ctxType = MUSIC_AUDIO_FLAC;
|
||||||
|
music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL);
|
||||||
|
|
||||||
if (music.ctxData != NULL)
|
if (music.ctxData != NULL)
|
||||||
{
|
{
|
||||||
stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info
|
drflac *ctxFlac = (drflac *)music.ctxData;
|
||||||
|
|
||||||
// OGG bit rate defaults to 16 bit, it's enough for compressed format
|
music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels);
|
||||||
music.stream = LoadAudioStream(info.sample_rate, 16, info.channels);
|
music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount;
|
||||||
|
|
||||||
// WARNING: It seems this function returns length in frames, not samples, so we multiply by channels
|
|
||||||
music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData);
|
|
||||||
music.looping = true; // Looping enabled by default
|
music.looping = true; // Looping enabled by default
|
||||||
musicLoaded = true;
|
musicLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1593,14 +1682,17 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
|
||||||
#if defined(SUPPORT_FILEFORMAT_WAV)
|
#if defined(SUPPORT_FILEFORMAT_WAV)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData);
|
else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData);
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
|
else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context*/ RL_FREE(music.ctxData); }
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||||
else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
|
else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
|
||||||
|
|
@ -1645,12 +1737,15 @@ void UnloadMusicStream(Music music)
|
||||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
|
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
|
||||||
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
|
||||||
#endif
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
|
else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context*/ RL_FREE(music.ctxData); }
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||||
else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
|
else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1700,12 +1795,15 @@ void StopMusicStream(Music music)
|
||||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break;
|
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
|
||||||
case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break;
|
|
||||||
#endif
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||||
case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break;
|
case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
|
case MUSIC_AUDIO_QOA: /*TODO: Restart QOA context to beginning*/ break;
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break;
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||||
case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break;
|
case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1732,11 +1830,14 @@ void SeekMusicStream(Music music, float position)
|
||||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||||
case MUSIC_AUDIO_OGG: stb_vorbis_seek_frame((stb_vorbis *)music.ctxData, positionInFrames); break;
|
case MUSIC_AUDIO_OGG: stb_vorbis_seek_frame((stb_vorbis *)music.ctxData, positionInFrames); break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
|
||||||
case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break;
|
|
||||||
#endif
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||||
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break;
|
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break;
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
|
case MUSIC_AUDIO_QOA: /*TODO: Seek to specific QOA frame*/ break;
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break;
|
||||||
#endif
|
#endif
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
@ -1754,6 +1855,7 @@ void UpdateMusicStream(Music music)
|
||||||
// On first call of this function we lazily pre-allocated a temp buffer to read audio files/memory data in
|
// On first call of this function we lazily pre-allocated a temp buffer to read audio files/memory data in
|
||||||
int frameSize = music.stream.channels*music.stream.sampleSize/8;
|
int frameSize = music.stream.channels*music.stream.sampleSize/8;
|
||||||
unsigned int pcmSize = subBufferSizeInFrames*frameSize;
|
unsigned int pcmSize = subBufferSizeInFrames*frameSize;
|
||||||
|
|
||||||
if (AUDIO.System.pcmBufferSize < pcmSize)
|
if (AUDIO.System.pcmBufferSize < pcmSize)
|
||||||
{
|
{
|
||||||
RL_FREE(AUDIO.System.pcmBuffer);
|
RL_FREE(AUDIO.System.pcmBuffer);
|
||||||
|
|
@ -1815,19 +1917,6 @@ void UpdateMusicStream(Music music)
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
|
||||||
case MUSIC_AUDIO_FLAC:
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int frameCountRed = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize));
|
|
||||||
frameCountRedTotal += frameCountRed;
|
|
||||||
frameCountStillNeeded -= frameCountRed;
|
|
||||||
if (frameCountStillNeeded == 0) break;
|
|
||||||
else drflac__seek_to_first_frame((drflac *)music.ctxData);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
#endif
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||||
case MUSIC_AUDIO_MP3:
|
case MUSIC_AUDIO_MP3:
|
||||||
{
|
{
|
||||||
|
|
@ -1841,6 +1930,25 @@ void UpdateMusicStream(Music music)
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
|
case MUSIC_AUDIO_QOA:
|
||||||
|
{
|
||||||
|
// TODO: Read QOA required framecount to fill buffer to keep music playing
|
||||||
|
} break;
|
||||||
|
#endif
|
||||||
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
case MUSIC_AUDIO_FLAC:
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int frameCountRed = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize));
|
||||||
|
frameCountRedTotal += frameCountRed;
|
||||||
|
frameCountStillNeeded -= frameCountRed;
|
||||||
|
if (frameCountStillNeeded == 0) break;
|
||||||
|
else drflac__seek_to_first_frame((drflac *)music.ctxData);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||||
case MUSIC_MODULE_XM:
|
case MUSIC_MODULE_XM:
|
||||||
{
|
{
|
||||||
|
|
@ -1999,8 +2107,8 @@ void UnloadAudioStream(AudioStream stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update audio stream buffers with data
|
// Update audio stream buffers with data
|
||||||
// NOTE 1: Only updates one buffer of the stream source: unqueue -> update -> queue
|
// NOTE 1: Only updates one buffer of the stream source: dequeue -> update -> queue
|
||||||
// NOTE 2: To unqueue a buffer it needs to be processed: IsAudioStreamProcessed()
|
// NOTE 2: To dequeue a buffer it needs to be processed: IsAudioStreamProcessed()
|
||||||
void UpdateAudioStream(AudioStream stream, const void *data, int frameCount)
|
void UpdateAudioStream(AudioStream stream, const void *data, int frameCount)
|
||||||
{
|
{
|
||||||
if (stream.buffer != NULL)
|
if (stream.buffer != NULL)
|
||||||
|
|
@ -2197,7 +2305,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer,
|
||||||
|
|
||||||
if (currentSubBufferIndex > 1) return 0;
|
if (currentSubBufferIndex > 1) return 0;
|
||||||
|
|
||||||
// Another thread can update the processed state of buffers so
|
// Another thread can update the processed state of buffers, so
|
||||||
// we just take a copy here to try and avoid potential synchronization problems
|
// we just take a copy here to try and avoid potential synchronization problems
|
||||||
bool isSubBufferProcessed[2] = { 0 };
|
bool isSubBufferProcessed[2] = { 0 };
|
||||||
isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0];
|
isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0];
|
||||||
|
|
@ -2211,7 +2319,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer,
|
||||||
{
|
{
|
||||||
// We break from this loop differently depending on the buffer's usage
|
// We break from this loop differently depending on the buffer's usage
|
||||||
// - For static buffers, we simply fill as much data as we can
|
// - For static buffers, we simply fill as much data as we can
|
||||||
// - For streaming buffers we only fill the halves of the buffer that are processed
|
// - For streaming buffers we only fill half of the buffer that are processed
|
||||||
// Unprocessed halves must keep their audio data in-tact
|
// Unprocessed halves must keep their audio data in-tact
|
||||||
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
|
||||||
{
|
{
|
||||||
|
|
@ -2268,7 +2376,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer,
|
||||||
|
|
||||||
// For static buffers we can fill the remaining frames with silence for safety, but we don't want
|
// For static buffers we can fill the remaining frames with silence for safety, but we don't want
|
||||||
// to report those frames as "read". The reason for this is that the caller uses the return value
|
// to report those frames as "read". The reason for this is that the caller uses the return value
|
||||||
// to know whether or not a non-looping sound has finished playback.
|
// to know whether a non-looping sound has finished playback.
|
||||||
if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining;
|
if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
16
src/raylib.h
16
src/raylib.h
|
|
@ -216,7 +216,7 @@ typedef struct Vector4 {
|
||||||
// Quaternion, 4 components (Vector4 alias)
|
// Quaternion, 4 components (Vector4 alias)
|
||||||
typedef Vector4 Quaternion;
|
typedef Vector4 Quaternion;
|
||||||
|
|
||||||
// Matrix, 4x4 components, column major, OpenGL style, right handed
|
// Matrix, 4x4 components, column major, OpenGL style, right-handed
|
||||||
typedef struct Matrix {
|
typedef struct Matrix {
|
||||||
float m0, m4, m8, m12; // Matrix first row (4 components)
|
float m0, m4, m8, m12; // Matrix first row (4 components)
|
||||||
float m1, m5, m9, m13; // Matrix second row (4 components)
|
float m1, m5, m9, m13; // Matrix second row (4 components)
|
||||||
|
|
@ -413,8 +413,8 @@ typedef struct Ray {
|
||||||
// RayCollision, ray hit information
|
// RayCollision, ray hit information
|
||||||
typedef struct RayCollision {
|
typedef struct RayCollision {
|
||||||
bool hit; // Did the ray hit something?
|
bool hit; // Did the ray hit something?
|
||||||
float distance; // Distance to nearest hit
|
float distance; // Distance to the nearest hit
|
||||||
Vector3 point; // Point of nearest hit
|
Vector3 point; // Point of the nearest hit
|
||||||
Vector3 normal; // Surface normal of hit
|
Vector3 normal; // Surface normal of hit
|
||||||
} RayCollision;
|
} RayCollision;
|
||||||
|
|
||||||
|
|
@ -681,7 +681,7 @@ typedef enum {
|
||||||
MOUSE_CURSOR_RESIZE_NS = 6, // Vertical resize/move arrow shape
|
MOUSE_CURSOR_RESIZE_NS = 6, // Vertical resize/move arrow shape
|
||||||
MOUSE_CURSOR_RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape
|
MOUSE_CURSOR_RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape
|
||||||
MOUSE_CURSOR_RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape
|
MOUSE_CURSOR_RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape
|
||||||
MOUSE_CURSOR_RESIZE_ALL = 9, // The omni-directional resize/move cursor shape
|
MOUSE_CURSOR_RESIZE_ALL = 9, // The omnidirectional resize/move cursor shape
|
||||||
MOUSE_CURSOR_NOT_ALLOWED = 10 // The operation-not-allowed shape
|
MOUSE_CURSOR_NOT_ALLOWED = 10 // The operation-not-allowed shape
|
||||||
} MouseCursor;
|
} MouseCursor;
|
||||||
|
|
||||||
|
|
@ -839,10 +839,10 @@ typedef enum {
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CUBEMAP_LAYOUT_AUTO_DETECT = 0, // Automatically detect layout type
|
CUBEMAP_LAYOUT_AUTO_DETECT = 0, // Automatically detect layout type
|
||||||
CUBEMAP_LAYOUT_LINE_VERTICAL, // Layout is defined by a vertical line with faces
|
CUBEMAP_LAYOUT_LINE_VERTICAL, // Layout is defined by a vertical line with faces
|
||||||
CUBEMAP_LAYOUT_LINE_HORIZONTAL, // Layout is defined by an horizontal line with faces
|
CUBEMAP_LAYOUT_LINE_HORIZONTAL, // Layout is defined by a horizontal line with faces
|
||||||
CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces
|
CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces
|
||||||
CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces
|
CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces
|
||||||
CUBEMAP_LAYOUT_PANORAMA // Layout is defined by a panorama image (equirectangular map)
|
CUBEMAP_LAYOUT_PANORAMA // Layout is defined by a panorama image (equirrectangular map)
|
||||||
} CubemapLayout;
|
} CubemapLayout;
|
||||||
|
|
||||||
// Font type, defines generation method
|
// Font type, defines generation method
|
||||||
|
|
@ -903,7 +903,7 @@ typedef enum {
|
||||||
} NPatchLayout;
|
} NPatchLayout;
|
||||||
|
|
||||||
// Callbacks to hook some internal functions
|
// Callbacks to hook some internal functions
|
||||||
// WARNING: This callbacks are intended for advance users
|
// WARNING: These callbacks are intended for advance users
|
||||||
typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages
|
typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages
|
||||||
typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data
|
typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data
|
||||||
typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data
|
typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data
|
||||||
|
|
@ -1222,7 +1222,7 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2);
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Image loading functions
|
// Image loading functions
|
||||||
// NOTE: This functions do not require GPU access
|
// NOTE: These functions do not require GPU access
|
||||||
RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM)
|
RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM)
|
||||||
RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data
|
RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data
|
||||||
RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data)
|
RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data)
|
||||||
|
|
|
||||||
|
|
@ -912,7 +912,7 @@ RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view)
|
||||||
{
|
{
|
||||||
Vector3 result = { 0 };
|
Vector3 result = { 0 };
|
||||||
|
|
||||||
// Calculate unproject matrix (multiply view matrix by projection matrix) and invert it
|
// Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it
|
||||||
Matrix matViewProj = { // MatrixMultiply(view, projection);
|
Matrix matViewProj = { // MatrixMultiply(view, projection);
|
||||||
view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12,
|
view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12,
|
||||||
view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13,
|
view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13,
|
||||||
|
|
@ -975,7 +975,7 @@ RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view)
|
||||||
// Create quaternion from source point
|
// Create quaternion from source point
|
||||||
Quaternion quat = { source.x, source.y, source.z, 1.0f };
|
Quaternion quat = { source.x, source.y, source.z, 1.0f };
|
||||||
|
|
||||||
// Multiply quat point by unproject matrix
|
// Multiply quat point by unprojecte matrix
|
||||||
Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv)
|
Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv)
|
||||||
matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w,
|
matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w,
|
||||||
matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w,
|
matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w,
|
||||||
|
|
|
||||||
|
|
@ -370,7 +370,7 @@ void CameraRoll(Camera3D *camera, float angle)
|
||||||
|
|
||||||
// Moves camera slightly to simulate a walking motion
|
// Moves camera slightly to simulate a walking motion
|
||||||
// Note: Only active if camera->swingCounter > 0
|
// Note: Only active if camera->swingCounter > 0
|
||||||
void CameraViewBobbing(Camera3D* camera)
|
void CameraViewBobbing(Camera3D *camera)
|
||||||
{
|
{
|
||||||
if (camera->swingCounter > 0)
|
if (camera->swingCounter > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -409,19 +409,10 @@ Matrix GetCameraProjectionMatrix(Camera3D *camera, float aspect)
|
||||||
|
|
||||||
|
|
||||||
#ifndef CAMERA_STANDALONE
|
#ifndef CAMERA_STANDALONE
|
||||||
|
|
||||||
static int init_frames = 3; // TODO review and remove
|
|
||||||
|
|
||||||
// Update camera position for selected mode
|
// Update camera position for selected mode
|
||||||
// Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM
|
// Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM
|
||||||
void UpdateCamera(Camera3D *camera, int mode)
|
void UpdateCamera(Camera3D *camera, int mode)
|
||||||
{
|
{
|
||||||
// Avoid inital mouse "jump"
|
|
||||||
if (init_frames > 0) {
|
|
||||||
SetMousePosition(0, 0);
|
|
||||||
init_frames--;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Vector2 mousePositionDelta = GetMouseDelta();
|
Vector2 mousePositionDelta = GetMouseDelta();
|
||||||
|
|
||||||
bool moveInWorldPlane = mode == CAMERA_FIRST_PERSON || mode == CAMERA_THIRD_PERSON;
|
bool moveInWorldPlane = mode == CAMERA_FIRST_PERSON || mode == CAMERA_THIRD_PERSON;
|
||||||
|
|
@ -457,8 +448,6 @@ void UpdateCamera(Camera3D *camera, int mode)
|
||||||
// Apply view bobbing when moving around (per default only active in CAMERA_FIRST_PERSON)
|
// Apply view bobbing when moving around (per default only active in CAMERA_FIRST_PERSON)
|
||||||
if (mode == CAMERA_FIRST_PERSON && (IsKeyDown(KEY_W) || IsKeyDown(KEY_A) || IsKeyDown(KEY_S) || IsKeyDown(KEY_D))) CameraViewBobbing(camera);
|
if (mode == CAMERA_FIRST_PERSON && (IsKeyDown(KEY_W) || IsKeyDown(KEY_A) || IsKeyDown(KEY_S) || IsKeyDown(KEY_D))) CameraViewBobbing(camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !CAMERA_STANDALONE
|
#endif // !CAMERA_STANDALONE
|
||||||
|
|
||||||
|
|
||||||
#endif // CAMERA_IMPLEMENTATION
|
#endif // CAMERA_IMPLEMENTATION
|
||||||
|
|
|
||||||
57
src/rcore.c
57
src/rcore.c
|
|
@ -242,6 +242,7 @@
|
||||||
#include <unistd.h> // Required for: usleep()
|
#include <unistd.h> // Required for: usleep()
|
||||||
|
|
||||||
//#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition
|
//#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition
|
||||||
|
void *glfwGetCocoaWindow(GLFWwindow* handle);
|
||||||
#include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow()
|
#include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -864,7 +865,7 @@ void InitWindow(int width, int height, const char *title)
|
||||||
LoadFontDefault();
|
LoadFontDefault();
|
||||||
#if defined(SUPPORT_MODULE_RSHAPES)
|
#if defined(SUPPORT_MODULE_RSHAPES)
|
||||||
Rectangle rec = GetFontDefault().recs[95];
|
Rectangle rec = GetFontDefault().recs[95];
|
||||||
// NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
|
// NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
|
||||||
SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes
|
SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
|
|
@ -1317,7 +1318,7 @@ void MaximizeWindow(void)
|
||||||
void MinimizeWindow(void)
|
void MinimizeWindow(void)
|
||||||
{
|
{
|
||||||
#if defined(PLATFORM_DESKTOP)
|
#if defined(PLATFORM_DESKTOP)
|
||||||
// NOTE: Following function launches callback that sets appropiate flag!
|
// NOTE: Following function launches callback that sets appropriate flag!
|
||||||
glfwIconifyWindow(CORE.Window.handle);
|
glfwIconifyWindow(CORE.Window.handle);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -1416,13 +1417,13 @@ void SetWindowState(unsigned int flags)
|
||||||
// State change: FLAG_WINDOW_TRANSPARENT
|
// State change: FLAG_WINDOW_TRANSPARENT
|
||||||
if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
|
if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
|
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
|
||||||
}
|
}
|
||||||
|
|
||||||
// State change: FLAG_WINDOW_HIGHDPI
|
// State change: FLAG_WINDOW_HIGHDPI
|
||||||
if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
|
if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
|
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
|
||||||
}
|
}
|
||||||
|
|
||||||
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
|
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
|
||||||
|
|
@ -1435,13 +1436,13 @@ void SetWindowState(unsigned int flags)
|
||||||
// State change: FLAG_MSAA_4X_HINT
|
// State change: FLAG_MSAA_4X_HINT
|
||||||
if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
|
if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
|
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
|
||||||
}
|
}
|
||||||
|
|
||||||
// State change: FLAG_INTERLACED_HINT
|
// State change: FLAG_INTERLACED_HINT
|
||||||
if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
|
if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
|
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -1524,13 +1525,13 @@ void ClearWindowState(unsigned int flags)
|
||||||
// State change: FLAG_WINDOW_TRANSPARENT
|
// State change: FLAG_WINDOW_TRANSPARENT
|
||||||
if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
|
if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
|
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
|
||||||
}
|
}
|
||||||
|
|
||||||
// State change: FLAG_WINDOW_HIGHDPI
|
// State change: FLAG_WINDOW_HIGHDPI
|
||||||
if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
|
if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
|
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
|
||||||
}
|
}
|
||||||
|
|
||||||
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
|
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
|
||||||
|
|
@ -1543,13 +1544,13 @@ void ClearWindowState(unsigned int flags)
|
||||||
// State change: FLAG_MSAA_4X_HINT
|
// State change: FLAG_MSAA_4X_HINT
|
||||||
if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
|
if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
|
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
|
||||||
}
|
}
|
||||||
|
|
||||||
// State change: FLAG_INTERLACED_HINT
|
// State change: FLAG_INTERLACED_HINT
|
||||||
if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
|
if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
|
||||||
{
|
{
|
||||||
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
|
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -1677,7 +1678,7 @@ void *GetWindowHandle(void)
|
||||||
#endif
|
#endif
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// NOTE: Returned handle is: (objc_object *)
|
// NOTE: Returned handle is: (objc_object *)
|
||||||
return NULL; // TODO: return (void *)glfwGetCocoaWindow(window);
|
return (void *)glfwGetCocoaWindow(CORE.Window.handle);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -2481,10 +2482,10 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
|
||||||
|
|
||||||
shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
|
shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
|
||||||
|
|
||||||
// All locations reseted to -1 (no location)
|
// All locations reset to -1 (no location)
|
||||||
for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
|
for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
|
||||||
|
|
||||||
// Get handles to GLSL input attibute locations
|
// Get handles to GLSL input attribute locations
|
||||||
shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
|
shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
|
||||||
shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
|
shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
|
||||||
shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
|
shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
|
||||||
|
|
@ -2552,7 +2553,7 @@ void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniform
|
||||||
{
|
{
|
||||||
rlEnableShader(shader.id);
|
rlEnableShader(shader.id);
|
||||||
rlSetUniform(locIndex, value, uniformType, count);
|
rlSetUniform(locIndex, value, uniformType, count);
|
||||||
//rlDisableShader(); // Avoid reseting current shader program, in case other uniforms are set
|
//rlDisableShader(); // Avoid resetting current shader program, in case other uniforms are set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2617,7 +2618,7 @@ Ray GetMouseRay(Vector2 mouse, Camera camera)
|
||||||
Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
|
Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
|
||||||
|
|
||||||
// Unproject the mouse cursor in the near plane.
|
// Unproject the mouse cursor in the near plane.
|
||||||
// We need this as the source position because orthographic projects, compared to perspect doesn't have a
|
// We need this as the source position because orthographic projects, compared to perspective doesn't have a
|
||||||
// convergence point, meaning that the "eye" of the camera is more like a plane than a point.
|
// convergence point, meaning that the "eye" of the camera is more like a plane than a point.
|
||||||
Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
|
Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
|
||||||
|
|
||||||
|
|
@ -2807,7 +2808,7 @@ double GetTime(void)
|
||||||
|
|
||||||
// Setup window configuration flags (view FLAGS)
|
// Setup window configuration flags (view FLAGS)
|
||||||
// NOTE: This function is expected to be called before window creation,
|
// NOTE: This function is expected to be called before window creation,
|
||||||
// because it setups some flags for the window creation process.
|
// because it sets up some flags for the window creation process.
|
||||||
// To configure window states after creation, just use SetWindowState()
|
// To configure window states after creation, just use SetWindowState()
|
||||||
void SetConfigFlags(unsigned int flags)
|
void SetConfigFlags(unsigned int flags)
|
||||||
{
|
{
|
||||||
|
|
@ -3032,7 +3033,7 @@ const char *GetDirectoryPath(const char *filePath)
|
||||||
if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/')
|
if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/')
|
||||||
{
|
{
|
||||||
// For security, we set starting path to current directory,
|
// For security, we set starting path to current directory,
|
||||||
// obtained path will be concated to this
|
// obtained path will be concatenated to this
|
||||||
dirPath[0] = '.';
|
dirPath[0] = '.';
|
||||||
dirPath[1] = '/';
|
dirPath[1] = '/';
|
||||||
}
|
}
|
||||||
|
|
@ -3928,7 +3929,7 @@ static bool InitGraphicsDevice(int width, int height)
|
||||||
CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default
|
CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default
|
||||||
|
|
||||||
// NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
|
// NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
|
||||||
// ...in top-down or left-right to match display aspect ratio (no weird scalings)
|
// ...in top-down or left-right to match display aspect ratio (no weird scaling)
|
||||||
|
|
||||||
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
|
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
|
||||||
glfwSetErrorCallback(ErrorCallback);
|
glfwSetErrorCallback(ErrorCallback);
|
||||||
|
|
@ -4099,8 +4100,8 @@ static bool InitGraphicsDevice(int width, int height)
|
||||||
// remember center for switchinging from fullscreen to window
|
// remember center for switchinging from fullscreen to window
|
||||||
if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
|
if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
|
||||||
{
|
{
|
||||||
// If screen width/height equal to the dislpay, we can't calclulate the window pos for toggling fullscreened/windowed.
|
// If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
|
||||||
// Toggling fullscreened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
|
// Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
|
||||||
CORE.Window.position.x = CORE.Window.display.width/4;
|
CORE.Window.position.x = CORE.Window.display.width/4;
|
||||||
CORE.Window.position.y = CORE.Window.display.height/4;
|
CORE.Window.position.y = CORE.Window.display.height/4;
|
||||||
}
|
}
|
||||||
|
|
@ -4137,7 +4138,7 @@ static bool InitGraphicsDevice(int width, int height)
|
||||||
// framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
|
// framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
|
||||||
// by the sides to fit all monitor space...
|
// by the sides to fit all monitor space...
|
||||||
|
|
||||||
// Try to setup the most appropiate fullscreen framebuffer for the requested screenWidth/screenHeight
|
// Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
|
||||||
// It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
|
// It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
|
||||||
// Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
|
// Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
|
||||||
// TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
|
// TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
|
||||||
|
|
@ -4204,7 +4205,7 @@ static bool InitGraphicsDevice(int width, int height)
|
||||||
// NOTE: V-Sync can be enabled by graphic driver configuration
|
// NOTE: V-Sync can be enabled by graphic driver configuration
|
||||||
if (CORE.Window.flags & FLAG_VSYNC_HINT)
|
if (CORE.Window.flags & FLAG_VSYNC_HINT)
|
||||||
{
|
{
|
||||||
// WARNING: It seems to hits a critical render path in Intel HD Graphics
|
// WARNING: It seems to hit a critical render path in Intel HD Graphics
|
||||||
glfwSwapInterval(1);
|
glfwSwapInterval(1);
|
||||||
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC");
|
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC");
|
||||||
}
|
}
|
||||||
|
|
@ -4215,12 +4216,12 @@ static bool InitGraphicsDevice(int width, int height)
|
||||||
#if defined(PLATFORM_DESKTOP)
|
#if defined(PLATFORM_DESKTOP)
|
||||||
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
|
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
|
||||||
{
|
{
|
||||||
// NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling
|
// NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling.
|
||||||
// Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
|
// Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
|
||||||
#if !defined(__APPLE__)
|
#if !defined(__APPLE__)
|
||||||
glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight);
|
glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight);
|
||||||
|
|
||||||
// Screen scaling matrix is required in case desired screen area is different than display area
|
// Screen scaling matrix is required in case desired screen area is different from display area
|
||||||
CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
|
CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
|
||||||
|
|
||||||
// Mouse input scaling for the new screen size
|
// Mouse input scaling for the new screen size
|
||||||
|
|
@ -4818,7 +4819,7 @@ static void InitTimer(void)
|
||||||
// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
|
// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
|
||||||
// take longer than expected... for that reason we use the busy wait loop
|
// take longer than expected... for that reason we use the busy wait loop
|
||||||
// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
|
// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
|
||||||
// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timming on Win32!
|
// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
|
||||||
void WaitTime(double seconds)
|
void WaitTime(double seconds)
|
||||||
{
|
{
|
||||||
#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
|
#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
|
||||||
|
|
@ -5413,7 +5414,7 @@ static void CharCallback(GLFWwindow *window, unsigned int key)
|
||||||
//TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
|
//TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
|
||||||
|
|
||||||
// NOTE: Registers any key down considering OS keyboard layout but
|
// NOTE: Registers any key down considering OS keyboard layout but
|
||||||
// do not detects action events, those should be managed by user...
|
// does not detect action events, those should be managed by user...
|
||||||
// Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
|
// Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
|
||||||
// Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
|
// Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
|
||||||
|
|
||||||
|
|
@ -5456,7 +5457,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
|
||||||
gestureEvent.position[0].x /= (float)GetScreenWidth();
|
gestureEvent.position[0].x /= (float)GetScreenWidth();
|
||||||
gestureEvent.position[0].y /= (float)GetScreenHeight();
|
gestureEvent.position[0].y /= (float)GetScreenHeight();
|
||||||
|
|
||||||
// Gesture data is sent to gestures system for processing
|
// Gesture data is sent to gestures-system for processing
|
||||||
ProcessGestureEvent(gestureEvent);
|
ProcessGestureEvent(gestureEvent);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -5487,7 +5488,7 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
|
||||||
gestureEvent.position[0].x /= (float)GetScreenWidth();
|
gestureEvent.position[0].x /= (float)GetScreenWidth();
|
||||||
gestureEvent.position[0].y /= (float)GetScreenHeight();
|
gestureEvent.position[0].y /= (float)GetScreenHeight();
|
||||||
|
|
||||||
// Gesture data is sent to gestures system for processing
|
// Gesture data is sent to gestures-system for processing
|
||||||
ProcessGestureEvent(gestureEvent);
|
ProcessGestureEvent(gestureEvent);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ static double rgGetCurrentTime(void);
|
||||||
// Module Functions Definition
|
// Module Functions Definition
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Enable only desired getures to be detected
|
// Enable only desired gestures to be detected
|
||||||
void SetGesturesEnabled(unsigned int flags)
|
void SetGesturesEnabled(unsigned int flags)
|
||||||
{
|
{
|
||||||
GESTURES.enabledFlags = flags;
|
GESTURES.enabledFlags = flags;
|
||||||
|
|
@ -300,7 +300,7 @@ void ProcessGestureEvent(GestureEvent event)
|
||||||
{
|
{
|
||||||
if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.upPosition = event.position[0];
|
if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.upPosition = event.position[0];
|
||||||
|
|
||||||
// NOTE: GESTURES.Drag.intensity dependend on the resolution of the screen
|
// NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen
|
||||||
GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
|
GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
|
||||||
GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.timeDuration));
|
GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.timeDuration));
|
||||||
|
|
||||||
|
|
@ -472,7 +472,7 @@ Vector2 GetGestureDragVector(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get drag angle
|
// Get drag angle
|
||||||
// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
|
// NOTE: Angle in degrees, horizontal-right is 0, counterclockwise
|
||||||
float GetGestureDragAngle(void)
|
float GetGestureDragAngle(void)
|
||||||
{
|
{
|
||||||
// NOTE: drag angle is calculated on one touch points TOUCH_ACTION_UP
|
// NOTE: drag angle is calculated on one touch points TOUCH_ACTION_UP
|
||||||
|
|
@ -488,8 +488,8 @@ Vector2 GetGesturePinchVector(void)
|
||||||
return GESTURES.Pinch.vector;
|
return GESTURES.Pinch.vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get angle beween two pinch points
|
// Get angle between two pinch points
|
||||||
// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
|
// NOTE: Angle in degrees, horizontal-right is 0, counterclockwise
|
||||||
float GetGesturePinchAngle(void)
|
float GetGesturePinchAngle(void)
|
||||||
{
|
{
|
||||||
// NOTE: pinch angle is calculated on two touch points TOUCH_ACTION_MOVE
|
// NOTE: pinch angle is calculated on two touch points TOUCH_ACTION_MOVE
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,7 @@ typedef enum {
|
||||||
} rlShaderAttributeDataType;
|
} rlShaderAttributeDataType;
|
||||||
|
|
||||||
// Framebuffer attachment type
|
// Framebuffer attachment type
|
||||||
// NOTE: By default up to 8 color channels defined but it can be more
|
// NOTE: By default up to 8 color channels defined, but it can be more
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0
|
RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0
|
||||||
RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1
|
RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1
|
||||||
|
|
|
||||||
|
|
@ -1125,7 +1125,7 @@ void UnloadModel(Model model)
|
||||||
|
|
||||||
// Unload materials maps
|
// Unload materials maps
|
||||||
// NOTE: As the user could be sharing shaders and textures between models,
|
// NOTE: As the user could be sharing shaders and textures between models,
|
||||||
// we don't unload the material but just free it's maps,
|
// we don't unload the material but just free its maps,
|
||||||
// the user is responsible for freeing models shaders and textures
|
// the user is responsible for freeing models shaders and textures
|
||||||
for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
|
for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
|
||||||
|
|
||||||
|
|
@ -1146,7 +1146,7 @@ void UnloadModelKeepMeshes(Model model)
|
||||||
{
|
{
|
||||||
// Unload materials maps
|
// Unload materials maps
|
||||||
// NOTE: As the user could be sharing shaders and textures between models,
|
// NOTE: As the user could be sharing shaders and textures between models,
|
||||||
// we don't unload the material but just free it's maps,
|
// we don't unload the material but just free its maps,
|
||||||
// the user is responsible for freeing models shaders and textures
|
// the user is responsible for freeing models shaders and textures
|
||||||
for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
|
for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
|
||||||
|
|
||||||
|
|
@ -1230,7 +1230,7 @@ void UploadMesh(Mesh *mesh, bool dynamic)
|
||||||
rlEnableVertexAttribute(1);
|
rlEnableVertexAttribute(1);
|
||||||
|
|
||||||
// WARNING: When setting default vertex attribute values, the values for each generic vertex attribute
|
// WARNING: When setting default vertex attribute values, the values for each generic vertex attribute
|
||||||
// is part of current state and it is maintained even if a different program object is used
|
// is part of current state, and it is maintained even if a different program object is used
|
||||||
|
|
||||||
if (mesh->normals != NULL)
|
if (mesh->normals != NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -1383,7 +1383,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a copy of current matrices to work with,
|
// Get a copy of current matrices to work with,
|
||||||
// just in case stereo render is required and we need to modify them
|
// just in case stereo render is required, and we need to modify them
|
||||||
// NOTE: At this point the modelview matrix just contains the view matrix (camera)
|
// NOTE: At this point the modelview matrix just contains the view matrix (camera)
|
||||||
// That's because BeginMode3D() sets it and there is no model-drawing function
|
// That's because BeginMode3D() sets it and there is no model-drawing function
|
||||||
// that modifies it, all use rlPushMatrix() and rlPopMatrix()
|
// that modifies it, all use rlPushMatrix() and rlPopMatrix()
|
||||||
|
|
@ -1396,7 +1396,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
|
||||||
if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView);
|
if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView);
|
||||||
if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection);
|
if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection);
|
||||||
|
|
||||||
// Model transformation matrix is send to shader uniform location: SHADER_LOC_MATRIX_MODEL
|
// Model transformation matrix is sent to shader uniform location: SHADER_LOC_MATRIX_MODEL
|
||||||
if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MODEL], transform);
|
if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MODEL], transform);
|
||||||
|
|
||||||
// Accumulate several model transformations:
|
// Accumulate several model transformations:
|
||||||
|
|
@ -1517,7 +1517,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
|
||||||
else rlDrawVertexArray(0, mesh.vertexCount);
|
else rlDrawVertexArray(0, mesh.vertexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unbind all binded texture maps
|
// Unbind all bound texture maps
|
||||||
for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
|
for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
|
||||||
{
|
{
|
||||||
if (material.maps[i].texture.id > 0)
|
if (material.maps[i].texture.id > 0)
|
||||||
|
|
@ -1587,7 +1587,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a copy of current matrices to work with,
|
// Get a copy of current matrices to work with,
|
||||||
// just in case stereo render is required and we need to modify them
|
// just in case stereo render is required, and we need to modify them
|
||||||
// NOTE: At this point the modelview matrix just contains the view matrix (camera)
|
// NOTE: At this point the modelview matrix just contains the view matrix (camera)
|
||||||
// That's because BeginMode3D() sets it and there is no model-drawing function
|
// That's because BeginMode3D() sets it and there is no model-drawing function
|
||||||
// that modifies it, all use rlPushMatrix() and rlPopMatrix()
|
// that modifies it, all use rlPushMatrix() and rlPopMatrix()
|
||||||
|
|
@ -1738,7 +1738,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
|
||||||
else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances);
|
else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unbind all binded texture maps
|
// Unbind all bound texture maps
|
||||||
for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
|
for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
|
||||||
{
|
{
|
||||||
if (material.maps[i].texture.id > 0)
|
if (material.maps[i].texture.id > 0)
|
||||||
|
|
@ -1875,34 +1875,34 @@ bool ExportMesh(Mesh mesh, const char *fileName)
|
||||||
// Process obj materials
|
// Process obj materials
|
||||||
static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount)
|
static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount)
|
||||||
{
|
{
|
||||||
// Init model materials
|
// Init model materials
|
||||||
for (int m = 0; m < materialCount; m++)
|
for (int m = 0; m < materialCount; m++)
|
||||||
{
|
{
|
||||||
// Init material to default
|
// Init material to default
|
||||||
// NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE
|
// NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE
|
||||||
rayMaterials[m] = LoadMaterialDefault();
|
rayMaterials[m] = LoadMaterialDefault();
|
||||||
|
|
||||||
// Get default texture, in case no texture is defined
|
// Get default texture, in case no texture is defined
|
||||||
// NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
|
// NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
|
||||||
rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
|
rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
|
||||||
|
|
||||||
if (materials[m].diffuse_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd
|
if (materials[m].diffuse_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd
|
||||||
|
|
||||||
rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3];
|
rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3];
|
||||||
rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f;
|
rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f;
|
||||||
|
|
||||||
if (materials[m].specular_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks
|
if (materials[m].specular_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks
|
||||||
rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3];
|
rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3];
|
||||||
rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f;
|
rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f;
|
||||||
|
|
||||||
if (materials[m].bump_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump
|
if (materials[m].bump_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump
|
||||||
rayMaterials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE;
|
rayMaterials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE;
|
||||||
rayMaterials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess;
|
rayMaterials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess;
|
||||||
|
|
||||||
rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3];
|
rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3];
|
||||||
|
|
||||||
if (materials[m].displacement_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp
|
if (materials[m].displacement_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -2559,7 +2559,7 @@ Mesh GenMeshSphere(float radius, int rings, int slices)
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate hemi-sphere mesh (half sphere, no bottom cap)
|
// Generate hemisphere mesh (half sphere, no bottom cap)
|
||||||
Mesh GenMeshHemiSphere(float radius, int rings, int slices)
|
Mesh GenMeshHemiSphere(float radius, int rings, int slices)
|
||||||
{
|
{
|
||||||
Mesh mesh = { 0 };
|
Mesh mesh = { 0 };
|
||||||
|
|
@ -3242,7 +3242,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move data from mapVertices temp arays to vertices float array
|
// Move data from mapVertices temp arrays to vertices float array
|
||||||
mesh.vertexCount = vCounter;
|
mesh.vertexCount = vCounter;
|
||||||
mesh.triangleCount = vCounter/3;
|
mesh.triangleCount = vCounter/3;
|
||||||
|
|
||||||
|
|
@ -3742,7 +3742,7 @@ RayCollision GetRayCollisionBox(Ray ray, BoundingBox box)
|
||||||
// NOTE: We use an additional .01 to fix numerical errors
|
// NOTE: We use an additional .01 to fix numerical errors
|
||||||
collision.normal = Vector3Scale(collision.normal, 2.01f);
|
collision.normal = Vector3Scale(collision.normal, 2.01f);
|
||||||
collision.normal = Vector3Divide(collision.normal, Vector3Subtract(box.max, box.min));
|
collision.normal = Vector3Divide(collision.normal, Vector3Subtract(box.max, box.min));
|
||||||
// The relevant elemets of the vector are now slightly larger than 1.0f (or smaller than -1.0f)
|
// The relevant elements of the vector are now slightly larger than 1.0f (or smaller than -1.0f)
|
||||||
// and the others are somewhere between -1.0 and 1.0 casting to int is exactly our wanted normal!
|
// and the others are somewhere between -1.0 and 1.0 casting to int is exactly our wanted normal!
|
||||||
collision.normal.x = (float)((int)collision.normal.x);
|
collision.normal.x = (float)((int)collision.normal.x);
|
||||||
collision.normal.y = (float)((int)collision.normal.y);
|
collision.normal.y = (float)((int)collision.normal.y);
|
||||||
|
|
@ -3842,7 +3842,7 @@ RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3
|
||||||
// Calculate u parameter and test bound
|
// Calculate u parameter and test bound
|
||||||
u = Vector3DotProduct(tv, p)*invDet;
|
u = Vector3DotProduct(tv, p)*invDet;
|
||||||
|
|
||||||
// The intersection lies outside of the triangle
|
// The intersection lies outside the triangle
|
||||||
if ((u < 0.0f) || (u > 1.0f)) return collision;
|
if ((u < 0.0f) || (u > 1.0f)) return collision;
|
||||||
|
|
||||||
// Prepare to test v parameter
|
// Prepare to test v parameter
|
||||||
|
|
@ -3851,7 +3851,7 @@ RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3
|
||||||
// Calculate V parameter and test bound
|
// Calculate V parameter and test bound
|
||||||
v = Vector3DotProduct(ray.direction, q)*invDet;
|
v = Vector3DotProduct(ray.direction, q)*invDet;
|
||||||
|
|
||||||
// The intersection lies outside of the triangle
|
// The intersection lies outside the triangle
|
||||||
if ((v < 0.0f) || ((u + v) > 1.0f)) return collision;
|
if ((v < 0.0f) || ((u + v) > 1.0f)) return collision;
|
||||||
|
|
||||||
t = Vector3DotProduct(edge2, q)*invDet;
|
t = Vector3DotProduct(edge2, q)*invDet;
|
||||||
|
|
@ -4041,7 +4041,7 @@ static Model LoadOBJ(const char *fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init model materials
|
// Init model materials
|
||||||
ProcessMaterialsOBJ(model.materials, materials, materialCount);
|
ProcessMaterialsOBJ(model.materials, materials, materialCount);
|
||||||
|
|
||||||
tinyobj_attrib_free(&attrib);
|
tinyobj_attrib_free(&attrib);
|
||||||
tinyobj_shapes_free(meshes, meshCount);
|
tinyobj_shapes_free(meshes, meshCount);
|
||||||
|
|
@ -4671,7 +4671,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat
|
||||||
{
|
{
|
||||||
Image image = { 0 };
|
Image image = { 0 };
|
||||||
|
|
||||||
if (cgltfImage->uri != NULL) // Check if image data is provided as a uri (base64 or path)
|
if (cgltfImage->uri != NULL) // Check if image data is provided as an uri (base64 or path)
|
||||||
{
|
{
|
||||||
if ((strlen(cgltfImage->uri) > 5) &&
|
if ((strlen(cgltfImage->uri) > 5) &&
|
||||||
(cgltfImage->uri[0] == 'd') &&
|
(cgltfImage->uri[0] == 'd') &&
|
||||||
|
|
@ -4959,7 +4959,7 @@ static Model LoadGLTF(const char *fileName)
|
||||||
|
|
||||||
for (unsigned int j = 0; j < data->meshes[i].primitives[p].attributes_count; j++)
|
for (unsigned int j = 0; j < data->meshes[i].primitives[p].attributes_count; j++)
|
||||||
{
|
{
|
||||||
// Check the different attributes for every pimitive
|
// Check the different attributes for every primitive
|
||||||
if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_position) // POSITION
|
if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_position) // POSITION
|
||||||
{
|
{
|
||||||
cgltf_accessor *attribute = data->meshes[i].primitives[p].attributes[j].data;
|
cgltf_accessor *attribute = data->meshes[i].primitives[p].attributes[j].data;
|
||||||
|
|
@ -5110,7 +5110,7 @@ static Model LoadGLTF(const char *fileName)
|
||||||
{
|
{
|
||||||
// The primitive actually keeps the pointer to the corresponding material,
|
// The primitive actually keeps the pointer to the corresponding material,
|
||||||
// raylib instead assigns to the mesh the by its index, as loaded in model.materials array
|
// raylib instead assigns to the mesh the by its index, as loaded in model.materials array
|
||||||
// To get the index, we check if material pointers match and we assign the corresponding index,
|
// To get the index, we check if material pointers match, and we assign the corresponding index,
|
||||||
// skipping index 0, the default material
|
// skipping index 0, the default material
|
||||||
if (&data->materials[m] == data->meshes[i].primitives[p].material)
|
if (&data->materials[m] == data->meshes[i].primitives[p].material)
|
||||||
{
|
{
|
||||||
|
|
@ -5613,7 +5613,7 @@ static Model LoadM3D(const char *fileName)
|
||||||
// Materials are grouped together
|
// Materials are grouped together
|
||||||
if (mi != m3d->face[i].materialid)
|
if (mi != m3d->face[i].materialid)
|
||||||
{
|
{
|
||||||
// there should be only one material switch per material kind, but be bulletproof for unoptimal model files
|
// there should be only one material switch per material kind, but be bulletproof for non-optimal model files
|
||||||
if (k + 1 >= model.meshCount)
|
if (k + 1 >= model.meshCount)
|
||||||
{
|
{
|
||||||
model.meshCount++;
|
model.meshCount++;
|
||||||
|
|
@ -5633,8 +5633,9 @@ static Model LoadM3D(const char *fileName)
|
||||||
model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, sizeof(float));
|
model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, sizeof(float));
|
||||||
model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
|
model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
|
||||||
|
|
||||||
// If color map is provided, we allocate storage for vertex colors
|
// If no map is provided, we allocate storage for vertex colors
|
||||||
if (m3d->cmap != NULL) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
|
// M3D specs only consider vertex colors if no material is provided
|
||||||
|
if (mi != M3D_UNDEF) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
|
||||||
|
|
||||||
if (m3d->numbone && m3d->numskin)
|
if (m3d->numbone && m3d->numskin)
|
||||||
{
|
{
|
||||||
|
|
@ -5838,7 +5839,7 @@ static Model LoadM3D(const char *fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets
|
// Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets
|
||||||
// called, but not before, however DrawMesh uses these if they exists (so not good if they are left empty).
|
// called, but not before, however DrawMesh uses these if they exist (so not good if they are left empty).
|
||||||
if (m3d->numbone && m3d->numskin)
|
if (m3d->numbone && m3d->numskin)
|
||||||
{
|
{
|
||||||
for(i = 0; i < model.meshCount; i++)
|
for(i = 0; i < model.meshCount; i++)
|
||||||
|
|
|
||||||
|
|
@ -978,7 +978,7 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
|
||||||
rlSetTexture(texShapes.id);
|
rlSetTexture(texShapes.id);
|
||||||
|
|
||||||
rlBegin(RL_QUADS);
|
rlBegin(RL_QUADS);
|
||||||
// Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
|
// Draw all the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
|
||||||
for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
|
for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
|
||||||
{
|
{
|
||||||
float angle = angles[k];
|
float angle = angles[k];
|
||||||
|
|
@ -1207,7 +1207,7 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
|
||||||
|
|
||||||
rlBegin(RL_QUADS);
|
rlBegin(RL_QUADS);
|
||||||
|
|
||||||
// Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
|
// Draw all the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
|
||||||
for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
|
for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
|
||||||
{
|
{
|
||||||
float angle = angles[k];
|
float angle = angles[k];
|
||||||
|
|
@ -1342,7 +1342,7 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo
|
||||||
// Use LINES to draw the outline
|
// Use LINES to draw the outline
|
||||||
rlBegin(RL_LINES);
|
rlBegin(RL_LINES);
|
||||||
|
|
||||||
// Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
|
// Draw all the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
|
||||||
for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
|
for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
|
||||||
{
|
{
|
||||||
float angle = angles[k];
|
float angle = angles[k];
|
||||||
|
|
|
||||||
22
src/rtext.c
22
src/rtext.c
|
|
@ -65,7 +65,7 @@
|
||||||
#include <stdio.h> // Required for: vsprintf()
|
#include <stdio.h> // Required for: vsprintf()
|
||||||
#include <string.h> // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
|
#include <string.h> // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
|
||||||
#include <stdarg.h> // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
|
#include <stdarg.h> // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
|
||||||
#include <ctype.h> // Requried for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
|
#include <ctype.h> // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
|
||||||
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_TTF)
|
#if defined(SUPPORT_FILEFORMAT_TTF)
|
||||||
#define STB_RECT_PACK_IMPLEMENTATION
|
#define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
|
@ -336,7 +336,7 @@ Font LoadFont(const char *fileName)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default we set point filter (best performance)
|
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_TTF_DEFAULT_SIZE, FONT_TTF_DEFAULT_NUMCHARS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -584,7 +584,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
|
||||||
glyphCount = (glyphCount > 0)? glyphCount : 95;
|
glyphCount = (glyphCount > 0)? glyphCount : 95;
|
||||||
|
|
||||||
// Fill fontChars in case not provided externally
|
// Fill fontChars in case not provided externally
|
||||||
// NOTE: By default we fill glyphCount consecutevely, starting at 32 (Space)
|
// NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
|
||||||
|
|
||||||
if (fontChars == NULL)
|
if (fontChars == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -647,7 +647,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get bounding box for character (may be offset to account for chars that dip above or below the line)
|
// Get bounding box for character (maybe offset to account for chars that dip above or below the line)
|
||||||
/*
|
/*
|
||||||
int chX1, chY1, chX2, chY2;
|
int chX1, chY1, chX2, chY2;
|
||||||
stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
|
stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
|
||||||
|
|
@ -777,7 +777,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
|
||||||
|
|
||||||
for (int i = 0; i < glyphCount; i++)
|
for (int i = 0; i < glyphCount; i++)
|
||||||
{
|
{
|
||||||
// It return char rectangles in atlas
|
// It returns char rectangles in atlas
|
||||||
recs[i].x = rects[i].x + (float)padding;
|
recs[i].x = rects[i].x + (float)padding;
|
||||||
recs[i].y = rects[i].y + (float)padding;
|
recs[i].y = rects[i].y + (float)padding;
|
||||||
recs[i].width = (float)chars[i].image.width;
|
recs[i].width = (float)chars[i].image.width;
|
||||||
|
|
@ -1040,7 +1040,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
|
||||||
|
|
||||||
int size = TextLength(text); // Total size in bytes of the text, scanned by codepoints in loop
|
int size = TextLength(text); // Total size in bytes of the text, scanned by codepoints in loop
|
||||||
|
|
||||||
int textOffsetY = 0; // Offset between lines (on line break '\n')
|
int textOffsetY = 0; // Offset between lines (on linebreak '\n')
|
||||||
float textOffsetX = 0.0f; // Offset X to next character to draw
|
float textOffsetX = 0.0f; // Offset X to next character to draw
|
||||||
|
|
||||||
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
|
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
|
||||||
|
|
@ -1053,7 +1053,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
|
||||||
int index = GetGlyphIndex(font, codepoint);
|
int index = GetGlyphIndex(font, codepoint);
|
||||||
|
|
||||||
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
||||||
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
|
// but we need to draw all the bad bytes using the '?' symbol moving one byte
|
||||||
if (codepoint == 0x3f) codepointByteCount = 1;
|
if (codepoint == 0x3f) codepointByteCount = 1;
|
||||||
|
|
||||||
if (codepoint == '\n')
|
if (codepoint == '\n')
|
||||||
|
|
@ -1119,7 +1119,7 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz
|
||||||
// Draw multiple character (codepoints)
|
// Draw multiple character (codepoints)
|
||||||
void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint)
|
void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint)
|
||||||
{
|
{
|
||||||
int textOffsetY = 0; // Offset between lines (on line break '\n')
|
int textOffsetY = 0; // Offset between lines (on linebreak '\n')
|
||||||
float textOffsetX = 0.0f; // Offset X to next character to draw
|
float textOffsetX = 0.0f; // Offset X to next character to draw
|
||||||
|
|
||||||
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
|
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
|
||||||
|
|
@ -1195,7 +1195,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
|
||||||
index = GetGlyphIndex(font, letter);
|
index = GetGlyphIndex(font, letter);
|
||||||
|
|
||||||
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
||||||
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
|
// but we need to draw all the bad bytes using the '?' symbol so to not skip any we set next = 1
|
||||||
if (letter == 0x3f) next = 1;
|
if (letter == 0x3f) next = 1;
|
||||||
i += next - 1;
|
i += next - 1;
|
||||||
|
|
||||||
|
|
@ -1410,7 +1410,7 @@ char *TextReplace(char *text, const char *replace, const char *by)
|
||||||
char *insertPoint = NULL; // Next insert point
|
char *insertPoint = NULL; // Next insert point
|
||||||
char *temp = NULL; // Temp pointer
|
char *temp = NULL; // Temp pointer
|
||||||
int replaceLen = 0; // Replace string length of (the string to remove)
|
int replaceLen = 0; // Replace string length of (the string to remove)
|
||||||
int byLen = 0; // Replacement length (the string to replace replace by)
|
int byLen = 0; // Replacement length (the string to replace by)
|
||||||
int lastReplacePos = 0; // Distance between replace and end of last replace
|
int lastReplacePos = 0; // Distance between replace and end of last replace
|
||||||
int count = 0; // Number of replacements
|
int count = 0; // Number of replacements
|
||||||
|
|
||||||
|
|
@ -1756,7 +1756,7 @@ const char *CodepointToUTF8(int codepoint, int *utf8Size)
|
||||||
#endif // SUPPORT_TEXT_MANIPULATION
|
#endif // SUPPORT_TEXT_MANIPULATION
|
||||||
|
|
||||||
// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found
|
// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found
|
||||||
// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
|
// When an invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
|
||||||
// Total number of bytes processed are returned as a parameter
|
// Total number of bytes processed are returned as a parameter
|
||||||
// NOTE: The standard says U+FFFD should be returned in case of errors
|
// NOTE: The standard says U+FFFD should be returned in case of errors
|
||||||
// but that character is not supported by the default font in raylib
|
// but that character is not supported by the default font in raylib
|
||||||
|
|
|
||||||
|
|
@ -751,7 +751,7 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner,
|
||||||
float factor = (dist - radius*density)/(radius*(1.0f - density));
|
float factor = (dist - radius*density)/(radius*(1.0f - density));
|
||||||
|
|
||||||
factor = (float)fmax(factor, 0.0f);
|
factor = (float)fmax(factor, 0.0f);
|
||||||
factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check
|
factor = (float)fmin(factor, 1.f); // dist can be bigger than radius, so we have to check
|
||||||
|
|
||||||
pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
|
pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
|
||||||
pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
|
pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
|
||||||
|
|
@ -898,7 +898,7 @@ Image GenImageCellular(int width, int height, int tileSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// I made this up but it seems to give good results at all tile sizes
|
// I made this up, but it seems to give good results at all tile sizes
|
||||||
int intensity = (int)(minDistance*256.0f/tileSize);
|
int intensity = (int)(minDistance*256.0f/tileSize);
|
||||||
if (intensity > 255) intensity = 255;
|
if (intensity > 255) intensity = 255;
|
||||||
|
|
||||||
|
|
@ -1176,7 +1176,7 @@ void ImageFormat(Image *image, int newFormat)
|
||||||
} break;
|
} break;
|
||||||
case PIXELFORMAT_UNCOMPRESSED_R32:
|
case PIXELFORMAT_UNCOMPRESSED_R32:
|
||||||
{
|
{
|
||||||
// WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit
|
// WARNING: Image is converted to GRAYSCALE equivalent 32bit
|
||||||
|
|
||||||
image->data = (float *)RL_MALLOC(image->width*image->height*sizeof(float));
|
image->data = (float *)RL_MALLOC(image->width*image->height*sizeof(float));
|
||||||
|
|
||||||
|
|
@ -1214,7 +1214,7 @@ void ImageFormat(Image *image, int newFormat)
|
||||||
RL_FREE(pixels);
|
RL_FREE(pixels);
|
||||||
pixels = NULL;
|
pixels = NULL;
|
||||||
|
|
||||||
// In case original image had mipmaps, generate mipmaps for formated image
|
// In case original image had mipmaps, generate mipmaps for formatted image
|
||||||
// NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost
|
// NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost
|
||||||
if (image->mipmaps > 1)
|
if (image->mipmaps > 1)
|
||||||
{
|
{
|
||||||
|
|
@ -1269,7 +1269,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
|
||||||
int size = (int)strlen(text); // Get size in bytes of text
|
int size = (int)strlen(text); // Get size in bytes of text
|
||||||
|
|
||||||
int textOffsetX = 0; // Image drawing position X
|
int textOffsetX = 0; // Image drawing position X
|
||||||
int textOffsetY = 0; // Offset between lines (on line break '\n')
|
int textOffsetY = 0; // Offset between lines (on linebreak '\n')
|
||||||
|
|
||||||
// NOTE: Text image is generated at font base size, later scaled to desired font size
|
// NOTE: Text image is generated at font base size, later scaled to desired font size
|
||||||
Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); // WARNING: Module required: rtext
|
Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); // WARNING: Module required: rtext
|
||||||
|
|
@ -1286,7 +1286,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
|
||||||
int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext
|
int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext
|
||||||
|
|
||||||
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
||||||
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
|
// but we need to draw all the bad bytes using the '?' symbol moving one byte
|
||||||
if (codepoint == 0x3f) codepointByteCount = 1;
|
if (codepoint == 0x3f) codepointByteCount = 1;
|
||||||
|
|
||||||
if (codepoint == '\n')
|
if (codepoint == '\n')
|
||||||
|
|
@ -1331,7 +1331,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crop image depending on alpha value
|
// Crop image depending on alpha value
|
||||||
// NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f
|
// NOTE: Threshold is defined as a percentage: 0.0f -> 1.0f
|
||||||
void ImageAlphaCrop(Image *image, float threshold)
|
void ImageAlphaCrop(Image *image, float threshold)
|
||||||
{
|
{
|
||||||
// Security check to avoid program crash
|
// Security check to avoid program crash
|
||||||
|
|
@ -1711,7 +1711,7 @@ void ImageResize(Image *image, int newWidth, int newHeight)
|
||||||
Color *pixels = LoadImageColors(*image);
|
Color *pixels = LoadImageColors(*image);
|
||||||
Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color));
|
Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color));
|
||||||
|
|
||||||
// NOTE: Color data is casted to (unsigned char *), there shouldn't been any problem...
|
// NOTE: Color data is cast to (unsigned char *), there shouldn't been any problem...
|
||||||
stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4);
|
stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4);
|
||||||
|
|
||||||
int format = image->format;
|
int format = image->format;
|
||||||
|
|
@ -1891,7 +1891,7 @@ void ImageMipmaps(Image *image)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
|
// Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
|
||||||
// NOTE: In case selected bpp do not represent an known 16bit format,
|
// NOTE: In case selected bpp do not represent a known 16bit format,
|
||||||
// dithered data is stored in the LSB part of the unsigned short
|
// dithered data is stored in the LSB part of the unsigned short
|
||||||
void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
|
void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
|
||||||
{
|
{
|
||||||
|
|
@ -2531,7 +2531,7 @@ void UnloadImagePalette(Color *colors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get image alpha border rectangle
|
// Get image alpha border rectangle
|
||||||
// NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f
|
// NOTE: Threshold is defined as a percentage: 0.0f -> 1.0f
|
||||||
Rectangle GetImageAlphaBorder(Image image, float threshold)
|
Rectangle GetImageAlphaBorder(Image image, float threshold)
|
||||||
{
|
{
|
||||||
Rectangle crop = { 0 };
|
Rectangle crop = { 0 };
|
||||||
|
|
@ -3049,7 +3049,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
|
||||||
if ((srcRec.y + srcRec.height) > src.height) srcRec.height = src.height - srcRec.y;
|
if ((srcRec.y + srcRec.height) > src.height) srcRec.height = src.height - srcRec.y;
|
||||||
|
|
||||||
// Check if source rectangle needs to be resized to destination rectangle
|
// Check if source rectangle needs to be resized to destination rectangle
|
||||||
// In that case, we make a copy of source and we apply all required transform
|
// In that case, we make a copy of source, and we apply all required transform
|
||||||
if (((int)srcRec.width != (int)dstRec.width) || ((int)srcRec.height != (int)dstRec.height))
|
if (((int)srcRec.width != (int)dstRec.width) || ((int)srcRec.height != (int)dstRec.height))
|
||||||
{
|
{
|
||||||
srcMod = ImageFromImage(src, srcRec); // Create image from another image
|
srcMod = ImageFromImage(src, srcRec); // Create image from another image
|
||||||
|
|
@ -3273,7 +3273,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
|
||||||
faces = GenImageColor(size, size*6, MAGENTA);
|
faces = GenImageColor(size, size*6, MAGENTA);
|
||||||
ImageFormat(&faces, image.format);
|
ImageFormat(&faces, image.format);
|
||||||
|
|
||||||
// NOTE: Image formating does not work with compressed textures
|
// NOTE: Image formatting does not work with compressed textures
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
|
for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
|
||||||
}
|
}
|
||||||
|
|
@ -3606,7 +3606,7 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2
|
||||||
// NOTE: Vertex position can be transformed using matrices
|
// NOTE: Vertex position can be transformed using matrices
|
||||||
// but the process is way more costly than just calculating
|
// but the process is way more costly than just calculating
|
||||||
// the vertex positions manually, like done above.
|
// the vertex positions manually, like done above.
|
||||||
// I leave here the old implementation for educational pourposes,
|
// I leave here the old implementation for educational purposes,
|
||||||
// just in case someone wants to do some performance test
|
// just in case someone wants to do some performance test
|
||||||
/*
|
/*
|
||||||
rlSetTexture(texture.id);
|
rlSetTexture(texture.id);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en-us">
|
<html lang="EN-us">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,10 @@
|
||||||
static int logTypeLevel = LOG_INFO; // Minimum log type level
|
static int logTypeLevel = LOG_INFO; // Minimum log type level
|
||||||
|
|
||||||
static TraceLogCallback traceLog = NULL; // TraceLog callback function pointer
|
static TraceLogCallback traceLog = NULL; // TraceLog callback function pointer
|
||||||
static LoadFileDataCallback loadFileData = NULL; // LoadFileData callback funtion pointer
|
static LoadFileDataCallback loadFileData = NULL; // LoadFileData callback function pointer
|
||||||
static SaveFileDataCallback saveFileData = NULL; // SaveFileText callback funtion pointer
|
static SaveFileDataCallback saveFileData = NULL; // SaveFileText callback function pointer
|
||||||
static LoadFileTextCallback loadFileText = NULL; // LoadFileText callback funtion pointer
|
static LoadFileTextCallback loadFileText = NULL; // LoadFileText callback function pointer
|
||||||
static SaveFileTextCallback saveFileText = NULL; // SaveFileText callback funtion pointer
|
static SaveFileTextCallback saveFileText = NULL; // SaveFileText callback function pointer
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
// Functions to set internal callbacks
|
// Functions to set internal callbacks
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user