diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml
index f9e51680d..f676877fd 100644
--- a/.github/workflows/webassembly.yml
+++ b/.github/workflows/webassembly.yml
@@ -29,7 +29,7 @@ jobs:
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v14
with:
- version: 3.1.54
+ version: 3.1.64
actions-cache-folder: 'emsdk-cache'
- name: Setup Release Version
diff --git a/BINDINGS.md b/BINDINGS.md
index 3e840d587..7fd0dfd93 100644
--- a/BINDINGS.md
+++ b/BINDINGS.md
@@ -83,6 +83,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib-raku](https://github.com/vushu/raylib-raku) | **auto** | [Raku](https://www.raku.org) | Artistic License 2.0 |
| [Raylib.lean](https://github.com/KislyjKisel/Raylib.lean) | 4.5 | [Lean4](https://lean-lang.org) | BSD-3-Clause |
| [raylib-cobol](https://codeberg.org/glowiak/raylib-cobol) | **auto** | [COBOL](https://gnucobol.sourceforge.io) | Public domain |
+| [raylib-apl](https://github.com/Brian-ED/raylib-apl) | **5.0** | [Dyalog APL](https://www.dyalog.com/) | MIT |
### Utility Wrapers
diff --git a/examples/Makefile b/examples/Makefile
index be8d87204..93b05a0c7 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -479,6 +479,7 @@ CORE = \
core/core_automation_events \
core/core_basic_screen_manager \
core/core_basic_window \
+ core/core_basic_window_web \
core/core_custom_frame_control \
core/core_custom_logging \
core/core_drop_files \
@@ -528,6 +529,7 @@ TEXTURES = \
textures/textures_draw_tiled \
textures/textures_fog_of_war \
textures/textures_gif_player \
+ textures/textures_image_channel \
textures/textures_image_drawing \
textures/textures_image_generation \
textures/textures_image_kernel \
@@ -566,6 +568,7 @@ TEXT = \
MODELS = \
models/models_animation \
models/models_billboard \
+ models/models_bone_socket \
models/models_box_collisions \
models/models_cubicmap \
models/models_draw_cube_texture \
@@ -586,6 +589,7 @@ MODELS = \
SHADERS = \
shaders/shaders_basic_lighting \
+ shaders/shaders_basic_pbr \
shaders/shaders_custom_uniform \
shaders/shaders_deferred_render \
shaders/shaders_eratosthenes \
@@ -608,7 +612,8 @@ SHADERS = \
shaders/shaders_texture_outline \
shaders/shaders_texture_tiling \
shaders/shaders_texture_waves \
- shaders/shaders_write_depth
+ shaders/shaders_write_depth \
+ shaders/shaders_vertex_displacement
AUDIO = \
audio/audio_mixed_processor \
diff --git a/examples/Makefile.Web b/examples/Makefile.Web
index af3325aa5..46a8a98ea 100644
--- a/examples/Makefile.Web
+++ b/examples/Makefile.Web
@@ -476,7 +476,8 @@ SHADERS = \
shaders/shaders_texture_outline \
shaders/shaders_texture_tiling \
shaders/shaders_texture_waves \
- shaders/shaders_write_depth
+ shaders/shaders_write_depth \
+ shaders/shaders_vertex_displacement
AUDIO = \
audio/audio_mixed_processor \
diff --git a/examples/README.md b/examples/README.md
index d790f7d84..317f43c89 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -178,6 +178,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete
| 115 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) |
| ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) |
| 116 | [shaders_spotlight](shaders/shaders_spotlight.c) |
| ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
| 117 | [shaders_deferred_render](shaders/shaders_deferred_render.c) |
| ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) |
+| 118 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) |
| ⭐️☆☆☆ | 1.5 | 1.5 | [Alex ZH](https://github.com/ZzzhHe) |
### category: audio
@@ -185,10 +186,10 @@ Examples using raylib audio functionality, including sound/music loading and pla
| ## | example | image | difficulty
level | version
created | last version
updated | original
developer |
|----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------|
-| 118 | [audio_module_playing](audio/audio_module_playing.c) |
| ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) |
-| 119 | [audio_music_stream](audio/audio_music_stream.c) |
| ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) |
-| 120 | [audio_raw_stream](audio/audio_raw_stream.c) |
| ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) |
-| 121 | [audio_sound_loading](audio/audio_sound_loading.c) |
| ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) |
+| 119 | [audio_module_playing](audio/audio_module_playing.c) |
| ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) |
+| 120 | [audio_music_stream](audio/audio_music_stream.c) |
| ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) |
+| 121 | [audio_raw_stream](audio/audio_raw_stream.c) |
| ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) |
+| 122 | [audio_sound_loading](audio/audio_sound_loading.c) |
| ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) |
### category: others
@@ -196,11 +197,11 @@ Examples showing raylib misc functionality that does not fit in other categories
| ## | example | image | difficulty
level | version
created | last version
updated | original
developer |
|----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------|
-| 122 | [rlgl_standalone](others/rlgl_standalone.c) |
| ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) |
-| 123 | [rlgl_compute_shader](others/rlgl_compute_shader.c) |
| ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) |
-| 124 | [easings_testbed](others/easings_testbed.c) |
| ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) |
-| 125 | [raylib_opengl_interop](others/raylib_opengl_interop.c) |
| ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) |
-| 126 | [embedded_files_loading](others/embedded_files_loading.c) |
| ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) |
+| 123 | [rlgl_standalone](others/rlgl_standalone.c) |
| ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) |
+| 124 | [rlgl_compute_shader](others/rlgl_compute_shader.c) |
| ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) |
+| 125 | [easings_testbed](others/easings_testbed.c) |
| ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) |
+| 126 | [raylib_opengl_interop](others/raylib_opengl_interop.c) |
| ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) |
+| 127 | [embedded_files_loading](others/embedded_files_loading.c) |
| ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) |
As always contributions are welcome, feel free to send new examples! Here is an [examples template](examples_template.c) to start with!
diff --git a/examples/audio/audio_mixed_processor.c b/examples/audio/audio_mixed_processor.c
index fd970dd19..df46edc4e 100644
--- a/examples/audio/audio_mixed_processor.c
+++ b/examples/audio/audio_mixed_processor.c
@@ -97,7 +97,7 @@ int main(void)
DrawRectangle(199, 199, 402, 34, LIGHTGRAY);
for (int i = 0; i < 400; i++)
{
- DrawLine(201 + i, 232 - (int)averageVolume[i] * 32, 201 + i, 232, MAROON);
+ DrawLine(201 + i, 232 - (int)(averageVolume[i] * 32), 201 + i, 232, MAROON);
}
DrawRectangleLines(199, 199, 402, 34, GRAY);
diff --git a/examples/core/core_input_gamepad_info.c b/examples/core/core_input_gamepad_info.c
index 66fedda90..096294681 100644
--- a/examples/core/core_input_gamepad_info.c
+++ b/examples/core/core_input_gamepad_info.c
@@ -47,25 +47,25 @@ int main(void)
ClearBackground(RAYWHITE);
- for (int i = 0, y = 10; i < 4; i++) // MAX_GAMEPADS = 4
+ for (int i = 0, y = 5; i < 4; i++) // MAX_GAMEPADS = 4
{
if (IsGamepadAvailable(i))
{
- DrawText(TextFormat("Gamepad name: %s", GetGamepadName(i)), 10, y, 20, BLACK);
- y += 30;
- DrawText(TextFormat("\tAxis count: %d", GetGamepadAxisCount(i)), 10, y, 20, BLACK);
- y += 30;
+ DrawText(TextFormat("Gamepad name: %s", GetGamepadName(i)), 10, y, 10, BLACK);
+ y += 11;
+ DrawText(TextFormat("\tAxis count: %d", GetGamepadAxisCount(i)), 10, y, 10, BLACK);
+ y += 11;
for (int axis = 0; axis < GetGamepadAxisCount(i); axis++)
{
- DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 20, BLACK);
- y += 30;
+ DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 10, BLACK);
+ y += 11;
}
for (int button = 0; button < 32; button++)
{
- DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 20, BLACK);
- y += 30;
+ DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 10, BLACK);
+ y += 11;
}
}
}
@@ -80,4 +80,4 @@ int main(void)
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
-}
\ No newline at end of file
+}
diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c
index c98fce931..021698edb 100644
--- a/examples/core/core_vr_simulator.c
+++ b/examples/core/core_vr_simulator.c
@@ -100,7 +100,7 @@ int main(void)
DisableCursor(); // Limit cursor to relative movement inside the window
- SetTargetFPS(90); // Set our game to run at 90 frames-per-second
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
diff --git a/examples/models/models_box_collisions.c b/examples/models/models_box_collisions.c
index 936f65f38..4d98a776a 100644
--- a/examples/models/models_box_collisions.c
+++ b/examples/models/models_box_collisions.c
@@ -109,7 +109,7 @@ int main(void)
EndMode3D();
- DrawText("Move player with cursors to collide", 220, 40, 20, GRAY);
+ DrawText("Move player with arrow keys to collide", 220, 40, 20, GRAY);
DrawFPS(10, 10);
diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c
index 91a11f73c..5374c035e 100644
--- a/examples/models/models_yaw_pitch_roll.c
+++ b/examples/models/models_yaw_pitch_roll.c
@@ -114,6 +114,7 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model); // Unload model data
+ UnloadTexture(texture); // Unload texture data
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/examples/shaders/resources/shaders/glsl100/raymarching.fs b/examples/shaders/resources/shaders/glsl100/raymarching.fs
index d58d9d331..a7339d216 100644
--- a/examples/shaders/resources/shaders/glsl100/raymarching.fs
+++ b/examples/shaders/resources/shaders/glsl100/raymarching.fs
@@ -38,7 +38,7 @@ uniform vec2 resolution;
//
// More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
-#define AA 1 // make this 1 is your machine is too slow
+#define AA 1 // make this 1 if your machine is too slow
//------------------------------------------------------------------
diff --git a/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs b/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs
new file mode 100644
index 000000000..3328a91e7
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs
@@ -0,0 +1,18 @@
+#version 100
+
+precision mediump float;
+
+// Input vertex attributes (from fragment shader)
+varying vec2 fragTexCoord;
+varying float height;
+
+
+void main()
+{
+ vec4 darkblue = vec4(0.0, 0.13, 0.18, 1.0);
+ vec4 lightblue = vec4(1.0, 1.0, 1.0, 1.0);
+ // Interpolate between two colors based on height
+ vec4 finalColor = mix(darkblue, lightblue, height);
+
+ gl_FragColor = finalColor;
+}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs b/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs
new file mode 100644
index 000000000..c7b926d4b
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs
@@ -0,0 +1,45 @@
+#version 100
+
+precision mediump float;
+
+attribute vec3 vertexPosition;
+attribute vec2 vertexTexCoord;
+attribute vec3 vertexNormal;
+attribute vec4 vertexColor;
+
+uniform mat4 mvp;
+uniform mat4 matModel;
+uniform mat4 matNormal;
+
+uniform float time;
+
+uniform sampler2D perlinNoiseMap;
+
+varying vec3 fragPosition;
+varying vec2 fragTexCoord;
+varying vec3 fragNormal;
+varying float height;
+
+void main()
+{
+ // Calculate animated texture coordinates based on time and vertex position
+ vec2 animatedTexCoord = sin(vertexTexCoord + vec2(sin(time + vertexPosition.x * 0.1), cos(time + vertexPosition.z * 0.1)) * 0.3);
+
+ // Normalize animated texture coordinates to range [0, 1]
+ animatedTexCoord = animatedTexCoord * 0.5 + 0.5;
+
+ // Fetch displacement from the perlin noise map
+ float displacement = texture2D(perlinNoiseMap, animatedTexCoord).r * 7.0; // Amplified displacement
+
+ // Displace vertex position
+ vec3 displacedPosition = vertexPosition + vec3(0.0, displacement, 0.0);
+
+ // Send vertex attributes to fragment shader
+ fragPosition = vec3(matModel * vec4(displacedPosition, 1.0));
+ fragTexCoord = vertexTexCoord;
+ fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 1.0)));
+ height = displacedPosition.y * 0.2; // send height to fragment shader for coloring
+
+ // Calculate final vertex position
+ gl_Position = mvp * vec4(displacedPosition, 1.0);
+}
diff --git a/examples/shaders/resources/shaders/glsl120/raymarching.fs b/examples/shaders/resources/shaders/glsl120/raymarching.fs
new file mode 100644
index 000000000..efe4fa108
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl120/raymarching.fs
@@ -0,0 +1,427 @@
+#version 120
+
+// Input vertex attributes (from vertex shader)
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+
+uniform vec3 viewEye;
+uniform vec3 viewCenter;
+uniform float runTime;
+uniform vec2 resolution;
+
+// The MIT License
+// Copyright © 2013 Inigo Quilez
+// 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.
+
+// A list of useful distance function to simple primitives, and an example on how to
+// do some interesting boolean operations, repetition and displacement.
+//
+// More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
+
+#define AA 1 // make this 1 if your machine is too slow
+
+//------------------------------------------------------------------
+
+float sdPlane( vec3 p )
+{
+ return p.y;
+}
+
+float sdSphere( vec3 p, float s )
+{
+ return length(p)-s;
+}
+
+float sdBox( vec3 p, vec3 b )
+{
+ vec3 d = abs(p) - b;
+ return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));
+}
+
+float sdEllipsoid( in vec3 p, in vec3 r )
+{
+ return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z);
+}
+
+float udRoundBox( vec3 p, vec3 b, float r )
+{
+ return length(max(abs(p)-b,0.0))-r;
+}
+
+float sdTorus( vec3 p, vec2 t )
+{
+ return length( vec2(length(p.xz)-t.x,p.y) )-t.y;
+}
+
+float sdHexPrism( vec3 p, vec2 h )
+{
+ vec3 q = abs(p);
+#if 0
+ return max(q.z-h.y,max((q.x*0.866025+q.y*0.5),q.y)-h.x);
+#else
+ float d1 = q.z-h.y;
+ float d2 = max((q.x*0.866025+q.y*0.5),q.y)-h.x;
+ return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.);
+#endif
+}
+
+float sdCapsule( vec3 p, vec3 a, vec3 b, float r )
+{
+ vec3 pa = p-a, ba = b-a;
+ float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
+ return length( pa - ba*h ) - r;
+}
+
+float sdEquilateralTriangle( in vec2 p )
+{
+ const float k = sqrt(3.0);
+ p.x = abs(p.x) - 1.0;
+ p.y = p.y + 1.0/k;
+ if( p.x + k*p.y > 0.0 ) p = vec2( p.x - k*p.y, -k*p.x - p.y )/2.0;
+ p.x += 2.0 - 2.0*clamp( (p.x+2.0)/2.0, 0.0, 1.0 );
+ return -length(p)*sign(p.y);
+}
+
+float sdTriPrism( vec3 p, vec2 h )
+{
+ vec3 q = abs(p);
+ float d1 = q.z-h.y;
+#if 1
+ // distance bound
+ float d2 = max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5;
+#else
+ // correct distance
+ h.x *= 0.866025;
+ float d2 = sdEquilateralTriangle(p.xy/h.x)*h.x;
+#endif
+ return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.);
+}
+
+float sdCylinder( vec3 p, vec2 h )
+{
+ vec2 d = abs(vec2(length(p.xz),p.y)) - h;
+ return min(max(d.x,d.y),0.0) + length(max(d,0.0));
+}
+
+float sdCone( in vec3 p, in vec3 c )
+{
+ vec2 q = vec2( length(p.xz), p.y );
+ float d1 = -q.y-c.z;
+ float d2 = max( dot(q,c.xy), q.y);
+ return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.);
+}
+
+float sdConeSection( in vec3 p, in float h, in float r1, in float r2 )
+{
+ float d1 = -p.y - h;
+ float q = p.y - h;
+ float si = 0.5*(r1-r2)/h;
+ float d2 = max( sqrt( dot(p.xz,p.xz)*(1.0-si*si)) + q*si - r2, q );
+ return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.);
+}
+
+float sdPryamid4(vec3 p, vec3 h ) // h = { cos a, sin a, height }
+{
+ // Tetrahedron = Octahedron - Cube
+ float box = sdBox( p - vec3(0,-2.0*h.z,0), vec3(2.0*h.z) );
+
+ float d = 0.0;
+ d = max( d, abs( dot(p, vec3( -h.x, h.y, 0 )) ));
+ d = max( d, abs( dot(p, vec3( h.x, h.y, 0 )) ));
+ d = max( d, abs( dot(p, vec3( 0, h.y, h.x )) ));
+ d = max( d, abs( dot(p, vec3( 0, h.y,-h.x )) ));
+ float octa = d - h.z;
+ return max(-box,octa); // Subtraction
+ }
+
+float length2( vec2 p )
+{
+ return sqrt( p.x*p.x + p.y*p.y );
+}
+
+float length6( vec2 p )
+{
+ p = p*p*p; p = p*p;
+ return pow( p.x + p.y, 1.0/6.0 );
+}
+
+float length8( vec2 p )
+{
+ p = p*p; p = p*p; p = p*p;
+ return pow( p.x + p.y, 1.0/8.0 );
+}
+
+float sdTorus82( vec3 p, vec2 t )
+{
+ vec2 q = vec2(length2(p.xz)-t.x,p.y);
+ return length8(q)-t.y;
+}
+
+float sdTorus88( vec3 p, vec2 t )
+{
+ vec2 q = vec2(length8(p.xz)-t.x,p.y);
+ return length8(q)-t.y;
+}
+
+float sdCylinder6( vec3 p, vec2 h )
+{
+ return max( length6(p.xz)-h.x, abs(p.y)-h.y );
+}
+
+//------------------------------------------------------------------
+
+float opS( float d1, float d2 )
+{
+ return max(-d2,d1);
+}
+
+vec2 opU( vec2 d1, vec2 d2 )
+{
+ return (d1.x0.0 ) tmax = min( tmax, tp1 );
+ float tp2 = (1.6-ro.y)/rd.y; if( tp2>0.0 ) { if( ro.y>1.6 ) tmin = max( tmin, tp2 );
+ else tmax = min( tmax, tp2 ); }
+#endif
+
+ float t = tmin;
+ float m = -1.0;
+ for( int i=0; i<64; i++ )
+ {
+ float precis = 0.0005*t;
+ vec2 res = map( ro+rd*t );
+ if( res.xtmax ) break;
+ t += res.x;
+ m = res.y;
+ }
+
+ if( t>tmax ) m=-1.0;
+ return vec2( t, m );
+}
+
+
+float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax )
+{
+ float res = 1.0;
+ float t = mint;
+ for( int i=0; i<16; i++ )
+ {
+ float h = map( ro + rd*t ).x;
+ res = min( res, 8.0*h/t );
+ t += clamp( h, 0.02, 0.10 );
+ if( h<0.001 || t>tmax ) break;
+ }
+ return clamp( res, 0.0, 1.0 );
+}
+
+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 );
+ /*
+ vec3 eps = vec3( 0.0005, 0.0, 0.0 );
+ vec3 nor = vec3(
+ map(pos+eps.xyy).x - map(pos-eps.xyy).x,
+ map(pos+eps.yxy).x - map(pos-eps.yxy).x,
+ map(pos+eps.yyx).x - map(pos-eps.yyx).x );
+ return normalize(nor);
+ */
+}
+
+float calcAO( in vec3 pos, in vec3 nor )
+{
+ float occ = 0.0;
+ float sca = 1.0;
+ for( int i=0; i<5; i++ )
+ {
+ float hr = 0.01 + 0.12*float(i)/4.0;
+ vec3 aopos = nor * hr + pos;
+ float dd = map( aopos ).x;
+ occ += -(dd-hr)*sca;
+ sca *= 0.95;
+ }
+ return clamp( 1.0 - 3.0*occ, 0.0, 1.0 );
+}
+
+// http://iquilezles.org/www/articles/checkerfiltering/checkerfiltering.htm
+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;
+}
+
+vec3 render( in vec3 ro, in vec3 rd )
+{
+ vec3 col = vec3(0.7, 0.9, 1.0) +rd.y*0.8;
+ vec2 res = castRay(ro,rd);
+ float t = res.x;
+ float m = res.y;
+ if( m>-0.5 )
+ {
+ vec3 pos = ro + t*rd;
+ vec3 nor = calcNormal( pos );
+ vec3 ref = reflect( rd, nor );
+
+ // material
+ col = 0.45 + 0.35*sin( vec3(0.05,0.08,0.10)*(m-1.0) );
+ if( m<1.5 )
+ {
+
+ float f = checkersGradBox( 5.0*pos.xz );
+ col = 0.3 + f*vec3(0.1);
+ }
+
+ // lighting
+ float occ = calcAO( pos, nor );
+ vec3 lig = normalize( vec3(cos(-0.4 * runTime), sin(0.7 * runTime), -0.6) );
+ vec3 hal = normalize( lig-rd );
+ float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 );
+ float dif = clamp( dot( nor, lig ), 0.0, 1.0 );
+ float bac = clamp( dot( nor, normalize(vec3(-lig.x,0.0,-lig.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
+ float dom = smoothstep( -0.1, 0.1, ref.y );
+ float fre = pow( clamp(1.0+dot(nor,rd),0.0,1.0), 2.0 );
+
+ dif *= calcSoftshadow( pos, lig, 0.02, 2.5 );
+ dom *= calcSoftshadow( pos, ref, 0.02, 2.5 );
+
+ float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0)*
+ dif *
+ (0.04 + 0.96*pow( clamp(1.0+dot(hal,rd),0.0,1.0), 5.0 ));
+
+ vec3 lin = vec3(0.0);
+ lin += 1.30*dif*vec3(1.00,0.80,0.55);
+ lin += 0.40*amb*vec3(0.40,0.60,1.00)*occ;
+ lin += 0.50*dom*vec3(0.40,0.60,1.00)*occ;
+ lin += 0.50*bac*vec3(0.25,0.25,0.25)*occ;
+ lin += 0.25*fre*vec3(1.00,1.00,1.00)*occ;
+ col = col*lin;
+ col += 10.00*spe*vec3(1.00,0.90,0.70);
+
+ col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) );
+ }
+
+ return vec3( clamp(col,0.0,1.0) );
+}
+
+mat3 setCamera( in vec3 ro, in vec3 ta, float cr )
+{
+ vec3 cw = normalize(ta-ro);
+ vec3 cp = vec3(sin(cr), cos(cr),0.0);
+ vec3 cu = normalize( cross(cw,cp) );
+ vec3 cv = normalize( cross(cu,cw) );
+ return mat3( cu, cv, cw );
+}
+
+void main()
+{
+ vec3 tot = vec3(0.0);
+#if AA>1
+ for( int m=0; m1
+ }
+ tot /= float(AA*AA);
+#endif
+
+ gl_FragColor = vec4( tot, 1.0 );
+}
diff --git a/examples/shaders/resources/shaders/glsl330/vertex_displacement.fs b/examples/shaders/resources/shaders/glsl330/vertex_displacement.fs
new file mode 100644
index 000000000..424f526e4
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl330/vertex_displacement.fs
@@ -0,0 +1,16 @@
+#version 330
+
+// Input fragment attributes (from fragment shader)
+in vec2 fragTexCoord;
+in float height;
+
+// Output fragment color
+out vec4 finalColor;
+
+void main()
+{
+ vec4 darkblue = vec4(0.0, 0.13, 0.18, 1.0);
+ vec4 lightblue = vec4(1.0, 1.0, 1.0, 1.0);
+ // interplate between two colors based on height
+ finalColor = mix(darkblue, lightblue, height);
+}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs b/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs
new file mode 100644
index 000000000..73cf16109
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs
@@ -0,0 +1,46 @@
+#version 330
+
+// Input vertex attributes
+in vec3 vertexPosition;
+in vec2 vertexTexCoord;
+in vec3 vertexNormal;
+in vec4 vertexColor;
+
+// Input uniform values
+uniform mat4 mvp;
+uniform mat4 matModel;
+uniform mat4 matNormal;
+
+uniform float time;
+
+uniform sampler2D perlinNoiseMap;
+
+// Output vertex attributes (to fragment shader)
+out vec3 fragPosition;
+out vec2 fragTexCoord;
+out vec3 fragNormal;
+out float height;
+
+void main()
+{
+ // Calculate animated texture coordinates based on time and vertex position
+ vec2 animatedTexCoord = sin(vertexTexCoord + vec2(sin(time + vertexPosition.x * 0.1), cos(time + vertexPosition.z * 0.1)) * 0.3);
+
+ // Normalize animated texture coordinates to range [0, 1]
+ animatedTexCoord = animatedTexCoord * 0.5 + 0.5;
+
+ // Fetch displacement from the perlin noise map
+ float displacement = texture(perlinNoiseMap, animatedTexCoord).r * 7; // Amplified displacement
+
+ // Displace vertex position
+ vec3 displacedPosition = vertexPosition + vec3(0.0, displacement, 0.0);
+
+ // Send vertex attributes to fragment shader
+ fragPosition = vec3(matModel*vec4(displacedPosition, 1.0));
+ fragTexCoord = vertexTexCoord;
+ fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0)));
+ height = displacedPosition.y * 0.2; // send height to fragment shader for coloring
+
+ // Calculate final vertex position
+ gl_Position = mvp*vec4(displacedPosition , 1.0);
+}
diff --git a/examples/shaders/shaders_basic_pbr.c b/examples/shaders/shaders_basic_pbr.c
index d025ab8a9..c3f2bad88 100644
--- a/examples/shaders/shaders_basic_pbr.c
+++ b/examples/shaders/shaders_basic_pbr.c
@@ -236,7 +236,7 @@ int main()
float emissiveIntensity = 0.01f;
SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, SHADER_UNIFORM_FLOAT);
- DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.005f, WHITE); // Draw car model
+ DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.25f, WHITE); // Draw car model
// Draw spheres to show the lights positions
for (int i = 0; i < MAX_LIGHTS; i++)
diff --git a/examples/shaders/shaders_lightmap.c b/examples/shaders/shaders_lightmap.c
index 01b80a6fb..91ed8bfd2 100644
--- a/examples/shaders/shaders_lightmap.c
+++ b/examples/shaders/shaders_lightmap.c
@@ -164,6 +164,8 @@ int main(void)
//--------------------------------------------------------------------------------------
UnloadMesh(mesh); // Unload the mesh
UnloadShader(shader); // Unload shader
+ UnloadTexture(texture); // Unload texture
+ UnloadTexture(light); // Unload texture
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/examples/shaders/shaders_texture_drawing.c b/examples/shaders/shaders_texture_drawing.c
index 52b8967f6..ada44299d 100644
--- a/examples/shaders/shaders_texture_drawing.c
+++ b/examples/shaders/shaders_texture_drawing.c
@@ -77,6 +77,7 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadShader(shader);
+ UnloadTexture(texture);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/examples/shaders/shaders_vertex_displacement.c b/examples/shaders/shaders_vertex_displacement.c
new file mode 100644
index 000000000..207a237d0
--- /dev/null
+++ b/examples/shaders/shaders_vertex_displacement.c
@@ -0,0 +1,119 @@
+/*******************************************************************************************
+*
+* raylib [shaders] example - Vertex displacement
+*
+* Example originally created with raylib 5.0, last time updated with raylib 4.5
+*
+* Example contributed by (@ZzzhHe) 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) 2023 (@ZzzhHe)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"
+
+#define RLIGHTS_IMPLEMENTATION
+#include "rlights.h"
+
+#if defined(PLATFORM_DESKTOP)
+ #define GLSL_VERSION 330
+#else // PLATFORM_ANDROID, PLATFORM_WEB
+ #define GLSL_VERSION 100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [shaders] example - vertex displacement");
+
+ // set up camera
+ Camera camera = {0};
+ camera.position = (Vector3) {20.0f, 5.0f, -20.0f};
+ camera.target = (Vector3) {0.0f, 0.0f, 0.0f};
+ camera.up = (Vector3) {0.0f, 1.0f, 0.0f};
+ camera.fovy = 60.0f;
+ camera.projection = CAMERA_PERSPECTIVE;
+
+ // Load vertex and fragment shaders
+ Shader shader = LoadShader(
+ TextFormat("resources/shaders/glsl%i/vertex_displacement.vs", GLSL_VERSION),
+ TextFormat("resources/shaders/glsl%i/vertex_displacement.fs", GLSL_VERSION));
+
+ // Load perlin noise texture
+ Image perlinNoiseImage = GenImagePerlinNoise(512, 512, 0, 0, 1.0f);
+ Texture perlinNoiseMap = LoadTextureFromImage(perlinNoiseImage);
+ UnloadImage(perlinNoiseImage);
+
+ // Set shader uniform location
+ int perlinNoiseMapLoc = GetShaderLocation(shader, "perlinNoiseMap");
+ rlEnableShader(shader.id);
+ rlActiveTextureSlot(1);
+ rlEnableTexture(perlinNoiseMap.id);
+ rlSetUniformSampler(perlinNoiseMapLoc, 1);
+
+ // Create a plane mesh and model
+ Mesh planeMesh = GenMeshPlane(50, 50, 50, 50);
+ Model planeModel = LoadModelFromMesh(planeMesh);
+ // Set plane model material
+ planeModel.materials[0].shader = shader;
+
+ float time = 0.0f;
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ UpdateCamera(&camera, CAMERA_FREE); // Update camera
+
+ time += GetFrameTime(); // Update time variable
+ SetShaderValue(shader, GetShaderLocation(shader, "time"), &time, SHADER_UNIFORM_FLOAT); // Send time value to shader
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ BeginMode3D(camera);
+
+ BeginShaderMode(shader);
+ // Draw plane model
+ DrawModel(planeModel, (Vector3){ 0.0f, 0.0f, 0.0f }, 1.0f, (Color) {255, 255, 255, 255});
+ EndShaderMode();
+
+ EndMode3D();
+
+ DrawText("Vertex displacement", 10, 10, 20, DARKGRAY);
+ DrawFPS(10, 40);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadShader(shader);
+ UnloadModel(planeModel);
+ UnloadTexture(perlinNoiseMap);
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
diff --git a/examples/shaders/shaders_vertex_displacement.png b/examples/shaders/shaders_vertex_displacement.png
new file mode 100644
index 000000000..e7acf5c65
Binary files /dev/null and b/examples/shaders/shaders_vertex_displacement.png differ
diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h
index 623115fce..16e01a28f 100644
--- a/examples/shapes/raygui.h
+++ b/examples/shapes/raygui.h
@@ -1,6 +1,6 @@
/*******************************************************************************************
*
-* raygui v4.0 - A simple and easy-to-use immediate-mode gui library
+* raygui v4.5-dev - A simple and easy-to-use immediate-mode gui library
*
* DESCRIPTION:
* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
@@ -26,7 +26,7 @@
* NOTES:
* - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for
* font atlas recs and glyphs, freeing that memory is (usually) up to the user,
-* no unload function is explicitly provided... but note that GuiLoadStyleDefaulf() unloads
+* no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
* by default any previously loaded font (texture, recs, glyphs).
* - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
*
@@ -141,6 +141,24 @@
* Draw text bounds rectangles for debug
*
* VERSIONS HISTORY:
+* 4.5-dev (Sep-2024) Current dev version...
+* ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
+* ADDED: GuiValueBoxFloat()
+* ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
+* ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+* ADDED: Multiple new icons
+* REVIEWED: GuiTabBar(), close tab with mouse middle button
+* REVIEWED: GuiScrollPanel(), scroll speed proportional to content
+* REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
+* REVIEWED: GuiTextBox(), cursor position initialization
+* REVIEWED: GuiSliderPro(), control value change check
+* REVIEWED: GuiGrid(), simplified implementation
+* REVIEWED: GuiIconText(), increase buffer size and reviewed padding
+* REVIEWED: GuiDrawText(), improved wrap mode drawing
+* REVIEWED: GuiScrollBar(), minor tweaks
+* REVIEWED: Functions descriptions, removed wrong return value reference
+* REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
+*
* 4.0 (12-Sep-2023) ADDED: GuiToggleSlider()
* ADDED: GuiColorPickerHSV() and GuiColorPanelHSV()
* ADDED: Multiple new icons, mostly compiler related
@@ -314,9 +332,9 @@
#define RAYGUI_H
#define RAYGUI_VERSION_MAJOR 4
-#define RAYGUI_VERSION_MINOR 0
+#define RAYGUI_VERSION_MINOR 5
#define RAYGUI_VERSION_PATCH 0
-#define RAYGUI_VERSION "4.0"
+#define RAYGUI_VERSION "4.5-dev"
#if !defined(RAYGUI_STANDALONE)
#include "raylib.h"
@@ -441,7 +459,6 @@
} Font;
#endif
-
// Style property
// NOTE: Used when exporting style as code for convenience
typedef struct GuiStyleProp {
@@ -546,7 +563,6 @@ typedef enum {
// TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and
// should be configured by user as needed while defining the UI layout
-
// Gui extended properties depend on control
// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties)
//----------------------------------------------------------------------------------
@@ -562,12 +578,12 @@ typedef enum {
TEXT_ALIGNMENT_VERTICAL, // Text vertical alignment inside text bounds (after border and padding)
TEXT_WRAP_MODE // Text wrap-mode inside text bounds
//TEXT_DECORATION // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline
- //TEXT_DECORATION_THICK // Text decoration line thikness
+ //TEXT_DECORATION_THICK // Text decoration line thickness
} GuiDefaultProperty;
// Other possible text properties:
// TEXT_WEIGHT // Normal, Italic, Bold -> Requires specific font change
-// TEXT_INDENT // Text indentation -> Now using TEXT_PADDING...
+// TEXT_INDENT // Text indentation -> Now using TEXT_PADDING...
// Label
//typedef enum { } GuiLabelProperty;
@@ -615,7 +631,9 @@ typedef enum {
// DropdownBox
typedef enum {
ARROW_PADDING = 16, // DropdownBox arrow separation from border and items
- DROPDOWN_ITEMS_SPACING // DropdownBox items separation
+ DROPDOWN_ITEMS_SPACING, // DropdownBox items separation
+ DROPDOWN_ARROW_HIDDEN, // DropdownBox arrow hidden
+ DROPDOWN_ROLL_UP // DropdownBox roll up flag (default rolls down)
} GuiDropdownBoxProperty;
// TextBox/TextBoxMulti/ValueBox/Spinner
@@ -635,6 +653,7 @@ typedef enum {
LIST_ITEMS_SPACING, // ListView items separation
SCROLLBAR_WIDTH, // ListView scrollbar size (usually width)
SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+ LIST_ITEMS_BORDER_WIDTH // ListView items border width
} GuiListViewProperty;
// ColorPicker
@@ -698,7 +717,6 @@ RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load
RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position
#endif
-
// Controls
//----------------------------------------------------------------------------------------------------------
// Container/separator controls, useful for controls organization
@@ -710,29 +728,30 @@ RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *act
RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control
// Basic controls set
-RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control, shows text
+RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control
RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked
-RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked
-RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control, returns true when active
-RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control, returns active toggle index
-RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control, returns true when clicked
+RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, returns true when clicked
+RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control
+RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control
+RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control
RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active
-RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control, returns selected item index
+RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control
-RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item
-RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value
+RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control
+RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control
RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers
+RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values
RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text
-RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control, returns selected value
-RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control, returns selected value
-RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control, shows current progress value
+RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control
+RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control
+RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control
RAYGUIAPI int GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text
RAYGUIAPI int GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders
-RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control, returns mouse cell position
+RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control
// Advance controls set
-RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control, returns selected list item index
+RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control
RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters
RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message
RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret
@@ -741,10 +760,9 @@ RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color);
RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha); // Color Bar Alpha control
RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value); // Color Bar Hue control
RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Picker control that avoids conversion to RGB on each call (multiple color controls)
-RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Panel control that returns HSV color value, used by GuiColorPickerHSV()
+RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Panel control that updates Hue-Saturation-Value color value, used by GuiColorPickerHSV()
//----------------------------------------------------------------------------------------------------------
-
#if !defined(RAYGUI_NO_ICONS)
#if !defined(RAYGUI_CUSTOM_ICONS)
@@ -972,14 +990,14 @@ typedef enum {
ICON_FOLDER = 217,
ICON_FILE = 218,
ICON_SAND_TIMER = 219,
- ICON_220 = 220,
- ICON_221 = 221,
- ICON_222 = 222,
- ICON_223 = 223,
- ICON_224 = 224,
- ICON_225 = 225,
- ICON_226 = 226,
- ICON_227 = 227,
+ ICON_WARNING = 220,
+ ICON_HELP_BOX = 221,
+ ICON_INFO_BOX = 222,
+ ICON_PRIORITY = 223,
+ ICON_LAYERS_ISO = 224,
+ ICON_LAYERS2 = 225,
+ ICON_MLAYERS = 226,
+ ICON_MAPS = 227,
ICON_228 = 228,
ICON_229 = 229,
ICON_230 = 230,
@@ -1290,14 +1308,14 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] =
0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_FOLDER
0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc, // ICON_FILE
0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0, // ICON_SAND_TIMER
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_220
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_221
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_222
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_223
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_224
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_225
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_226
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_227
+ 0x01800000, 0x02400240, 0x05a00420, 0x09900990, 0x11881188, 0x21842004, 0x40024182, 0x00003ffc, // ICON_WARNING
+ 0x7ffe0000, 0x4ff24002, 0x4c324ff2, 0x4f824c02, 0x41824f82, 0x41824002, 0x40024182, 0x00007ffe, // ICON_HELP_BOX
+ 0x7ffe0000, 0x41824002, 0x40024182, 0x41824182, 0x41824182, 0x41824182, 0x40024182, 0x00007ffe, // ICON_INFO_BOX
+ 0x01800000, 0x04200240, 0x10080810, 0x7bde2004, 0x0a500a50, 0x08500bd0, 0x08100850, 0x00000ff0, // ICON_PRIORITY
+ 0x01800000, 0x18180660, 0x80016006, 0x98196006, 0x99996666, 0x19986666, 0x01800660, 0x00000000, // ICON_LAYERS_ISO
+ 0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0, // ICON_LAYERS2
+ 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0, // ICON_MLAYERS
+ 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0, // ICON_MAPS
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_228
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_229
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_230
@@ -1328,7 +1346,7 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] =
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_255
};
-// NOTE: We keep a pointer to the icons array, useful to point to other sets if required
+// NOTE: A pointer to current icons array should be defined
static unsigned int *guiIconsPtr = guiIcons;
#endif // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS
@@ -1363,8 +1381,8 @@ static unsigned int guiIconScale = 1; // Gui icon default scale (if ic
static bool guiTooltip = false; // Tooltip enabled/disabled
static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user)
-static bool guiSliderDragging = false; // Gui slider drag state (no inputs processed except dragged slider)
-static Rectangle guiSliderActive = { 0 }; // Gui slider active bounds rectangle, used as an unique identifier
+static bool guiControlExclusiveMode = false; // Gui control exclusive mode (no inputs processed except current control)
+static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier
static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*()
//static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking
@@ -1450,6 +1468,7 @@ static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if
static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings
static int TextToInteger(const char *text); // Get integer value from text
+static float TextToFloat(const char *text); // Get float value from text
static int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded text
static const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode codepoint into UTF-8 text (char array size returned as parameter)
@@ -1744,6 +1763,9 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
if (toggle) *active = i;
}
+ // Close tab with middle mouse button pressed
+ if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i;
+
GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding);
GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment);
@@ -1775,10 +1797,10 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector
{
#define RAYGUI_MIN_SCROLLBAR_WIDTH 40
#define RAYGUI_MIN_SCROLLBAR_HEIGHT 40
+ #define RAYGUI_MIN_MOUSE_WHEEL_SPEED 20
int result = 0;
GuiState state = guiState;
- float mouseWheelSpeed = 20.0f; // Default movement speed with mouse wheel
Rectangle temp = { 0 };
if (view == NULL) view = &temp;
@@ -1820,17 +1842,8 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector
};
// Make sure scroll bars have a minimum width/height
- // NOTE: If content >>> bounds, size could be very small or even 0
- if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH)
- {
- horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH;
- mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.height vs bounds.height
- }
- if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT)
- {
- verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT;
- mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.width vs bounds.width
- }
+ if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH;
+ if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT;
// Calculate view area (area without the scrollbars)
*view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)?
@@ -1873,9 +1886,14 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector
#endif
float wheelMove = GetMouseWheelMove();
+ // Set scrolling speed with mouse wheel based on ratio between bounds and content
+ Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height };
+ if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+ if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+
// Horizontal and vertical scrolling with mouse wheel
- if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed;
- else scrollPos.y += wheelMove*mouseWheelSpeed; // Vertical scroll
+ if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x;
+ else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll
}
}
@@ -1921,7 +1939,7 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector
}
// Draw scrollbar lines depending on current state
- GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK);
+ GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK);
// Set scrollbar slider size back to the way it was before
GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider);
@@ -1959,7 +1977,7 @@ int GuiButton(Rectangle bounds, const char *text)
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -1997,7 +2015,7 @@ int GuiLabelButton(Rectangle bounds, const char *text)
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -2020,7 +2038,7 @@ int GuiLabelButton(Rectangle bounds, const char *text)
return pressed;
}
-// Toggle Button control, returns true when active
+// Toggle Button control
int GuiToggle(Rectangle bounds, const char *text, bool *active)
{
int result = 0;
@@ -2031,7 +2049,7 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active)
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -2068,7 +2086,7 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active)
return result;
}
-// Toggle Group control, returns toggled button codepointIndex
+// Toggle Group control
int GuiToggleGroup(Rectangle bounds, const char *text, int *active)
{
#if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS)
@@ -2117,7 +2135,7 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int *active)
return result;
}
-// Toggle Slider control extended, returns true when clicked
+// Toggle Slider control extended
int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
{
int result = 0;
@@ -2211,7 +2229,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked)
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -2256,7 +2274,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked)
return result;
}
-// Combo Box control, returns selected item codepointIndex
+// Combo Box control
int GuiComboBox(Rectangle bounds, const char *text, int *active)
{
int result = 0;
@@ -2279,7 +2297,7 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active)
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -2327,21 +2345,28 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod
int result = 0;
GuiState state = guiState;
+ int temp = 0;
+ if (active == NULL) active = &temp;
+
int itemSelected = *active;
int itemFocused = -1;
+ int direction = 0; // Dropdown box open direction: down (default)
+ if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up
+
// Get substrings items from text (items pointers, lengths and count)
int itemCount = 0;
const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
Rectangle boundsOpen = bounds;
boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+ if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING);
Rectangle itemBounds = bounds;
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -2362,7 +2387,8 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod
for (int i = 0; i < itemCount; i++)
{
// Update item rectangle y position for next item
- itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+ if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+ else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
if (CheckCollisionPointRec(mousePoint, itemBounds))
{
@@ -2406,7 +2432,8 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod
for (int i = 0; i < itemCount; i++)
{
// Update item rectangle y position for next item
- itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+ if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+ else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
if (i == itemSelected)
{
@@ -2422,14 +2449,17 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod
}
}
- // Draw arrows (using icon if available)
+ if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN))
+ {
+ // Draw arrows (using icon if available)
#if defined(RAYGUI_NO_ICONS)
- GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
- TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+ GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
+ TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
#else
- GuiDrawText("#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 },
- TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL
+ GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 },
+ TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL
#endif
+ }
//--------------------------------------------------------------------
*active = itemSelected;
@@ -2440,7 +2470,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod
// Text Box control
// NOTE: Returns true on ENTER pressed (useful for data validation)
-int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
+int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
{
#if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
#define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 40 // Frames to wait for autocursor movement
@@ -2456,7 +2486,10 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
- int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex);
+ int textLength = (int)strlen(text); // Get current text length
+ int thisCursorIndex = textBoxCursorIndex;
+ if (thisCursorIndex > textLength) thisCursorIndex = textLength;
+ int textWidth = GetTextWidth(text) - GetTextWidth(text + thisCursorIndex);
int textIndexOffset = 0; // Text index offset to start drawing in the box
// Cursor rectangle
@@ -2496,7 +2529,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
if ((state != STATE_DISABLED) && // Control not disabled
!GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode
!guiLocked && // Gui not locked
- !guiSliderDragging && // No gui slider on dragging
+ !guiControlExclusiveMode && // No gui slider on dragging
(wrapMode == TEXT_WRAP_NONE)) // No wrap mode
{
Vector2 mousePosition = GetMousePosition();
@@ -2505,6 +2538,8 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
{
state = STATE_PRESSED;
+ if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
+
// If text does not fit in the textbox and current cursor position is out of bounds,
// we add an index offset to text for drawing only what requires depending on cursor
while (textWidth >= textBounds.width)
@@ -2517,19 +2552,16 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex);
}
- int textLength = (int)strlen(text); // Get current text length
int codepoint = GetCharPressed(); // Get Unicode codepoint
if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n';
- if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
-
// Encode codepoint as UTF-8
int codepointSize = 0;
const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
// Add codepoint to text, at current cursor position
// NOTE: Make sure we do not overflow buffer size
- if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < bufferSize))
+ if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
{
// Move forward data from cursor position
for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
@@ -2564,6 +2596,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
for (int i = textBoxCursorIndex; i < textLength; i++) text[i] = text[i + nextCodepointSize];
textLength -= codepointSize;
+ if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
// Make sure text last character is EOL
text[textLength] = '\0';
@@ -2583,6 +2616,8 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
// Move backward text from cursor position
for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
+ // TODO Check: >= cursor+codepointsize and <= length-codepointsize
+
// Prevent cursor index from decrementing past 0
if (textBoxCursorIndex > 0)
{
@@ -2653,7 +2688,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
{
mouseCursor.x = textBounds.x + textEndWidth;
- mouseCursorIndex = (int)strlen(text);
+ mouseCursorIndex = textLength;
}
// Place cursor at required index on mouse click
@@ -2685,7 +2720,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
- textBoxCursorIndex = (int)strlen(text); // GLOBAL: Place cursor index to the end of current text
+ textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text
result = 1;
}
}
@@ -2727,7 +2762,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
/*
// Text Box control with multiple lines and word-wrap
// NOTE: This text-box is readonly, no editing supported by default
-bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode)
+bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode)
{
bool pressed = false;
@@ -2736,7 +2771,7 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode
GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP);
// TODO: Implement methods to calculate cursor position properly
- pressed = GuiTextBox(bounds, text, bufferSize, editMode);
+ pressed = GuiTextBox(bounds, text, textSize, editMode);
GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE);
GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE);
@@ -2771,7 +2806,7 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -2846,7 +2881,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -2890,7 +2925,13 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
//if (*value > maxValue) *value = maxValue;
//else if (*value < minValue) *value = minValue;
- if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1;
+ if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
+ {
+ if (*value > maxValue) *value = maxValue;
+ else if (*value < minValue) *value = minValue;
+
+ result = 1;
+ }
}
else
{
@@ -2930,55 +2971,152 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
return result;
}
+// Floating point Value Box control, updates input val_str with numbers
+// NOTE: Requires static variables: frameCounter
+int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode)
+{
+ #if !defined(RAYGUI_VALUEBOX_MAX_CHARS)
+ #define RAYGUI_VALUEBOX_MAX_CHARS 32
+ #endif
+
+ int result = 0;
+ GuiState state = guiState;
+
+ //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
+ //sprintf(textValue, "%2.2f", *value);
+
+ Rectangle textBounds = {0};
+ if (text != NULL)
+ {
+ textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+ textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+ textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+ if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+ }
+
+ // Update control
+ //--------------------------------------------------------------------
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+ {
+ Vector2 mousePoint = GetMousePosition();
+
+ bool valueHasChanged = false;
+
+ if (editMode)
+ {
+ state = STATE_PRESSED;
+
+ int keyCount = (int)strlen(textValue);
+
+ // Only allow keys in range [48..57]
+ if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+ {
+ if (GetTextWidth(textValue) < bounds.width)
+ {
+ int key = GetCharPressed();
+ if (((key >= 48) && (key <= 57)) ||
+ (key == '.') ||
+ ((keyCount == 0) && (key == '+')) || // NOTE: Sign can only be in first position
+ ((keyCount == 0) && (key == '-')))
+ {
+ textValue[keyCount] = (char)key;
+ keyCount++;
+
+ valueHasChanged = true;
+ }
+ }
+ }
+
+ // Pressed backspace
+ if (IsKeyPressed(KEY_BACKSPACE))
+ {
+ if (keyCount > 0)
+ {
+ keyCount--;
+ textValue[keyCount] = '\0';
+ valueHasChanged = true;
+ }
+ }
+
+ if (valueHasChanged) *value = TextToFloat(textValue);
+
+ if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1;
+ }
+ else
+ {
+ if (CheckCollisionPointRec(mousePoint, bounds))
+ {
+ state = STATE_FOCUSED;
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+ }
+ }
+ }
+ //--------------------------------------------------------------------
+
+ // Draw control
+ //--------------------------------------------------------------------
+ Color baseColor = BLANK;
+ if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
+ else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
+
+ GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
+ GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
+
+ // Draw cursor
+ if (editMode)
+ {
+ // NOTE: ValueBox internal text is always centered
+ Rectangle cursor = {bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1,
+ bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4,
+ bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)};
+ GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
+ }
+
+ // Draw text label if provided
+ GuiDrawText(text, textBounds,
+ (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT,
+ GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+ //--------------------------------------------------------------------
+
+ return result;
+}
+
// Slider control with pro parameters
// NOTE: Other GuiSlider*() controls use this one
int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth)
{
int result = 0;
- float oldValue = *value;
GuiState state = guiState;
float temp = (maxValue - minValue)/2.0f;
if (value == NULL) value = &temp;
-
- int sliderValue = (int)(((*value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
+ float oldValue = *value;
Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
- if (sliderWidth > 0) // Slider
- {
- slider.x += (sliderValue - sliderWidth/2);
- slider.width = (float)sliderWidth;
- }
- else if (sliderWidth == 0) // SliderBar
- {
- slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
- slider.width = (float)sliderValue;
- }
-
// Update control
//--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked)
{
Vector2 mousePoint = GetMousePosition();
- if (guiSliderDragging) // Keep dragging outside of bounds
+ if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
- if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
+ if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
{
state = STATE_PRESSED;
-
// Get equivalent value and slider position from mousePosition.x
- *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue;
+ *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
}
}
else
{
- guiSliderDragging = false;
- guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+ guiControlExclusiveMode = false;
+ guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
}
}
else if (CheckCollisionPointRec(mousePoint, bounds))
@@ -2986,16 +3124,13 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
state = STATE_PRESSED;
- guiSliderDragging = true;
- guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
+ guiControlExclusiveMode = true;
+ guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
if (!CheckCollisionPointRec(mousePoint, slider))
{
// Get equivalent value and slider position from mousePosition.x
- *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue;
-
- if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider
- else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar
+ *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
}
}
else state = STATE_FOCUSED;
@@ -3006,17 +3141,22 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
}
// Control value change check
- if(oldValue == *value) result = 0;
+ if (oldValue == *value) result = 0;
else result = 1;
- // Bar limits check
+ // Slider bar limits check
+ float sliderValue = (((*value - minValue)/(maxValue - minValue))*(bounds.width - sliderWidth - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
if (sliderWidth > 0) // Slider
{
+ slider.x += sliderValue;
+ slider.width = (float)sliderWidth;
if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH);
else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH);
}
else if (sliderWidth == 0) // SliderBar
{
+ slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
+ slider.width = sliderValue;
if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
}
//--------------------------------------------------------------------
@@ -3171,7 +3311,7 @@ int GuiDummyRec(Rectangle bounds, const char *text)
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -3238,7 +3378,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
@@ -3291,6 +3431,8 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
// Draw visible items
for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
{
+ GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+
if (state == STATE_DISABLED)
{
if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
@@ -3353,83 +3495,32 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
return result;
}
-// Color Panel control
+// Color Panel control - Color (RGBA) variant.
int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
{
int result = 0;
- GuiState state = guiState;
- Vector2 pickerSelector = { 0 };
-
- const Color colWhite = { 255, 255, 255, 255 };
- const Color colBlack = { 0, 0, 0, 255 };
Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f };
Vector3 hsv = ConvertRGBtoHSV(vcolor);
+ Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv.
- pickerSelector.x = bounds.x + (float)hsv.y*bounds.width; // HSV: Saturation
- pickerSelector.y = bounds.y + (1.0f - (float)hsv.z)*bounds.height; // HSV: Value
+ GuiColorPanelHSV(bounds, text, &hsv);
- Vector3 maxHue = { hsv.x, 1.0f, 1.0f };
- Vector3 rgbHue = ConvertHSVtoRGB(maxHue);
- Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x),
- (unsigned char)(255.0f*rgbHue.y),
- (unsigned char)(255.0f*rgbHue.z), 255 };
-
- // Update control
- //--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ // Check if the hsv was changed, only then change the color.
+ // This is required, because the Color->HSV->Color conversion has precision errors.
+ // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value.
+ // Otherwise GuiColorPanel would often modify it's color without user input.
+ // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check.
+ if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
{
- Vector2 mousePoint = GetMousePosition();
+ Vector3 rgb = ConvertHSVtoRGB(hsv);
- if (CheckCollisionPointRec(mousePoint, bounds))
- {
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
- {
- state = STATE_PRESSED;
- pickerSelector = mousePoint;
-
- // Calculate color from picker
- Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
-
- colorPick.x /= (float)bounds.width; // Get normalized value on x
- colorPick.y /= (float)bounds.height; // Get normalized value on y
-
- hsv.y = colorPick.x;
- hsv.z = 1.0f - colorPick.y;
-
- Vector3 rgb = ConvertHSVtoRGB(hsv);
-
- // NOTE: Vector3ToColor() only available on raylib 1.8.1
- *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x),
- (unsigned char)(255.0f*rgb.y),
- (unsigned char)(255.0f*rgb.z),
- (unsigned char)(255.0f*(float)color->a/255.0f) };
-
- }
- else state = STATE_FOCUSED;
- }
+ // NOTE: Vector3ToColor() only available on raylib 1.8.1
+ *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x),
+ (unsigned char)(255.0f*rgb.y),
+ (unsigned char)(255.0f*rgb.z),
+ color->a };
}
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- if (state != STATE_DISABLED)
- {
- DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha));
- DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0));
-
- // Draw color picker: selector
- Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) };
- GuiDrawRectangle(selector, 0, BLANK, colWhite);
- }
- else
- {
- DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha));
- }
-
- GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
- //--------------------------------------------------------------------
-
return result;
}
@@ -3451,11 +3542,11 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
{
Vector2 mousePoint = GetMousePosition();
- if (guiSliderDragging) // Keep dragging outside of bounds
+ if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
- if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
+ if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
{
state = STATE_PRESSED;
@@ -3466,8 +3557,8 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
}
else
{
- guiSliderDragging = false;
- guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+ guiControlExclusiveMode = false;
+ guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
}
}
else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
@@ -3475,8 +3566,8 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
state = STATE_PRESSED;
- guiSliderDragging = true;
- guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
+ guiControlExclusiveMode = true;
+ guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
*alpha = (mousePoint.x - bounds.x)/bounds.width;
if (*alpha <= 0.0f) *alpha = 0.0f;
@@ -3537,11 +3628,11 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue)
{
Vector2 mousePoint = GetMousePosition();
- if (guiSliderDragging) // Keep dragging outside of bounds
+ if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
- if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
+ if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
{
state = STATE_PRESSED;
@@ -3552,8 +3643,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue)
}
else
{
- guiSliderDragging = false;
- guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+ guiControlExclusiveMode = false;
+ guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
}
}
else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
@@ -3561,8 +3652,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue)
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
state = STATE_PRESSED;
- guiSliderDragging = true;
- guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
+ guiControlExclusiveMode = true;
+ guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
*hue = (mousePoint.y - bounds.y)*360/bounds.height;
if (*hue <= 0.0f) *hue = 0.0f;
@@ -3615,6 +3706,7 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue)
// float GuiColorBarAlpha(Rectangle bounds, float alpha)
// float GuiColorBarHue(Rectangle bounds, float value)
// NOTE: bounds define GuiColorPanel() size
+// NOTE: this picker converts RGB to HSV, which can cause the Hue control to jump. If you have this problem, consider using the HSV variant instead
int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
{
int result = 0;
@@ -3627,6 +3719,7 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
//Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
+ // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used.
Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
GuiColorBarHue(boundsHue, NULL, &hsv.x);
@@ -3668,8 +3761,7 @@ int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
return result;
}
-// Color Panel control, returns HSV color value in *colorHsv.
-// Used by GuiColorPickerHSV()
+// Color Panel control - HSV variant
int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
{
int result = 0;
@@ -3690,15 +3782,47 @@ int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked)
{
Vector2 mousePoint = GetMousePosition();
- if (CheckCollisionPointRec(mousePoint, bounds))
+ if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+ {
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+ {
+ if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+ {
+ pickerSelector = mousePoint;
+
+ if (pickerSelector.x < bounds.x) pickerSelector.x = bounds.x;
+ if (pickerSelector.x > bounds.x + bounds.width) pickerSelector.x = bounds.x + bounds.width;
+ if (pickerSelector.y < bounds.y) pickerSelector.y = bounds.y;
+ if (pickerSelector.y > bounds.y + bounds.height) pickerSelector.y = bounds.y + bounds.height;
+
+ // Calculate color from picker
+ Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
+
+ colorPick.x /= (float)bounds.width; // Get normalized value on x
+ colorPick.y /= (float)bounds.height; // Get normalized value on y
+
+ colorHsv->y = colorPick.x;
+ colorHsv->z = 1.0f - colorPick.y;
+
+ }
+ }
+ else
+ {
+ guiControlExclusiveMode = false;
+ guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+ }
+ }
+ else if (CheckCollisionPointRec(mousePoint, bounds))
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
state = STATE_PRESSED;
+ guiControlExclusiveMode = true;
+ guiControlExclusiveRec = bounds;
pickerSelector = mousePoint;
// Calculate color from picker
@@ -3760,9 +3884,9 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons
int textWidth = GetTextWidth(message) + 2;
Rectangle textBounds = { 0 };
- textBounds.x = bounds.x + bounds.width/2 - textWidth/2;
+ textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
- textBounds.width = (float)textWidth;
+ textBounds.width = bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*2;
textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
// Draw control
@@ -3905,7 +4029,7 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect
// Update control
//--------------------------------------------------------------------
- if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+ if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
if (CheckCollisionPointRec(mousePoint, bounds))
{
@@ -3925,14 +4049,14 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect
// Draw vertical grid lines
for (int i = 0; i < linesV; i++)
{
- Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height };
+ Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height + 1 };
GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA));
}
// Draw horizontal grid lines
for (int i = 0; i < linesH; i++)
{
- Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width, 1 };
+ Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width + 1, 1 };
GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA));
}
}
@@ -3954,7 +4078,6 @@ void GuiDisableTooltip(void) { guiTooltip = false; }
// Set tooltip string
void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; }
-
//----------------------------------------------------------------------------------
// Styles loading functions
//----------------------------------------------------------------------------------
@@ -3967,6 +4090,7 @@ void GuiLoadStyle(const char *fileName)
#define MAX_LINE_BUFFER_SIZE 256
bool tryBinary = false;
+ if (!guiStyleLoaded) GuiLoadStyleDefault();
// Try reading the files as text file first
FILE *rgsFile = fopen(fileName, "rt");
@@ -4600,7 +4724,7 @@ static int GetTextWidth(const char *text)
}
}
- if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE - ICON_TEXT_PADDING);
+ if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
}
return (int)textSize.x;
@@ -4773,6 +4897,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
// Get text position depending on alignment and iconId
//---------------------------------------------------------------------------------
Vector2 textBoundsPosition = { textBounds.x, textBounds.y };
+ float textBoundsWidthOffset = 0.0f;
// NOTE: We get text size after icon has been processed
// WARNING: GetTextWidth() also processes text icon to get width! -> Really needed?
@@ -4798,6 +4923,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
default: break;
}
+ if (textSizeX > textBounds.width && (lines[i] != NULL) && (lines[i][0] != '\0')) textBoundsPosition.x = textBounds.x;
+
switch (alignmentVertical)
{
// Only valid in case of wordWrap = 0;
@@ -4820,7 +4947,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
{
// NOTE: We consider icon height, probably different than text size
GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint);
- textBoundsPosition.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+ textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+ textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
}
#endif
// Get size in bytes of text,
@@ -4829,9 +4957,15 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ }
float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize;
+ int lastSpaceIndex = 0;
+ bool tempWrapCharMode = false;
+
int textOffsetY = 0;
float textOffsetX = 0.0f;
float glyphWidth = 0;
+
+ int ellipsisWidth = GetTextWidth("...");
+ bool textOverflow = false;
for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
{
int codepoint = GetCodepointNext(&lines[i][c], &codepointSize);
@@ -4839,36 +4973,51 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
// 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
- if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size
+ if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size
- // Wrap mode text measuring to space to validate if it can be drawn or
- // a new line is required
+ // Get glyph width to check if it goes out of bounds
+ if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor);
+ else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor;
+
+ // Wrap mode text measuring, to validate if
+ // it can be drawn or a new line is required
if (wrapMode == TEXT_WRAP_CHAR)
{
- // Get glyph width to check if it goes out of bounds
- if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor);
- else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor;
-
// Jump to next line if current character reach end of the box limits
- if ((textOffsetX + glyphWidth) > textBounds.width)
+ if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset)
{
textOffsetX = 0.0f;
textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+
+ if (tempWrapCharMode) // Wrap at char level when too long words
+ {
+ wrapMode = TEXT_WRAP_WORD;
+ tempWrapCharMode = false;
+ }
}
}
else if (wrapMode == TEXT_WRAP_WORD)
{
+ if (codepoint == 32) lastSpaceIndex = c;
+
// Get width to next space in line
int nextSpaceIndex = 0;
float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex);
- if ((textOffsetX + nextSpaceWidth) > textBounds.width)
+ int nextSpaceIndex2 = 0;
+ float nextWordSize = GetNextSpaceWidth(lines[i] + lastSpaceIndex + 1, &nextSpaceIndex2);
+
+ if (nextWordSize > textBounds.width - textBoundsWidthOffset)
+ {
+ // Considering the case the next word is longer than bounds
+ tempWrapCharMode = true;
+ wrapMode = TEXT_WRAP_CHAR;
+ }
+ else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset)
{
textOffsetX = 0.0f;
textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
}
-
- // TODO: Consider case: (nextSpaceWidth >= textBounds.width)
}
if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint
@@ -4881,7 +5030,23 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
if (wrapMode == TEXT_WRAP_NONE)
{
// Draw only required text glyphs fitting the textBounds.width
- if (textOffsetX <= (textBounds.width - glyphWidth))
+ if (textSizeX > textBounds.width)
+ {
+ if (textOffsetX <= (textBounds.width - glyphWidth - textBoundsWidthOffset - ellipsisWidth))
+ {
+ DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+ }
+ else if (!textOverflow)
+ {
+ textOverflow = true;
+
+ for (int j = 0; j < ellipsisWidth; j += ellipsisWidth/3)
+ {
+ DrawTextCodepoint(guiFont, '.', RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX + j, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+ }
+ }
+ }
+ else
{
DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
}
@@ -4937,7 +5102,7 @@ static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor,
// Draw tooltip using control bounds
static void GuiTooltip(Rectangle controlRec)
{
- if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiSliderDragging)
+ if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiControlExclusiveMode)
{
Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
@@ -5003,7 +5168,7 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i
buffer[i] = '\0'; // Set an end of string at this point
counter++;
- if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+ if (counter > RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
}
}
@@ -5163,8 +5328,11 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
if (value > maxValue) value = maxValue;
if (value < minValue) value = minValue;
- const int valueRange = maxValue - minValue;
+ int valueRange = maxValue - minValue;
+ if (valueRange <= 0) valueRange = 1;
+
int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+ if (sliderSize < 1) sliderSize = 1; // TODO: Consider a minimum slider size
// Calculate rectangles for all of the components
arrowUpLeft = RAYGUI_CLITERAL(Rectangle){
@@ -5205,13 +5373,13 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
{
Vector2 mousePoint = GetMousePosition();
- if (guiSliderDragging) // Keep dragging outside of bounds
+ if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) &&
!CheckCollisionPointRec(mousePoint, arrowUpLeft) &&
!CheckCollisionPointRec(mousePoint, arrowDownRight))
{
- if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
+ if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
{
state = STATE_PRESSED;
@@ -5221,8 +5389,8 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
}
else
{
- guiSliderDragging = false;
- guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+ guiControlExclusiveMode = false;
+ guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
}
}
else if (CheckCollisionPointRec(mousePoint, bounds))
@@ -5236,8 +5404,8 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
// Handle mouse button down
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
- guiSliderDragging = true;
- guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
+ guiControlExclusiveMode = true;
+ guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
// Check arrows click
if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
@@ -5437,6 +5605,37 @@ static int TextToInteger(const char *text)
return value*sign;
}
+// Get float value from text
+// NOTE: This function replaces atof() [stdlib.h]
+// WARNING: Only '.' character is understood as decimal point
+static float TextToFloat(const char *text)
+{
+ float value = 0.0f;
+ float sign = 1.0f;
+
+ if ((text[0] == '+') || (text[0] == '-'))
+ {
+ if (text[0] == '-') sign = -1.0f;
+ text++;
+ }
+
+ int i = 0;
+ for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
+
+ if (text[i++] != '.') value *= sign;
+ else
+ {
+ float divisor = 10.0f;
+ for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+ {
+ value += ((float)(text[i] - '0'))/divisor;
+ divisor = divisor*10.0f;
+ }
+ }
+
+ return value;
+}
+
// Encode codepoint into UTF-8 text (char array size returned as parameter)
static const char *CodepointToUTF8(int codepoint, int *byteSize)
{
@@ -5490,21 +5689,21 @@ static int GetCodepointNext(const char *text, int *codepointSize)
if (0xf0 == (0xf8 & ptr[0]))
{
// 4 byte UTF-8 codepoint
- if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
+ if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
*codepointSize = 4;
}
else if (0xe0 == (0xf0 & ptr[0]))
{
// 3 byte UTF-8 codepoint */
- if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
+ if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
*codepointSize = 3;
}
else if (0xc0 == (0xe0 & ptr[0]))
{
// 2 byte UTF-8 codepoint
- if((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks
+ if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks
codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
*codepointSize = 2;
}
@@ -5515,9 +5714,8 @@ static int GetCodepointNext(const char *text, int *codepointSize)
*codepointSize = 1;
}
-
return codepoint;
}
#endif // RAYGUI_STANDALONE
-#endif // RAYGUI_IMPLEMENTATION
+#endif // RAYGUI_IMPLEMENTATION
\ No newline at end of file
diff --git a/examples/shapes/shapes_bouncing_ball.c b/examples/shapes/shapes_bouncing_ball.c
index 7a3068163..df0c37c1c 100644
--- a/examples/shapes/shapes_bouncing_ball.c
+++ b/examples/shapes/shapes_bouncing_ball.c
@@ -62,7 +62,7 @@ int main(void)
ClearBackground(RAYWHITE);
DrawCircleV(ballPosition, (float)ballRadius, MAROON);
- //DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY);
+ DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY);
// On pause, we draw a blinking message
if (pause && ((framesCounter/30)%2)) DrawText("PAUSED", 350, 200, 30, GRAY);
diff --git a/examples/shapes/shapes_draw_circle_sector.c b/examples/shapes/shapes_draw_circle_sector.c
index 30eaaf4bd..cb9ab859b 100644
--- a/examples/shapes/shapes_draw_circle_sector.c
+++ b/examples/shapes/shapes_draw_circle_sector.c
@@ -63,11 +63,11 @@ int main(void)
// Draw GUI controls
//------------------------------------------------------------------------------
- GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", NULL, &startAngle, 0, 720);
- GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", NULL, &endAngle, 0, 720);
+ GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, 0, 720);
+ GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, 0, 720);
- GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", NULL, &outerRadius, 0, 200);
- GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", NULL, &segments, 0, 100);
+ GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200);
+ GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 100);
//------------------------------------------------------------------------------
minSegments = truncf(ceilf((endAngle - startAngle) / 90));
diff --git a/examples/shapes/shapes_draw_rectangle_rounded.c b/examples/shapes/shapes_draw_rectangle_rounded.c
index 19280ed00..58991884c 100644
--- a/examples/shapes/shapes_draw_rectangle_rounded.c
+++ b/examples/shapes/shapes_draw_rectangle_rounded.c
@@ -66,11 +66,11 @@ int main(void)
// Draw GUI controls
//------------------------------------------------------------------------------
- GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", NULL, &width, 0, (float)GetScreenWidth() - 300);
- GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", NULL, &height, 0, (float)GetScreenHeight() - 50);
- GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", NULL, &roundness, 0.0f, 1.0f);
- GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", NULL, &lineThick, 0, 20);
- GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", NULL, &segments, 0, 60);
+ GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", TextFormat("%.2f", width), &width, 0, (float)GetScreenWidth() - 300);
+ GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", TextFormat("%.2f", height), &height, 0, (float)GetScreenHeight() - 50);
+ GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", TextFormat("%.2f", roundness), &roundness, 0.0f, 1.0f);
+ GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", TextFormat("%.2f", lineThick), &lineThick, 0, 20);
+ GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 60);
GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", &drawRoundedRect);
GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", &drawRoundedLines);
diff --git a/examples/shapes/shapes_draw_ring.c b/examples/shapes/shapes_draw_ring.c
index 60c95a4c0..8bccdf729 100644
--- a/examples/shapes/shapes_draw_ring.c
+++ b/examples/shapes/shapes_draw_ring.c
@@ -69,13 +69,13 @@ int main(void)
// Draw GUI controls
//------------------------------------------------------------------------------
- GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", NULL, &startAngle, -450, 450);
- GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", NULL, &endAngle, -450, 450);
+ GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, -450, 450);
+ GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, -450, 450);
- GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", NULL, &innerRadius, 0, 100);
- GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", NULL, &outerRadius, 0, 200);
+ GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", TextFormat("%.2f", innerRadius), &innerRadius, 0, 100);
+ GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200);
- GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", NULL, &segments, 0, 100);
+ GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", TextFormat("%.2f", segments), &segments, 0, 100);
GuiCheckBox((Rectangle){ 600, 320, 20, 20 }, "Draw Ring", &drawRing);
GuiCheckBox((Rectangle){ 600, 350, 20, 20 }, "Draw RingLines", &drawRingLines);
diff --git a/examples/text/text_input_box.c b/examples/text/text_input_box.c
index eeb338f1f..c53057711 100644
--- a/examples/text/text_input_box.c
+++ b/examples/text/text_input_box.c
@@ -35,7 +35,7 @@ int main(void)
int framesCounter = 0;
- SetTargetFPS(10); // Set our game to run at 10 frames-per-second
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
diff --git a/examples/text/text_writing_anim.c b/examples/text/text_writing_anim.c
index 1e7cd569f..8e2820fc9 100644
--- a/examples/text/text_writing_anim.c
+++ b/examples/text/text_writing_anim.c
@@ -52,7 +52,7 @@ int main(void)
DrawText(TextSubtext(message, 0, framesCounter/10), 210, 160, 20, MAROON);
DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, LIGHTGRAY);
- DrawText("PRESS [SPACE] to SPEED UP!", 239, 300, 20, LIGHTGRAY);
+ DrawText("HOLD [SPACE] to SPEED UP!", 239, 300, 20, LIGHTGRAY);
EndDrawing();
//----------------------------------------------------------------------------------
diff --git a/examples/textures/textures_blend_modes.c b/examples/textures/textures_blend_modes.c
index 660aa57a7..d97432cb2 100644
--- a/examples/textures/textures_blend_modes.c
+++ b/examples/textures/textures_blend_modes.c
@@ -43,6 +43,9 @@ int main(void)
const int blendCountMax = 4;
BlendMode blendMode = 0;
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+ //---------------------------------------------------------------------------------------
+
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
diff --git a/examples/textures/textures_image_rotate.c b/examples/textures/textures_image_rotate.c
index 51724e037..94c42034b 100644
--- a/examples/textures/textures_image_rotate.c
+++ b/examples/textures/textures_image_rotate.c
@@ -43,6 +43,8 @@ int main(void)
textures[2] = LoadTextureFromImage(imageNeg90);
int currentTexture = 0;
+
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
// Main game loop
diff --git a/examples/textures/textures_logo_raylib.c b/examples/textures/textures_logo_raylib.c
index f170dab48..35baa1223 100644
--- a/examples/textures/textures_logo_raylib.c
+++ b/examples/textures/textures_logo_raylib.c
@@ -27,6 +27,8 @@ int main(void)
// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
Texture2D texture = LoadTexture("resources/raylib_logo.png"); // Texture loading
+
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
// Main game loop
diff --git a/examples/textures/textures_raw_data.c b/examples/textures/textures_raw_data.c
index 38229f1ba..6fb41b143 100644
--- a/examples/textures/textures_raw_data.c
+++ b/examples/textures/textures_raw_data.c
@@ -63,6 +63,8 @@ int main(void)
Texture2D checked = LoadTextureFromImage(checkedIm);
UnloadImage(checkedIm); // Unload CPU (RAM) image data (pixels)
+
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
// Main game loop
diff --git a/examples/textures/textures_sprite_explosion.c b/examples/textures/textures_sprite_explosion.c
index 06b4f1dad..a65426cb6 100644
--- a/examples/textures/textures_sprite_explosion.c
+++ b/examples/textures/textures_sprite_explosion.c
@@ -48,8 +48,8 @@ int main(void)
bool active = false;
int framesCounter = 0;
- SetTargetFPS(120);
- //--------------------------------------------------------------------------------------
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+ //---------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
diff --git a/examples/textures/textures_to_image.c b/examples/textures/textures_to_image.c
index 2d09623eb..e1d88c4cd 100644
--- a/examples/textures/textures_to_image.c
+++ b/examples/textures/textures_to_image.c
@@ -38,6 +38,8 @@ int main(void)
texture = LoadTextureFromImage(image); // Recreate texture from retrieved image data (RAM -> VRAM)
UnloadImage(image); // Unload retrieved image data from CPU memory (RAM)
+
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
// Main game loop
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index dd940b36c..9735e267f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -70,7 +70,8 @@ endif()
if (${PLATFORM} MATCHES "Web")
target_link_options(raylib PUBLIC "-sUSE_GLFW=3")
if(${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_ES3")
- target_link_options(raylib PUBLIC "-sFULL_ES3")
+ target_link_options(raylib PUBLIC "-sMIN_WEBGL_VERSION=2")
+ target_link_options(raylib PUBLIC "-sMAX_WEBGL_VERSION=2")
endif()
endif()
diff --git a/src/Makefile b/src/Makefile
index c68a03797..4740f1f01 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -467,7 +467,7 @@ INCLUDE_PATHS = -I.
ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
INCLUDE_PATHS += -Iexternal/glfw/include
ifeq ($(PLATFORM_OS),BSD)
- INCLUDE_PATHS += -I/usr/local/include
+ INCLUDE_PATHS += -I/usr/local/include -I/usr/pkg/include -I/usr/X11R7/include
endif
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL)
@@ -522,7 +522,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION)
endif
ifeq ($(PLATFORM_OS),BSD)
- LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib
+ LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib -L/usr/pkg/lib -Wl,-R/usr/pkg/lib
endif
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL)
diff --git a/src/build.zig b/src/build.zig
index 2da5cbd04..4e112d78f 100644
--- a/src/build.zig
+++ b/src/build.zig
@@ -28,6 +28,7 @@ pub fn addRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
.shared = options.shared,
.linux_display_backend = options.linux_display_backend,
.opengl_version = options.opengl_version,
+ .config = options.config,
});
const raylib = raylib_dep.artifact("raylib");
@@ -52,6 +53,36 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
"-DGL_SILENCE_DEPRECATION=199309L",
"-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/3674
});
+ if (options.config) |config| {
+ const file = try std.fs.path.join(b.allocator, &.{ std.fs.path.dirname(@src().file) orelse ".", "config.h" });
+ defer b.allocator.free(file);
+ const content = try std.fs.cwd().readFileAlloc(b.allocator, file, std.math.maxInt(usize));
+ defer b.allocator.free(content);
+
+ var lines = std.mem.splitScalar(u8, content, '\n');
+ while (lines.next()) |line| {
+ if (!std.mem.containsAtLeast(u8, line, 1, "SUPPORT")) continue;
+ if (std.mem.startsWith(u8, line, "//")) continue;
+
+ var flag = std.mem.trimLeft(u8, line, " \t"); // Trim whitespace
+ flag = flag["#define ".len - 1 ..]; // Remove #define
+ flag = std.mem.trimLeft(u8, flag, " \t"); // Trim whitespace
+ flag = flag[0..std.mem.indexOf(u8, flag, " ").?]; // Flag is only one word, so capture till space
+ flag = try std.fmt.allocPrint(b.allocator, "-D{s}", .{flag}); // Prepend with -D
+
+ // If user specifies the flag skip it
+ if (std.mem.containsAtLeast(u8, config, 1, flag)) continue;
+
+ // Append default value from config.h to compile flags
+ try raylib_flags_arr.append(b.allocator, flag);
+ }
+
+ // Append config flags supplied by user to compile flags
+ try raylib_flags_arr.append(b.allocator, config);
+
+ try raylib_flags_arr.append(b.allocator, "-DEXTERNAL_CONFIG_FLAGS");
+ }
+
if (options.shared) {
try raylib_flags_arr.appendSlice(b.allocator, shared_flags);
}
@@ -118,12 +149,18 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
raylib.addLibraryPath(.{ .cwd_relative = "/usr/lib" });
raylib.addIncludePath(.{ .cwd_relative = "/usr/include" });
if (options.linux_display_backend == .X11 or options.linux_display_backend == .Both) {
-
- raylib.defineCMacro("_GLFW_X11", null);
- raylib.linkSystemLibrary("X11");
+ raylib.defineCMacro("_GLFW_X11", null);
+ raylib.linkSystemLibrary("X11");
}
if (options.linux_display_backend == .Wayland or options.linux_display_backend == .Both) {
+ _ = b.findProgram(&.{"wayland-scanner"}, &.{}) catch {
+ std.log.err(
+ \\ Wayland may not be installed on the system.
+ \\ You can switch to X11 in your `build.zig` by changing `Options.linux_display_backend`
+ , .{});
+ @panic("No Wayland");
+ };
raylib.defineCMacro("_GLFW_WAYLAND", null);
raylib.linkSystemLibrary("wayland-client");
raylib.linkSystemLibrary("wayland-cursor");
@@ -253,6 +290,7 @@ pub const Options = struct {
shared: bool = false,
linux_display_backend: LinuxDisplayBackend = .Both,
opengl_version: OpenglVersion = .auto,
+ config: ?[]const u8 = null,
raygui_dependency_name: []const u8 = "raygui",
};
@@ -307,6 +345,7 @@ pub fn build(b: *std.Build) !void {
.shared = b.option(bool, "shared", "Compile as shared library") orelse defaults.shared,
.linux_display_backend = b.option(LinuxDisplayBackend, "linux_display_backend", "Linux display backend to use") orelse defaults.linux_display_backend,
.opengl_version = b.option(OpenglVersion, "opengl_version", "OpenGL version to use") orelse defaults.opengl_version,
+ .config = b.option([]const u8, "config", "Compile with custom define macros overriding config.h") orelse null,
};
const lib = try compileRaylib(b, target, optimize, options);
diff --git a/src/external/jar_xm.h b/src/external/jar_xm.h
index 4a1bfbf67..b5e80e48a 100644
--- a/src/external/jar_xm.h
+++ b/src/external/jar_xm.h
@@ -1204,7 +1204,7 @@ static void jar_xm_tone_portamento(jar_xm_context_t* ctx, jar_xm_channel_context
}
static void jar_xm_pitch_slide(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, float period_offset) {
- /* Don't ask about the 4.f coefficient. I found mention of it nowhere. Found by ear™. */
+ /* Don't ask about the 4.f coefficient. I found mention of it nowhere. Found by ear. */
if(ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES) {period_offset *= 4.f; }
ch->period += period_offset;
jar_xm_CLAMP_DOWN(ch->period);
@@ -1507,7 +1507,7 @@ static void jar_xm_handle_note_and_instrument(jar_xm_context_t* ctx, jar_xm_chan
jar_xm_volume_slide(ch, ch->fine_volume_slide_param);
break;
case 0xD: /* EDy: Note delay */
- /* XXX: figure this out better. EDx triggers the note even when there no note and no instrument. But ED0 acts like like a ghost note, EDx (x ≠ 0) does not. */
+ /* XXX: figure this out better. EDx triggers the note even when there no note and no instrument. But ED0 acts like like a ghost note, EDx (x != 0) does not. */
if(s->note == 0 && s->instrument == 0) {
unsigned int flags = jar_xm_TRIGGER_KEEP_VOLUME;
if(ch->current->effect_param & 0x0F) {
@@ -1795,7 +1795,7 @@ static void jar_xm_tick(jar_xm_context_t* ctx) {
if(ch->current->effect_param > 0) {
char arp_offset = ctx->tempo % 3;
switch(arp_offset) {
- case 2: /* 0 -> x -> 0 -> y -> x -> … */
+ case 2: /* 0 -> x -> 0 -> y -> x -> ... */
if(ctx->current_tick == 1) {
ch->arp_in_progress = true;
ch->arp_note_offset = ch->current->effect_param >> 4;
@@ -1803,7 +1803,7 @@ static void jar_xm_tick(jar_xm_context_t* ctx) {
break;
}
/* No break here, this is intended */
- case 1: /* 0 -> 0 -> y -> x -> … */
+ case 1: /* 0 -> 0 -> y -> x -> ... */
if(ctx->current_tick == 0) {
ch->arp_in_progress = false;
ch->arp_note_offset = 0;
@@ -1811,7 +1811,7 @@ static void jar_xm_tick(jar_xm_context_t* ctx) {
break;
}
/* No break here, this is intended */
- case 0: /* 0 -> y -> x -> … */
+ case 0: /* 0 -> y -> x -> ... */
jar_xm_arpeggio(ctx, ch, ch->current->effect_param, ctx->current_tick - arp_offset);
default:
break;
diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h
index 787c626ad..ad113337e 100644
--- a/src/external/miniaudio.h
+++ b/src/external/miniaudio.h
@@ -36078,9 +36078,15 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext
ma_uint32 channels;
ma_uint32 sampleRate;
+#ifdef __NetBSD__
+ if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) {
+ return MA_ERROR;
+ }
+#else
if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
return MA_ERROR;
}
+#endif
if (deviceType == ma_device_type_playback) {
channels = fdInfo.play.channels;
@@ -36358,7 +36364,11 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c
/* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */
int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0);
if (fdctl != -1) {
+#ifdef __NetBSD__
+ fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo);
+#else
fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo);
+#endif
close(fdctl);
}
}
diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c
index d03bac153..d64bf60db 100644
--- a/src/platforms/rcore_desktop_glfw.c
+++ b/src/platforms/rcore_desktop_glfw.c
@@ -1275,6 +1275,11 @@ int InitPlatform(void)
//glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
//glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
+ // Disable GlFW auto iconify behaviour
+ // Auto Iconify automatically minimizes (iconifies) the window if the window loses focus
+ // additionally auto iconify restores the hardware resolution of the monitor if the window that loses focus is a fullscreen window
+ glfwWindowHint(GLFW_AUTO_ICONIFY, 0);
+
// Check window creation flags
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
@@ -1454,11 +1459,6 @@ int InitPlatform(void)
// No-fullscreen window creation
bool requestWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0);
- // If we are windowed fullscreen, ensures that window does not minimize when focus is lost.
- // This hinting code will not work if the user already specified the correct monitor dimensions;
- // at this point we don't know the monitor's dimensions. (Though, how did the user then?)
- if (requestWindowedFullscreen) glfwWindowHint(GLFW_AUTO_ICONIFY, 0);
-
// Default to at least one pixel in size, as creation with a zero dimension is not allowed.
int creationWidth = CORE.Window.screen.width != 0 ? CORE.Window.screen.width : 1;
int creationHeight = CORE.Window.screen.height != 0 ? CORE.Window.screen.height : 1;
@@ -1630,15 +1630,16 @@ int InitPlatform(void)
char *glfwPlatform = "";
switch (glfwGetPlatform())
{
- case GLFW_PLATFORM_WIN32: glfwPlatform = "Win32"; break;
- case GLFW_PLATFORM_COCOA: glfwPlatform = "Cocoa"; break;
+ case GLFW_PLATFORM_WIN32: glfwPlatform = "Win32"; break;
+ case GLFW_PLATFORM_COCOA: glfwPlatform = "Cocoa"; break;
case GLFW_PLATFORM_WAYLAND: glfwPlatform = "Wayland"; break;
- case GLFW_PLATFORM_X11: glfwPlatform = "X11"; break;
- case GLFW_PLATFORM_NULL: glfwPlatform = "Null"; break;
+ case GLFW_PLATFORM_X11: glfwPlatform = "X11"; break;
+ case GLFW_PLATFORM_NULL: glfwPlatform = "Null"; break;
+ default: break;
}
#endif
- TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully");
+ TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW - %s): Initialized successfully", glfwPlatform);
return 0;
}
diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c
index 937e15acd..ff7a92dbc 100644
--- a/src/platforms/rcore_web.c
+++ b/src/platforms/rcore_web.c
@@ -129,7 +129,7 @@ static void CursorEnterCallback(GLFWwindow *window, int enter);
// Emscripten window callback events
static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
-static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
+// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
// Emscripten input callback events
@@ -981,7 +981,7 @@ void PollInputEvents(void)
default: break;
}
- if (button != -1) // Check for valid button
+ if (button + 1 != 0) // Check for valid button
{
if (gamepadState.digitalButton[j] == 1)
{
@@ -1572,12 +1572,12 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte
}
// Register window resize event
-static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
-{
- // TODO: Implement EmscriptenWindowResizedCallback()?
+// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
+// {
+// // TODO: Implement EmscriptenWindowResizedCallback()?
- return 1; // The event was consumed by the callback handler
-}
+// return 1; // The event was consumed by the callback handler
+// }
EM_JS(int, GetWindowInnerWidth, (), { return window.innerWidth; });
EM_JS(int, GetWindowInnerHeight, (), { return window.innerHeight; });
@@ -1593,11 +1593,11 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *
int width = GetWindowInnerWidth();
int height = GetWindowInnerHeight();
- if (width < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width;
- else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width;
+ if (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width;
+ else if (width > (int)CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width;
- if (height < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height;
- else if (height > CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height;
+ if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height;
+ else if (height > (int)CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height;
emscripten_set_canvas_element_size("#canvas", width, height);
diff --git a/src/rcamera.h b/src/rcamera.h
index d3042d306..c7f6fed3a 100644
--- a/src/rcamera.h
+++ b/src/rcamera.h
@@ -196,12 +196,12 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect);
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define CAMERA_MOVE_SPEED 0.09f
+#define CAMERA_MOVE_SPEED 5.4f // Units per second
#define CAMERA_ROTATION_SPEED 0.03f
#define CAMERA_PAN_SPEED 0.2f
// Camera mouse movement sensitivity
-#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate
+#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f
// Camera orbital speed in CAMERA_ORBITAL mode
#define CAMERA_ORBITAL_SPEED 0.5f // Radians per second
@@ -481,10 +481,11 @@ void UpdateCamera(Camera *camera, int mode)
}
// Keyboard support
- if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
- if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
- if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
- if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
+ float cameraMoveSpeed = CAMERA_MOVE_SPEED*GetFrameTime();
+ if (IsKeyDown(KEY_W)) CameraMoveForward(camera, cameraMoveSpeed, moveInWorldPlane);
+ if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -cameraMoveSpeed, moveInWorldPlane);
+ if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -cameraMoveSpeed, moveInWorldPlane);
+ if (IsKeyDown(KEY_D)) CameraMoveRight(camera, cameraMoveSpeed, moveInWorldPlane);
// Gamepad movement
if (IsGamepadAvailable(0))
@@ -493,16 +494,16 @@ void UpdateCamera(Camera *camera, int mode)
CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X)*2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget);
CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y)*2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp);
- if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
- if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
- if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.25f) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane);
- if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
+ if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, cameraMoveSpeed, moveInWorldPlane);
+ if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -cameraMoveSpeed, moveInWorldPlane);
+ if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.25f) CameraMoveForward(camera, -cameraMoveSpeed, moveInWorldPlane);
+ if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, cameraMoveSpeed, moveInWorldPlane);
}
if (mode == CAMERA_FREE)
{
- if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED);
- if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED);
+ if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, cameraMoveSpeed);
+ if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -cameraMoveSpeed);
}
}
diff --git a/src/rcore.c b/src/rcore.c
index fb71da0f6..5485ce8ce 100644
--- a/src/rcore.c
+++ b/src/rcore.c
@@ -367,9 +367,9 @@ static int screenshotCounter = 0; // Screenshots counter
#endif
#if defined(SUPPORT_GIF_RECORDING)
-unsigned int gifFrameCounter = 0; // GIF frames counter
-bool gifRecording = false; // GIF recording state
-MsfGifState gifState = { 0 }; // MSGIF context state
+static unsigned int gifFrameCounter = 0; // GIF frames counter
+static bool gifRecording = false; // GIF recording state
+static MsfGifState gifState = { 0 }; // MSGIF context state
#endif
#if defined(SUPPORT_AUTOMATION_EVENTS)
@@ -668,15 +668,7 @@ void InitWindow(int width, int height, const char *title)
SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes
#endif
#endif
-#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
- if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
- {
- // Set default font texture filter for HighDPI (blurry)
- // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
- rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR);
- rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
- }
-#endif
+
CORE.Time.frameCounter = 0;
CORE.Window.shouldClose = false;
@@ -2960,10 +2952,14 @@ int GetGamepadAxisCount(int gamepad)
// Get axis movement vector for a gamepad
float GetGamepadAxisMovement(int gamepad, int axis)
{
- float value = 0;
+ float value = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
- if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) &&
- (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis]; // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA
+ if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS)) {
+ float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]);
+
+ // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA
+ if (movement > value + 0.1f) value = CORE.Input.Gamepad.axisState[gamepad][axis];
+ }
return value;
}
@@ -3607,7 +3603,8 @@ static void RecordAutomationEvent(void)
for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
{
// Event type: INPUT_GAMEPAD_AXIS_MOTION
- if (CORE.Input.Gamepad.axisState[gamepad][axis] > 0.1f)
+ float defaultMovement = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
+ if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement)
{
currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION;
diff --git a/src/rlgl.h b/src/rlgl.h
index ccb53a624..5fb9497cd 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -3994,18 +3994,18 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
unsigned int fragmentShaderId = 0;
// Compile vertex shader (if provided)
+ // NOTE: If not vertex shader is provided, use default one
if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER);
- // In case no vertex shader was provided or compilation failed, we use default vertex shader
- if (vertexShaderId == 0) vertexShaderId = RLGL.State.defaultVShaderId;
+ else vertexShaderId = RLGL.State.defaultVShaderId;
// Compile fragment shader (if provided)
+ // NOTE: If not vertex shader is provided, use default one
if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER);
- // In case no fragment shader was provided or compilation failed, we use default fragment shader
- if (fragmentShaderId == 0) fragmentShaderId = RLGL.State.defaultFShaderId;
+ else fragmentShaderId = RLGL.State.defaultFShaderId;
// In case vertex and fragment shader are the default ones, no need to recompile, we can just assign the default shader program id
if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId;
- else
+ else if ((vertexShaderId > 0) && (fragmentShaderId > 0))
{
// One of or both shader are new, we need to compile a new shader program
id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId);
@@ -4083,6 +4083,8 @@ unsigned int rlCompileShader(const char *shaderCode, int type)
//case GL_GEOMETRY_SHADER:
#if defined(GRAPHICS_API_OPENGL_43)
case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile compute shader code", shader); break;
+ #elif defined(GRAPHICS_API_OPENGL_33)
+ case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break;
#endif
default: break;
}
@@ -4098,6 +4100,8 @@ unsigned int rlCompileShader(const char *shaderCode, int type)
TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shader, log);
RL_FREE(log);
}
+
+ shader = 0;
}
else
{
@@ -4108,6 +4112,8 @@ unsigned int rlCompileShader(const char *shaderCode, int type)
//case GL_GEOMETRY_SHADER:
#if defined(GRAPHICS_API_OPENGL_43)
case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader compiled successfully", shader); break;
+ #elif defined(GRAPHICS_API_OPENGL_33)
+ case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break;
#endif
default: break;
}
@@ -4348,6 +4354,8 @@ unsigned int rlLoadComputeShaderProgram(unsigned int shaderId)
TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", program);
}
+#else
+ TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43");
#endif
return program;
@@ -4372,6 +4380,8 @@ unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHi
glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY);
if (data == NULL) glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL); // Clear buffer data to 0
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+#else
+ TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43");
#endif
return ssbo;
@@ -4382,7 +4392,10 @@ void rlUnloadShaderBuffer(unsigned int ssboId)
{
#if defined(GRAPHICS_API_OPENGL_43)
glDeleteBuffers(1, &ssboId);
+#else
+ TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43");
#endif
+
}
// Update SSBO buffer data
@@ -4397,7 +4410,7 @@ void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSi
// Get SSBO buffer size
unsigned int rlGetShaderBufferSize(unsigned int id)
{
- long long size = 0;
+ GLint64 size = 0;
#if defined(GRAPHICS_API_OPENGL_43)
glBindBuffer(GL_SHADER_STORAGE_BUFFER, id);
@@ -4442,6 +4455,8 @@ void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool re
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
glBindImageTexture(index, id, 0, 0, 0, readonly? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat);
+#else
+ TRACELOG(RL_LOG_WARNING, "TEXTURE: Image texture binding not enabled. Define GRAPHICS_API_OPENGL_43");
#endif
}
@@ -4756,7 +4771,16 @@ static void rlLoadShaderDefault(void)
"out vec2 fragTexCoord; \n"
"out vec4 fragColor; \n"
#endif
-#if defined(GRAPHICS_API_OPENGL_ES2)
+
+#if defined(GRAPHICS_API_OPENGL_ES3)
+ "#version 300 es \n"
+ "precision mediump float; \n" // Precision required for OpenGL ES3 (WebGL 2) (on some browsers)
+ "in vec3 vertexPosition; \n"
+ "in vec2 vertexTexCoord; \n"
+ "in vec4 vertexColor; \n"
+ "out vec2 fragTexCoord; \n"
+ "out vec4 fragColor; \n"
+#elif defined(GRAPHICS_API_OPENGL_ES2)
"#version 100 \n"
"precision mediump float; \n" // Precision required for OpenGL ES2 (WebGL) (on some browsers)
"attribute vec3 vertexPosition; \n"
@@ -4765,6 +4789,7 @@ static void rlLoadShaderDefault(void)
"varying vec2 fragTexCoord; \n"
"varying vec4 fragColor; \n"
#endif
+
"uniform mat4 mvp; \n"
"void main() \n"
"{ \n"
@@ -4799,7 +4824,21 @@ static void rlLoadShaderDefault(void)
" finalColor = texelColor*colDiffuse*fragColor; \n"
"} \n";
#endif
-#if defined(GRAPHICS_API_OPENGL_ES2)
+
+#if defined(GRAPHICS_API_OPENGL_ES3)
+ "#version 300 es \n"
+ "precision mediump float; \n" // Precision required for OpenGL ES3 (WebGL 2)
+ "in vec2 fragTexCoord; \n"
+ "in vec4 fragColor; \n"
+ "out vec4 finalColor; \n"
+ "uniform sampler2D texture0; \n"
+ "uniform vec4 colDiffuse; \n"
+ "void main() \n"
+ "{ \n"
+ " vec4 texelColor = texture(texture0, fragTexCoord); \n"
+ " finalColor = texelColor*colDiffuse*fragColor; \n"
+ "} \n";
+#elif defined(GRAPHICS_API_OPENGL_ES2)
"#version 100 \n"
"precision mediump float; \n" // Precision required for OpenGL ES2 (WebGL)
"varying vec2 fragTexCoord; \n"
@@ -4969,7 +5008,8 @@ static int rlGetPixelDataSize(int width, int height, int format)
default: break;
}
- dataSize = width*height*bpp/8; // Total data size in bytes
+ double bytesPerPixel = (double)bpp/8.0;
+ dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
// Most compressed formats works on 4x4 blocks,
// if texture is smaller, minimum dataSize is 8 or 16
diff --git a/src/rmodels.c b/src/rmodels.c
index c1d56765e..36a7e254d 100644
--- a/src/rmodels.c
+++ b/src/rmodels.c
@@ -5741,11 +5741,11 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_
cgltf_accessor_read_float(output, 3*keyframe+1, tmp, 4);
Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]};
cgltf_accessor_read_float(output, 3*keyframe+2, tmp, 4);
- Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2]};
+ Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2], 0.0f};
cgltf_accessor_read_float(output, 3*(keyframe+1)+1, tmp, 4);
Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]};
cgltf_accessor_read_float(output, 3*(keyframe+1), tmp, 4);
- Vector4 inTangent2 = {tmp[0], tmp[1], tmp[2]};
+ Vector4 inTangent2 = {tmp[0], tmp[1], tmp[2], 0.0f};
Vector4 *r = data;
v1 = QuaternionNormalize(v1);
diff --git a/src/rshapes.c b/src/rshapes.c
index 89339708a..676b2521a 100644
--- a/src/rshapes.c
+++ b/src/rshapes.c
@@ -79,8 +79,8 @@
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
-Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (white pixel loaded by rlgl)
-Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing
+static Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (white pixel loaded by rlgl)
+static Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
diff --git a/src/rtextures.c b/src/rtextures.c
index d70e2cde8..5fa01c9df 100644
--- a/src/rtextures.c
+++ b/src/rtextures.c
@@ -503,7 +503,16 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
Image image = { 0 };
// Security check for input data
- if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image;
+ if ((fileData == NULL) || (dataSize == 0))
+ {
+ TRACELOG(LOG_WARNING, "IMAGE: Invalid file data");
+ return image;
+ }
+ if (fileType == NULL)
+ {
+ TRACELOG(LOG_WARNING, "IMAGE: Missing file extension");
+ return image;
+ }
if ((false)
#if defined(SUPPORT_FILEFORMAT_PNG)
@@ -5394,7 +5403,8 @@ int GetPixelDataSize(int width, int height, int format)
default: break;
}
- dataSize = width*height*bpp/8; // Total data size in bytes
+ double bytesPerPixel = (double)bpp/8.0;
+ dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
// Most compressed formats works on 4x4 blocks,
// if texture is smaller, minimum dataSize is 8 or 16